no message

zik-webrtc
Zohaib Iqbal Kambrani 3 years ago
parent a37cd47edb
commit b21e71ee16

@ -79,8 +79,8 @@ android {
// productionReleaseImplementation
// }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}

@ -5,8 +5,8 @@ const ONLY_NUMBERS = "[0-9]";
const ONLY_LETTERS = "[a-zA-Z &'\"]";
const ONLY_DATE = "[0-9/]";
const BASE_URL_LIVE_CARE = 'https://livecare.hmg.com/';
// const BASE_URL = 'https://hmgwebservices.com/';
const BASE_URL = 'https://uat.hmgwebservices.com/';
const BASE_URL = 'https://hmgwebservices.com/';
// const BASE_URL = 'https://uat.hmgwebservices.com/';
const PHARMACY_ITEMS_URL = "Services/Lists.svc/REST/GetPharmcyItems_Region_enh";
const PHARMACY_LIST_URL = "Services/Patients.svc/REST/GetPharmcyList";
const PATIENT_PROGRESS_NOTE_URL =
@ -436,7 +436,7 @@ const TRANSACTION_NO = 0;
const LANGUAGE_ID = 2;
const STAMP = '2020-04-27T12:17:17.721Z';
const IP_ADDRESS = '9.9.9.9';
const VERSION_ID = 6.4;
const VERSION_ID = 6.7;
const CHANNEL = 9;
const SESSION_ID = 'BlUSkYymTt';
const IS_LOGIN_FOR_DOCTOR_APP = true;

@ -148,7 +148,7 @@ class AuthenticationViewModel extends BaseViewModel {
iMEI: user!.iMEI,
facilityId: user!.projectID,
memberID: user!.doctorID,
loginDoctorID: int.parse(user!.editedBy.toString()),
loginDoctorID: int.tryParse(user!.editedBy.toString()),
zipCode: user!.outSA == true ? '971' : '966',
mobileNumber: user!.mobile,
oTPSendType: authMethodType.getTypeIdService(),
@ -158,7 +158,7 @@ class AuthenticationViewModel extends BaseViewModel {
await _authService
.sendActivationCodeVerificationScreen(activationCodeModel);
if (_authService.hasError) {
error = _authService.error!;
error = _authService.error;
setState(ViewState.ErrorLocal);
} else
setState(ViewState.Idle);

@ -227,9 +227,7 @@ class _VerificationMethodsScreenState extends State<VerificationMethodsScreen> {
.user!
.editedOn!),
isMonthShort: true)
: authenticationViewModel
.user!.createdOn! !=
null
: authenticationViewModel.user!.createdOn != null
? AppDateUtils.getDayMonthYearDateFormatted(
AppDateUtils
.convertStringToDate(
@ -247,16 +245,12 @@ class _VerificationMethodsScreenState extends State<VerificationMethodsScreen> {
letterSpacing: -0.48,
),
AppText(
authenticationViewModel
.user!.editedOn! !=
null
authenticationViewModel.user!.editedOn != null
? AppDateUtils.getHour(AppDateUtils
.convertStringToDate(
authenticationViewModel
.user!.editedOn!))
: authenticationViewModel
.user!.createdOn! !=
null
: authenticationViewModel.user!.createdOn != null
? AppDateUtils.getHour(
AppDateUtils
.convertStringToDate(

@ -13,6 +13,7 @@ import 'package:doctor_app_flutter/screens/home/dashboard_slider-item-widget.dar
import 'package:doctor_app_flutter/screens/home/dashboard_swipe_widget.dart';
import 'package:doctor_app_flutter/screens/home/home_patient_card.dart';
import 'package:doctor_app_flutter/screens/live_care/live_care_patient_screen.dart';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/web_rtc/call_home_page_.dart';
import 'package:doctor_app_flutter/screens/medicine/medicine_search_screen.dart';
import 'package:doctor_app_flutter/screens/patients/In_patient/in_patient_screen.dart';
import 'package:doctor_app_flutter/screens/patients/out_patient/out_patient_screen.dart';
@ -35,7 +36,6 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:sticky_headers/sticky_headers/widget.dart';
import '../../routes.dart';
import '../../widgets/shared/app_texts_widget.dart';
import 'label.dart';
@ -396,10 +396,19 @@ class _HomeScreenState extends State<HomeScreen> {
// "isSearchAndOut": false,
// "isFromLiveCare": true,
// });
// /* */
// Navigator.push(
// context,
// FadePage(
// page: LiveCarePatientScreen(),
// ),
// );
Navigator.push(
context,
FadePage(
page: LiveCarePatientScreen(),
page: CallHomePage(callerId: "ZohaibCaller", receiverId: "12340987"),
),
);
},

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
class ClippedVideo extends StatefulWidget {
final double width;
final double height;
final Widget child;
const ClippedVideo({
Key? key,
required this.width,
required this.height,
required this.child,
}) : super(key: key);
@override
_ClippedVideoState createState() => _ClippedVideoState();
}
class _ClippedVideoState extends State<ClippedVideo> {
@override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.height,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(20)),
border: Border.all(
color: Colors.white24,
),
),
child: ClipRRect(
child: widget.child,
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
);
}
}

@ -0,0 +1,237 @@
import 'dart:async';
import 'package:after_layout/after_layout.dart';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/widgets/circle_button.dart';
import 'package:flutter/material.dart';
class ConferenceButtonBar extends StatefulWidget {
final VoidCallback onVideoEnabled;
final VoidCallback onAudioEnabled;
final VoidCallback onHangup;
final VoidCallback onSwitchCamera;
final VoidCallback onPersonAdd;
final VoidCallback onPersonRemove;
final void Function(double) onHeight;
final VoidCallback onHide;
final VoidCallback onShow;
final Stream<bool> videoEnabled;
final Stream<bool> audioEnabled;
const ConferenceButtonBar({
Key? key,
required this.onVideoEnabled,
required this.onAudioEnabled,
required this.onHangup,
required this.onSwitchCamera,
required this.onPersonAdd,
required this.onPersonRemove,
required this.videoEnabled,
required this.audioEnabled,
required this.onHeight,
required this.onHide,
required this.onShow,
}) : assert(videoEnabled != null),
assert(audioEnabled != null),
super(key: key);
@override
_ConferenceButtonBarState createState() => _ConferenceButtonBarState();
}
class _ConferenceButtonBarState extends State<ConferenceButtonBar> with AfterLayoutMixin<ConferenceButtonBar> {
var _bottom = -100.0;
late Timer? _timer;
late int _remaining;
var _videoEnabled = true;
var _audioEnabled = true;
late double _hidden;
late double _visible;
final _keyButtonBarHeight = GlobalKey();
final Duration timeout = const Duration(seconds: 5);
final Duration ms = const Duration(milliseconds: 1);
final Duration periodicDuration = const Duration(milliseconds: 100);
Timer startTimeout([int? milliseconds]) {
final duration = milliseconds == null ? timeout : ms * milliseconds;
_remaining = duration.inMilliseconds;
return Timer.periodic(periodicDuration, (Timer timer) {
_remaining -= periodicDuration.inMilliseconds;
if (_remaining <= 0) {
timer.cancel();
_toggleBar();
}
});
}
void _pauseTimer() {
if (_timer == null) {
return;
}
_timer?.cancel();
_timer = null;
}
void _resumeTimer() {
// resume the timer only when there is no timer active or when
// the bar is not already hidden.
if ((_timer != null && _timer!.isActive) || _bottom == _hidden) {
return;
}
_timer = startTimeout(_remaining);
}
void _toggleBar() {
setState(() {
_bottom = _bottom == _visible ? _hidden : _visible;
if (_bottom == _visible && widget.onShow != null) {
widget.onShow();
}
if (_bottom == _hidden && widget.onHide != null) {
widget.onHide();
}
});
}
void _toggleBarOnEnd() {
if (_timer != null) {
if (_timer!.isActive) {
_timer!.cancel();
}
_timer = null;
}
if (_bottom == 0) {
_timer = startTimeout();
}
}
@override
void initState() {
super.initState();
_timer = startTimeout();
}
@override
void didChangeDependencies() {
_visible = MediaQuery.of(context).viewPadding.bottom;
super.didChangeDependencies();
}
@override
void afterFirstLayout(BuildContext context) {
final RenderBox? renderBoxButtonBar = _keyButtonBarHeight.currentContext?.findRenderObject() as RenderBox?;
final heightButtonBar = renderBoxButtonBar?.size.height ?? 0.0;
// Because the `didChangeDependencies` fires before the `afterFirstLayout`, we can use the `_visible` property here.
_hidden = -(heightButtonBar + _visible);
widget.onHeight(heightButtonBar);
_toggleBar();
}
@override
void dispose() {
super.dispose();
if (_timer != null && _timer!.isActive) {
_timer?.cancel();
_timer = null;
}
}
@override
Widget build(BuildContext context) {
return Positioned(
top: 0,
left: 0,
right: 0,
bottom: 0,
child: GestureDetector(
key: Key('show-hide-button-bar-gesture'),
behavior: HitTestBehavior.translucent,
onTapDown: (_) => _pauseTimer(),
onTapUp: (_) => _toggleBar(),
onTapCancel: () => _resumeTimer(),
child: Stack(
children: <Widget>[
AnimatedPositioned(
key: Key('button-bar'),
bottom: _bottom,
left: 0,
right: 0,
duration: const Duration(milliseconds: 300),
curve: Curves.linear,
child: _buildRow(context),
onEnd: _toggleBarOnEnd,
),
],
),
),
);
}
void _onPressed(VoidCallback callback) {
if (callback != null) {
callback();
}
if (_timer != null && _timer!.isActive) {
_timer?.cancel();
}
_timer = startTimeout();
}
Widget _buildRow(BuildContext context) {
return Padding(
key: _keyButtonBarHeight,
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
CircleButton(
child: StreamBuilder<bool>(
stream: widget.videoEnabled,
initialData: _videoEnabled,
builder: (context, snapshot) {
_videoEnabled = snapshot.data!;
return Icon(
_videoEnabled ? Icons.videocam : Icons.videocam_off,
color: Colors.white,
);
}),
key: Key('camera-button'),
onPressed: () => _onPressed(widget.onVideoEnabled),
),
CircleButton(
child: StreamBuilder<bool>(
stream: widget.audioEnabled,
initialData: _audioEnabled,
builder: (context, snapshot) {
_audioEnabled = snapshot.data!;
return Icon(
_audioEnabled ? Icons.mic : Icons.mic_off,
color: Colors.white,
);
}),
key: Key('microphone-button'),
onPressed: () => _onPressed(widget.onAudioEnabled),
),
CircleButton(
child: const Icon(Icons.switch_camera, color: Colors.white),
key: Key('switch-camera-button'),
onPressed: () => _onPressed(widget.onSwitchCamera),
),
CircleButton(
radius: 35,
child: const RotationTransition(
turns: AlwaysStoppedAnimation<double>(135 / 360),
child: Icon(
Icons.phone,
color: Colors.white,
size: 40,
),
),
color: Colors.red.withAlpha(200),
key: Key('hangup-button'),
onPressed: () => _onPressed(widget.onHangup),
),
],
),
);
}
}

@ -0,0 +1,390 @@
// import 'dart:async';
//
// import 'package:doctor_app_flutter/models/LiveCare/room_model.dart';
// import 'package:doctor_app_flutter/pages/conference/conference_button_bar.dart';
// import 'package:doctor_app_flutter/pages/conference/conference_room.dart';
// import 'package:doctor_app_flutter/pages/landing/landing_page.dart';
// import 'package:doctor_app_flutter/pages/conference/draggable_publisher.dart';
// import 'package:doctor_app_flutter/pages/conference/participant_widget.dart';
// import 'package:doctor_app_flutter/pages/conference/widgets/noise_box.dart';
// import 'package:doctor_app_flutter/pages/conference/widgets/platform_alert_dialog.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter/services.dart';
// import 'package:wakelock/wakelock.dart';
//
// class ConferencePage extends StatefulWidget {
// final RoomModel roomModel;
//
// const ConferencePage({Key key, this.roomModel}) : super(key: key);
//
// @override
// _ConferencePageState createState() => _ConferencePageState();
// }
//
// class _ConferencePageState extends State<ConferencePage> {
// final StreamController<bool> _onButtonBarVisibleStreamController = StreamController<bool>.broadcast();
// final StreamController<double> _onButtonBarHeightStreamController = StreamController<double>.broadcast();
// // ConferenceRoom _conferenceRoom;
// StreamSubscription _onConferenceRoomException;
//
// @override
// void initState() {
// super.initState();
// _lockInPortrait();
// _connectToRoom();
// _wakeLock(true);
// }
//
// void _connectToRoom() async {
// try {
// final conferenceRoom = ConferenceRoom(
// name: widget.roomModel.name,
// token: widget.roomModel.token,
// identity: widget.roomModel.identity,
// );
// await conferenceRoom.connect();
// setState(() {
// _conferenceRoom = conferenceRoom;
// _onConferenceRoomException = _conferenceRoom.onException.listen((err) async {
// await PlatformAlertDialog(
// title: err is PlatformException ? err.message : 'An error occured',
// content: err is PlatformException ? err.details : err.toString(),
// defaultActionText: 'OK',
// ).show(context);
// });
// _conferenceRoom.addListener(_conferenceRoomUpdated);
// });
// } catch (err) {
// print(err);
// await PlatformAlertDialog(
// title: err is PlatformException ? err.message : 'An error occured',
// content: err is PlatformException ? err.details : err.toString(),
// defaultActionText: 'OK',
// ).show(context);
//
// Navigator.of(context).pop();
// }
// }
//
// Future<void> _lockInPortrait() async {
// await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ]);
// }
//
// @override
// void dispose() {
// _freePortraitLock();
// _wakeLock(false);
// _disposeStreamsAndSubscriptions();
// if (_conferenceRoom != null) _conferenceRoom.removeListener(_conferenceRoomUpdated);
// super.dispose();
// }
//
// Future<void> _freePortraitLock() async {
// await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
// DeviceOrientation.landscapeRight,
// DeviceOrientation.landscapeLeft,
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ]);
// }
//
// Future<void> _disposeStreamsAndSubscriptions() async {
// if (_onButtonBarVisibleStreamController != null) await _onButtonBarVisibleStreamController.close();
// if (_onButtonBarHeightStreamController != null) await _onButtonBarHeightStreamController.close();
// if (_onConferenceRoomException != null) await _onConferenceRoomException.cancel();
// }
//
// @override
// Widget build(BuildContext context) {
// return WillPopScope(
// onWillPop: () async => false,
// child: Scaffold(
// backgroundColor: Colors.white,
// body: _conferenceRoom == null ? showProgress() : buildLayout(),
// ),
// );
// }
//
// LayoutBuilder buildLayout() {
// return LayoutBuilder(
// builder: (BuildContext context, BoxConstraints constraints) {
// return Stack(
// children: <Widget>[
// _buildParticipants(context, constraints.biggest, _conferenceRoom),
// ConferenceButtonBar(
// audioEnabled: _conferenceRoom.onAudioEnabled,
// videoEnabled: _conferenceRoom.onVideoEnabled,
// onAudioEnabled: _conferenceRoom.toggleAudioEnabled,
// onVideoEnabled: _conferenceRoom.toggleVideoEnabled,
// onHangup: _onHangup,
// onSwitchCamera: _conferenceRoom.switchCamera,
// onPersonAdd: _onPersonAdd,
// onPersonRemove: _onPersonRemove,
// onHeight: _onHeightBar,
// onShow: _onShowBar,
// onHide: _onHideBar,
// ),
// ],
// );
// },
// );
// }
//
// Widget showProgress() {
// return Column(
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: <Widget>[
// Center(child: CircularProgressIndicator()),
// SizedBox(
// height: 10,
// ),
// Text(
// 'Connecting to the call...',
// style: TextStyle(color: Colors.white),
// ),
// ],
// );
// }
//
// Future<void> _onHangup() async {
// print('onHangup');
// await _conferenceRoom.disconnect();
// LandingPage.isOpenCallPage = false;
// Navigator.of(context).pop();
// }
//
// void _onPersonAdd() {
// print('onPersonAdd');
// try {
// _conferenceRoom.addDummy(
// child: Stack(
// children: <Widget>[
// const Placeholder(),
// Center(
// child: Text(
// (_conferenceRoom.participants.length + 1).toString(),
// style: const TextStyle(
// shadows: <Shadow>[
// Shadow(
// blurRadius: 3.0,
// color: Color.fromARGB(255, 0, 0, 0),
// ),
// Shadow(
// blurRadius: 8.0,
// color: Color.fromARGB(255, 255, 255, 255),
// ),
// ],
// fontSize: 80,
// ),
// ),
// ),
// ],
// ),
// );
// } on PlatformException catch (err) {
// PlatformAlertDialog(
// title: err.message,
// content: err.details,
// defaultActionText: 'OK',
// ).show(context);
// }
// }
//
// void _onPersonRemove() {
// print('onPersonRemove');
// _conferenceRoom.removeDummy();
// }
//
// Widget _buildParticipants(BuildContext context, Size size, ConferenceRoom conferenceRoom) {
// final children = <Widget>[];
// final length = conferenceRoom.participants.length;
//
// if (length <= 2) {
// _buildOverlayLayout(context, size, children);
// return Stack(children: children);
// }
//
// void buildInCols(bool removeLocalBeforeChunking, bool moveLastOfEachRowToNextRow, int columns) {
// _buildLayoutInGrid(
// context,
// size,
// children,
// removeLocalBeforeChunking: removeLocalBeforeChunking,
// moveLastOfEachRowToNextRow: moveLastOfEachRowToNextRow,
// columns: columns,
// );
// }
//
// // if (length <= 3) {
// // buildInCols(true, false, 1);
// // } else if (length == 5) {
// // buildInCols(false, true, 2);
// // } else if (length <= 6 || length == 8) {
// // buildInCols(false, false, 2);
// // } else if (length == 7 || length == 9) {
// // buildInCols(true, false, 2);
// // } else if (length == 10) {
// // buildInCols(false, true, 3);
// // } else if (length == 13 || length == 16) {
// // buildInCols(true, false, 3);
// // } else if (length <= 18) {
// // buildInCols(false, false, 3);
// // }
//
// return Column(
// children: children,
// );
// }
//
// void _buildOverlayLayout(BuildContext context, Size size, List<Widget> children) {
// final participants = _conferenceRoom.participants;
// if (participants.length == 1) {
// children.add(_buildNoiseBox());
// } else {
// final remoteParticipant = participants.firstWhere((ParticipantWidget participant) => participant.isRemote, orElse: () => null);
// if (remoteParticipant != null) {
// children.add(remoteParticipant);
// }
// }
//
// final localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null);
// if (localParticipant != null) {
// children.add(DraggablePublisher(
// key: Key('publisher'),
// child: localParticipant,
// availableScreenSize: size,
// onButtonBarVisible: _onButtonBarVisibleStreamController.stream,
// onButtonBarHeight: _onButtonBarHeightStreamController.stream,
// ));
// }
// }
//
// void _buildLayoutInGrid(
// BuildContext context,
// Size size,
// List<Widget> children, {
// bool removeLocalBeforeChunking = false,
// bool moveLastOfEachRowToNextRow = false,
// int columns = 2,
// }) {
// final participants = _conferenceRoom.participants;
// ParticipantWidget localParticipant;
// if (removeLocalBeforeChunking) {
// localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null);
// if (localParticipant != null) {
// participants.remove(localParticipant);
// }
// }
// final chunkedParticipants = chunk(array: participants, size: columns);
// if (localParticipant != null) {
// chunkedParticipants.last.add(localParticipant);
// participants.add(localParticipant);
// }
//
// if (moveLastOfEachRowToNextRow) {
// for (var i = 0; i < chunkedParticipants.length - 1; i++) {
// var participant = chunkedParticipants[i].removeLast();
// chunkedParticipants[i + 1].insert(0, participant);
// }
// }
//
// for (final participantChunk in chunkedParticipants) {
// final rowChildren = <Widget>[];
// for (final participant in participantChunk) {
// rowChildren.add(
// Container(
// width: size.width / participantChunk.length,
// height: size.height / chunkedParticipants.length,
// child: participant,
// ),
// );
// }
// children.add(
// Container(
// height: size.height / chunkedParticipants.length,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: rowChildren,
// ),
// ),
// );
// }
// }
//
// NoiseBox _buildNoiseBox() {
// return NoiseBox(
// density: NoiseBoxDensity.xLow,
// backgroundColor: Colors.grey.shade900,
// child: Center(
// child: Container(
// color: Colors.black54,
// width: double.infinity,
// height: 40,
// child: Center(
// child: Text(
// 'Waiting for another participant to connect to the call...',
// key: Key('text-wait'),
// textAlign: TextAlign.center,
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// );
// }
//
// List<List<T>> chunk<T>({required List<T> array, required int size}) {
// final result = <List<T>>[];
// if (array.isEmpty || size <= 0) {
// return result;
// }
// var first = 0;
// var last = size;
// final totalLoop = array.length % size == 0 ? array.length ~/ size : array.length ~/ size + 1;
// for (var i = 0; i < totalLoop; i++) {
// if (last > array.length) {
// result.add(array.sublist(first, array.length));
// } else {
// result.add(array.sublist(first, last));
// }
// first = last;
// last = last + size;
// }
// return result;
// }
//
// void _onHeightBar(double height) {
// _onButtonBarHeightStreamController.add(height);
// }
//
// void _onShowBar() {
// setState(() {
// SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]);
// });
// _onButtonBarVisibleStreamController.add(true);
// }
//
// void _onHideBar() {
// setState(() {
// SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
// });
// _onButtonBarVisibleStreamController.add(false);
// }
//
// Future<void> _wakeLock(bool enable) async {
// try {
// return await (enable ? Wakelock.enable() : Wakelock.disable());
// } catch (err) {
// print('Unable to change the Wakelock and set it to $enable');
// print(err);
// }
// }
//
// void _conferenceRoomUpdated() {
// setState(() {});
// }
// }

@ -0,0 +1,529 @@
// import 'dart:async';
// import 'dart:typed_data';
//
// import 'package:doctor_app_flutter/pages/conference/participant_widget.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter/services.dart';
//
// class ConferenceRoom with ChangeNotifier {
// final String name;
// final String token;
// final String identity;
//
// final StreamController<bool> _onAudioEnabledStreamController = StreamController<bool>.broadcast();
// Stream<bool> onAudioEnabled;
// final StreamController<bool> _onVideoEnabledStreamController = StreamController<bool>.broadcast();
// Stream<bool> onVideoEnabled;
// final StreamController<Exception> _onExceptionStreamController = StreamController<Exception>.broadcast();
// Stream<Exception> onException;
//
// final Completer<Room> _completer = Completer<Room>();
//
// final List<ParticipantWidget> _participants = [];
// final List<ParticipantBuffer> _participantBuffer = [];
// final List<StreamSubscription> _streamSubscriptions = [];
// final List<RemoteDataTrack> _dataTracks = [];
// final List<String> _messages = [];
//
// CameraCapturer _cameraCapturer;
// Room _room;
// Timer _timer;
//
// ConferenceRoom({
// required this.name,
// required this.token,
// required this.identity,
// }) {
// onAudioEnabled = _onAudioEnabledStreamController.stream;
// onVideoEnabled = _onVideoEnabledStreamController.stream;
// onException = _onExceptionStreamController.stream;
// }
//
// List<ParticipantWidget> get participants {
// return [..._participants];
// }
//
// Future<Room> connect() async {
// print('ConferenceRoom.connect()');
// try {
// await TwilioProgrammableVideo.debug(dart: true, native: true);
// await TwilioProgrammableVideo.setSpeakerphoneOn(true);
//
// _cameraCapturer = CameraCapturer(CameraSource.FRONT_CAMERA);
// var connectOptions = ConnectOptions(
// token,
// roomName: name,
// preferredAudioCodecs: [OpusCodec()],
// audioTracks: [LocalAudioTrack(true)],
// dataTracks: [LocalDataTrack()],
// videoTracks: [LocalVideoTrack(true, _cameraCapturer)],
// enableDominantSpeaker: true,
// );
//
// _room = await TwilioProgrammableVideo.connect(connectOptions);
//
// _streamSubscriptions.add(_room.onConnected.listen(_onConnected));
// _streamSubscriptions.add(_room.onConnectFailure.listen(_onConnectFailure));
//
// return _completer.future;
// } catch (err) {
// print(err);
// rethrow;
// }
// }
//
// Future<void> disconnect() async {
// print('ConferenceRoom.disconnect()');
// if (_timer != null) {
// _timer.cancel();
// }
// await _room.disconnect();
// }
//
// @override
// void dispose() {
// print('ConferenceRoom.dispose()');
// _disposeStreamsAndSubscriptions();
// super.dispose();
// }
//
// Future<void> _disposeStreamsAndSubscriptions() async {
// await _onAudioEnabledStreamController.close();
// await _onVideoEnabledStreamController.close();
// await _onExceptionStreamController.close();
// for (var streamSubscription in _streamSubscriptions) {
// await streamSubscription.cancel();
// }
// }
//
// Future<void> sendMessage(String message) async {
// final tracks = _room.localParticipant.localDataTracks;
// final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
// if (localDataTrack == null || _messages.isNotEmpty) {
// print('ConferenceRoom.sendMessage => Track is not available yet, buffering message.');
// _messages.add(message);
// return;
// }
// await localDataTrack.send(message);
// }
//
// Future<void> sendBufferMessage(ByteBuffer message) async {
// final tracks = _room.localParticipant.localDataTracks;
// final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
// if (localDataTrack == null) {
// return;
// }
// await localDataTrack.sendBuffer(message);
// }
//
// Future<void> toggleVideoEnabled() async {
// final tracks = _room.localParticipant.localVideoTracks;
// final localVideoTrack = tracks.isEmpty ? null : tracks[0].localVideoTrack;
// if (localVideoTrack == null) {
// print('ConferenceRoom.toggleVideoEnabled() => Track is not available yet!');
// return;
// }
// await localVideoTrack.enable(!localVideoTrack.isEnabled);
//
// var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
// if (index < 0) {
// return;
// }
// var participant = _participants[index];
// _participants.replaceRange(
// index,
// index + 1,
// [
// participant.copyWith(videoEnabled: localVideoTrack.isEnabled),
// ],
// );
// print('ConferenceRoom.toggleVideoEnabled() => ${localVideoTrack.isEnabled}');
// _onVideoEnabledStreamController.add(localVideoTrack.isEnabled);
// notifyListeners();
// }
//
// Future<void> toggleAudioEnabled() async {
// final tracks = _room.localParticipant.localAudioTracks;
// final localAudioTrack = tracks.isEmpty ? null : tracks[0].localAudioTrack;
// if (localAudioTrack == null) {
// print('ConferenceRoom.toggleAudioEnabled() => Track is not available yet!');
// return;
// }
// await localAudioTrack.enable(!localAudioTrack.isEnabled);
//
// var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
// if (index < 0) {
// return;
// }
// var participant = _participants[index];
// _participants.replaceRange(
// index,
// index + 1,
// [
// participant.copyWith(audioEnabled: localAudioTrack.isEnabled),
// ],
// );
// print('ConferenceRoom.toggleAudioEnabled() => ${localAudioTrack.isEnabled}');
// _onAudioEnabledStreamController.add(localAudioTrack.isEnabled);
// notifyListeners();
// }
//
// Future<void> switchCamera() async {
// print('ConferenceRoom.switchCamera()');
// try {
// await _cameraCapturer.switchCamera();
// } on FormatException catch (e) {
// print(
// 'ConferenceRoom.switchCamera() failed because of FormatException with message: ${e.message}',
// );
// }
// }
//
// void addDummy({Widget child}) {
// print('ConferenceRoom.addDummy()');
// if (_participants.length >= 18) {
// throw PlatformException(
// code: 'ConferenceRoom.maximumReached',
// message: 'Maximum reached',
// details: 'Currently the lay-out can only render a maximum of 18 participants',
// );
// }
// _participants.insert(
// 0,
// ParticipantWidget(
// id: (_participants.length + 1).toString(),
// child: child,
// isRemote: true,
// audioEnabled: true,
// videoEnabled: true,
// isDummy: true,
// ),
// );
// notifyListeners();
// }
//
// void removeDummy() {
// print('ConferenceRoom.removeDummy()');
// var dummy = _participants.firstWhere((participant) => participant.isDummy, orElse: () => null);
// if (dummy != null) {
// _participants.remove(dummy);
// notifyListeners();
// }
// }
//
// void _onConnected(Room room) {
// print('ConferenceRoom._onConnected => state: ${room.state}');
//
// // When connected for the first time, add remote participant listeners
// _streamSubscriptions.add(_room.onParticipantConnected.listen(_onParticipantConnected));
// _streamSubscriptions.add(_room.onParticipantDisconnected.listen(_onParticipantDisconnected));
// _streamSubscriptions.add(_room.onDominantSpeakerChange.listen(_onDominantSpeakerChanged));
// // Only add ourselves when connected for the first time too.
// _participants.add(
// _buildParticipant(
// child: room.localParticipant.localVideoTracks[0].localVideoTrack.widget(),
// id: identity,
// audioEnabled: true,
// videoEnabled: true,
// ),
// );
// for (final remoteParticipant in room.remoteParticipants) {
// var participant = _participants.firstWhere((participant) => participant.id == remoteParticipant.sid, orElse: () => null);
// if (participant == null) {
// print('Adding participant that was already present in the room ${remoteParticipant.sid}, before I connected');
// _addRemoteParticipantListeners(remoteParticipant);
// }
// }
// // We have to listen for the [onDataTrackPublished] event on the [LocalParticipant] in
// // order to be able to use the [send] method.
// _streamSubscriptions.add(room.localParticipant.onDataTrackPublished.listen(_onLocalDataTrackPublished));
// notifyListeners();
// _completer.complete(room);
//
// _timer = Timer.periodic(const Duration(minutes: 1), (_) {
// // Let's see if we can send some data over the DataTrack API
// sendMessage('And another minute has passed since I connected...');
// // Also try the ByteBuffer way of sending data
// final list = 'This data has been sent over the ByteBuffer channel of the DataTrack API'.codeUnits;
// var bytes = Uint8List.fromList(list);
// sendBufferMessage(bytes.buffer);
// });
// }
//
// void _onLocalDataTrackPublished(LocalDataTrackPublishedEvent event) {
// // Send buffered messages, if any...
// while (_messages.isNotEmpty) {
// var message = _messages.removeAt(0);
// print('Sending buffered message: $message');
// event.localDataTrackPublication.localDataTrack.send(message);
// }
// }
//
// void _onConnectFailure(RoomConnectFailureEvent event) {
// print('ConferenceRoom._onConnectFailure: ${event.exception}');
// _completer.completeError(event.exception);
// }
//
// void _onDominantSpeakerChanged(DominantSpeakerChangedEvent event) {
// print('ConferenceRoom._onDominantSpeakerChanged: ${event.remoteParticipant.identity}');
// var oldDominantParticipant = _participants.firstWhere((p) => p.isDominant, orElse: () => null);
// if (oldDominantParticipant != null) {
// var oldDominantParticipantIndex = _participants.indexOf(oldDominantParticipant);
// _participants.replaceRange(oldDominantParticipantIndex, oldDominantParticipantIndex + 1, [oldDominantParticipant.copyWith(isDominant: false)]);
// }
//
// var newDominantParticipant = _participants.firstWhere((p) => p.id == event.remoteParticipant.sid);
// var newDominantParticipantIndex = _participants.indexOf(newDominantParticipant);
// _participants.replaceRange(newDominantParticipantIndex, newDominantParticipantIndex + 1, [newDominantParticipant.copyWith(isDominant: true)]);
// notifyListeners();
// }
//
// void _onParticipantConnected(RoomParticipantConnectedEvent event) {
// print('ConferenceRoom._onParticipantConnected, ${event.remoteParticipant.sid}');
// _addRemoteParticipantListeners(event.remoteParticipant);
// }
//
// void _onParticipantDisconnected(RoomParticipantDisconnectedEvent event) {
// print('ConferenceRoom._onParticipantDisconnected: ${event.remoteParticipant.sid}');
// _participants.removeWhere((ParticipantWidget p) => p.id == event.remoteParticipant.sid);
// notifyListeners();
// }
//
// ParticipantWidget _buildParticipant({
// required Widget child,
// required String id,
// required bool audioEnabled,
// required bool videoEnabled,
// RemoteParticipant remoteParticipant,
// }) {
// return ParticipantWidget(
// id: remoteParticipant?.sid,
// isRemote: remoteParticipant != null,
// child: child,
// audioEnabled: audioEnabled,
// videoEnabled: videoEnabled,
// );
// }
//
// void _addRemoteParticipantListeners(RemoteParticipant remoteParticipant) {
// print('ConferenceRoom._addRemoteParticipantListeners() => Adding listeners to remoteParticipant ${remoteParticipant.sid}');
// _streamSubscriptions.add(remoteParticipant.onAudioTrackDisabled.listen(_onAudioTrackDisabled));
// _streamSubscriptions.add(remoteParticipant.onAudioTrackEnabled.listen(_onAudioTrackEnabled));
// _streamSubscriptions.add(remoteParticipant.onAudioTrackPublished.listen(_onAudioTrackPublished));
// _streamSubscriptions.add(remoteParticipant.onAudioTrackSubscribed.listen(_onAudioTrackSubscribed));
// _streamSubscriptions.add(remoteParticipant.onAudioTrackSubscriptionFailed.listen(_onAudioTrackSubscriptionFailed));
// _streamSubscriptions.add(remoteParticipant.onAudioTrackUnpublished.listen(_onAudioTrackUnpublished));
// _streamSubscriptions.add(remoteParticipant.onAudioTrackUnsubscribed.listen(_onAudioTrackUnsubscribed));
//
// _streamSubscriptions.add(remoteParticipant.onDataTrackPublished.listen(_onDataTrackPublished));
// _streamSubscriptions.add(remoteParticipant.onDataTrackSubscribed.listen(_onDataTrackSubscribed));
// _streamSubscriptions.add(remoteParticipant.onDataTrackSubscriptionFailed.listen(_onDataTrackSubscriptionFailed));
// _streamSubscriptions.add(remoteParticipant.onDataTrackUnpublished.listen(_onDataTrackUnpublished));
// _streamSubscriptions.add(remoteParticipant.onDataTrackUnsubscribed.listen(_onDataTrackUnsubscribed));
//
// _streamSubscriptions.add(remoteParticipant.onVideoTrackDisabled.listen(_onVideoTrackDisabled));
// _streamSubscriptions.add(remoteParticipant.onVideoTrackEnabled.listen(_onVideoTrackEnabled));
// _streamSubscriptions.add(remoteParticipant.onVideoTrackPublished.listen(_onVideoTrackPublished));
// _streamSubscriptions.add(remoteParticipant.onVideoTrackSubscribed.listen(_onVideoTrackSubscribed));
// _streamSubscriptions.add(remoteParticipant.onVideoTrackSubscriptionFailed.listen(_onVideoTrackSubscriptionFailed));
// _streamSubscriptions.add(remoteParticipant.onVideoTrackUnpublished.listen(_onVideoTrackUnpublished));
// _streamSubscriptions.add(remoteParticipant.onVideoTrackUnsubscribed.listen(_onVideoTrackUnsubscribed));
// }
//
// void _onAudioTrackDisabled(RemoteAudioTrackEvent event) {
// print('ConferenceRoom._onAudioTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
// _setRemoteAudioEnabled(event);
// }
//
// void _onAudioTrackEnabled(RemoteAudioTrackEvent event) {
// print('ConferenceRoom._onAudioTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
// _setRemoteAudioEnabled(event);
// }
//
// void _onAudioTrackPublished(RemoteAudioTrackEvent event) {
// print('ConferenceRoom._onAudioTrackPublished(), ${event.remoteParticipant.sid}}');
// }
//
// void _onAudioTrackSubscribed(RemoteAudioTrackSubscriptionEvent event) {
// print('ConferenceRoom._onAudioTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
// _addOrUpdateParticipant(event);
// }
//
// void _onAudioTrackSubscriptionFailed(RemoteAudioTrackSubscriptionFailedEvent event) {
// print('ConferenceRoom._onAudioTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
// _onExceptionStreamController.add(
// PlatformException(
// code: 'ConferenceRoom.audioTrackSubscriptionFailed',
// message: 'AudioTrack Subscription Failed',
// details: event.exception.toString(),
// ),
// );
// }
//
// void _onAudioTrackUnpublished(RemoteAudioTrackEvent event) {
// print('ConferenceRoom._onAudioTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
// }
//
// void _onAudioTrackUnsubscribed(RemoteAudioTrackSubscriptionEvent event) {
// print('ConferenceRoom._onAudioTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrack.sid}');
// }
//
// void _onDataTrackPublished(RemoteDataTrackEvent event) {
// print('ConferenceRoom._onDataTrackPublished(), ${event.remoteParticipant.sid}}');
// }
//
// void _onDataTrackSubscribed(RemoteDataTrackSubscriptionEvent event) {
// print('ConferenceRoom._onDataTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
// final dataTrack = event.remoteDataTrackPublication.remoteDataTrack;
// _dataTracks.add(dataTrack);
// _streamSubscriptions.add(dataTrack.onMessage.listen(_onMessage));
// _streamSubscriptions.add(dataTrack.onBufferMessage.listen(_onBufferMessage));
// }
//
// void _onDataTrackSubscriptionFailed(RemoteDataTrackSubscriptionFailedEvent event) {
// print('ConferenceRoom._onDataTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
// _onExceptionStreamController.add(
// PlatformException(
// code: 'ConferenceRoom.dataTrackSubscriptionFailed',
// message: 'DataTrack Subscription Failed',
// details: event.exception.toString(),
// ),
// );
// }
//
// void _onDataTrackUnpublished(RemoteDataTrackEvent event) {
// print('ConferenceRoom._onDataTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
// }
//
// void _onDataTrackUnsubscribed(RemoteDataTrackSubscriptionEvent event) {
// print('ConferenceRoom._onDataTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrack.sid}');
// }
//
// void _onVideoTrackDisabled(RemoteVideoTrackEvent event) {
// print('ConferenceRoom._onVideoTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
// _setRemoteVideoEnabled(event);
// }
//
// void _onVideoTrackEnabled(RemoteVideoTrackEvent event) {
// print('ConferenceRoom._onVideoTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
// _setRemoteVideoEnabled(event);
// }
//
// void _onVideoTrackPublished(RemoteVideoTrackEvent event) {
// print('ConferenceRoom._onVideoTrackPublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
// }
//
// void _onVideoTrackSubscribed(RemoteVideoTrackSubscriptionEvent event) {
// print('ConferenceRoom._onVideoTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
// _addOrUpdateParticipant(event);
// }
//
// void _onVideoTrackSubscriptionFailed(RemoteVideoTrackSubscriptionFailedEvent event) {
// print('ConferenceRoom._onVideoTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
// _onExceptionStreamController.add(
// PlatformException(
// code: 'ConferenceRoom.videoTrackSubscriptionFailed',
// message: 'VideoTrack Subscription Failed',
// details: event.exception.toString(),
// ),
// );
// }
//
// void _onVideoTrackUnpublished(RemoteVideoTrackEvent event) {
// print('ConferenceRoom._onVideoTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
// }
//
// void _onVideoTrackUnsubscribed(RemoteVideoTrackSubscriptionEvent event) {
// print('ConferenceRoom._onVideoTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
// }
//
// void _onMessage(RemoteDataTrackStringMessageEvent event) {
// print('onMessage => ${event.remoteDataTrack.sid}, ${event.message}');
// }
//
// void _onBufferMessage(RemoteDataTrackBufferMessageEvent event) {
// print('onBufferMessage => ${event.remoteDataTrack.sid}, ${String.fromCharCodes(event.message.asUint8List())}');
// }
//
// void _setRemoteAudioEnabled(RemoteAudioTrackEvent event) {
// if (event.remoteAudioTrackPublication == null) {
// return;
// }
// var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
// if (index < 0) {
// return;
// }
// var participant = _participants[index];
// _participants.replaceRange(
// index,
// index + 1,
// [
// participant.copyWith(audioEnabled: event.remoteAudioTrackPublication.isTrackEnabled),
// ],
// );
// notifyListeners();
// }
//
// void _setRemoteVideoEnabled(RemoteVideoTrackEvent event) {
// if (event.remoteVideoTrackPublication == null) {
// return;
// }
// var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
// if (index < 0) {
// return;
// }
// var participant = _participants[index];
// _participants.replaceRange(
// index,
// index + 1,
// [
// participant.copyWith(videoEnabled: event.remoteVideoTrackPublication.isTrackEnabled),
// ],
// );
// notifyListeners();
// }
//
// void _addOrUpdateParticipant(RemoteParticipantEvent event) {
// print('ConferenceRoom._addOrUpdateParticipant(), ${event.remoteParticipant.sid}');
// final participant = _participants.firstWhere(
// (ParticipantWidget participant) => participant.id == event.remoteParticipant.sid,
// orElse: () => null,
// );
// if (participant != null) {
// print('Participant found: ${participant.id}, updating A/V enabled values');
// _setRemoteVideoEnabled(event);
// _setRemoteAudioEnabled(event);
// } else {
// final bufferedParticipant = _participantBuffer.firstWhere(
// (ParticipantBuffer participant) => participant.id == event.remoteParticipant.sid,
// orElse: () => null,
// );
// if (bufferedParticipant != null) {
// _participantBuffer.remove(bufferedParticipant);
// } else if (event is RemoteAudioTrackEvent) {
// print('Audio subscription came first, waiting for the video subscription...');
// _participantBuffer.add(
// ParticipantBuffer(
// id: event.remoteParticipant.sid,
// audioEnabled: event.remoteAudioTrackPublication?.remoteAudioTrack?.isEnabled ?? true,
// ),
// );
// return;
// }
// if (event is RemoteVideoTrackSubscriptionEvent) {
// print('New participant, adding: ${event.remoteParticipant.sid}');
// _participants.insert(
// 0,
// _buildParticipant(
// child: event.remoteVideoTrack.widget(),
// id: event.remoteParticipant.sid,
// remoteParticipant: event.remoteParticipant,
// audioEnabled: bufferedParticipant?.audioEnabled ?? true,
// videoEnabled: event.remoteVideoTrackPublication?.remoteVideoTrack?.isEnabled ?? true,
// ),
// );
// }
// notifyListeners();
// }
// }
// }

@ -0,0 +1,172 @@
import 'dart:async';
import 'dart:io';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/clipped_video.dart';
import 'package:flutter/material.dart';
class DraggablePublisher extends StatefulWidget {
final Size availableScreenSize;
final Widget child;
final double scaleFactor;
final Stream<bool> onButtonBarVisible;
final Stream<double> onButtonBarHeight;
const DraggablePublisher({
Key? key,
required this.availableScreenSize,
required this.child,
required this.onButtonBarVisible,
required this.onButtonBarHeight,
/// The portion of the screen the DraggableWidget should use.
this.scaleFactor = .25,
}) : assert(scaleFactor != null && scaleFactor > 0 && scaleFactor <= .4),
assert(availableScreenSize != null),
assert(onButtonBarVisible != null),
assert(onButtonBarHeight != null),
super(key: key);
@override
_DraggablePublisherState createState() => _DraggablePublisherState();
}
class _DraggablePublisherState extends State<DraggablePublisher> {
bool _isButtonBarVisible = true;
double _buttonBarHeight = 0;
late double _width;
late double _height;
late double _top;
late double _left;
late double _viewPaddingTop;
late double _viewPaddingBottom;
final double _padding = 8.0;
final Duration _duration300ms = const Duration(milliseconds: 300);
final Duration _duration0ms = const Duration(milliseconds: 0);
late Duration _duration;
late StreamSubscription _streamSubscription;
late StreamSubscription _streamHeightSubscription;
@override
void initState() {
super.initState();
_duration = _duration300ms;
_width = widget.availableScreenSize.width * widget.scaleFactor;
_height = _width * (widget.availableScreenSize.height / widget.availableScreenSize.width);
_top = widget.availableScreenSize.height - (_buttonBarHeight + _padding) - _height;
_left = widget.availableScreenSize.width - _padding - _width;
_streamSubscription = widget.onButtonBarVisible.listen(_buttonBarVisible);
_streamHeightSubscription = widget.onButtonBarHeight.listen(_getButtonBarHeight);
}
@override
void didChangeDependencies() {
final mediaQuery = MediaQuery.of(context);
_viewPaddingTop = mediaQuery.viewPadding.top;
_viewPaddingBottom = mediaQuery.viewPadding.bottom;
super.didChangeDependencies();
}
@override
void dispose() {
_streamSubscription.cancel();
_streamHeightSubscription.cancel();
super.dispose();
}
void _getButtonBarHeight(double height) {
setState(() {
_buttonBarHeight = height;
_positionWidget();
});
}
void _buttonBarVisible(bool visible) {
if (!mounted) {
return;
}
setState(() {
_isButtonBarVisible = visible;
if (_duration == _duration300ms) {
// only position the widget when we are not currently dragging it around
_positionWidget();
}
});
}
@override
Widget build(BuildContext context) {
return AnimatedPositioned(
top: _top,
left: _left,
width: _width,
height: _height,
duration: _duration,
child: Listener(
onPointerDown: (_) => _duration = _duration0ms,
onPointerMove: (PointerMoveEvent event) {
setState(() {
_left = (_left + event.delta.dx).roundToDouble();
_top = (_top + event.delta.dy).roundToDouble();
});
},
onPointerUp: (_) => _positionWidget(),
onPointerCancel: (_) => _positionWidget(),
child: ClippedVideo(
height: _height,
width: _width,
child: widget.child,
),
),
);
}
double _getCurrentStatusBarHeight() {
if (_isButtonBarVisible) {
return _viewPaddingTop;
}
final _defaultViewPaddingTop = Platform.isIOS ? 20.0 : Platform.isAndroid ? 24.0 : 0.0;
if (_viewPaddingTop > _defaultViewPaddingTop) {
// There must be a hardware notch in the display.
return _viewPaddingTop;
}
return 0.0;
}
double _getCurrentButtonBarHeight() {
if (_isButtonBarVisible) {
return _buttonBarHeight + _viewPaddingBottom;
}
return _viewPaddingBottom;
}
void _positionWidget() {
// Determine the center of the object being dragged so we can decide
// in which corner the object should be placed.
var dx = (_width / 2) + _left;
dx = dx < 0 ? 0 : dx >= widget.availableScreenSize.width ? widget.availableScreenSize.width - 1 : dx;
var dy = (_height / 2) + _top;
dy = dy < 0 ? 0 : dy >= widget.availableScreenSize.height ? widget.availableScreenSize.height - 1 : dy;
final draggableCenter = Offset(dx, dy);
setState(() {
_duration = _duration300ms;
if (Rect.fromLTRB(0, 0, widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// Top-left
_top = _getCurrentStatusBarHeight() + _padding;
_left = _padding;
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, 0, widget.availableScreenSize.width, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// Top-right
_top = _getCurrentStatusBarHeight() + _padding;
_left = widget.availableScreenSize.width - _padding - _width;
} else if (Rect.fromLTRB(0, widget.availableScreenSize.height / 2, widget.availableScreenSize.width / 2, widget.availableScreenSize.height).contains(draggableCenter)) {
// Bottom-left
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
_left = _padding;
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2, widget.availableScreenSize.width, widget.availableScreenSize.height).contains(draggableCenter)) {
// Bottom-right
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
_left = widget.availableScreenSize.width - _padding - _width;
}
});
}
}

@ -0,0 +1,197 @@
import 'dart:ui';
import 'package:flutter/material.dart';
class ParticipantBuffer {
final bool audioEnabled;
final String id;
ParticipantBuffer({
required this.audioEnabled,
required this.id,
}) : assert(audioEnabled != null),
assert(id != null);
}
class ParticipantWidget extends StatelessWidget {
final Widget child;
final String id;
final bool audioEnabled;
final bool videoEnabled;
final bool isRemote;
final bool isDummy;
final bool isDominant;
const ParticipantWidget({
Key? key,
required this.child,
required this.audioEnabled,
required this.videoEnabled,
required this.id,
required this.isRemote,
this.isDominant = false,
this.isDummy = false,
}) : assert(child != null),
assert(audioEnabled != null),
assert(videoEnabled != null),
assert(isRemote != null),
assert(isDominant != null),
assert(isDummy != null),
super(key: key);
ParticipantWidget copyWith({
Widget? child,
bool? audioEnabled,
bool? videoEnabled,
bool? isDominant,
}) {
return ParticipantWidget(
id: id,
child: child ?? this.child,
audioEnabled: audioEnabled ?? this.audioEnabled,
videoEnabled: videoEnabled ?? this.videoEnabled,
isDominant: isDominant ?? this.isDominant,
isRemote: isRemote,
);
}
@override
Widget build(BuildContext context) {
final children = <Widget>[];
final icons = <Widget>[];
if (!videoEnabled) {
icons.add(_buildVideoEnabledIcon());
children.add(
ClipRect(
// Need to clip this BackdropFilter, otherwise it will blur the entire screen
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
decoration: BoxDecoration(color: Colors.black.withOpacity(.1)),
child: child,
),
),
),
);
} else {
children.add(child);
}
children.add(Padding(
padding: const EdgeInsets.all(8.0),
child: AnimatedOpacity(
duration: Duration(milliseconds: 500),
opacity: isDominant ? 1 : 0,
child: Icon(
Icons.volume_up,
color: Colors.white,
),
),
));
if (!audioEnabled) {
icons.add(_buildAudioEnabledIcon());
}
if (icons.isNotEmpty) {
if (isRemote) {
final rows = <Widget>[];
rows.add(_buildRow(icons));
if (!audioEnabled && !videoEnabled) {
rows.add(_buildRow(_fitText('The camera and microphone are off', Colors.white24)));
} else if (!audioEnabled) {
rows.add(_buildRow(_fitText('The microphone is off', Colors.black26)));
} else if (!videoEnabled) {
rows.add(_buildRow(_fitText('The camera is off', Colors.white24)));
}
children.add(
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: rows,
),
);
} else {
children.add(Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: icons,
));
}
}
return Stack(
children: children,
);
}
List<Widget> _fitText(String text, Color color) {
return [
Flexible(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
child: Text(text, maxLines: 1, style: _buildTextStyle(color)),
),
),
),
];
}
TextStyle _buildTextStyle(Color color) {
return TextStyle(
color: color,
shadows: <Shadow>[
Shadow(
blurRadius: 1.0,
color: Color.fromARGB(255, 0, 0, 0),
),
Shadow(
blurRadius: 1.0,
color: Color.fromARGB(24, 255, 255, 255),
),
],
fontSize: 15,
);
}
Widget _buildRow(List<Widget> children) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
);
}
Widget _buildAudioEnabledIcon() {
return Padding(
padding: const EdgeInsets.all(8),
child: CircleAvatar(
maxRadius: 15,
child: FittedBox(
child: Icon(
Icons.mic_off,
color: Colors.black,
key: Key('microphone-off-icon'),
),
),
backgroundColor: Colors.white24,
),
);
}
Widget _buildVideoEnabledIcon() {
return Padding(
padding: const EdgeInsets.all(8),
child: CircleAvatar(
maxRadius: 15,
child: FittedBox(
child: Icon(
Icons.videocam_off,
color: Colors.black,
key: Key('videocam-off-icon'),
),
),
backgroundColor: Colors.white24,
),
);
}
}

