public async Task BeforeConnect() { // Add local video track channel to #1 var settings = new PeerConnection.LocalVideoTrackSettings(); LocalVideoTrack track1 = await pc1_.AddLocalVideoTrackAsync(settings); Assert.NotNull(track1); Assert.AreEqual(pc1_, track1.PeerConnection); // Wait for local SDP re-negotiation on #1. // This will not create an offer, since we're not connected yet. Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); // Connect Assert.True(pc1_.CreateOffer()); WaitForSdpExchangeCompleted(); Assert.True(pc1_.IsConnected); Assert.True(pc2_.IsConnected); // Remove the track from #1 renegotiationEvent1_.Reset(); pc1_.RemoveLocalVideoTrack(track1); Assert.IsNull(track1.PeerConnection); track1.Dispose(); // Wait for local SDP re-negotiation on #1 Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); // Confirm remote track was removed from #2 Assert.True(trackRemovedEvent2_.Wait(TimeSpan.FromSeconds(60.0))); // Wait until SDP renegotiation finished WaitForSdpExchangeCompleted(); }
public void OnClientDisconnected() { localAudioTrack?.Dispose(); localVideoTrack?.Dispose(); audioTrackSource?.Dispose(); videoTrackSource?.Dispose(); }
static async Task Main(string[] args) { Transceiver audioTransceiver = null; Transceiver videoTransceiver = null; AudioTrackSource audioTrackSource = null; VideoTrackSource videoTrackSource = null; LocalAudioTrack localAudioTrack = null; LocalVideoTrack localVideoTrack = null; try { bool needVideo = Array.Exists(args, arg => (arg == "-v") || (arg == "--video")); bool needAudio = Array.Exists(args, arg => (arg == "-a") || (arg == "--audio")); // Asynchronously retrieve a list of available video capture devices (webcams). var deviceList = await PeerConnection.GetVideoCaptureDevicesAsync(); // For example, print them to the standard output foreach (var device in deviceList) { Console.WriteLine($"Found webcam {device.name} (id: {device.id})"); } // Create a new peer connection automatically disposed at the end of the program using var pc = new PeerConnection(); // Initialize the connection with a STUN server to allow remote access var config = new PeerConnectionConfiguration { IceServers = new List <IceServer> { new IceServer { Urls = { "stun:stun.l.google.com:19302" } } } }; await pc.InitializeAsync(config); Console.WriteLine("Peer connection initialized."); // Record video from local webcam, and send to remote peer if (needVideo) { Console.WriteLine("Opening local webcam..."); videoTrackSource = await DeviceVideoTrackSource.CreateAsync(); Console.WriteLine("Create local video track..."); var trackSettings = new LocalVideoTrackInitConfig { trackName = "webcam_track" }; localVideoTrack = LocalVideoTrack.CreateFromSource(videoTrackSource, trackSettings); Console.WriteLine("Create video transceiver and add webcam track..."); videoTransceiver = pc.AddTransceiver(MediaKind.Video); videoTransceiver.DesiredDirection = Transceiver.Direction.SendReceive; videoTransceiver.LocalVideoTrack = localVideoTrack; } // Record audio from local microphone, and send to remote peer if (needAudio) { Console.WriteLine("Opening local microphone..."); audioTrackSource = await DeviceAudioTrackSource.CreateAsync(); Console.WriteLine("Create local audio track..."); var trackSettings = new LocalAudioTrackInitConfig { trackName = "mic_track" }; localAudioTrack = LocalAudioTrack.CreateFromSource(audioTrackSource, trackSettings); Console.WriteLine("Create audio transceiver and add mic track..."); audioTransceiver = pc.AddTransceiver(MediaKind.Audio); audioTransceiver.DesiredDirection = Transceiver.Direction.SendReceive; audioTransceiver.LocalAudioTrack = localAudioTrack; } // Setup signaling Console.WriteLine("Starting signaling..."); var signaler = new NamedPipeSignaler.NamedPipeSignaler(pc, "testpipe"); signaler.SdpMessageReceived += async(SdpMessage message) => { await pc.SetRemoteDescriptionAsync(message); if (message.Type == SdpMessageType.Offer) { pc.CreateAnswer(); } }; signaler.IceCandidateReceived += (IceCandidate candidate) => { pc.AddIceCandidate(candidate); }; await signaler.StartAsync(); // Start peer connection pc.Connected += () => { Console.WriteLine("PeerConnection: connected."); }; pc.IceStateChanged += (IceConnectionState newState) => { Console.WriteLine($"ICE state: {newState}"); }; int numFrames = 0; pc.VideoTrackAdded += (RemoteVideoTrack track) => { track.I420AVideoFrameReady += (I420AVideoFrame frame) => { ++numFrames; if (numFrames % 60 == 0) { Console.WriteLine($"Received video frames: {numFrames}"); } }; }; if (signaler.IsClient) { Console.WriteLine("Connecting to remote peer..."); pc.CreateOffer(); } else { Console.WriteLine("Waiting for offer from remote peer..."); } Console.WriteLine("Press a key to stop recording..."); Console.ReadKey(true); signaler.Stop(); } catch (Exception e) { Console.WriteLine(e.Message); } localAudioTrack?.Dispose(); localVideoTrack?.Dispose(); Console.WriteLine("Program termined."); localAudioTrack.Dispose(); localVideoTrack.Dispose(); audioTrackSource.Dispose(); videoTrackSource.Dispose(); }
public async Task BeforeConnect() { // Create video transceiver on #1 var transceiver_settings = new TransceiverInitSettings { Name = "transceiver1", InitialDesiredDirection = Transceiver.Direction.SendReceive }; var transceiver1 = pc1_.AddTransceiver(MediaKind.Video, transceiver_settings); Assert.NotNull(transceiver1); // Wait for local SDP re-negotiation event on #1. // This will not create an offer, since we're not connected yet. Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); // Create video track source var source1 = await DeviceVideoTrackSource.CreateAsync(); Assert.IsNotNull(source1); // Create local video track var settings = new LocalVideoTrackInitConfig(); LocalVideoTrack track1 = LocalVideoTrack.CreateFromSource(source1, settings); Assert.IsNotNull(track1); // Add local video track to #1 renegotiationEvent1_.Reset(); transceiver1.LocalVideoTrack = track1; Assert.IsFalse(renegotiationEvent1_.IsSet); // renegotiation not needed Assert.AreEqual(pc1_, track1.PeerConnection); Assert.AreEqual(track1, transceiver1.LocalTrack); Assert.IsNull(transceiver1.RemoteTrack); Assert.IsTrue(pc1_.Transceivers.Contains(transceiver1)); Assert.IsTrue(pc1_.LocalVideoTracks.Contains(track1)); Assert.AreEqual(0, pc1_.RemoteVideoTracks.Count()); // Connect StartOfferWith(pc1_); WaitForTransportsWritable(); Assert.True(pc1_.IsConnected); Assert.True(pc2_.IsConnected); // Now remote peer #2 has a 1 remote track Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(1, pc2_.RemoteVideoTracks.Count()); // Wait until the SDP exchange is completed WaitForSdpExchangeCompleted(); // Remove the track from #1 renegotiationEvent1_.Reset(); transceiver1.LocalVideoTrack = null; Assert.IsFalse(renegotiationEvent1_.IsSet); // renegotiation not needed Assert.IsNull(track1.PeerConnection); Assert.IsNull(track1.Transceiver); Assert.AreEqual(0, pc1_.LocalVideoTracks.Count()); Assert.IsTrue(pc1_.Transceivers.Contains(transceiver1)); // never removed Assert.IsNull(transceiver1.LocalTrack); Assert.IsNull(transceiver1.RemoteTrack); track1.Dispose(); // Destroy the video source source1.Dispose(); // SetLocalTrack() does not change the transceiver directions, even when the local // sending track is disposed of. Assert.AreEqual(Transceiver.Direction.SendReceive, transceiver1.DesiredDirection); Assert.AreEqual(Transceiver.Direction.SendOnly, transceiver1.NegotiatedDirection); // Remote peer #2 still has a track, because the transceiver is still receiving, // even if there is no track on the sending side (so effectively it receives only // black frames). Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(1, pc2_.RemoteVideoTracks.Count()); // Change the transceiver direction to stop receiving. This requires a renegotiation // to take effect, so nothing changes for now. // Note: In Plan B, a renegotiation needed event is manually forced for parity with // Unified Plan. However setting the transceiver to inactive removes the remote peer's // remote track, which causes another renegotiation needed event. So we suspend the // automatic offer to trigger it manually. suspendOffer1_ = true; remoteDescAppliedEvent1_.Reset(); remoteDescAppliedEvent2_.Reset(); transceiver1.DesiredDirection = Transceiver.Direction.Inactive; Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); // Renegotiate await DoNegotiationStartFrom(pc1_); Assert.True(remoteDescAppliedEvent1_.Wait(TimeSpan.FromSeconds(60.0))); Assert.True(remoteDescAppliedEvent2_.Wait(TimeSpan.FromSeconds(60.0))); // Now the remote track got removed from #2 Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc2_.RemoteVideoTracks.Count()); }
public async Task AfterConnect() { // Connect StartOfferWith(pc1_); WaitForTransportsWritable(); Assert.True(pc1_.IsConnected); Assert.True(pc2_.IsConnected); // Wait for all transceivers to be updated on both peers WaitForSdpExchangeCompleted(); Assert.True(remoteDescAppliedEvent1_.Wait(TimeSpan.FromSeconds(20.0))); Assert.True(remoteDescAppliedEvent2_.Wait(TimeSpan.FromSeconds(20.0))); remoteDescAppliedEvent1_.Reset(); remoteDescAppliedEvent2_.Reset(); // No track yet Assert.AreEqual(0, pc1_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc1_.RemoteVideoTracks.Count()); Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc2_.RemoteVideoTracks.Count()); // Create video transceiver on #1 -- this generates a renegotiation renegotiationEvent1_.Reset(); var transceiver_settings = new TransceiverInitSettings { Name = "transceiver1", InitialDesiredDirection = Transceiver.Direction.SendReceive }; var transceiver1 = pc1_.AddTransceiver(MediaKind.Video, transceiver_settings); Assert.NotNull(transceiver1); Assert.IsTrue(pc1_.Transceivers.Contains(transceiver1)); // Confirm (inactive) remote track was added on #2 due to transceiver being added Assert.True(videoTrackAddedEvent2_.Wait(TimeSpan.FromSeconds(60.0))); // Wait until SDP renegotiation finished Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); WaitForSdpExchangeCompleted(); // Now remote peer #2 has a 1 remote track (which is inactive). // Note that tracks are updated before transceivers here. This might be unintuitive, so we might // want to revisit this later. Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(1, pc2_.RemoteVideoTracks.Count()); // Transceiver has been updated to Send+Receive (desired direction when added), but since peer #2 // doesn't intend to send, the actually negotiated direction on #1 is Send only. Assert.AreEqual(Transceiver.Direction.SendReceive, transceiver1.DesiredDirection); Assert.AreEqual(Transceiver.Direction.SendOnly, transceiver1.NegotiatedDirection); // Create video track source var source1 = await DeviceVideoTrackSource.CreateAsync(); Assert.IsNotNull(source1); // Create local video track renegotiationEvent1_.Reset(); var settings = new LocalVideoTrackInitConfig(); LocalVideoTrack track1 = LocalVideoTrack.CreateFromSource(source1, settings); Assert.IsNotNull(track1); Assert.IsNull(track1.PeerConnection); Assert.IsNull(track1.Transceiver); Assert.IsFalse(renegotiationEvent1_.IsSet); // renegotiation not needed // Add local video track to #1 renegotiationEvent1_.Reset(); transceiver1.LocalVideoTrack = track1; Assert.IsFalse(renegotiationEvent1_.IsSet); // renegotiation not needed Assert.AreEqual(pc1_, track1.PeerConnection); Assert.AreEqual(track1, transceiver1.LocalTrack); Assert.IsNull(transceiver1.RemoteTrack); Assert.IsTrue(pc1_.Transceivers.Contains(transceiver1)); Assert.IsTrue(pc1_.LocalVideoTracks.Contains(track1)); Assert.AreEqual(0, pc1_.RemoteVideoTracks.Count()); // SetLocalTrack() does not change the transceiver directions Assert.AreEqual(Transceiver.Direction.SendReceive, transceiver1.DesiredDirection); Assert.AreEqual(Transceiver.Direction.SendOnly, transceiver1.NegotiatedDirection); // Remove the track from #1 renegotiationEvent1_.Reset(); transceiver1.LocalVideoTrack = null; Assert.IsFalse(renegotiationEvent1_.IsSet); // renegotiation not needed Assert.IsNull(track1.PeerConnection); Assert.IsNull(track1.Transceiver); Assert.AreEqual(0, pc1_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc1_.RemoteVideoTracks.Count()); Assert.IsTrue(pc1_.Transceivers.Contains(transceiver1)); // never removed Assert.IsNull(transceiver1.LocalTrack); Assert.IsNull(transceiver1.RemoteTrack); track1.Dispose(); // Destroy the video source source1.Dispose(); // SetLocalTrack() does not change the transceiver directions, even when the local // sending track is disposed of. Assert.AreEqual(Transceiver.Direction.SendReceive, transceiver1.DesiredDirection); Assert.AreEqual(Transceiver.Direction.SendOnly, transceiver1.NegotiatedDirection); // Remote peer #2 still has a track, because the transceiver is still receiving, // even if there is no track on the sending side (so effectively it receives only // black frames). Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(1, pc2_.RemoteVideoTracks.Count()); // Change the transceiver direction to stop receiving. This requires a renegotiation // to take effect, so nothing changes for now. // Note: In Plan B, a renegotiation needed event is manually forced for parity with // Unified Plan. However setting the transceiver to inactive removes the remote peer's // remote track, which causes another renegotiation needed event. So we suspend the // automatic offer to trigger it manually. suspendOffer1_ = true; remoteDescAppliedEvent1_.Reset(); remoteDescAppliedEvent2_.Reset(); transceiver1.DesiredDirection = Transceiver.Direction.Inactive; Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); // Renegotiate await DoNegotiationStartFrom(pc1_); Assert.True(remoteDescAppliedEvent1_.Wait(TimeSpan.FromSeconds(60.0))); Assert.True(remoteDescAppliedEvent2_.Wait(TimeSpan.FromSeconds(60.0))); // Now the remote track got removed from #2 Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc2_.RemoteVideoTracks.Count()); }