You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
7.3 KiB
Dart
232 lines
7.3 KiB
Dart
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']);
|
|
}
|