@ -0,0 +1,231 @@
import 'dart:convert';
import 'package:doctor_app_flutter/util/SignalRUtil.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
const PATIENT_PUSH_TOKEN = "eUA6FUiOTbSellej2JQ8yg:APA91bFuDledDvwbeZJ8KU8drHQ7kse7h9UjKPaBZsQgKi-0GCSPRB_yIO9O3PXysWLnLMJYwVmEJWd-jPIqbrLz_Z_yzE--3mC-GZJj92BT0nSzAVd-JVr28pUtejgSjTpZryNFlSn5";
typedef void StreamStateCallback(MediaStream stream);
typedef void RTCIceGatheringStateCallback(RTCIceGatheringState state);
typedef void RTCPeerConnectionStateCallback(RTCPeerConnectionState state);
typedef void RTCSignalingStateCallback(RTCSignalingState state);
Map<String, dynamic> snapsis_ice_config = {
'iceServers': [
{ "urls": 'stun:15.185.116.59:3478' },
{ "urls": "turn:15.185.116.59:3479", "username": "admin", "credential": "admin" },
],
// 'sdpSemantics': 'unified-plan'
};
Map<String, dynamic> twilio_ice_config = {
"ice_servers": [
{
"url": "stun:global.stun.twilio.com:3478?transport=udp",
"urls": "stun:global.stun.twilio.com:3478?transport=udp"
},
{
"url": "turn:global.turn.twilio.com:3478?transport=udp",
"username": "ce8042842b62c21bd20b176f80d6067fd3db81b1e9766312418ef5421d9ca2a2",
"urls": "turn:global.turn.twilio.com:3478?transport=udp",
"credential": "UzGOsiLwPZJ32cjafAebfDDpVrqeQjgpFHZEdau/8r4="
},
{
"url": "turn:global.turn.twilio.com:3478?transport=tcp",
"username": "ce8042842b62c21bd20b176f80d6067fd3db81b1e9766312418ef5421d9ca2a2",
"urls": "turn:global.turn.twilio.com:3478?transport=tcp",
"credential": "UzGOsiLwPZJ32cjafAebfDDpVrqeQjgpFHZEdau/8r4="
},
{
"url": "turn:global.turn.twilio.com:443?transport=tcp",
"username": "ce8042842b62c21bd20b176f80d6067fd3db81b1e9766312418ef5421d9ca2a2",
"urls": "turn:global.turn.twilio.com:443?transport=tcp",
"credential": "UzGOsiLwPZJ32cjafAebfDDpVrqeQjgpFHZEdau/8r4="
}
],
// 'sdpSemantics': 'unified-plan'
};
Map<String, dynamic> google_ice_config = {
'iceServers': [
{
'urls': [
'stun:stun.l.google.com:19302',
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302',
'stun:stun3.l.google.com:19302'
]
},
],
// 'sdpSemantics': 'unified-plan'
};
Map<String, dynamic> aws_ice_config = {
'iceServers': [
{'url': "stun:ec2-15-185-116-59.me-south-1.compute.amazonaws.com:3478"},
{'url': "turn:ec2-15-185-116-59.me-south-1.compute.amazonaws.com:3479", 'credential': "admin", 'username': "admin"}
],
// 'sdpSemantics': 'unified-plan'
};
final Map<String, dynamic> _peer_config = {
'mandatory': {},
'optional': [
{'DtlsSrtpKeyAgreement': true},
]
};
class Signaling {
dispose(){
if(peerConnection != null)
peerConnection.dispose();
signalR?.closeConnection();
}
init(){
// Create Peer Connection
createPeerConnection(google_ice_config,_peer_config).then((value){
peerConnection = value;
registerPeerConnectionListeners();
});
}
initializeSignalR(String userName) async{
if(signalR != null)
await signalR?.closeConnection();
// https://vcallapi.hmg.com/webRTCHub?source=web&username=zohaib
signalR = SignalRUtil(hubName: "https://vcallapi.hmg.com/webRTCHub?source=mobile&username=$userName");
final connected = await signalR?.openConnection();
if(connected != null && !connected)
throw 'Failed to connect SignalR';
}
SignalRUtil? signalR;
late RTCPeerConnection peerConnection;
MediaStream? localStream;
MediaStream? remoteStream;
RTCDataChannel? dataChannel;
Future<bool> acceptCall(String caller, String receiver, {required MediaStream localMediaStream, required Function(MediaStream) onRemoteMediaStream}) async{
await initializeSignalR(caller);
signalR?.setContributors(caller: caller, receiver: receiver);
await signalR?.acceptCall(receiver, caller).catchError((e) => throw 'Failed to inform signalR that i accepted a call');
peerConnection.addStream(localMediaStream);
peerConnection.onAddStream = (MediaStream stream) {
remoteStream = stream;
onRemoteMediaStream.call(stream);
};
return true;
}
Future<bool> initiateCall(String caller, String receiver, {required MediaStream localMediaStream, required Function(MediaStream) onRemoteMediaStream}) async{
await initializeSignalR(caller);
signalR?.setContributors(caller: caller, receiver: receiver);
await signalR?.callUserMobile(caller, receiver).catchError((e) => throw 'Failed to inform signalR to call user: $receiver');
peerConnection.addStream(localMediaStream);
peerConnection.onAddStream = (MediaStream stream) {
remoteStream = stream;
onRemoteMediaStream.call(stream);
};
return true;
}
Future hangupCall(String caller, String receiver) async{
await signalR?.hangupCall(caller, receiver);
dispose();
}
answerOffer(Map offerSdp) async{
final caller = offerSdp['caller'];
final receiver = offerSdp['target'];
peerConnection.setRemoteDescription(rtcSessionDescriptionFrom(offerSdp))
.then((value) {
return peerConnection.createAnswer();
})
.then((anwser) {
return peerConnection.setLocalDescription(anwser);
})
.then((value) {
return peerConnection.getLocalDescription();
})
.then((answer) {
return signalR?.answerOffer(answer, caller, receiver);
});
}
sdpOfferAnswered(Map sdp){
final answerSdp = rtcSessionDescriptionFrom(sdp);
peerConnection.setRemoteDescription(answerSdp)
.then((value) {
}).catchError((e) => print(e));
}
Future<void> hangUp(RTCVideoRenderer localVideo) async {
}
Future<String> createSdpAnswer(String toOfferSdp) async {
final offerSdp = rtcSessionDescriptionFrom(jsonDecode(toOfferSdp));
peerConnection.setRemoteDescription(offerSdp);
final answer = await peerConnection.createAnswer();
var answerSdp = json.encode(answer); // Send SDP via Push or any channel
return answerSdp;
}
Future<String> createSdpOffer(String caller, String receiver) async {
await Future.delayed(Duration(seconds: 1));
final offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
final map = offer.toMap();
var offerSdp = json.encode({'sdp':map, 'target':receiver, 'caller':caller}); // Send SDP via Push or any channel
return offerSdp;
}
addCandidate(Map candidate){
peerConnection.addCandidate(rtcIceCandidateFrom(candidate));
}
void registerPeerConnectionListeners() {
peerConnection.onIceCandidate = (RTCIceCandidate candidate){
print(json.encode(candidate.toMap()));
signalR?.addIceCandidate(candidate);
};
peerConnection.onIceGatheringState = (RTCIceGatheringState state) {
print('ICE gathering state changed: $state');
};
peerConnection.onConnectionState = (RTCPeerConnectionState state) {
print('Connection state change: $state ${state.index}');
};
peerConnection.onSignalingState = (RTCSignalingState state) {
print('Signaling state change: $state');
};
}
}
rtcSessionDescriptionFrom(Map sdp){
return RTCSessionDescription(
sdp['sdp'],sdp['type'],
);
}
rtcIceCandidateFrom(Map candidate){
final _candidate = candidate['candidate'];
return RTCIceCandidate(_candidate['candidate'], _candidate['sdpMid'], _candidate['sdpMLineIndex']);
}

