Example #1
0
        public void MultiThreadedLifetime()
        {
            for (int iteration = 0; iteration < 2; ++iteration)
            {
                // Now check if connections can be created/destroyed on multiple threads without crashing
                var threadIds = new HashSet <int>();

                Parallel.For(0, 16, i =>
                {
                    using (new PeerConnection(new PeerConnectionOptions()))
                        using (new PeerConnection(new PeerConnectionOptions()))
                        {
                            Assert.IsTrue(PeerConnection.HasFactory);
                        }

                    threadIds.Add(Thread.CurrentThread.ManagedThreadId);
                });

                Assert.IsTrue(threadIds.Count >= 2);
                Assert.IsFalse(PeerConnection.HasFactory);

                // Disable auto-shutdown
                PeerConnection.Configure(new GlobalOptions {
                    AutoShutdown = false
                });

                Parallel.For(0, 16, i =>
                {
                    using (new PeerConnection(new PeerConnectionOptions()))
                        using (new PeerConnection(new PeerConnectionOptions()))
                        {
                            Assert.IsTrue(PeerConnection.HasFactory);
                        }

                    threadIds.Add(Thread.CurrentThread.ManagedThreadId);
                });

                // Shutdown manually
                PeerConnection.Shutdown();

                Assert.IsFalse(PeerConnection.HasFactory);

                // Enable auto-shutdown
                PeerConnection.Configure(new GlobalOptions {
                    AutoShutdown = true
                });
            }
        }
        // TODO: Can't get this test running with async/await, gets stuck while disposing, deadlocks

        public static void Main()
        {
            PeerConnection.Configure(new GlobalOptions
            {
                UseFakeDecoders  = true,
                LogToDebugOutput = false,
                MinimumLogLevel  = TraceLevel.Info
            });

            while (true)
            {
                Render();

                GC.Collect();
                GC.WaitForPendingFinalizers();

                GC.Collect();
                GC.WaitForPendingFinalizers();

                Console.WriteLine("Press ENTER");
                Console.ReadLine();
            }
        }
Example #3
0
 protected override void OnLocalVideoFrameProcessed(PeerConnection pc, int trackId, IntPtr rgbaPixels, bool isEncoded)
 {
     _localVideoFrameProcessedStream.TryOnNext(new VideoFrameMessage(trackId, rgbaPixels, isEncoded));
     base.OnLocalVideoFrameProcessed(pc, trackId, rgbaPixels, isEncoded);
 }
Example #4
0
 protected override void OnLocalVideoFrameEncoded(PeerConnection pc, int trackId, IntPtr rgbaPixels)
 {
     _localVideoFrameEncodedStream.OnNext(new VideoFrameMessage(trackId, rgbaPixels));
     base.OnLocalVideoFrameEncoded(pc, trackId, rgbaPixels);
 }
