/// <summary> /// Creates JSON object from SDP. /// </summary> /// <param name="description">RTC session description.</param> /// <returns>JSON object.</returns> private string SdpToJsonString(IRTCSessionDescription description) { JsonObject json = null; Debug.WriteLine($"Sent session description: {description.Sdp}"); string messageType = null; switch (description.SdpType) { case RTCSdpType.Offer: messageType = "offer"; break; case RTCSdpType.Answer: messageType = "answer"; break; case RTCSdpType.Pranswer: messageType = "pranswer"; break; default: Debug.Assert(false, description.SdpType.ToString()); break; } json = new JsonObject { { NegotiationAtributes.Type, JsonValue.CreateStringValue(messageType) }, { NegotiationAtributes.Sdp, JsonValue.CreateStringValue(description.Sdp) } }; return(json.Stringify()); //SendMessage(json); }
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); }
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(); }