@ -0,0 +1,198 @@
import 'dart:async';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/signaling.dart';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/web_rtc/widgets/cam_view_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import '../conference_button_bar.dart';
class CallHomePage extends StatefulWidget {
final String receiverId;
final String callerId;
const CallHomePage({Key? key, required this.receiverId, required this.callerId}) : super(key: key);
@override
_CallHomePageState createState() => _CallHomePageState();
}
class _CallHomePageState extends State<CallHomePage> {
String get caller => widget.callerId;
String get receiver => widget.receiverId;
bool showNoise = true;
RTCVideoRenderer _localRenderer = RTCVideoRenderer();
RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
final StreamController<bool> _audioButton = StreamController<bool>.broadcast();
final StreamController<bool> _videoButton = StreamController<bool>.broadcast();
final StreamController<bool> _onButtonBarVisibleStreamController = StreamController<bool>.broadcast();
final StreamController<double> _onButtonBarHeightStreamController = StreamController<double>.broadcast();
//Stream to enable video
late MediaStream localMediaStream;
MediaStream? remoteMediaStream;
Signaling signaling = Signaling()..init();
@override
void initState() {
// TODO: implement initState
super.initState();
startCall();
}
startCall() async{
await _localRenderer.initialize();
await _remoteRenderer.initialize();
final connected = await callUser();
}
Future<bool> callUser() async {
//Stream local media
localMediaStream = await navigator.mediaDevices.getUserMedia({'video': true, 'audio': true});
_localRenderer.srcObject = localMediaStream;
final connected = await signaling.initiateCall(widget.callerId, widget.receiverId, localMediaStream: localMediaStream, onRemoteMediaStream: (remoteMediaStream){
setState(() {
this.remoteMediaStream = remoteMediaStream;
_remoteRenderer.srcObject = remoteMediaStream;
});
});
if(connected){
signaling.signalR?.listen(
onAcceptCall: (arg0) async{
final offer = await signaling.createSdpOffer(caller, receiver);
signaling.signalR?.offer(receiver, caller, offer).catchError((e){
print(e.toString());
});
},
onCandidate: (candidateJson){
signaling.addCandidate(candidateJson);
},
onDeclineCall: (arg0,arg1){
// _onHangup();
},
onHangupCall: (arg0){
// _onHangup();
},
onSdpAnswer: (answerSdp){
signaling.sdpOfferAnswered(answerSdp);
},
onSdpOffer: (offerSdp, user) async{
print('${offerSdp.toString()} | ${user.toString()}');
await signaling.answerOffer(offerSdp);
}
);
}
return connected;
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
_localRenderer.dispose();
_remoteRenderer.dispose();
_audioButton.close();
_videoButton.close();
localMediaStream?.dispose();
remoteMediaStream?.dispose();
_disposeStreamsAndSubscriptions();
}
Future<void> _disposeStreamsAndSubscriptions() async {
if (_onButtonBarVisibleStreamController != null) await _onButtonBarVisibleStreamController.close();
if (_onButtonBarHeightStreamController != null) await _onButtonBarHeightStreamController.close();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: buildLayout(),
);
}
LayoutBuilder buildLayout() {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Stack(
children: [
CamViewWidget(
localRenderer: _localRenderer,
remoteRenderer: _remoteRenderer,
constraints: constraints,
onButtonBarVisibleStreamController: _onButtonBarVisibleStreamController,
onButtonBarHeightStreamController: _onButtonBarHeightStreamController,
),
ConferenceButtonBar(
audioEnabled: _audioButton.stream,
videoEnabled: _videoButton.stream,
onAudioEnabled: _onAudioEnable,
onVideoEnabled: _onVideoEnabled,
onSwitchCamera: _onSwitchCamera,
onHangup: _onHangup,
onPersonAdd: () {},
onPersonRemove: () {},
onHeight: _onHeightBar,
onShow: _onShowBar,
onHide: _onHideBar,
),
],
);
},
);
}
_onAudioEnable() {
final audioTrack = localMediaStream?.getAudioTracks()[0];
if(audioTrack != null){
final mute = audioTrack.muted ?? false;
Helper.setMicrophoneMute(mute, audioTrack);
_audioButton.add(mute);
}
}
_onVideoEnabled() {
final videoTrack = localMediaStream?.getVideoTracks()[0];
if(videoTrack != null){
bool videoEnabled = videoTrack.enabled;
localMediaStream?.getVideoTracks()[0].enabled = !videoEnabled;
_videoButton.add(!videoEnabled);
}
}
_onSwitchCamera() {
Helper.switchCamera(localMediaStream.getVideoTracks()[0]);
}
void _onShowBar() {
setState(() {
});
_onButtonBarVisibleStreamController.add(true);
}
void _onHeightBar(double height) {
_onButtonBarHeightStreamController.add(height);
}
void _onHideBar() {
setState(() {
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
});
_onButtonBarVisibleStreamController.add(false);
}
Future<void> _onHangup() async {
signaling.hangupCall(widget.callerId, widget.receiverId);
signaling.dispose();
print('onHangup');
Navigator.of(context).pop();
}
}