Example #5
0
        private void Run()
        {
            try
            {
                // For debugging, run everything on this thread.
                // Should never be done in production.
                // Note that webrtc callbacks are done on the signaling thread, and must return asap.
                PeerConnection.Configure(new GlobalOptions
                {
                    UseWorkerThread           = false,
                    UseSignalingThread        = false,
                    ForceSoftwareVideoEncoder = true,
                    MinimumLogLevel           = System.Diagnostics.TraceLevel.Info,
                    LogToStandardError        = false,
                    LogToDebugOutput          = false
                });

                PeerConnection.MessageLogged += (message, severity) =>
                {
                    severity.WriteToConsole(message);
                };

                Console.OutputEncoding = Encoding.UTF8;

                const int frameWidth  = 320;
                const int frameHeight = 180;
                const int frameRate   = 10;

                using (var senderOutgoingMessages = new ReplaySubject <DataMessage>())
                    using (var sender = new ObservablePeerConnection(new PeerConnectionOptions
                    {
                        Name = "Sender"
                    }))
                        using (var receiver = new ObservablePeerConnection(new PeerConnectionOptions
                        {
                            Name = "Receiver",
                            CanReceiveVideo = true
                        }))
                            using (var background = Image.Load <Argb32>("background-small.jpg"))
                                using (receiver.ReceivedVideoStream.Buffer(2).Subscribe(SaveFrame))
                                    using (var imageFrame = new Image <Argb32>(frameWidth, frameHeight))
                                        using (var videoTrack = new VideoTrack(sender,
                                                                               VideoEncoderOptions.OptimizedFor(frameWidth, frameHeight, frameRate)))
                                        {
                                            background.Mutate(ctx => ctx.Resize(frameWidth, frameHeight));

                                            senderOutgoingMessages.OnNext(new DataMessage("data", "Hello"));

                                            sender.CreateOffer();

                                            sender.Connect(senderOutgoingMessages, receiver.LocalSessionDescriptionStream, receiver.LocalIceCandidateStream);

                                            var receiverOutgoingMessages = receiver
                                                                           .ReceivedDataStream
                                                                           .Where(msg => msg.AsText == "Hello")
                                                                           .Do(msg => Console.WriteLine($"Received message {msg.AsText}"))
                                                                           .Select(msg => new DataMessage(msg.Label, "World"));

                                            receiver.Connect(receiverOutgoingMessages, sender.LocalSessionDescriptionStream, sender.LocalIceCandidateStream);

                                            sender.AddDataChannel(new DataChannelOptions());

                                            Console.WriteLine("Press any key to exit");

                                            int localFrameIndex = 0;

                                            var timeout = TimeSpan.FromMilliseconds(1000.0 / frameRate);
                                            //while (!Console.KeyAvailable && PeerConnection.PumpQueuedMessages(timeout))
                                            while (PeerConnection.PumpQueuedMessages(timeout))
                                            {
                                                var frame  = imageFrame.Frames[0];
                                                var pixels = MemoryMarshal.Cast <Argb32, uint>(frame.GetPixelSpan());
                                                videoTrack.SendVideoFrame(MemoryMarshal.GetReference(pixels),
                                                                          frame.Width * 4,
                                                                          frame.Width,
                                                                          frame.Height,
                                                                          VideoFrameFormat.Argb32);

                                                imageFrame.Mutate(ctx => ctx.DrawImage(GraphicsOptions.Default, background).Rotate(localFrameIndex * 10).Crop(frameWidth, frameHeight));

                                                ++localFrameIndex;
                                            }

                                            sender.RemoveDataChannel("data");
                                        }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"*** FAILURE: {ex}");
            }

            Console.WriteLine("Press ENTER to exit");
            Console.ReadLine();
        }
        public void RendersAndSendsFrameUsingD3D11()
        {
            bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
            bool hasNvEnc  = PeerConnection.SupportsHardwareTextureEncoding;

            if (isWindows && hasNvEnc)
            {
                PeerConnection.Configure(new GlobalOptions
                {
                    UseFakeDecoders  = true,
                    LogToDebugOutput = false,
                    MinimumLogLevel  = TraceLevel.Info
                });

                using (var sender = new ObservablePeerConnection(new PeerConnectionOptions()))
                    using (var receiver = new ObservablePeerConnection(new PeerConnectionOptions {
                        CanReceiveVideo = true
                    }))
                        using (var vt = new ObservableVideoTrack(sender, VideoEncoderOptions.OptimizedFor(320, 240, 10)))
                        {
                            using (var rnd = new VideoRenderer(vt, new RendererOptions {
                                VideoFrameQueueSize = 2
                            }))
                            {
                                // Wait until sender and receiver are connected,
                                // signaling is complete,
                                // and video track is added.

                                // TODO: When using tasks for this, this test hangs when disposing!

                                // ReSharper disable once InvokeAsExtensionMethod
                                //var ready = Observable.Zip(
                                //    receiver.ConnectionStateStream.FirstAsync(s => s == ConnectionState.Connected),
                                //    sender.ConnectionStateStream.FirstAsync(s => s == ConnectionState.Connected),
                                //    receiver.SignalingStateStream.FirstAsync(s => s == SignalingState.Stable),
                                //    sender.SignalingStateStream.FirstAsync(s => s == SignalingState.Stable),
                                //    receiver.RemoteTrackChangeStream.FirstAsync(
                                //        c => !string.IsNullOrEmpty(c.TransceiverMid) &
                                //             c.MediaKind == TrackMediaKind.Video &&
                                //             c.ChangeKind == TrackChangeKind.Changed),
                                //    (a, b, c, d, e) => true);
                                //// Wait until connected and video track is ready.
                                //var ev = new AutoResetEvent(false);
                                //ready.Subscribe(_ => ev.Set());

                                receiver.Connect(
                                    Observable.Never <DataMessage>(),
                                    sender.LocalSessionDescriptionStream,
                                    sender.LocalIceCandidateStream);

                                sender.Connect(
                                    Observable.Never <DataMessage>(),
                                    receiver.LocalSessionDescriptionStream,
                                    receiver.LocalIceCandidateStream);

                                sender.CreateOffer();

                                int remoteVideoFrameReceivedCount = 0;

                                receiver.RemoteVideoFrameReceived += (pc, frame) =>
                                {
                                    remoteVideoFrameReceivedCount += 1;
                                };

                                // The remote peer connection is not immediately ready to receive frames,
                                // so we keep sending until it succeeds.
                                // TODO: Figure out what webrtc event can be used for this.
                                while (remoteVideoFrameReceivedCount == 0)
                                {
                                    using (rnd.TakeNextFrameForSending())
                                    {
                                    }
                                }

                                // Continue sending until the video queue is empty
                                while (rnd.VideoFrameQueueCount > 0)
                                {
                                    using (rnd.TakeNextFrameForSending())
                                    {
                                    }
                                }
                            }

                            // The video renderer is now disposed while the video track is still encoding some textures
                            // This should not crash.
                            // We need to wait a while before disposing the video-track and peer-connection to check this.
                            Thread.Sleep(100);
                        }
            }
        }
