/// <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
            });
        }
Example #4
0
        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;
                }
            }
        }
Example #5
0
        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);
                }
            }
        }
Example #6
0
        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();
        }
Example #7
0
        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();
        }
Example #8
0
        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());
        }
Example #9
0
        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);
        }
Example #10
0
        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());
        }
Example #11
0
        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());
        }
Example #12
0
        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);
        }
Example #13
0
    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");
    }