@ -0,0 +1,82 @@
import 'dart:async';
import 'dart:core';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/widgets/noise_box.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'draggable_cam.dart';
class CamViewWidget extends StatefulWidget {
RTCVideoRenderer localRenderer;
RTCVideoRenderer remoteRenderer;
MediaStream? localStream;
BoxConstraints constraints;
StreamController<bool> onButtonBarVisibleStreamController;
StreamController<double> onButtonBarHeightStreamController;
CamViewWidget({required this.localRenderer, required this.remoteRenderer, required this.constraints, required this.onButtonBarVisibleStreamController, required this.onButtonBarHeightStreamController});
@override
_CamViewWidgetState createState() => _CamViewWidgetState();
}
class _CamViewWidgetState extends State<CamViewWidget> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
child: Stack(
children: [
FractionallySizedBox(
heightFactor: 1, widthFactor: 1,
child: Container(
color: Colors.black87,
child: RTCVideoView(widget.remoteRenderer, mirror: true,filterQuality: FilterQuality.medium,),
),
),
if(widget.remoteRenderer.srcObject == null)
Positioned.fill(child: _buildNoiseBox()),
Positioned.fill(
child: RTCVideoView(widget.remoteRenderer)
),
DraggableCam(
key: Key('publisher'),
onButtonBarHeight: widget.onButtonBarHeightStreamController.stream,
onButtonBarVisible: widget.onButtonBarVisibleStreamController.stream,
availableScreenSize: widget.constraints.biggest,
child: RTCVideoView(widget.localRenderer)
),
if(widget.remoteRenderer.srcObject == null)
Container(
margin: EdgeInsets.all(MediaQuery.of(context).size.width/8),
child: Text(
'Waiting for another participant to connect to the call...',
key: Key('text-wait'),
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
)
),
],
),
);
}
Widget _buildNoiseBox() {
return NoiseBox(
density: NoiseBoxDensity.xHigh,
backgroundColor: Colors.grey.shade900,
);
}
}