Example #7
0
        public void SingleThreadedLifetime()
        {
            for (int iteration = 0; iteration < 2; ++iteration)
            {
                // Test if a single peer-connection creation/destruction auto-shutdowns the global factory
                using (new PeerConnection(new PeerConnectionOptions()))
                {
                    Assert.IsTrue(PeerConnection.HasFactory);
                }

                Assert.IsFalse(PeerConnection.HasFactory);

                // Test if we can still create a peer-connection after a previous shutdown
                using (new PeerConnection(new PeerConnectionOptions()))
                {
                    Assert.IsTrue(PeerConnection.HasFactory);
                }

                Assert.IsFalse(PeerConnection.HasFactory);

                // Test if we can create multiple peer connections
                using (new PeerConnection(new PeerConnectionOptions()))
                    using (new PeerConnection(new PeerConnectionOptions()))
                    {
                        Assert.IsTrue(PeerConnection.HasFactory);
                    }

                Assert.IsFalse(PeerConnection.HasFactory);

                // Disable auto-shutdown
                PeerConnection.Configure(new GlobalOptions {
                    AutoShutdown = false
                });

                // Test if a single peer-connection creation/destruction does not auto-shutdown the global factory anymore
                using (new PeerConnection(new PeerConnectionOptions()))
                {
                    Assert.IsTrue(PeerConnection.HasFactory);
                }

                Assert.IsTrue(PeerConnection.HasFactory);

                // Test if we can still create a peer-connection
                using (new PeerConnection(new PeerConnectionOptions()))
                {
                    Assert.IsTrue(PeerConnection.HasFactory);
                }

                Assert.IsTrue(PeerConnection.HasFactory);

                // Test if we can create multiple peer connections
                using (new PeerConnection(new PeerConnectionOptions()))
                    using (new PeerConnection(new PeerConnectionOptions()))
                    {
                        Assert.IsTrue(PeerConnection.HasFactory);
                    }

                // Shutdown manually
                PeerConnection.Shutdown();

                Assert.IsFalse(PeerConnection.HasFactory);

                // Enable auto-shutdown
                PeerConnection.Configure(new GlobalOptions {
                    AutoShutdown = true
                });
            }
        }