/// <summary> /// Create the local sender track from the current media track source if that source /// is active and enabled. Otherwise do nothing. /// </summary> private void CreateSenderIfNeeded() { // Only create a sender track if the source is active, i.e. has an underlying frame source. if (_senderTrack == null && _source != null && _source.isActiveAndEnabled) { if (MediaKind == MediaKind.Audio) { var audioSource = (_source as AudioTrackSource); var initConfig = new LocalAudioTrackInitConfig { trackName = _senderTrackName }; _senderTrack = LocalAudioTrack.CreateFromSource(audioSource.Source, initConfig); } else { Debug.Assert(MediaKind == MediaKind.Video); var videoSource = (_source as VideoTrackSource); var initConfig = new LocalVideoTrackInitConfig { trackName = _senderTrackName }; _senderTrack = LocalVideoTrack.CreateFromSource(videoSource.Source, initConfig); } } }
private void CreateSender() { Debug.Assert(_senderTrack == null); if (MediaKind == MediaKind.Audio) { var audioSource = (AudioTrackSource)_source; var initConfig = new LocalAudioTrackInitConfig { trackName = _senderTrackName }; _senderTrack = LocalAudioTrack.CreateFromSource(audioSource.Source, initConfig); } else { Debug.Assert(MediaKind == MediaKind.Video); var videoSource = (VideoTrackSource)_source; var initConfig = new LocalVideoTrackInitConfig { trackName = _senderTrackName }; _senderTrack = LocalVideoTrack.CreateFromSource(videoSource.Source, initConfig); } }
public async Task AddVideoTrackFromDeviceAsync(string trackName) { await RequestMediaAccessAsync(StreamingCaptureMode.Video); // Create the source VideoCaptureDeviceInfo deviceInfo = VideoCaptureDevices.SelectedItem; if (deviceInfo == null) { throw new InvalidOperationException("No video capture device selected"); } var deviceConfig = new LocalVideoDeviceInitConfig { videoDevice = new VideoCaptureDevice { id = deviceInfo.Id }, }; VideoCaptureFormatViewModel formatInfo = VideoCaptureFormats.SelectedItem; if (formatInfo != null) { deviceConfig.width = formatInfo.Format.width; deviceConfig.height = formatInfo.Format.height; deviceConfig.framerate = formatInfo.Format.framerate; } if (deviceInfo.SupportsVideoProfiles) { MediaCaptureVideoProfile profile = VideoProfiles.SelectedItem; deviceConfig.videoProfileId = profile?.Id; deviceConfig.videoProfileKind = SelectedVideoProfileKind; } var source = await DeviceVideoTrackSource.CreateAsync(deviceConfig); // FIXME - this leaks the source, never disposed // Crate the track var trackConfig = new LocalVideoTrackInitConfig { trackName = trackName, }; var track = LocalVideoTrack.CreateFromSource(source, trackConfig); // FIXME - this probably leaks the track, never disposed SessionModel.Current.VideoTracks.Add(new VideoTrackViewModel { Source = source, Track = track, TrackImpl = track, IsRemote = false, DeviceName = deviceInfo.DisplayName }); SessionModel.Current.LocalTracks.Add(new TrackViewModel(Symbol.Video) { DisplayName = deviceInfo.DisplayName }); }
public async Task VideoCall() { // This test use manual offers suspendOffer1_ = true; // Create video transceiver on #1. This triggers a renegotiation needed event. string name1 = "video_feed"; var initSettings = new TransceiverInitSettings { Name = name1, InitialDesiredDirection = Transceiver.Direction.SendOnly, StreamIDs = new List <string> { "id1", "id2" } }; var transceiver1 = pc1_.AddTransceiver(MediaKind.Video, initSettings); var track_config = new LocalVideoTrackInitConfig { trackName = "custom_i420a" }; // Add a local video track. using (var source = ExternalVideoTrackSource.CreateFromI420ACallback( VideoTrackSourceTests.CustomI420AFrameCallback)) { using (var localTrack = LocalVideoTrack.CreateFromSource(source, track_config)) { transceiver1.LocalVideoTrack = localTrack; // Connect await DoNegotiationStartFrom(pc1_); // Find the remote track Assert.AreEqual(1, pc2_.Transceivers.Count); var transceiver2 = pc2_.Transceivers[0]; var remoteTrack = transceiver2.RemoteVideoTrack; Assert.IsNotNull(remoteTrack); Assert.AreEqual(transceiver2, remoteTrack.Transceiver); Assert.AreEqual(pc2_, remoteTrack.PeerConnection); // Remote track receives frames. VideoTrackSourceTests.TestFrameReadyCallbacks(remoteTrack); // Cleanup. transceiver1.LocalVideoTrack = null; } } }
public void FrameReadyCallbacks() { var track_config = new LocalVideoTrackInitConfig { trackName = "custom_i420a" }; using (var source = ExternalVideoTrackSource.CreateFromI420ACallback( VideoTrackSourceTests.CustomI420AFrameCallback)) { using (var track = LocalVideoTrack.CreateFromSource(source, track_config)) { VideoTrackSourceTests.TestFrameReadyCallbacks(track); } } }
public MediaPlayerViewModel() { _videoPlayer.CurrentStateChanged += OnMediaStateChanged; _videoPlayer.MediaOpened += OnMediaOpened; _videoPlayer.MediaFailed += OnMediaFailed; _videoPlayer.MediaEnded += OnMediaEnded; _videoPlayer.RealTimePlayback = true; _videoPlayer.AutoPlay = false; AudioTrackTypeList.Add(new AudioTrackTypeViewModel { DisplayName = "Local microphone (default device)", Factory = async() => { // FIXME - this leaks 'source', never disposed (and is the track itself disposed??) var source = await DeviceAudioTrackSource.CreateAsync(); var settings = new LocalAudioTrackInitConfig(); return(LocalAudioTrack.CreateFromSource(source, settings)); } }); VideoTrackTypeList.Add(new VideoTrackTypeViewModel { DisplayName = "Local webcam (default device)", Factory = async() => { // FIXME - this leaks 'source', never disposed (and is the track itself disposed??) var source = await DeviceVideoTrackSource.CreateAsync(); var settings = new LocalVideoTrackInitConfig(); return(LocalVideoTrack.CreateFromSource(source, settings)); } }); _videoStatsTimer.Interval = TimeSpan.FromMilliseconds(300); _videoStatsTimer.Tick += (_1, _2) => UpdateVideoStats(); }
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 void MultiExternalI420A() { // Batch changes in this test, and manually (re)negotiate suspendOffer1_ = true; // Connect StartOfferWith(pc1_); WaitForTransportsWritable(); Assert.True(pc1_.IsConnected); Assert.True(pc2_.IsConnected); WaitForSdpExchangeCompleted(); // 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 external I420A source var source1 = ExternalVideoTrackSource.CreateFromI420ACallback( VideoTrackSourceTests.CustomI420AFrameCallback); Assert.NotNull(source1); Assert.AreEqual(0, source1.Tracks.Count()); // Add external I420A tracks const int kNumTracks = 5; var transceivers = new Transceiver[kNumTracks]; var tracks = new LocalVideoTrack[kNumTracks]; for (int i = 0; i < kNumTracks; ++i) { var transceiver_settings = new TransceiverInitSettings { Name = $"transceiver1_{i}", }; transceivers[i] = pc1_.AddTransceiver(MediaKind.Video, transceiver_settings); Assert.NotNull(transceivers[i]); var track_config = new LocalVideoTrackInitConfig { trackName = $"track_i420a_{i}" }; tracks[i] = LocalVideoTrack.CreateFromSource(source1, track_config); Assert.NotNull(tracks[i]); Assert.IsTrue(source1.Tracks.Contains(tracks[i])); transceivers[i].LocalVideoTrack = tracks[i]; Assert.AreEqual(pc1_, tracks[i].PeerConnection); Assert.IsTrue(pc1_.LocalVideoTracks.Contains(tracks[i])); } Assert.AreEqual(kNumTracks, source1.Tracks.Count()); Assert.AreEqual(kNumTracks, pc1_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc1_.RemoteVideoTracks.Count()); // Wait for local SDP re-negotiation on #1 Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); // Renegotiate StartOfferWith(pc1_); // Confirm remote track was added on #2 Assert.True(videoTrackAddedEvent2_.Wait(TimeSpan.FromSeconds(60.0))); // Wait until SDP renegotiation finished WaitForSdpExchangeCompleted(); Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(kNumTracks, pc2_.RemoteVideoTracks.Count()); // Remove the track from #1 renegotiationEvent1_.Reset(); for (int i = 0; i < kNumTracks; ++i) { transceivers[i].LocalVideoTrack = null; Assert.IsNull(tracks[i].PeerConnection); Assert.IsFalse(pc1_.LocalVideoTracks.Contains(tracks[i])); Assert.IsTrue(source1.Tracks.Contains(tracks[i])); // does not change yet tracks[i].Dispose(); tracks[i] = null; Assert.IsFalse(source1.Tracks.Contains(tracks[i])); } Assert.AreEqual(0, pc1_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc1_.RemoteVideoTracks.Count()); Assert.AreEqual(0, source1.Tracks.Count()); // Dispose of source source1.Dispose(); source1 = null; // Changes on transceiver's local track do not require renegotiation Assert.False(renegotiationEvent1_.IsSet); // Change the transceivers direction to stop sending renegotiationEvent1_.Reset(); videoTrackRemovedEvent2_.Reset(); remoteDescAppliedEvent1_.Reset(); remoteDescAppliedEvent2_.Reset(); for (int i = 0; i < kNumTracks; ++i) { transceivers[i].DesiredDirection = Transceiver.Direction.Inactive; } // Renegotiate manually the batch of changes Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); StartOfferWith(pc1_); // Wait everything to be ready WaitForSdpExchangeCompleted(); // Confirm remote tracks were removed from #2 as part of removing all transceivers Assert.True(videoTrackRemovedEvent2_.IsSet); // Remote peer #2 doesn't have any track anymore Assert.AreEqual(0, pc2_.LocalVideoTracks.Count()); Assert.AreEqual(0, pc2_.RemoteVideoTracks.Count()); }
public void SimpleExternalArgb32() { // Connect StartOfferWith(pc1_); WaitForTransportsWritable(); Assert.True(pc1_.IsConnected); Assert.True(pc2_.IsConnected); WaitForSdpExchangeCompleted(); // 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 external ARGB32 source var source1 = ExternalVideoTrackSource.CreateFromArgb32Callback( VideoTrackSourceTests.CustomArgb32FrameCallback); Assert.NotNull(source1); Assert.AreEqual(0, source1.Tracks.Count()); // Add video transceiver #1 renegotiationEvent1_.Reset(); remoteDescAppliedEvent1_.Reset(); remoteDescAppliedEvent2_.Reset(); Assert.IsFalse(videoTrackAddedEvent2_.IsSet); var transceiver_settings = new TransceiverInitSettings { Name = "transceiver1", }; var transceiver1 = pc1_.AddTransceiver(MediaKind.Video, transceiver_settings); Assert.NotNull(transceiver1); Assert.IsNull(transceiver1.LocalTrack); Assert.IsNull(transceiver1.RemoteTrack); Assert.AreEqual(pc1_, transceiver1.PeerConnection); // Wait for renegotiation Assert.True(renegotiationEvent1_.Wait(TimeSpan.FromSeconds(60.0))); Assert.True(videoTrackAddedEvent2_.Wait(TimeSpan.FromSeconds(60.0))); Assert.True(remoteDescAppliedEvent1_.Wait(TimeSpan.FromSeconds(60.0))); Assert.True(remoteDescAppliedEvent2_.Wait(TimeSpan.FromSeconds(60.0))); WaitForSdpExchangeCompleted(); // Create external ARGB32 track var track_config1 = new LocalVideoTrackInitConfig { trackName = "custom_argb32" }; var track1 = LocalVideoTrack.CreateFromSource(source1, track_config1); Assert.NotNull(track1); Assert.AreEqual(source1, track1.Source); Assert.IsNull(track1.PeerConnection); Assert.IsNull(track1.Transceiver); Assert.IsFalse(pc1_.LocalVideoTracks.Contains(track1)); // Set track on transceiver renegotiationEvent1_.Reset(); transceiver1.LocalVideoTrack = track1; Assert.AreEqual(pc1_, track1.PeerConnection); Assert.IsTrue(pc1_.LocalVideoTracks.Contains(track1)); Assert.IsFalse(renegotiationEvent1_.IsSet); // renegotiation not needed // Remove the track from #1 renegotiationEvent1_.Reset(); transceiver1.LocalVideoTrack = null; Assert.IsNull(track1.PeerConnection); Assert.IsNull(track1.Transceiver); Assert.IsFalse(pc1_.LocalVideoTracks.Contains(track1)); // Dispose of the track and its source track1.Dispose(); track1 = null; source1.Dispose(); source1 = null; // On peer #1 the track was replaced on the transceiver, but the transceiver stays // on the peer connection, so no renegotiation is needed. Assert.IsFalse(renegotiationEvent1_.IsSet); }
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()); }
static private async Task StartStend() { var autoEvent = new AutoResetEvent(false); bool video_translator = true; bool file_created = false; FileStream file = null; Quartus quartus = Quartus.GetInstance(); Microcontroller arduino = Microcontroller.Create(); if (video_translator) { // Asynchronously retrieve a list of available video capture devices (webcams). var deviceList = await DeviceVideoTrackSource.GetCaptureDevicesAsync(); // 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 var pc = new PeerConnection(); // Initialize the connection with a STUN server to allow remote access var config = SystemConfiguration.PeerConnectionSettings; await pc.InitializeAsync(config); Console.WriteLine("Peer connection initialized."); //var chen = await pc.AddDataChannelAsync("sendDataChannel", true, true, cancellationToken: default); Console.WriteLine("Opening local webcam..."); // pc - PeerConnection object Transceiver videoTransceiver = null; VideoTrackSource videoTrackSource = null; LocalVideoTrack localVideoTrack = null; LocalVideoDeviceInitConfig c = new LocalVideoDeviceInitConfig(); await VideoDeviceSelection(); videoTrackSource = await Camera.CreateAsync(SystemConfiguration.VideoDeviceSettings); WebSocketSharp.WebSocket signaling = new WebSocketSharp.WebSocket(CreateSignalingServerUrl(), "id_token", "alpine"); pc.LocalSdpReadytoSend += (SdpMessage message) => { //Console.WriteLine(SdpMessage.TypeToString(message.Type)); Console.WriteLine(message.Content); //Console.WriteLine(HttpUtility.JavaScriptStringEncode(message.Content)); Console.WriteLine("Sdp offer to send: {\"data\":{\"description\":{\"type\":\"" + SdpMessage.TypeToString(message.Type) + "\",\"sdp\":\"" + HttpUtility.JavaScriptStringEncode(message.Content) + "\"}}}"); signaling.Send(message.ToABJson()); }; pc.RenegotiationNeeded += () => { Console.WriteLine("Regotiation needed"); bool OfferCreated = pc.CreateOffer(); Console.WriteLine("OfferCreated? {0}", OfferCreated); }; pc.DataChannelAdded += (DataChannel channel) => { Console.WriteLine("Added data channel ID: {0}, Label: {1}; Reliable: {2}, Ordered: {3}", channel.ID, channel.Label, channel.Reliable, channel.Ordered); if (channel.Label == "sendDataChannel") { channel.MessageReceived += (byte[] mess) => { try { CTP_packet command = JsonSerializer.Deserialize <CTP_packet>(mess); Console.WriteLine(arduino.SendCTP_Command(command)); } catch (Exception e) { Console.WriteLine(e.Message); } }; } else { if (file_created == false) { file = new FileStream(channel.Label, FileMode.Append); file_created = true; } channel.MessageReceived += async(byte[] mess) => { // Console.WriteLine(System.Text.Encoding.Default.GetString(mess)); if (mess.Length == 3 && System.Text.Encoding.Default.GetString(mess) == "EOF") { string file_name = file.Name; file.Close(); string t = await quartus.RunQuartusCommandAsync($"quartus_pgm -m jtag –o \"p;{file_name}@1\""); File.Delete(file_name); file_created = false; } else { WriteFileSegment(mess, file); } }; } channel.StateChanged += () => { Console.WriteLine("State change: {0}", channel.State); }; }; pc.IceCandidateReadytoSend += (IceCandidate candidate) => { //Console.WriteLine("Content: {0}, SdpMid: {1}, SdpMlineIndex: {2}", candidate.Content, candidate.SdpMid, candidate.SdpMlineIndex); try { Console.WriteLine("Candidate to send: Content: {0}, SdpMid: {1}, SdpMlineIndex: {2}", candidate.Content, candidate.SdpMid, candidate.SdpMlineIndex); signaling.Send(candidate.ToABJson()); } catch (Exception e) { Console.WriteLine("Error to send local ice candidate"); } }; //videoTrackSource.I420AVideoFrameReady += (frame) => //{ // Console.WriteLine("Argb32 frame ready. {0} : {1}", frame.width, frame.height); // Console.WriteLine("DataA: {0}, DataU: {1}, DataV: {2}, DataY: {3}", Marshal.SizeOf(frame.dataA), // Marshal.SizeOf(frame.dataU), // Marshal.SizeOf(frame.dataV), // Marshal.SizeOf(frame.dataY)); //}; signaling.OnMessage += async(sender, message) => { (string header, string correct_message) = message.Data.DivideHeaderAndOriginalJSON(); Console.WriteLine("Correct message: {0}", correct_message); Console.WriteLine("Header: {0}", header); if (header == "{\"data\":{\"getRemoteMedia\":" && correct_message == "true") { Console.WriteLine("Create local video track..."); var trackSettings = new LocalVideoTrackInitConfig { trackName = "webcam_track" }; localVideoTrack = LocalVideoTrack.CreateFromSource(videoTrackSource, new LocalVideoTrackInitConfig { trackName = "webcam_track" }); Console.WriteLine("Create video transceiver and add webcam track..."); TransceiverInitSettings option = new TransceiverInitSettings(); option.Name = "webcam_track"; option.StreamIDs = new List <string> { "webcam_name" }; videoTransceiver = pc.AddTransceiver(MediaKind.Video, option); videoTransceiver.DesiredDirection = Transceiver.Direction.SendOnly; videoTransceiver.LocalVideoTrack = localVideoTrack; bool OfferCreated = pc.CreateOffer(); Console.WriteLine("OfferCreated? {0}", OfferCreated); } //Console.WriteLine(message.Data); if (header.IndexOf("candidate") != -1 && correct_message != "null") { try { var candidate = JsonSerializer.Deserialize <ICEJavaScriptNotation>(correct_message); Console.WriteLine("Content of ice: {0}, SdpMid: {1}, SdpMLineIndex: {2}", candidate.candidate, candidate.sdpMid, candidate.sdpMLineIndex); pc.AddIceCandidate(candidate.ToMRNetCoreNotation()); Console.WriteLine("Deserialized by ice_candidate"); //return; } catch (Exception) { Console.WriteLine("Could not deserialize as ice candidate"); } } if (header.IndexOf("description") != -1) { try { SdpMessage received_description = JsonSerializer.Deserialize <SDPJavaScriptNotation>(correct_message).ToMRNetCoreNotation(); await pc.SetRemoteDescriptionAsync(received_description); if (received_description.Type == SdpMessageType.Offer) { bool res = pc.CreateAnswer(); Console.WriteLine("Answer created? {0}", res); } Console.WriteLine("Deserialized by sdp_message"); } catch (Exception) { Console.WriteLine("Could not deserialize as sdp message"); } } }; pc.Connected += () => { Console.WriteLine("Connected"); }; pc.IceStateChanged += (IceConnectionState newState) => { if (newState == IceConnectionState.Disconnected) { Console.WriteLine("Disconected"); } }; signaling.Connect(); if (!video_translator) { signaling.Send("{\"data\":{\"getRemoteMedia\":true}}"); } //Console.WriteLine("Press a key to terminate the application..."); Console.ReadKey(true); Console.WriteLine("Program termined."); file?.Close(); pc?.Close(); signaling?.Close(); //arduino?.Close(); //(var a, var b) = ConvertString("{\"data\":{\"candidate\":null}}"); //Console.WriteLine("{0}, {1}", a, b); }
async void OnClientConnected() { var pc = signaler.PeerConnection; // Record video from local webcam, and send to remote peer if (NeedVideo) { // For example, print them to the standard output var deviceSettings = new LocalVideoDeviceInitConfig { width = VideoWidth, height = VideoHeight, }; if (VideoFps > 0) { deviceSettings.framerate = VideoFps; } if (VideoProfileId.Length > 0) { deviceSettings.videoProfileId = VideoProfileId; } Debug.Log($"Attempt to grab Camera - {deviceSettings.videoProfileId}: {deviceSettings.width}x{deviceSettings.height}@{deviceSettings.framerate}fps"); videoTrackSource = await DeviceVideoTrackSource.CreateAsync(deviceSettings); Debug.Log($"Create local video track... {videoTrackSource}"); var trackSettings = new LocalVideoTrackInitConfig { trackName = "webcam_track" }; localVideoTrack = LocalVideoTrack.CreateFromSource(videoTrackSource, trackSettings); Debug.Log("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) { Debug.Log("Opening local microphone..."); audioTrackSource = await DeviceAudioTrackSource.CreateAsync(); Debug.Log("Create local audio track..."); var trackSettings = new LocalAudioTrackInitConfig { trackName = "mic_track" }; localAudioTrack = LocalAudioTrack.CreateFromSource(audioTrackSource, trackSettings); Debug.Log("Create audio transceiver and add mic track..."); audioTransceiver = pc.AddTransceiver(MediaKind.Audio); audioTransceiver.DesiredDirection = Transceiver.Direction.SendReceive; audioTransceiver.LocalAudioTrack = localAudioTrack; } // Start peer connection int numFrames = 0; pc.VideoTrackAdded += (RemoteVideoTrack track) => { Debug.Log($"Attach Frame Listener..."); track.I420AVideoFrameReady += (I420AVideoFrame frame) => { ++numFrames; if (numFrames % 60 == 0) { Debug.Log($"Received video frames: {numFrames}"); } }; }; // we need a short delay here for the video stream to settle... // I assume my Logitech webcam is sending some garbage frames in the beginning. await Task.Delay(200); pc.CreateOffer(); Debug.Log("Send offer to remote peer"); }