@ -0,0 +1,173 @@
import 'dart:async';
import 'dart:io';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/clipped_video.dart';
import 'package:flutter/material.dart';
class DraggableCam extends StatefulWidget {
final Size availableScreenSize;
final Widget child;
final double scaleFactor;
final Stream<bool> onButtonBarVisible;
final Stream<double> onButtonBarHeight;
const DraggableCam({
Key? key,
required this.availableScreenSize,
required this.child,
required this.onButtonBarVisible,
required this.onButtonBarHeight,
/// The portion of the screen the DraggableWidget should use.
this.scaleFactor = .25,
}) : assert(scaleFactor != null && scaleFactor > 0 && scaleFactor <= .4),
assert(availableScreenSize != null),
assert(onButtonBarVisible != null),
assert(onButtonBarHeight != null),
super(key: key);
@override
_DraggablePublisherState createState() => _DraggablePublisherState();
}
class _DraggablePublisherState extends State<DraggableCam> {
bool _isButtonBarVisible = true;
double _buttonBarHeight = 0;
late double _width;
late double _height;
late double _top;
late double _left;
late double _viewPaddingTop;
late double _viewPaddingBottom;
final double _padding = 8.0;
final Duration _duration300ms = const Duration(milliseconds: 300);
final Duration _duration0ms = const Duration(milliseconds: 0);
late Duration _duration;
late StreamSubscription _streamSubscription;
late StreamSubscription _streamHeightSubscription;
@override
void initState() {
super.initState();
_duration = _duration300ms;
_width = widget.availableScreenSize.width * widget.scaleFactor;
_height = _width * (widget.availableScreenSize.height / widget.availableScreenSize.width);
_top = widget.availableScreenSize.height - (_buttonBarHeight + _padding) - _height;
_left = widget.availableScreenSize.width - _padding - _width;
_streamSubscription = widget.onButtonBarVisible.listen(_buttonBarVisible);
_streamHeightSubscription = widget.onButtonBarHeight.listen(_getButtonBarHeight);
}
@override
void didChangeDependencies() {
final mediaQuery = MediaQuery.of(context);
_viewPaddingTop = mediaQuery.viewPadding.top;
_viewPaddingBottom = mediaQuery.viewPadding.bottom;
super.didChangeDependencies();
}
@override
void dispose() {
_streamSubscription.cancel();
_streamHeightSubscription.cancel();
super.dispose();
}
void _getButtonBarHeight(double height) {
setState(() {
_buttonBarHeight = height;
_positionWidget();
});
}
void _buttonBarVisible(bool visible) {
if (!mounted) {
return;
}
setState(() {
_isButtonBarVisible = visible;
if (_duration == _duration300ms) {
// only position the widget when we are not currently dragging it around
_positionWidget();
}
});
}
@override
Widget build(BuildContext context) {
return AnimatedPositioned(
top: _top,
left: _left,
width: _width,
height: _height,
duration: _duration,
child: Listener(
onPointerDown: (_) => _duration = _duration0ms,
onPointerMove: (PointerMoveEvent event) {
setState(() {
_left = (_left + event.delta.dx).roundToDouble();
_top = (_top + event.delta.dy).roundToDouble();
});
},
onPointerUp: (_) => _positionWidget(),
onPointerCancel: (_) => _positionWidget(),
child: ClippedVideo(
height: _height,
width: _width,
child: widget.child,
),
),
);
}
double _getCurrentStatusBarHeight() {
if (_isButtonBarVisible) {
return _viewPaddingTop;
}
final _defaultViewPaddingTop = Platform.isIOS ? 20.0 : Platform.isAndroid ? 24.0 : 0.0;
if (_viewPaddingTop > _defaultViewPaddingTop) {
// There must be a hardware notch in the display.
return _viewPaddingTop;
}
return 0.0;
}
double _getCurrentButtonBarHeight() {
if (_isButtonBarVisible) {
return _buttonBarHeight + _viewPaddingBottom;
}
return _viewPaddingBottom;
}
void _positionWidget() {
// Determine the center of the object being dragged so we can decide
// in which corner the object should be placed.
var dx = (_width / 2) + _left;
dx = dx < 0 ? 0 : dx >= widget.availableScreenSize.width ? widget.availableScreenSize.width - 1 : dx;
var dy = (_height / 2) + _top;
dy = dy < 0 ? 0 : dy >= widget.availableScreenSize.height ? widget.availableScreenSize.height - 1 : dy;
final draggableCenter = Offset(dx, dy);
setState(() {
_duration = _duration300ms;
if (Rect.fromLTRB(0, 0, widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// Top-left
_top = _getCurrentStatusBarHeight() + _padding;
_left = _padding;
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, 0, widget.availableScreenSize.width, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// Top-right
_top = _getCurrentStatusBarHeight() + _padding;
_left = widget.availableScreenSize.width - _padding - _width;
} else if (Rect.fromLTRB(0, widget.availableScreenSize.height / 2, widget.availableScreenSize.width / 2, widget.availableScreenSize.height).contains(draggableCenter)) {
// Bottom-left
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
_left = _padding;
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2, widget.availableScreenSize.width, widget.availableScreenSize.height).contains(draggableCenter)) {
// Bottom-right
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
_left = widget.availableScreenSize.width - _padding - _width;
}
});
}
}

