private static IRenderer CreateRenderer(ObservableVideoTrack videoTrack, ILogger logger) { PeerConnection.Configure(new GlobalOptions { MinimumLogLevel = TraceLevel.Info }); bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); bool supportsNvEnc = PeerConnection.SupportsHardwareTextureEncoding; // TODO: Add support for OpenGL, and test it. // Maybe use https://github.com/mellinoe/veldrid return(isWindows && supportsNvEnc ? (IRenderer) new BouncingBallRenderer(videoTrack, 8, new BoundingBallOptions { VideoFrameWidth = VideoFrameWidth, VideoFrameHeight = VideoFrameHeight, PreviewWindowOptions = new PreviewWindowOptions { Width = 1920 / 2 }, TimeRulerOptions = new TimeRulerOptions() }) : new ImageSharpRenderer(VideoFrameWidth, VideoFrameHeight, videoTrack)); }
static RtcRenderingServer() { var options = new GlobalOptions { MinimumLogLevel = TraceLevel.Info }; PeerConnection.Configure(options); }
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(); } }
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); } } }
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 }); } }