requestCallToUser method
Implementation
RequestCallToUserInstance requestCallToUser ({
required localStream,
required remoteUserId,
Map<String,dynamic>? additionalData,
}) {
assert(!_functionCalled, "Please, create another instance of this class, this one is already in use");
assert(!_callServiceExpired, "Please, create another instance of this class and use it right away, because of getWebRTCParams");
_callServiceExpired = _functionCalled = true;
resultCompleterForRequestingCall = Completer();
this.remoteUserId = remoteUserId;
this.localStream = localStream;
this.additionalData = additionalData ?? <String, dynamic>{};
_listenToCallToClose(
onCallAnswered: (event) async {
logger("Uhull! Call has been answered! Accepted: ${event["callAccepted"]}");
if (disposed) {
logger("success, but it was canceled");
return;
}
if (event["callAccepted"] != true) {
logger("callAccepted field is ${(event["callAccepted"] ?? 'null')}");
doDisposeCallback(result: RequestCallResult(callAccepted: false, additionalData: remoteUserAdditionalData));
return;
}
assert(event["sdp"]["type"] == "answer");
logger (">> 2 - Setting remote description from answer! ${event["sdp"]["type"]} and ${event["sdp"]["sdp"]}");
// call accepted..
await rtcPeerConnection!.setRemoteDescription(
RTCSessionDescription(
event["sdp"]["sdp"],
event["sdp"]["type"]
)
);
logger("--->");
logger(Map.from(candidatesByPort).toString());
final iceCandidateListWebsocketResponse = await AsklessClient.instance.create(
route: "askless-internal/call/ice-candidate-list",
body: {
"remoteUserId": remoteUserId,
"iceCandidateList": rtcIceCandidateList.map((iceCandidate) {
return {
"candidate": iceCandidate.candidate,
"id": iceCandidate.sdpMid,
"label": iceCandidate.sdpMLineIndex,
};
}).toList()
}
);
if (!iceCandidateListWebsocketResponse.success) {
final errorMessage = "could not inform the iceCandidateList: \"${iceCandidateListWebsocketResponse.error!.code}: ${iceCandidateListWebsocketResponse.error!.description}\"";
logger(errorMessage, level: Level.error);
doDisposeCallback(error: errorMessage);
return;
}
}
);
_setup(
impl: () async {
if (disposed) {
doDisposeCallback(error: 'disposing...');
return;
}
final sdpOffer = await rtcPeerConnection!.createOffer({'offerToReceiveVideo': 1, 'offerToReceiveAudio': 1});
await rtcPeerConnection!.setLocalDescription(sdpOffer);
logger(">> 1 - SDP OFFER GENERATED AND SET LOCAL DESCRIPTION LOCALLY sdpOffer");
rtcPeerConnection!.onIceCandidate = (RTCIceCandidate candidate) {
logger("rtcPeerConnection -> onIceCandidate #1");
rtcIceCandidateList.add(candidate);
logger(json.encode({
'candidate': candidate.candidate,
'sdpMid': candidate.sdpMid,
'sdpMlineIndex': candidate.sdpMLineIndex
}));
if (candidate.candidate != null && candidate.candidate!.contains('srflx')) {
final cand = parseCandidate(candidate.candidate!);
if (candidatesByPort[cand["relatedPort"]] == null) {
candidatesByPort[cand["relatedPort"]] = [];
}
logger('adding --> '+Map.from(cand).toString());
candidatesByPort[cand["relatedPort"]].add(cand["port"]);
} else if (candidate.candidate == null) {
if (candidatesByPort.keys.length == 1) {
final ports = candidatesByPort[candidatesByPort.keys.first];
logger(ports.length == 1 ? 'cool nat' : 'symmetric nat');
}
}
};
logger("askless-internal/call/request: requesting call...");
final res = await AsklessClient.instance.create(
route: "askless-internal/call/request",
neverTimeout: false,
/** TODO: add controller to let the user be able to cancel the request */
/** TODO: custom timeout? */
body: {
"remoteUserId": remoteUserId,
"sdp": sdpOffer.toMap(),
"additionalData": additionalData,
}
);
logger("askless-internal/call/request: requesting call: Call request response is ${res.success} ${res.error?.code ?? ''} ${res.error?.description ?? ''} ");
if (!res.success) {
doDisposeCallback(error: res.error!.description);
}
}
);
return requestCallInstance = RequestCallToUserInstance(
resultCompleter: resultCompleterForRequestingCall!,
dispose: doDisposeCallback,
);
}