public IEnumerator MediaStreamTrackThrowExceptionAfterPeerDisposed() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; AudioStreamTrack track = new AudioStreamTrack(); peer1.AddTrack(track); MediaStreamTrack track1 = null; peer2.OnTrack = e => { track1 = e.Track; }; yield return(SignalingOffer(peer1, peer2)); Assert.That(track1, Is.Not.Null); peer2.Dispose(); Assert.That(() => track1.Id, Throws.TypeOf <InvalidOperationException>()); track.Dispose(); track1.Dispose(); }
public IEnumerator MediaStreamTrackThrowExceptionAfterPeerDisposed() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; var obj = new GameObject("audio"); var source = obj.AddComponent <AudioSource>(); source.clip = AudioClip.Create("test", 480, 2, 48000, false); AudioStreamTrack track = new AudioStreamTrack(source); peer1.AddTrack(track); MediaStreamTrack track1 = null; peer2.OnTrack = e => { track1 = e.Track; }; yield return(SignalingOffer(peer1, peer2)); Assert.That(track1, Is.Not.Null); peer2.Dispose(); Assert.That(() => track1.Id, Throws.TypeOf <ObjectDisposedException>()); track.Dispose(); track1.Dispose(); Object.DestroyImmediate(source.clip); Object.DestroyImmediate(obj); }
private void Call() { Debug.Log("Starting calls"); pcLocal = new RTCPeerConnection(ref configuration); pcRemote = new RTCPeerConnection(ref configuration); pcRemote.OnTrack = e => receiveVideoStream.AddTrack(e.Track); pcLocal.OnIceCandidate = candidate => pcRemote.AddIceCandidate(candidate); pcRemote.OnIceCandidate = candidate => pcLocal.AddIceCandidate(candidate); Debug.Log("pc1: created local and remote peer connection object"); foreach (var track in sourceVideoStream.GetTracks()) { pcLocal.AddTrack(track, sourceVideoStream); } Debug.Log("Adding local stream to pcLocal"); callButton.interactable = false; createOfferButton.interactable = true; createAnswerButton.interactable = true; setOfferButton.interactable = true; setAnswerButton.interactable = true; hangUpButton.interactable = true; }
public IEnumerator TransceiverReturnsSender() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; AudioStreamTrack track1 = new AudioStreamTrack(); peer1.AddTrack(track1); yield return(SignalingOffer(peer1, peer2)); Assert.That(peer2.GetTransceivers().Count(), Is.EqualTo(1)); RTCRtpSender sender1 = peer2.GetTransceivers().First().Sender; Assert.That(sender1, Is.Not.Null); AudioStreamTrack track2 = new AudioStreamTrack(); RTCRtpSender sender2 = peer2.AddTrack(track2); Assert.That(sender2, Is.Not.Null); Assert.That(sender1, Is.EqualTo(sender2)); track1.Dispose(); track2.Dispose(); peer1.Dispose(); peer2.Dispose(); }
/// <summary> /// Process JSON messages received from the remote peer. The only messages will be the SDP answer and /// ICE candidates. /// </summary> private void OnMessage(string jsonStr, RTCPeerConnection pc) { var msgType = JsonRTC.TryGetType(jsonStr); Debug.WriteLine($"JSON message type {msgType}."); switch (msgType) { case JsonRTC.JsonMessageType.IceCandidate: if (JsonRTC.TryParseIceCandidate(jsonStr, out var iceCandidateInit)) { Debug.WriteLine($"Got remote ICE candidate, {iceCandidateInit.candidate}."); _pc.AddIceCandidate(ref iceCandidateInit); } break; case JsonRTC.JsonMessageType.SdpDescription: if (JsonRTC.TryParseDescription(jsonStr, out var desc)) { Debug.WriteLine($"Got remote SDP, type {desc.type}."); Debug.WriteLine(desc.sdp); var setRemoteOp = _pc.SetRemoteDescription(ref desc); bool isDone = setRemoteOp.MoveNext(); Debug.WriteLine($"Set remote description is done {isDone}, error {setRemoteOp.IsError}, signalling state {pc.SignalingState}."); } break; default: Debug.WriteLine($"node-dss could not parse JSON message. {jsonStr}"); break; } }
private void OnWSMessage(object sender, MessageEventArgs args) { var message = JsonUtility.FromJson <SignalingMessage>(args.Data); var data = message.data; switch (message.type) { case "message": Debug.Log($"Receive a message from Node: {data.message}"); break; case "offer": Debug.Log("Receive an offer from Node"); var offerDesc = new RTCSessionDescription { type = RTCSdpType.Offer, sdp = data.sdp }; onOfferCallBack = state => { StartCoroutine(OnOffer(offerDesc)); }; mainThreadSyncCtx.Post(onOfferCallBack, null); break; case "candidate": var candidateInfo = new RTCIceCandidateInit { candidate = data.candidate, sdpMid = data.sdpMid, sdpMLineIndex = data.sdpMLineIndex }; pc.AddIceCandidate(new RTCIceCandidate(candidateInfo)); break; default: Debug.Log($"Receive unknown message type: {message.type}"); break; } }
private void Call() { Debug.Log("Starting calls"); pc1Local = new RTCPeerConnection(ref configuration); pc1Remote = new RTCPeerConnection(ref configuration); pc1Remote.OnTrack = e => receiveVideoStream1.AddTrack(e.Track); pc1Local.OnIceCandidate = candidate => pc1Remote.AddIceCandidate(ref candidate); pc1Remote.OnIceCandidate = candidate => pc1Local.AddIceCandidate(ref candidate); Debug.Log("pc1: created local and remote peer connection object"); pc2Local = new RTCPeerConnection(ref configuration); pc2Remote = new RTCPeerConnection(ref configuration); pc2Remote.OnTrack = e => receiveVideoStream2.AddTrack(e.Track); pc2Local.OnIceCandidate = candidate => pc2Remote.AddIceCandidate(ref candidate); pc2Remote.OnIceCandidate = candidate => pc2Local.AddIceCandidate(ref candidate); Debug.Log("pc2: created local and remote peer connection object"); foreach (var track in sourceVideoStream.GetTracks()) { pc1Local.AddTrack(track, sourceVideoStream); pc2Local.AddTrack(track, sourceVideoStream); } Debug.Log("Adding local stream to pc1Local/pc2Local"); StartCoroutine(NegotiationPeer(pc1Local, pc1Remote)); StartCoroutine(NegotiationPeer(pc2Local, pc2Remote)); callButton.interactable = false; hangUpButton.interactable = true; }
private void onIceCandidate(string clientId, string candidate, string sdpMid, int sdpMLineIndex) { var cand = new RTCIceCandidate { candidate = candidate, sdpMid = sdpMid, sdpMLineIndex = sdpMLineIndex }; pc.AddIceCandidate(ref cand); }
public IEnumerator RemoteOnRemoveTrack() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; var stream = new MediaStream(); MediaStream receiveStream = null; var track = new AudioStreamTrack(); stream.AddTrack(track); RTCRtpSender sender = peer1.AddTrack(track, stream); bool isInvokeNegotiationNeeded1 = false; peer1.OnNegotiationNeeded = () => isInvokeNegotiationNeeded1 = true; bool isInvokeOnRemoveTrack = false; peer2.OnTrack = e => { Assert.That(e.Streams, Has.Count.EqualTo(1)); receiveStream = e.Streams.First(); receiveStream.OnRemoveTrack = ev => isInvokeOnRemoveTrack = true; }; yield return(SignalingOffer(peer1, peer2)); peer1.RemoveTrack(sender); var op9 = new WaitUntilWithTimeout(() => isInvokeNegotiationNeeded1, 5000); yield return(op9); Assert.That(op9.IsCompleted, Is.True); yield return(SignalingOffer(peer1, peer2)); var op10 = new WaitUntilWithTimeout(() => isInvokeOnRemoveTrack, 5000); yield return(op10); Assert.That(op10.IsCompleted, Is.True); stream.Dispose(); receiveStream.Dispose(); track.Dispose(); peer1.Dispose(); peer2.Dispose(); }
private static IEnumerator SignalingPeers(RTCPeerConnection offerPc, RTCPeerConnection answerPc) { offerPc.OnIceCandidate = candidate => answerPc.AddIceCandidate(ref candidate); answerPc.OnIceCandidate = candidate => offerPc.AddIceCandidate(ref candidate); var offerOption = new RTCOfferOptions {offerToReceiveVideo = true}; var answerOption = new RTCAnswerOptions {iceRestart = false}; var pc1CreateOffer = offerPc.CreateOffer(ref offerOption); yield return pc1CreateOffer; Assert.False(pc1CreateOffer.IsError); var offerDesc = pc1CreateOffer.Desc; var pc1SetLocalDescription = offerPc.SetLocalDescription(ref offerDesc); yield return pc1SetLocalDescription; Assert.False(pc1SetLocalDescription.IsError); var pc2SetRemoteDescription = answerPc.SetRemoteDescription(ref offerDesc); yield return pc2SetRemoteDescription; Assert.False(pc2SetRemoteDescription.IsError); var pc2CreateAnswer = answerPc.CreateAnswer(ref answerOption); yield return pc2CreateAnswer; Assert.False(pc2CreateAnswer.IsError); var answerDesc = pc2CreateAnswer.Desc; var pc2SetLocalDescription = answerPc.SetLocalDescription(ref answerDesc); yield return pc2SetLocalDescription; Assert.False(pc2SetLocalDescription.IsError); var pc1SetRemoteDescription = offerPc.SetRemoteDescription(ref answerDesc); yield return pc1SetRemoteDescription; Assert.False(pc1SetRemoteDescription.IsError); var waitConnectOfferPc = new WaitUntilWithTimeout(() => offerPc.IceConnectionState == RTCIceConnectionState.Connected || offerPc.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return waitConnectOfferPc; Assert.True(waitConnectOfferPc.IsCompleted); var waitConnectAnswerPc = new WaitUntilWithTimeout(() => answerPc.IceConnectionState == RTCIceConnectionState.Connected || answerPc.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return waitConnectAnswerPc; Assert.True(waitConnectAnswerPc.IsCompleted); var checkSenders = new WaitUntilWithTimeout(() => offerPc.GetSenders().Any(), 5000); yield return checkSenders; Assert.True(checkSenders.IsCompleted); var checkReceivers = new WaitUntilWithTimeout(() => answerPc.GetReceivers().Any(), 5000); yield return checkReceivers; Assert.True(checkReceivers.IsCompleted); }
private void Signaling_OnIceCandidate(AntMediaSignalingMessage msg) { var candidate = new RTCIceCandidate(new RTCIceCandidateInit { candidate = msg.candidate, sdpMLineIndex = msg.label, sdpMid = msg.id }); peer.AddIceCandidate(candidate); }
private void OnIceCandidate(RTCIceCandidate e) { if (!string.IsNullOrEmpty(e.candidate)) { mPeer.AddIceCandidate(ref e); DebugUtility.Log(LoggerTags.Online, "{0} ICE candidate:\n {1}", host, e.candidate); } else { DebugUtility.Log(LoggerTags.Online, "{0} ICE empty candidate:\n", host); } }
private void Signaling_OnIceCandidate(AntMediaSignalingMessage msg) { OnLogEvent.Invoke("Signaling OnIceCandidate", ""); OnLogEvent.Invoke("AddIceCandidate", ""); var candidate = new RTCIceCandidate(new RTCIceCandidateInit { candidate = msg.candidate, sdpMLineIndex = msg.label, sdpMid = msg.id }); peer.AddIceCandidate(candidate); }
public void OnMessage(Message message) { if (message.candidate != null) { if (!pc.AddIceCandidate(message.candidate) && !ignoreOffer) { Debug.LogError($"{this} this candidate can't accept current signaling state {pc.SignalingState}."); } return; } parent.StartCoroutine(OfferAnswerProcess(message.description)); }
public IEnumerator RestartIceInvokeOnNegotiationNeeded() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; var obj = new GameObject("audio"); var source = obj.AddComponent <AudioSource>(); source.clip = AudioClip.Create("test", 480, 2, 48000, false); AudioStreamTrack track = new AudioStreamTrack(source); peer1.AddTrack(track); yield return(SignalingOffer(peer1, peer2)); bool isInvokeOnNegotiationNeeded1 = false; bool isInvokeOnNegotiationNeeded2 = false; peer1.OnNegotiationNeeded = () => isInvokeOnNegotiationNeeded1 = true; peer2.OnNegotiationNeeded = () => isInvokeOnNegotiationNeeded2 = true; peer1.RestartIce(); var op9 = new WaitUntilWithTimeout(() => isInvokeOnNegotiationNeeded1, 5000); yield return(op9); Assert.That(op9.IsCompleted, Is.True); peer2.RestartIce(); var op10 = new WaitUntilWithTimeout(() => isInvokeOnNegotiationNeeded2, 5000); yield return(op10); Assert.That(op10.IsCompleted, Is.True); track.Dispose(); peer1.Close(); peer2.Close(); Object.DestroyImmediate(source.clip); Object.DestroyImmediate(obj); }
void RecieveIceCandidate() { _signalingNCMB.FetchObject((obj) => { var json = NCMB_RTC.GetJson_SDPData(obj, _IsOffer); var data = JsonConverter.FromJson <RTCSendData>(json); var remoteICE = new List <RTCIceCandidate>(); foreach (var target in data.candidateJson) { remoteICE.Add(JsonConverter.FromJson <RTCIceCandidate>(target)); } remoteICE.ForEach(x => localConnection.AddIceCandidate(ref x)); Debug.Log($"recieveJson {gameObject.name}"); }); }
public IEnumerator TransceiverReturnsSender() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; var obj1 = new GameObject("audio1"); var source1 = obj1.AddComponent <AudioSource>(); source1.clip = AudioClip.Create("test1", 480, 2, 48000, false); AudioStreamTrack track1 = new AudioStreamTrack(source1); peer1.AddTrack(track1); yield return(SignalingOffer(peer1, peer2)); Assert.That(peer2.GetTransceivers().Count(), Is.EqualTo(1)); RTCRtpSender sender1 = peer2.GetTransceivers().First().Sender; Assert.That(sender1, Is.Not.Null); var obj2 = new GameObject("audio2"); var source2 = obj2.AddComponent <AudioSource>(); source2.clip = AudioClip.Create("test2", 480, 2, 48000, false); AudioStreamTrack track2 = new AudioStreamTrack(source2); RTCRtpSender sender2 = peer2.AddTrack(track2); Assert.That(sender2, Is.Not.Null); Assert.That(sender1, Is.EqualTo(sender2)); track1.Dispose(); track2.Dispose(); peer1.Dispose(); peer2.Dispose(); Object.DestroyImmediate(source1.clip); Object.DestroyImmediate(source2.clip); Object.DestroyImmediate(obj1); Object.DestroyImmediate(obj2); }
public IEnumerator RestartIceInvokeOnNegotiationNeeded() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; AudioStreamTrack track = new AudioStreamTrack(); peer1.AddTrack(track); yield return(SignalingOffer(peer1, peer2)); bool isInvokeOnNegotiationNeeded1 = false; bool isInvokeOnNegotiationNeeded2 = false; peer1.OnNegotiationNeeded = () => isInvokeOnNegotiationNeeded1 = true; peer2.OnNegotiationNeeded = () => isInvokeOnNegotiationNeeded2 = true; peer1.RestartIce(); var op9 = new WaitUntilWithTimeout(() => isInvokeOnNegotiationNeeded1, 5000); yield return(op9); Assert.That(op9.IsCompleted, Is.True); peer2.RestartIce(); var op10 = new WaitUntilWithTimeout(() => isInvokeOnNegotiationNeeded2, 5000); yield return(op10); Assert.That(op10.IsCompleted, Is.True); track.Dispose(); peer1.Close(); peer2.Close(); }
private void onTextData(WebSocket client, string data) { var msg = JsonUtility.FromJson <SignalingMessage>(data); if (msg.type == "candidate") { log.Print("on ice candidate"); RTCIceCandidate candidate; candidate.candidate = msg.candidate; candidate.sdpMid = msg.sdpMid; candidate.sdpMLineIndex = msg.sdpMLineIndex; log.Print("AddIceCandidate"); pc.AddIceCandidate(ref candidate); } else if (msg.type == "answer") { log.Print("on answer"); StartCoroutine(proccessAnswer(msg.sdp)); } }
private void HandleWebsocketMessage(byte[] data) { SignalingMessage message = JsonUtility.FromJson <SignalingMessage>(Encoding.UTF8.GetString(data)); Debug.LogFormat("Receivied message with type {0}", message.type); if (localConnection == null) { CreatePeerConnection(); } if (!string.IsNullOrEmpty(message.sdp)) { StartCoroutine(SetDescription(message.ToDesc(), Side.Remote)); } if (!string.IsNullOrEmpty(message.candidate)) { localConnection.AddIceCandidate(message.ToCand()); } }
private void Call() { Debug.Log("Starting calls"); pcLocal = new RTCPeerConnection(ref configuration); pcRemote = new RTCPeerConnection(ref configuration); pcRemote.OnTrack = e => receiveVideoStream.AddTrack(e.Track); pcLocal.OnIceCandidate = candidate => pcRemote.AddIceCandidate(candidate); pcRemote.OnIceCandidate = candidate => pcLocal.AddIceCandidate(candidate); Debug.Log("pc1: created local and remote peer connection object"); var senders = new List <RTCRtpSender>(); foreach (var track in sourceVideoStream.GetTracks()) { senders.Add(pcLocal.AddTrack(track, sourceVideoStream)); } if (WebRTCSettings.UseVideoCodec != null) { var codecs = new[] { WebRTCSettings.UseVideoCodec }; foreach (var transceiver in pcLocal.GetTransceivers()) { if (senders.Contains(transceiver.Sender)) { transceiver.SetCodecPreferences(codecs); } } } Debug.Log("Adding local stream to pcLocal"); callButton.interactable = false; createOfferButton.interactable = true; createAnswerButton.interactable = true; setOfferButton.interactable = true; setAnswerButton.interactable = true; hangUpButton.interactable = true; }
private void HandleCandidate(string room, JsonMessageData data) { Debug.Log("HandleCandidate"); if (room != roomName) { Debug.LogError("Wrong room name " + room); return; } if (data.candidate == null || data.candidate.Length == 0) { Debug.LogError("Empty candidate"); return; } var candidate = new RTCIceCandidate { candidate = data.candidate, sdpMid = data.id, sdpMLineIndex = (int)data.label }; peerConnection.AddIceCandidate(ref candidate); }
public IEnumerator Signaling() { offerPc.OnIceCandidate = candidate => answerPc.AddIceCandidate(candidate); answerPc.OnIceCandidate = candidate => offerPc.AddIceCandidate(candidate); var pc1CreateOffer = offerPc.CreateOffer(); yield return(pc1CreateOffer); Assert.That(pc1CreateOffer.IsError, Is.False, () => $"Failed {nameof(pc1CreateOffer)}, error:{pc1CreateOffer.Error.message}"); var offerDesc = pc1CreateOffer.Desc; var pc1SetLocalDescription = offerPc.SetLocalDescription(ref offerDesc); yield return(pc1SetLocalDescription); Assert.That(pc1SetLocalDescription.IsError, Is.False, () => $"Failed {nameof(pc1SetLocalDescription)}, error:{pc1SetLocalDescription.Error.message}"); var pc2SetRemoteDescription = answerPc.SetRemoteDescription(ref offerDesc); yield return(pc2SetRemoteDescription); Assert.That(pc2SetRemoteDescription.IsError, Is.False, () => $"Failed {nameof(pc2SetRemoteDescription)}, error:{pc2SetRemoteDescription.Error.message}"); var pc2CreateAnswer = answerPc.CreateAnswer(); yield return(pc2CreateAnswer); Assert.That(pc2CreateAnswer.IsError, Is.False, () => $"Failed {nameof(pc2CreateAnswer)}, error:{pc2CreateAnswer.Error.message}"); var answerDesc = pc2CreateAnswer.Desc; var pc2SetLocalDescription = answerPc.SetLocalDescription(ref answerDesc); yield return(pc2SetLocalDescription); Assert.That(pc2SetLocalDescription.IsError, Is.False, () => $"Failed {nameof(pc2SetLocalDescription)}, error:{pc2SetLocalDescription.Error.message}"); var pc1SetRemoteDescription = offerPc.SetRemoteDescription(ref answerDesc); yield return(pc1SetRemoteDescription); Assert.That(pc1SetRemoteDescription.IsError, Is.False, () => $"Failed {nameof(pc1SetRemoteDescription)}, error:{pc1SetRemoteDescription.Error.message}"); var waitConnectOfferPc = new WaitUntilWithTimeout(() => offerPc.IceConnectionState == RTCIceConnectionState.Connected || offerPc.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return(waitConnectOfferPc); Assert.That(waitConnectOfferPc.IsCompleted, Is.True); var waitConnectAnswerPc = new WaitUntilWithTimeout(() => answerPc.IceConnectionState == RTCIceConnectionState.Connected || answerPc.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return(waitConnectAnswerPc); Assert.That(waitConnectAnswerPc.IsCompleted, Is.True); var checkSenders = new WaitUntilWithTimeout(() => offerPc.GetSenders().Any(), 5000); yield return(checkSenders); Assert.That(checkSenders.IsCompleted, Is.True); var checkReceivers = new WaitUntilWithTimeout(() => answerPc.GetReceivers().Any(), 5000); yield return(checkReceivers); Assert.That(checkReceivers.IsCompleted, Is.True); }
private void Call() { Debug.Log("Starting calls"); pc1Local = new RTCPeerConnection(ref configuration); pc1Remote = new RTCPeerConnection(ref configuration); pc1Remote.OnTrack = e => { if (e.Track is VideoStreamTrack videoTrack) { videoTrack.OnVideoReceived += tex => { receiveImage1.texture = tex; }; } if (e.Track is AudioStreamTrack audioTrack) { receiveAudio1.SetTrack(audioTrack); receiveAudio1.loop = true; receiveAudio1.Play(); } }; pc1Local.OnIceCandidate = candidate => pc1Remote.AddIceCandidate(candidate); pc1Remote.OnIceCandidate = candidate => pc1Local.AddIceCandidate(candidate); Debug.Log("pc1: created local and remote peer connection object"); pc2Local = new RTCPeerConnection(ref configuration); pc2Remote = new RTCPeerConnection(ref configuration); pc2Remote.OnTrack = e => { if (e.Track is VideoStreamTrack videoTrack) { videoTrack.OnVideoReceived += tex => { receiveImage2.texture = tex; }; } if (e.Track is AudioStreamTrack audioTrack) { receiveAudio2.SetTrack(audioTrack); receiveAudio2.loop = true; receiveAudio2.Play(); } }; pc2Local.OnIceCandidate = candidate => pc2Remote.AddIceCandidate(candidate); pc2Remote.OnIceCandidate = candidate => pc2Local.AddIceCandidate(candidate); Debug.Log("pc2: created local and remote peer connection object"); var pc1VideoSenders = new List <RTCRtpSender>(); var pc2VideoSenders = new List <RTCRtpSender>(); foreach (var track in sourceStream.GetTracks()) { var pc1Sender = pc1Local.AddTrack(track, sourceStream); var pc2Sender = pc2Local.AddTrack(track, sourceStream); if (track.Kind == TrackKind.Video) { pc1VideoSenders.Add(pc1Sender); pc2VideoSenders.Add(pc2Sender); } } if (WebRTCSettings.UseVideoCodec != null) { var codecs = new[] { WebRTCSettings.UseVideoCodec }; foreach (var transceiver in pc1Local.GetTransceivers()) { if (pc1VideoSenders.Contains(transceiver.Sender)) { transceiver.SetCodecPreferences(codecs); } } foreach (var transceiver in pc2Local.GetTransceivers()) { if (pc2VideoSenders.Contains(transceiver.Sender)) { transceiver.SetCodecPreferences(codecs); } } } Debug.Log("Adding local stream to pc1Local/pc2Local"); StartCoroutine(NegotiationPeer(pc1Local, pc1Remote)); StartCoroutine(NegotiationPeer(pc2Local, pc2Remote)); callButton.interactable = false; hangUpButton.interactable = true; }
public IEnumerator IceConnectionStateChange() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(ref candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(ref candidate); }; MediaStream stream = Audio.CaptureStream(); peer1.AddTrack(stream.GetTracks().First()); RTCOfferOptions options1 = default; RTCAnswerOptions options2 = default; var op1 = peer1.CreateOffer(ref options1); yield return(op1); var desc = op1.Desc; var op2 = peer1.SetLocalDescription(ref desc); yield return(op2); var op3 = peer2.SetRemoteDescription(ref desc); yield return(op3); var op4 = peer2.CreateAnswer(ref options2); yield return(op4); desc = op4.Desc; var op5 = peer2.SetLocalDescription(ref desc); yield return(op5); var op6 = peer1.SetRemoteDescription(ref desc); yield return(op6); var op7 = new WaitUntilWithTimeout( () => peer1.IceConnectionState == RTCIceConnectionState.Connected || peer1.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return(op7); Assert.True(op7.IsCompleted); var op8 = new WaitUntilWithTimeout( () => peer2.IceConnectionState == RTCIceConnectionState.Connected || peer2.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return(op8); Assert.True(op8.IsCompleted); stream.Dispose(); peer1.Close(); peer2.Close(); }
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(); }
public IEnumerator DataChannel_EventsAreSentToOther() { RTCConfiguration config = default; config.iceServers = new RTCIceServer[] { new RTCIceServer { urls = new string[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); RTCDataChannel channel1 = null, channel2 = null; peer1.OnIceCandidate = new DelegateOnIceCandidate(candidate => { peer2.AddIceCandidate(ref candidate); }); peer2.OnIceCandidate = new DelegateOnIceCandidate(candidate => { peer1.AddIceCandidate(ref candidate); }); peer2.OnDataChannel = new DelegateOnDataChannel(channel => { channel2 = channel; }); var conf = new RTCDataChannelInit(true); channel1 = peer1.CreateDataChannel("data", ref conf); RTCOfferOptions options1 = default; RTCAnswerOptions options2 = default; var op1 = peer1.CreateOffer(ref options1); yield return(op1); var op2 = peer1.SetLocalDescription(ref op1.desc); yield return(op2); var op3 = peer2.SetRemoteDescription(ref op1.desc); yield return(op3); var op4 = peer2.CreateAnswer(ref options2); yield return(op4); var op5 = peer2.SetLocalDescription(ref op4.desc); yield return(op5); var op6 = peer1.SetRemoteDescription(ref op4.desc); yield return(op6); yield return(new WaitUntil(() => peer1.IceConnectionState == RTCIceConnectionState.Connected || peer1.IceConnectionState == RTCIceConnectionState.Completed)); yield return(new WaitUntil(() => peer2.IceConnectionState == RTCIceConnectionState.Connected || peer2.IceConnectionState == RTCIceConnectionState.Completed)); yield return(new WaitUntil(() => channel2 != null)); Assert.AreEqual(channel1.Label, channel2.Label); Assert.AreEqual(channel1.Id, channel2.Id); string message1 = "hello"; string message2 = null; channel2.OnMessage = new DelegateOnMessage(bytes => { message2 = System.Text.Encoding.UTF8.GetString(bytes); }); channel1.Send(message1); yield return(new WaitUntil(() => !string.IsNullOrEmpty(message2))); Assert.AreEqual(message1, message2); byte[] message3 = { 1, 2, 3 }; byte[] message4 = null; channel2.OnMessage = new DelegateOnMessage(bytes => { message4 = bytes; }); channel1.Send(message3); yield return(new WaitUntil(() => message4 != null)); Assert.AreEqual(message3, message4); peer1.Close(); peer2.Close(); }
public IEnumerator PeerConnectionStateChange() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); RTCPeerConnectionState state1 = default; RTCPeerConnectionState state2 = default; RTCIceConnectionState iceState1 = default; RTCIceConnectionState iceState2 = default; peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; peer1.OnConnectionStateChange = state => { state1 = state; }; peer2.OnConnectionStateChange = state => { state2 = state; }; peer1.OnIceConnectionChange = state => { iceState1 = state; }; peer2.OnIceConnectionChange = state => { iceState2 = state; }; Assert.That(state1, Is.EqualTo(RTCPeerConnectionState.New)); Assert.That(state2, Is.EqualTo(RTCPeerConnectionState.New)); var obj = new GameObject("audio"); var source = obj.AddComponent <AudioSource>(); source.clip = AudioClip.Create("test", 480, 2, 48000, false); AudioStreamTrack track1 = new AudioStreamTrack(source); peer1.AddTrack(track1); var op1 = peer1.CreateOffer(); yield return(op1); var desc = op1.Desc; var op2 = peer1.SetLocalDescription(ref desc); yield return(op2); var op3 = peer2.SetRemoteDescription(ref desc); yield return(op3); var op4 = peer2.CreateAnswer(); yield return(op4); desc = op4.Desc; var op5 = peer2.SetLocalDescription(ref desc); yield return(op5); var op6 = peer1.SetRemoteDescription(ref desc); yield return(op6); var op7 = new WaitUntilWithTimeout(() => state1 == RTCPeerConnectionState.Connected && state2 == RTCPeerConnectionState.Connected, 5000); yield return(op7); Assert.That(op7.IsCompleted, Is.True); var op8 = new WaitUntilWithTimeout(() => (iceState1 == RTCIceConnectionState.Connected || iceState1 == RTCIceConnectionState.Completed) && (iceState2 == RTCIceConnectionState.Connected || iceState2 == RTCIceConnectionState.Completed) , 5000); yield return(op8); Assert.That(op8.IsCompleted, Is.True); peer1.Close(); var op9 = new WaitUntilWithTimeout(() => state1 == RTCPeerConnectionState.Closed && iceState2 == RTCIceConnectionState.Disconnected, 5000); yield return(op9); Assert.That(op9.IsCompleted, Is.True); track1.Dispose(); peer2.Close(); Object.DestroyImmediate(source.clip); Object.DestroyImmediate(obj); }
/// <summary> /// Handler for Signaller's OnMessageFromPeer event. /// </summary> /// <param name="peerId">ID of the peer.</param> /// <param name="message">Message from the peer.</param> private void Signaller_OnMessageFromPeer(int peerId, string message) { Task.Run(async() => { Debug.Assert(_peerId == peerId || _peerId == -1); Debug.Assert(message.Length > 0); if (_peerId != peerId && _peerId != -1) { Debug.WriteLine("[Error] Conductor: Received a message from unknown peer while already in a conversation with a different peer."); return; } JsonObject jMessage; if (!JsonObject.TryParse(message, out jMessage)) { Debug.WriteLine("[Error] Conductor: Received unknown message." + message); return; } string type = jMessage.ContainsKey(kSessionDescriptionTypeName) ? jMessage.GetNamedString(kSessionDescriptionTypeName) : null; #if ORTCLIB bool created = false; #endif if (_peerConnection == null) { if (!IsNullOrEmpty(type)) { // Create the peer connection only when call is // about to get initiated. Otherwise ignore the // messages from peers which could be a result // of old (but not yet fully closed) connections. if (type == "offer" || type == "answer" || type == "json") { Debug.Assert(_peerId == -1); _peerId = peerId; IEnumerable <Peer> enumerablePeer = Peers.Where(x => x.Id == peerId); Peer = enumerablePeer.First(); #if ORTCLIB created = true; _signalingMode = Helper.SignalingModeForClientName(Peer.Name); #endif _connectToPeerCancelationTokenSource = new CancellationTokenSource(); _connectToPeerTask = CreatePeerConnection(_connectToPeerCancelationTokenSource.Token); bool connectResult = await _connectToPeerTask; _connectToPeerTask = null; _connectToPeerCancelationTokenSource.Dispose(); if (!connectResult) { Debug.WriteLine("[Error] Conductor: Failed to initialize our PeerConnection instance"); await Signaller.SignOut(); return; } else if (_peerId != peerId) { Debug.WriteLine("[Error] Conductor: Received a message from unknown peer while already in a conversation with a different peer."); return; } } } else { Debug.WriteLine("[Warn] Conductor: Received an untyped message after closing peer connection."); return; } } if (_peerConnection != null && !IsNullOrEmpty(type)) { if (type == "offer-loopback") { // Loopback not supported Debug.Assert(false); } string sdp = null; #if ORTCLIB if (jMessage.ContainsKey(kSessionDescriptionJsonName)) { var containerObject = new JsonObject { { kSessionDescriptionJsonName, jMessage.GetNamedObject(kSessionDescriptionJsonName) } }; sdp = containerObject.Stringify(); } else if (jMessage.ContainsKey(kSessionDescriptionSdpName)) { sdp = jMessage.GetNamedString(kSessionDescriptionSdpName); } #else sdp = jMessage.ContainsKey(kSessionDescriptionSdpName) ? jMessage.GetNamedString(kSessionDescriptionSdpName) : null; #endif if (IsNullOrEmpty(sdp)) { Debug.WriteLine("[Error] Conductor: Can't parse received session description message."); return; } #if ORTCLIB RTCSessionDescriptionSignalingType messageType = RTCSessionDescriptionSignalingType.SdpOffer; switch (type) { case "json": messageType = RTCSessionDescriptionSignalingType.Json; break; case "offer": messageType = RTCSessionDescriptionSignalingType.SdpOffer; break; case "answer": messageType = RTCSessionDescriptionSignalingType.SdpAnswer; break; case "pranswer": messageType = RTCSessionDescriptionSignalingType.SdpPranswer; break; default: Debug.Assert(false, type); break; } #else 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; } #endif Debug.WriteLine("Conductor: Received session description: " + message); await _peerConnection.SetRemoteDescription(new RTCSessionDescription(messageType, sdp)); #if ORTCLIB if ((messageType == RTCSessionDescriptionSignalingType.SdpOffer) || ((created) && (messageType == RTCSessionDescriptionSignalingType.Json))) #else if (messageType == RTCSdpType.Offer) #endif { var answer = await _peerConnection.CreateAnswer(); await _peerConnection.SetLocalDescription(answer); // Send answer SendSdp(answer); #if ORTCLIB OrtcStatsManager.Instance.StartCallWatch(SessionId, false); #endif } } else { RTCIceCandidate candidate = null; #if ORTCLIB if (RTCPeerConnectionSignalingMode.Json != _signalingMode) #endif { var sdpMid = jMessage.ContainsKey(kCandidateSdpMidName) ? jMessage.GetNamedString(kCandidateSdpMidName) : null; var sdpMlineIndex = jMessage.ContainsKey(kCandidateSdpMlineIndexName) ? jMessage.GetNamedNumber(kCandidateSdpMlineIndexName) : -1; var sdp = jMessage.ContainsKey(kCandidateSdpName) ? jMessage.GetNamedString(kCandidateSdpName) : null; //TODO: Check is this proper condition ((String.IsNullOrEmpty(sdpMid) && (sdpMlineIndex == -1)) || String.IsNullOrEmpty(sdp)) if (IsNullOrEmpty(sdpMid) || sdpMlineIndex == -1 || IsNullOrEmpty(sdp)) { Debug.WriteLine("[Error] Conductor: Can't parse received message.\n" + message); return; } #if ORTCLIB candidate = IsNullOrEmpty(sdpMid) ? RTCIceCandidate.FromSdpStringWithMLineIndex(sdp, (ushort)sdpMlineIndex) : RTCIceCandidate.FromSdpStringWithMid(sdp, sdpMid); #else candidate = new RTCIceCandidate(sdp, sdpMid, (ushort)sdpMlineIndex); #endif } #if ORTCLIB else { candidate = RTCIceCandidate.FromJsonString(message); } _peerConnection?.AddIceCandidate(candidate); #else await _peerConnection.AddIceCandidate(candidate); #endif Debug.WriteLine("Conductor: Received candidate : " + message); } }).Wait(); }
public IEnumerator EventsAreSentToOther() { RTCConfiguration config = default; config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); RTCDataChannel channel2 = null; peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); }; peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); }; peer2.OnDataChannel = channel => { channel2 = channel; }; var channel1 = peer1.CreateDataChannel("data"); bool channel1Opened = false; bool channel1Closed = false; channel1.OnOpen = () => { channel1Opened = true; }; channel1.OnClose = () => { channel1Closed = true; }; RTCOfferOptions options1 = default; RTCAnswerOptions options2 = default; var op1 = peer1.CreateOffer(ref options1); yield return(op1); var desc = op1.Desc; var op2 = peer1.SetLocalDescription(ref desc); yield return(op2); var op3 = peer2.SetRemoteDescription(ref desc); yield return(op3); var op4 = peer2.CreateAnswer(ref options2); yield return(op4); desc = op4.Desc; var op5 = peer2.SetLocalDescription(ref desc); yield return(op5); var op6 = peer1.SetRemoteDescription(ref desc); yield return(op6); var op7 = new WaitUntilWithTimeout( () => peer1.IceConnectionState == RTCIceConnectionState.Connected || peer1.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return(op7); Assert.True(op7.IsCompleted); var op8 = new WaitUntilWithTimeout( () => peer2.IceConnectionState == RTCIceConnectionState.Connected || peer2.IceConnectionState == RTCIceConnectionState.Completed, 5000); yield return(op8); Assert.True(op8.IsCompleted); var op9 = new WaitUntilWithTimeout(() => channel2 != null, 5000); yield return(op9); Assert.True(op9.IsCompleted); Assert.True(channel1Opened); Assert.AreEqual(channel1.Label, channel2.Label); Assert.AreEqual(channel1.Id, channel2.Id); const string message1 = "hello"; string message2 = null; channel2.OnMessage = bytes => { message2 = System.Text.Encoding.UTF8.GetString(bytes); }; channel1.Send(message1); var op10 = new WaitUntilWithTimeout(() => !string.IsNullOrEmpty(message2), 5000); yield return(op10); Assert.True(op10.IsCompleted); Assert.AreEqual(message1, message2); byte[] message3 = { 1, 2, 3 }; byte[] message4 = null; channel2.OnMessage = bytes => { message4 = bytes; }; channel1.Send(message3); var op11 = new WaitUntilWithTimeout(() => message4 != null, 5000); yield return(op11); Assert.True(op11.IsCompleted); Assert.AreEqual(message3, message4); channel1.Close(); var op12 = new WaitUntilWithTimeout(() => channel1Closed, 5000); yield return(op12); Assert.True(op12.IsCompleted); channel2.Close(); peer1.Close(); peer2.Close(); }