@ -0,0 +1,106 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
class ButtonToProgress extends StatefulWidget {
final double height;
final double progressHeight;
final String loadingText;
final Duration duration;
final TextStyle? loadingTextStyle;
final VoidCallback? onPressed;
final Stream<bool> onLoading;
final Widget child;
const ButtonToProgress({
Key? key,
this.height = 40.0,
this.progressHeight = 5.0,
required this.loadingText,
this.duration = const Duration(milliseconds: 300),
this.loadingTextStyle,
this.onPressed,
required this.onLoading,
required this.child,
}) : assert(child != null),
assert(height != null && height > 0),
assert(progressHeight != null && progressHeight > 0 && progressHeight <= height),
super(key: key);
@override
_ButtonToProgressState createState() => _ButtonToProgressState();
}
class _ButtonToProgressState extends State<ButtonToProgress> {
late double _height;
double _opacity = 0;
bool _isLoading = false;
late StreamSubscription<bool> _subscription;
@override
void initState() {
super.initState();
_height = widget.height;
if (widget.onLoading != null) {
_subscription = widget.onLoading.listen((bool isLoading) {
setState(() {
_isLoading = isLoading;
_height = isLoading ? widget.progressHeight : widget.height;
_opacity = isLoading ? 1 : 0;
});
});
}
}
@override
void dispose() {
if (_subscription != null) {
_subscription.cancel();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
height: widget.height,
child: Stack(
children: [
if (widget.loadingText == null)
Container()
else
Padding(
padding: EdgeInsets.only(bottom: widget.progressHeight),
child: AnimatedOpacity(
child: Center(
child: FittedBox(
child: Text(
widget.loadingText,
style: widget.loadingTextStyle,
),
),
),
opacity: _opacity,
duration: Duration(milliseconds: widget.duration.inMilliseconds + 200),
curve: Curves.easeInCubic,
),
),
AnimatedPadding(
duration: widget.duration,
padding: EdgeInsets.only(
top: math.max(widget.height - _height, 0),
),
child: AnimatedContainer(
duration: widget.duration,
height: _height,
width: double.infinity,
child: _isLoading ? const LinearProgressIndicator() : widget.child,
),
),
],
),
);
}
}

@ -0,0 +1,103 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:native_device_orientation/native_device_orientation.dart';
class CircleButton extends StatefulWidget {
final VoidCallback? onLongPress;
final VoidCallback? onPressed;
final GestureTapDownCallback? onTapDown;
final VoidCallback? onTapCancel;
final Widget child;
final Color? color;
final double radius;
const CircleButton({
Key? key,
this.onLongPress,
this.onPressed,
required this.child,
this.color,
this.radius = 25.0,
this.onTapCancel,
this.onTapDown,
}) : assert(radius != null),
super(key: key);
@override
_CircleButtonState createState() => _CircleButtonState();
}
class _CircleButtonState extends State<CircleButton> {
double _rotationAngle = 0.0;
final Stream<NativeDeviceOrientation> _orientationStream = NativeDeviceOrientationCommunicator().onOrientationChanged(useSensor: true);
late StreamSubscription<NativeDeviceOrientation> _orientationSubscription;
void _handleOrientationChange(NativeDeviceOrientation orientation) {
var targetAngle = 0.0;
switch (orientation) {
case NativeDeviceOrientation.unknown:
case NativeDeviceOrientation.portraitUp:
targetAngle = 0.0;
break;
case NativeDeviceOrientation.portraitDown:
targetAngle = 180.0;
break;
case NativeDeviceOrientation.landscapeLeft:
targetAngle = 90.0;
break;
case NativeDeviceOrientation.landscapeRight:
targetAngle = 270.0;
break;
}
setState(() {
_rotationAngle = targetAngle;
});
}
@override
void initState() {
super.initState();
_orientationSubscription = _orientationStream.listen(
_handleOrientationChange,
onError: (dynamic err) => print(err),
);
}
@override
void dispose() {
super.dispose();
_orientationSubscription.cancel();
}
@override
Widget build(BuildContext context) {
final size = 2 * widget.radius;
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: (widget.color ?? Colors.blue).withAlpha(200),
borderRadius: BorderRadius.all(
Radius.circular(widget.radius),
),
),
child: GestureDetector(
onLongPress: widget.onLongPress,
onTapDown: widget.onTapDown,
onTapCancel: widget.onTapCancel,
child: RawMaterialButton(
onPressed: widget.onPressed,
child: RotationTransition(
child: widget.child,
turns: AlwaysStoppedAnimation<double>(_rotationAngle / 360),
),
elevation: 0,
shape: const CircleBorder(),
),
),
);
}
}

@ -0,0 +1,147 @@
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
enum NoiseBoxDensity {
high,
medium,
low,
xHigh,
xLow,
}
class NoiseBox extends StatefulWidget {
final NoiseBoxDensity density;
final Color backgroundColor;
final Widget? child;
const NoiseBox({
Key? key,
required this.backgroundColor,
this.child,
this.density = NoiseBoxDensity.medium,
}) : assert(density != null),
super(key: key);
@override
_NoiseBoxState createState() => _NoiseBoxState();
}
class _NoiseBoxState extends State<NoiseBox> with TickerProviderStateMixin {
late AnimationController _animationController;
late int _density;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 60),
);
_animationController.repeat();
switch (widget.density) {
case NoiseBoxDensity.high:
_density = 5;
break;
case NoiseBoxDensity.medium:
_density = 7;
break;
case NoiseBoxDensity.low:
_density = 10;
break;
case NoiseBoxDensity.xHigh:
_density = 3;
break;
case NoiseBoxDensity.xLow:
_density = 12;
break;
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) => Container(
color: widget.backgroundColor,
width: constraints.biggest.width,
height: constraints.biggest.height,
child: AnimatedBuilder(
animation: _animationController,
builder: (BuildContext context, Widget? child) {
final children = <Widget>[
CustomPaint(
painter: NoisePainter(
width: constraints.biggest.width,
height: constraints.biggest.height,
density: _density,
),
),
];
if (widget.child != null) {
children.add(widget.child!);
}
return Stack(
children: children,
);
},
),
),
);
}
}
class NoisePainter extends CustomPainter {
final double width;
final double height;
final int density;
NoisePainter({
required this.width,
required this.height,
required this.density,
}) : assert(width != null),
assert(height != null),
assert(density != null &&
density >= 3 &&
density < math.min(width, height));
List<Color> colors = <Color>[
Colors.black,
Colors.grey,
Colors.blueGrey,
Colors.red,
Colors.green,
Colors.blue,
Colors.white,
];
@override
void paint(Canvas canvas, Size size) {
final random = math.Random();
for (var w = 0; w < width; w += density) {
for (var h = 0; h < height; h += density) {
final offset = Offset(
random.nextDouble() * width,
random.nextDouble() * height,
);
final paint = Paint();
paint.color = colors[random.nextInt(colors.length)];
paint.strokeWidth = random.nextDouble() * 2;
canvas.drawPoints(PointMode.points, <Offset>[offset], paint);
}
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

@ -0,0 +1,98 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import './platform_widget.dart';
class PlatformAlertDialog extends PlatformWidget {
PlatformAlertDialog({required this.title, required this.content, required this.defaultActionText, this.cancelActionText})
: assert(title != null),
assert(content != null),
assert(defaultActionText != null);
final String title;
final String content;
final String defaultActionText;
final String? cancelActionText;
Future<bool?> show(BuildContext context) async {
return Platform.isIOS
? await showCupertinoDialog<bool>(
context: context,
builder: (BuildContext context) => this,
)
: await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) => this,
);
}
@override
Widget buildCupertinoWidget(BuildContext context) {
return CupertinoAlertDialog(
title: Text(title),
content: Text(content),
actions: _buildActions(context),
);
}
@override
Widget buildMaterialWidget(BuildContext context) {
return AlertDialog(
title: Text(title),
content: Text(content),
actions: _buildActions(context),
);
}
List<Widget> _buildActions(BuildContext context) {
final actions = <Widget>[];
if (cancelActionText != null) {
actions.add(
PlatformAlertDialogAction(
child: Text(cancelActionText!),
onPressed: () {
Navigator.of(context).pop(false);
},
),
);
}
actions.add(
PlatformAlertDialogAction(
child: Text(defaultActionText),
onPressed: () {
Navigator.of(context).pop(true);
},
),
);
return actions;
}
}
class PlatformAlertDialogAction extends PlatformWidget {
PlatformAlertDialogAction({
required this.child,
required this.onPressed,
});
final Widget child;
final VoidCallback onPressed;
@override
Widget buildCupertinoWidget(BuildContext context) {
return CupertinoDialogAction(
child: child,
onPressed: onPressed,
);
}
@override
Widget buildMaterialWidget(BuildContext context) {
return FlatButton(
child: child,
onPressed: onPressed,
);
}
}

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './platform_alert_dialog.dart';
class PlatformExceptionAlertDialog extends PlatformAlertDialog {
PlatformExceptionAlertDialog({
String title = 'An error occurred',
required Exception exception,
}) : super(
title: title,
content: exception is PlatformException ? _message(exception) : exception.toString(),
defaultActionText: 'OK',
);
static String _message(PlatformException exception) {
return _errors[exception.code] ?? (exception.details != null ? (exception.details['message'] ?? exception.message) : exception.message);
}
static final Map<String, String> _errors = <String, String>{
'ERROR_CODE': 'Error description...',
};
}

@ -0,0 +1,17 @@
import 'dart:io';
import 'package:flutter/material.dart';
abstract class PlatformWidget extends StatelessWidget {
Widget buildCupertinoWidget(BuildContext context);
Widget buildMaterialWidget(BuildContext context);
@override
Widget build(BuildContext context) {
if (Platform.isIOS) {
return buildCupertinoWidget(context);
}
return buildMaterialWidget(context);
}
}

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
typedef ResponsiveBuilder = Widget Function(
BuildContext context,
Size size,
);
class ResponsiveSafeArea extends StatelessWidget {
const ResponsiveSafeArea({
required ResponsiveBuilder builder,
Key? key,
}) : responsiveBuilder = builder,
assert(builder != null),
super(key: key);
final ResponsiveBuilder responsiveBuilder;
@override
Widget build(BuildContext context) {
return SafeArea(
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return responsiveBuilder(
context,
constraints.biggest,
);
},
),
);
}
}

