public async Task <ICallInfo> PlaceCallAsync(CallConfiguration config) { Debug.Assert(_peerId == -1); if (PeerConnection != null) { Debug.WriteLine("[Error] We only support connection to one peer at a time."); return(null); } if (CreatePeerConnection()) { string selectedAudioCodecName = (string)_localSettings.Values["SelectedAudioCodecName"]; string selectedVideoCodecName = (string)_localSettings.Values["SelectedVideoCodecName"]; _peerId = PeerId; var offerOptions = new RTCOfferOptions(); offerOptions.OfferToReceiveAudio = true; offerOptions.OfferToReceiveVideo = true; IRTCSessionDescription offer = await PeerConnection.CreateOffer(offerOptions); // Alter sdp to force usage of selected codecs string modifiedSdp = offer.Sdp; SdpUtils.SelectCodec(ref modifiedSdp, selectedAudioCodecName, "audio"); SdpUtils.SelectCodec(ref modifiedSdp, selectedVideoCodecName, "video"); RTCSessionDescriptionInit sdpInit = new RTCSessionDescriptionInit(); sdpInit.Sdp = modifiedSdp; sdpInit.Type = offer.SdpType; var modifiedOffer = new RTCSessionDescription(sdpInit); await PeerConnection.SetLocalDescription(modifiedOffer); Debug.WriteLine($"Sending offer: {modifiedOffer.Sdp}"); string jsonString = SdpToJsonString(modifiedOffer); CallInfo callInfo = new CallInfo(); callInfo.SetCall(new Call()); callInfo.SetSdp(modifiedSdp); callInfo.SetJsonString(jsonString); OnSendMessageToRemotePeer.Invoke(this, jsonString); return(callInfo); } return(null); }
/// <summary> /// Called when WebRTC detects another ICE candidate. /// This candidate needs to be sent to the other peer. /// </summary> /// <param name="Event">Details about RTCPeerConnectionIceEvent</param> private void PeerConnection_OnIceCandidate(IRTCPeerConnectionIceEvent Event) { if (Event.Candidate == null) { return; } double index = (double)Event.Candidate.SdpMLineIndex; JsonObject json = null; json = new JsonObject { { NegotiationAtributes.SdpMid, JsonValue.CreateStringValue(Event.Candidate.SdpMid) }, { NegotiationAtributes.SdpMLineIndex, JsonValue.CreateNumberValue(index) }, { NegotiationAtributes.Candidate, JsonValue.CreateStringValue(Event.Candidate.Candidate) } }; Debug.WriteLine($"Send ice candidate:\n{json.Stringify()}"); OnSendMessageToRemotePeer.Invoke(this, json.Stringify()); }
public void MessageFromPeerTaskRun(int peerId, string content) { PeerId = peerId; Task.Run(async() => { Debug.Assert(_peerId == PeerId || _peerId == -1); Debug.Assert(content.Length > 0); if (_peerId != PeerId && _peerId != -1) { Debug.WriteLine("Received a message from unknown peer " + "while already in a conversation with a different peer."); return; } if (!JsonObject.TryParse(content, out JsonObject jMessage)) { Debug.WriteLine($"Received unknown message: {content}"); return; } string type = jMessage.ContainsKey(NegotiationAtributes.Type) ? jMessage.GetNamedString(NegotiationAtributes.Type) : null; if (PeerConnection == null) { if (!string.IsNullOrEmpty(type)) { // Create the peer connection only when call is // about to get initiated. Otherwise ignore the // message from peers which could be result // of old (but not yet fully closed) connections. if (type == "offer" || type == "answer" || type == "json") { Debug.Assert(_peerId == -1); _peerId = PeerId; if (!CreatePeerConnection()) { Debug.WriteLine("Failed to initialize our PeerConnection instance"); OnSignedOut.Invoke(this, null); return; } else if (_peerId != PeerId) { Debug.WriteLine("Received a message from unknown peer while already " + "in a conversation with a different peer."); return; } } } else { Debug.WriteLine("[Warn] Received an untyped message after closing peer connection."); return; } } if (PeerConnection != null && !string.IsNullOrEmpty(type)) { if (type == "offer-loopback") { // Loopback not supported Debug.Assert(false); } string sdp = null; sdp = jMessage.ContainsKey(NegotiationAtributes.Sdp) ? jMessage.GetNamedString(NegotiationAtributes.Sdp) : null; if (string.IsNullOrEmpty(sdp)) { Debug.WriteLine("[Error] Can't parse received session description message."); return; } Debug.WriteLine($"Received session description:\n{content}"); RTCSdpType messageType = RTCSdpType.Offer; switch (type) { case "offer": messageType = RTCSdpType.Offer; break; case "answer": messageType = RTCSdpType.Answer; break; case "pranswer": messageType = RTCSdpType.Pranswer; break; default: Debug.Assert(false, type); break; } var sdpInit = new RTCSessionDescriptionInit(); sdpInit.Sdp = sdp; sdpInit.Type = messageType; var description = new RTCSessionDescription(sdpInit); await PeerConnection.SetRemoteDescription(description); if (messageType == RTCSdpType.Offer) { var answerOptions = new RTCAnswerOptions(); IRTCSessionDescription answer = await PeerConnection.CreateAnswer(answerOptions); await PeerConnection.SetLocalDescription(answer); string jsonString = SdpToJsonString(answer); // Send answer OnSendMessageToRemotePeer.Invoke(this, jsonString); } } else { RTCIceCandidate candidate = null; string sdpMid = jMessage.ContainsKey(NegotiationAtributes.SdpMid) ? jMessage.GetNamedString(NegotiationAtributes.SdpMid) : null; double sdpMLineIndex = jMessage.ContainsKey(NegotiationAtributes.SdpMLineIndex) ? jMessage.GetNamedNumber(NegotiationAtributes.SdpMLineIndex) : -1; string sdpCandidate = jMessage.ContainsKey(NegotiationAtributes.Candidate) ? jMessage.GetNamedString(NegotiationAtributes.Candidate) : null; if (string.IsNullOrEmpty(sdpMid) || sdpMLineIndex == -1 || string.IsNullOrEmpty(sdpCandidate)) { Debug.WriteLine($"[Error] Can't parse received message.\n{content}"); return; } var candidateInit = new RTCIceCandidateInit(); candidateInit.Candidate = sdpCandidate; candidateInit.SdpMid = sdpMid; candidateInit.SdpMLineIndex = (ushort)sdpMLineIndex; candidate = new RTCIceCandidate(candidateInit); await PeerConnection.AddIceCandidate(candidate); Debug.WriteLine($"Receiving ice candidate:\n{content}"); } }).Wait(); }