@ -0,0 +1,212 @@
import 'dart:convert';
import 'dart:io';
import 'package:doctor_app_flutter/screens/live_care/web-rtc/signaling.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:http/io_client.dart';
import 'package:signalr_core/signalr_core.dart';
class SignalRUtil {
late String sourceUser;
late String destinationUser;
setContributors({required String caller, required String receiver}){
this.sourceUser = caller;
this.destinationUser = receiver;
}
Function(bool)? onConnected;
String hubName;
SignalRUtil({required this.hubName});
late HubConnection connectionHub;
closeConnection() async{
if(connectionHub != null) {
connectionHub.off('OnIncomingCallAsync');
connectionHub.off('OnCallDeclinedAsync');
connectionHub.off('OnCallAcceptedAsync');
connectionHub.off('nHangUpAsync');
connectionHub.off('OnIceCandidateAsync');
connectionHub.off('OnOfferAsync');
await connectionHub.stop();
}
}
Future<bool> openConnection() async {
connectionHub = HubConnectionBuilder()
.withUrl(
hubName,
HttpConnectionOptions(
logMessageContent: true,
client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true),
logging: (level, message) => print(message),
)).build();
await connectionHub.start();
await Future.delayed(Duration(seconds: 1));
connectionHub.on('ReceiveMessage', (message) {
handleIncomingMessage(message);
});
return getConnectionState();
}
void handleIncomingMessage(List<dynamic>? message) {
print(message.toString());
}
void sendMessage(List<dynamic> args) async {
await connectionHub.invoke('SendMessage', args: args); //['Bob', 'Says hi!']
}
listen({ required Function(CallUser) onAcceptCall, onHangupCall, required Function(Map, CallUser) onDeclineCall, onSdpOffer, onSdpAnswer, required Function(Map) onCandidate}){
connectionHub.on('OnIncomingCallAsync', (arguments) {
print('OnIncomingCallAsync: ${arguments.toString()}');
});
connectionHub.on('OnCallDeclinedAsync', (arguments) {
print('OnCallDeclinedAsync: ${arguments.toString()}');
final data = json.decode(arguments?.first);
onDeclineCall(data, CallUser.from(arguments?.last));
});
connectionHub.on('OnCallAcceptedAsync', (arguments) {
print('OnCallAcceptedAsync: ${arguments.toString()}');
onAcceptCall(CallUser.from(arguments?.last));
});
connectionHub.on('OnHangUpAsync', (arguments) {
print('nHangUpAsync: ${arguments.toString()}');
onHangupCall(CallUser.from(arguments?.first));
});
connectionHub.on('OnIceCandidateAsync', (arguments) {
print('OnIceCandidateAsync: ${arguments.toString()}');
final data = json.decode(arguments?.first);
onCandidate(data);
});
connectionHub.on('OnOfferAsync', (arguments) {
print('OnOfferAsync: ${arguments.toString()}');
final data = json.decode(arguments?.first);
onSdpOffer(data, CallUser.from(arguments?.last));
});
connectionHub.on('OnAnswerOffer', (arguments) {
print('OnAnswerOffer: ${arguments.toString()}');
final sdp = json.decode(arguments?.first)['sdp'];
onSdpAnswer(sdp);
});
}
// CallUserAsyncMobile(string currentUserId, string targerUserId, string patientInfoJson)
Future<dynamic> callUserMobile(String from, to) async{
final p_info = patientInfo(from, to, PATIENT_PUSH_TOKEN);
return await connectionHub.invoke('CallUserAsyncMobile', args: [p_info]);
}
// CallUserAsyncRemote(string currentUserId, string targerUserId)
Future<dynamic> callUserRemote(String from, to) async{
return await connectionHub.invoke('CallUserAsyncRemote', args: [from, to]);
}
// CallUserAsync(string currentUserId, string targerUserId, string patientInfoJson)
Future<dynamic> callUserWeb(String from, to) async{
final p_info = patientInfo(from, to, PATIENT_PUSH_TOKEN);
return await connectionHub.invoke('CallUserAsync', args: [from, to, p_info]);
}
patientInfo(String from, to, patientToken){
final json_ = {
"SessionID" : '123456',
"Sess_token" : 'admin',
"_Token" : patientToken,
"AppointmentNo" : '123456',
'ProjectID' : '12',
"projectName" : 'Olaya',
'doctorname' : 'Dr.Habib',
'Docspeciality' : 'Cardiologist',
'ClincName' : 'Cardiology',
'PatientID' : to,
'callerID' : from
};
return json.encode(json_);
}
// CallDeclinedAsync(string currentUserId, string targerUserId)
Future<dynamic> declineCall(String from, to) async{
return await connectionHub.invoke('CallDeclinedAsync', args: [from, to]);
}
// AnswerCallAsync(string currentUserId, string targetUserId)
Future<dynamic> answerCall(String from, to) async{
return await connectionHub.invoke('AnswerCallAsync', args: [from, to]);
}
// IceCandidateAsync(string targetUserId, string candidate)
Future<dynamic> addIceCandidate(RTCIceCandidate candidate) async{
final target = destinationUser;
final _candidate = json.encode({'candidate' : candidate.toMap()});
return await connectionHub.invoke('IceCandidateAsync', args: [target, _candidate]);
}
// OfferAsync(string targetUserId,string currentUserId, string targetOffer)
Future<dynamic> offer(String from, to, offer) async{
return await connectionHub.invoke('OfferAsync', args: [from, to, offer]);
}
// AnswerOfferAsync(string targetUserId, string CallerOffer)
Future<dynamic> answerOffer(RTCSessionDescription? answerSdp, caller, receiver) async{
final payload = {
'target': receiver,
'caller': caller,
'sdp': answerSdp?.toMap() ?? {},
};
return await connectionHub.invoke('AnswerOfferAsync', args: [caller, jsonEncode(payload)]);
}
// HangUpAsync(string currentUserId, string targetUserId)
Future<dynamic> hangupCall(String from, to) async{
return await connectionHub.invoke('HangUpAsync', args: [from, to]);
}
// CallAccepted(string currentUserId,string targetUserId)
Future<dynamic> acceptCall(String from, to) async{
// return await connectionHub.send(methodName: 'CallAccepted', args: [from, to]);
return await connectionHub.invoke("CallAccepted", args: [ from, to]);
}
bool getConnectionState() {
if (connectionHub.state == HubConnectionState.connected) return true;
if (connectionHub.state == HubConnectionState.disconnected) return false;
return false;
}
}
class CallUser{
String? Id;
String? UserName;
String? Email;
String? Phone;
String? Title;
dynamic? UserStatus;
String? Image;
int UnreadMessageCount = 0;
CallUser.from(Map map){
Id = map['Id'];
UserName = map['UserName'];
Email = map['Email'];
Phone = map['Phone'];
Title = map['Title'];
UserStatus = map['UserStatus'];
Image = map['Image'];
UnreadMessageCount = map['UnreadMessageCount'];
}
}

@ -0,0 +1,98 @@
v=0
o=- 5927491327118611904 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=extmap-allow-mixed
a=msid-semantic: WMS e53e47c9-d409-4522-bcc9-49c8b1d093b7
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:TPqz
a=ice-pwd:Av/F5Hz76zQdybHAG2DomDog
a=ice-options:trickle renomination
a=fingerprint:sha-256 1F:D1:DD:59:42:65:91:E2:41:99:ED:6C:DB:30:76:4A:6B:5A:F9:82:7C:07:29:66:80:DC:AF:E2:34:05:43:18
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:3000425778 cname:kibCM8KmL1PJhPlQ
a=ssrc:3000425778 msid:e53e47c9-d409-4522-bcc9-49c8b1d093b7 a82ca475-9a94-4bdc-a91c-1137d80a0753
a=ssrc:3000425778 mslabel:e53e47c9-d409-4522-bcc9-49c8b1d093b7
a=ssrc:3000425778 label:a82ca475-9a94-4bdc-a91c-1137d80a0753
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 35 36 100 101 127
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:TPqz
a=ice-pwd:Av/F5Hz76zQdybHAG2DomDog
a=ice-options:trickle renomination
a=fingerprint:sha-256 1F:D1:DD:59:42:65:91:E2:41:99:ED:6C:DB:30:76:4A:6B:5A:F9:82:7C:07:29:66:80:DC:AF:E2:34:05:43:18
a=setup:actpass
a=mid:video
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:35 AV1X/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:100 red/90000
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 ulpfec/90000
a=ssrc-group:FID 1697527935 3482614104
a=ssrc:1697527935 cname:kibCM8KmL1PJhPlQ
a=ssrc:1697527935 msid:e53e47c9-d409-4522-bcc9-49c8b1d093b7 49e96524-ed6c-4fa3-9bc3-745d716698c0
a=ssrc:1697527935 mslabel:e53e47c9-d409-4522-bcc9-49c8b1d093b7
a=ssrc:1697527935 label:49e96524-ed6c-4fa3-9bc3-745d716698c0
a=ssrc:3482614104 cname:kibCM8KmL1PJhPlQ
a=ssrc:3482614104 msid:e53e47c9-d409-4522-bcc9-49c8b1d093b7 49e96524-ed6c-4fa3-9bc3-745d716698c0
a=ssrc:3482614104 mslabel:e53e47c9-d409-4522-bcc9-49c8b1d093b7
a=ssrc:3482614104 label:49e96524-ed6c-4fa3-9bc3-745d716698c0

@ -0,0 +1,98 @@
v=0
o=- 1852792980555145345 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=extmap-allow-mixed
a=msid-semantic: WMS c435b864-09cb-4e9b-b467-0215765153b2
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:yZwB
a=ice-pwd:xoT7qMaStLtOryw8gBoh9HBE
a=ice-options:trickle
a=fingerprint:sha-256 E6:93:81:34:89:3A:AE:E9:66:B3:B1:BF:81:F4:F9:EF:71:84:41:21:09:98:BC:D2:0C:7F:98:EF:CA:E5:47:3A
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:467703518 cname:LpqUpo/SZG/oP5nR
a=ssrc:467703518 msid:c435b864-09cb-4e9b-b467-0215765153b2 112bdcb4-6696-4473-921b-54a9b864134e
a=ssrc:467703518 mslabel:c435b864-09cb-4e9b-b467-0215765153b2
a=ssrc:467703518 label:112bdcb4-6696-4473-921b-54a9b864134e
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 35 36 100 101 127
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:mrRm
a=ice-pwd:eQ/xWrHgvgZol4cryWYtf6kM
a=ice-options:trickle renomination
a=fingerprint:sha-256 08:BF:EB:45:0B:49:A0:51:B1:D8:45:4E:BA:03:81:98:95:64:0A:2A:27:0E:B6:2D:2B:E7:9E:C4:0E:A5:53:14
a=setup:actpass
a=mid:video
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:35 AV1X/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:100 red/90000
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 ulpfec/90000
a=ssrc-group:FID 2760462647 1007249032
a=ssrc:2760462647 cname:LpqUpo/SZG/oP5nR
a=ssrc:2760462647 msid:c435b864-09cb-4e9b-b467-0215765153b2 8d8b378e-fc04-428d-a4df-50af401e70d2
a=ssrc:2760462647 mslabel:c435b864-09cb-4e9b-b467-0215765153b2
a=ssrc:2760462647 label:8d8b378e-fc04-428d-a4df-50af401e70d2
a=ssrc:1007249032 cname:LpqUpo/SZG/oP5nR
a=ssrc:1007249032 msid:c435b864-09cb-4e9b-b467-0215765153b2 8d8b378e-fc04-428d-a4df-50af401e70d2
a=ssrc:1007249032 mslabel:c435b864-09cb-4e9b-b467-0215765153b2
a=ssrc:1007249032 label:8d8b378e-fc04-428d-a4df-50af401e70d2

@ -0,0 +1,109 @@
v=0
o=- 3297723553455155496 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:yZwB
a=ice-pwd:xoT7qMaStLtOryw8gBoh9HBE
a=ice-options:trickle
a=fingerprint:sha-256 E6:93:81:34:89:3A:AE:E9:66:B3:B1:BF:81:F4:F9:EF:71:84:41:21:09:98:BC:D2:0C:7F:98:EF:CA:E5:47:3A
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM ee14b0b6-bef2-4a88-8550-a7120c16eb41
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:2100092274 cname:dMYQoj76poQISooy
a=ssrc:2100092274 msid:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM ee14b0b6-bef2-4a88-8550-a7120c16eb41
a=ssrc:2100092274 mslabel:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM
a=ssrc:2100092274 label:ee14b0b6-bef2-4a88-8550-a7120c16eb41
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 122 121 127
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:yZwB
a=ice-pwd:xoT7qMaStLtOryw8gBoh9HBE
a=ice-options:trickle
a=fingerprint:sha-256 E6:93:81:34:89:3A:AE:E9:66:B3:B1:BF:81:F4:F9:EF:71:84:41:21:09:98:BC:D2:0C:7F:98:EF:CA:E5:47:3A
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:11 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM 5c21bd40-52fb-4c67-9bd2-cd5b64dc9de1
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 H264/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:122 red/90000
a=rtpmap:121 rtx/90000
a=fmtp:121 apt=122
a=rtpmap:127 ulpfec/90000
a=ssrc-group:FID 2717479063 1967595658
a=ssrc:2717479063 cname:dMYQoj76poQISooy
a=ssrc:2717479063 msid:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM 5c21bd40-52fb-4c67-9bd2-cd5b64dc9de1
a=ssrc:2717479063 mslabel:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM
a=ssrc:2717479063 label:5c21bd40-52fb-4c67-9bd2-cd5b64dc9de1
a=ssrc:1967595658 cname:dMYQoj76poQISooy
a=ssrc:1967595658 msid:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM 5c21bd40-52fb-4c67-9bd2-cd5b64dc9de1
a=ssrc:1967595658 mslabel:h3MZrNWyvufOFj9hhh4M1dQjm53cZXMbEKxM
a=ssrc:1967595658 label:5c21bd40-52fb-4c67-9bd2-cd5b64dc9de1

@ -0,0 +1,114 @@
v=0
o=- 5927491327118611904 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=extmap-allow-mixed
a=msid-semantic: WMS e53e47c9-d409-4522-bcc9-49c8b1d093b7
m=audio 49168 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126
c=IN IP4 15.185.116.59
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:2473295229 1 udp 2122262783 2a02:cb80:4078:40c3:18c0:72ff:fe47:4312 51566 typ host generation 0 network-id 4 network-cost 10
a=candidate:3370617219 1 udp 2122194687 172.20.10.11 59256 typ host generation 0 network-id 3 network-cost 10
a=candidate:559267639 1 udp 2122136831 ::1 57659 typ host generation 0 network-id 2
a=candidate:1510613869 1 udp 2122063615 127.0.0.1 50875 typ host generation 0 network-id 1
a=candidate:1876313031 1 tcp 1518157055 ::1 39063 typ host tcptype passive generation 0 network-id 2
a=candidate:344579997 1 tcp 1518083839 127.0.0.1 49823 typ host tcptype passive generation 0 network-id 1
a=candidate:842163049 1 udp 1685987071 51.36.24.254 1652 typ srflx raddr 172.20.10.11 rport 59256 generation 0 network-id 3 network-cost 10
a=candidate:3951447094 1 udp 41819903 15.185.116.59 49168 typ relay raddr 51.36.24.254 rport 1652 generation 0 network-id 3 network-cost 10
a=ice-ufrag:TPqz
a=ice-pwd:Av/F5Hz76zQdybHAG2DomDog
a=ice-options:trickle renomination
a=fingerprint:sha-256 1F:D1:DD:59:42:65:91:E2:41:99:ED:6C:DB:30:76:4A:6B:5A:F9:82:7C:07:29:66:80:DC:AF:E2:34:05:43:18
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:3000425778 cname:kibCM8KmL1PJhPlQ
a=ssrc:3000425778 msid:e53e47c9-d409-4522-bcc9-49c8b1d093b7 a82ca475-9a94-4bdc-a91c-1137d80a0753
a=ssrc:3000425778 mslabel:e53e47c9-d409-4522-bcc9-49c8b1d093b7
a=ssrc:3000425778 label:a82ca475-9a94-4bdc-a91c-1137d80a0753
m=video 49057 UDP/TLS/RTP/SAVPF 96 97 98 99 35 36 100 101 127
c=IN IP4 15.185.116.59
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:2473295229 1 udp 2122262783 2a02:cb80:4078:40c3:18c0:72ff:fe47:4312 39324 typ host generation 0 network-id 4 network-cost 10
a=candidate:3370617219 1 udp 2122194687 172.20.10.11 41527 typ host generation 0 network-id 3 network-cost 10
a=candidate:559267639 1 udp 2122136831 ::1 37365 typ host generation 0 network-id 2
a=candidate:1510613869 1 udp 2122063615 127.0.0.1 43321 typ host generation 0 network-id 1
a=candidate:1876313031 1 tcp 1518157055 ::1 40159 typ host tcptype passive generation 0 network-id 2
a=candidate:344579997 1 tcp 1518083839 127.0.0.1 36533 typ host tcptype passive generation 0 network-id 1
a=candidate:842163049 1 udp 1685987071 51.36.24.254 1653 typ srflx raddr 172.20.10.11 rport 41527 generation 0 network-id 3 network-cost 10
a=candidate:3951447094 1 udp 41819903 15.185.116.59 49057 typ relay raddr 51.36.24.254 rport 1653 generation 0 network-id 3 network-cost 10
a=ice-ufrag:TPqz
a=ice-pwd:Av/F5Hz76zQdybHAG2DomDog
a=ice-options:trickle renomination
a=fingerprint:sha-256 1F:D1:DD:59:42:65:91:E2:41:99:ED:6C:DB:30:76:4A:6B:5A:F9:82:7C:07:29:66:80:DC:AF:E2:34:05:43:18
a=setup:actpass
a=mid:video
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:35 AV1X/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:100 red/90000
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 ulpfec/90000
a=ssrc-group:FID 1697527935 3482614104
a=ssrc:1697527935 cname:kibCM8KmL1PJhPlQ
a=ssrc:1697527935 msid:e53e47c9-d409-4522-bcc9-49c8b1d093b7 49e96524-ed6c-4fa3-9bc3-745d716698c0
a=ssrc:1697527935 mslabel:e53e47c9-d409-4522-bcc9-49c8b1d093b7
a=ssrc:1697527935 label:49e96524-ed6c-4fa3-9bc3-745d716698c0
a=ssrc:3482614104 cname:kibCM8KmL1PJhPlQ
a=ssrc:3482614104 msid:e53e47c9-d409-4522-bcc9-49c8b1d093b7 49e96524-ed6c-4fa3-9bc3-745d716698c0
a=ssrc:3482614104 mslabel:e53e47c9-d409-4522-bcc9-49c8b1d093b7
a=ssrc:3482614104 label:49e96524-ed6c-4fa3-9bc3-745d716698c0

@ -0,0 +1,110 @@
v=0
o=- 1852792980555145345 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=extmap-allow-mixed
a=msid-semantic: WMS c435b864-09cb-4e9b-b467-0215765153b2
m=audio 2488 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126
c=IN IP4 51.39.198.194
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:2080483951 1 udp 2122260223 192.168.8.146 53264 typ host generation 0 network-id 3 network-cost 10
a=candidate:559267639 1 udp 2122202367 ::1 58708 typ host generation 0 network-id 2
a=candidate:1510613869 1 udp 2122129151 127.0.0.1 41026 typ host generation 0 network-id 1
a=candidate:1876313031 1 tcp 1518222591 ::1 40989 typ host tcptype passive generation 0 network-id 2
a=candidate:344579997 1 tcp 1518149375 127.0.0.1 58889 typ host tcptype passive generation 0 network-id 1
a=candidate:842163049 1 udp 1686052607 51.39.198.194 2488 typ srflx raddr 192.168.8.146 rport 53264 generation 0 network-id 3 network-cost 10
a=ice-ufrag:mrRm
a=ice-pwd:eQ/xWrHgvgZol4cryWYtf6kM
a=ice-options:trickle renomination
a=fingerprint:sha-256 08:BF:EB:45:0B:49:A0:51:B1:D8:45:4E:BA:03:81:98:95:64:0A:2A:27:0E:B6:2D:2B:E7:9E:C4:0E:A5:53:14
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:467703518 cname:LpqUpo/SZG/oP5nR
a=ssrc:467703518 msid:c435b864-09cb-4e9b-b467-0215765153b2 112bdcb4-6696-4473-921b-54a9b864134e
a=ssrc:467703518 mslabel:c435b864-09cb-4e9b-b467-0215765153b2
a=ssrc:467703518 label:112bdcb4-6696-4473-921b-54a9b864134e
m=video 2485 UDP/TLS/RTP/SAVPF 96 97 98 99 35 36 100 101 127
c=IN IP4 51.39.198.194
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:2080483951 1 udp 2122260223 192.168.8.146 33465 typ host generation 0 network-id 3 network-cost 10
a=candidate:559267639 1 udp 2122202367 ::1 33641 typ host generation 0 network-id 2
a=candidate:1510613869 1 udp 2122129151 127.0.0.1 41957 typ host generation 0 network-id 1
a=candidate:1876313031 1 tcp 1518222591 ::1 46553 typ host tcptype passive generation 0 network-id 2
a=candidate:344579997 1 tcp 1518149375 127.0.0.1 41591 typ host tcptype passive generation 0 network-id 1
a=candidate:842163049 1 udp 1686052607 51.39.198.194 2485 typ srflx raddr 192.168.8.146 rport 33465 generation 0 network-id 3 network-cost 10
a=ice-ufrag:mrRm
a=ice-pwd:eQ/xWrHgvgZol4cryWYtf6kM
a=ice-options:trickle renomination
a=fingerprint:sha-256 08:BF:EB:45:0B:49:A0:51:B1:D8:45:4E:BA:03:81:98:95:64:0A:2A:27:0E:B6:2D:2B:E7:9E:C4:0E:A5:53:14
a=setup:actpass
a=mid:video
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:35 AV1X/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:100 red/90000
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 ulpfec/90000
a=ssrc-group:FID 2760462647 1007249032
a=ssrc:2760462647 cname:LpqUpo/SZG/oP5nR
a=ssrc:2760462647 msid:c435b864-09cb-4e9b-b467-0215765153b2 8d8b378e-fc04-428d-a4df-50af401e70d2
a=ssrc:2760462647 mslabel:c435b864-09cb-4e9b-b467-0215765153b2
a=ssrc:2760462647 label:8d8b378e-fc04-428d-a4df-50af401e70d2
a=ssrc:1007249032 cname:LpqUpo/SZG/oP5nR
a=ssrc:1007249032 msid:c435b864-09cb-4e9b-b467-0215765153b2 8d8b378e-fc04-428d-a4df-50af401e70d2
a=ssrc:1007249032 mslabel:c435b864-09cb-4e9b-b467-0215765153b2
a=ssrc:1007249032 label:8d8b378e-fc04-428d-a4df-50af401e70d2

@ -112,6 +112,13 @@ dependencies:
# hijri: ^2.0.3
hijri_picker: ^3.0.0
#Dependencies for video call implementation
signalr_core: ^1.1.1
flutter_webrtc: ^0.8.0
native_device_orientation: ^1.0.0
wakelock: ^0.5.6
after_layout: ^1.1.0
# flutter_math_fork: ^0.6.0
# flutter_math_fork: ^0.6.0
@ -120,8 +127,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: any
build_web_compilers: any
## build_runner: any
# build_web_compilers: any
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

@ -1,11 +1,11 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(5.6.1))
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.4.2))
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=
java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home
jvm.arguments=
offline.mode=false
override.workspace.settings=true

Loading…
Cancel
Save