Beispiel #1
0
        protected virtual void OnEnable()
        {
            if (Source != null)
            {
                return;
            }

            // Create the external source
            //< TODO - Better abstraction
            if (typeof(T) == typeof(I420AVideoFrameStorage))
            {
                Source = ExternalVideoTrackSource.CreateFromI420ACallback(OnFrameRequested);
            }
            else if (typeof(T) == typeof(Argb32VideoFrameStorage))
            {
                Source = ExternalVideoTrackSource.CreateFromArgb32Callback(OnFrameRequested);
            }
            else
            {
                throw new NotSupportedException("This frame storage is not supported. Use I420AVideoFrameStorage or Argb32VideoFrameStorage.");
            }
            if (Source == null)
            {
                throw new Exception("Failed to create external video track source.");
            }

            VideoStreamStarted.Invoke(Source);
        }
 internal LocalVideoTrack(LocalVideoTrackHandle nativeHandle, PeerConnection peer, string trackName, ExternalVideoTrackSource source = null)
 {
     _nativeHandle  = nativeHandle;
     PeerConnection = peer;
     Name           = trackName;
     Source         = source;
     RegisterInteropCallbacks();
 }
Beispiel #3
0
 protected override void DestroyLocalVideoTrack()
 {
     base.DestroyLocalVideoTrack();
     if (Source != null)
     {
         Source.Dispose();
         Source = null;
     }
 }
 // Constructor for interop-based creation; SetHandle() will be called later
 // Constructor for a track associated with a peer connection.
 internal LocalVideoTrack(PeerConnection peer,
                          Transceiver transceiver, string trackName, ExternalVideoTrackSource source = null) : base(peer, trackName)
 {
     Debug.Assert(transceiver.MediaKind == MediaKind.Video);
     Debug.Assert(transceiver.LocalVideoTrack == null);
     Transceiver = transceiver;
     transceiver.LocalVideoTrack = this;
     Source = source;
     source?.OnTrackAddedToSource(this);
 }
Beispiel #5
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;
                }
            }
        }
Beispiel #6
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);
                }
            }
        }
Beispiel #7
0
        protected override Task CreateLocalVideoTrackAsync()
        {
            // Ensure the track has a valid name
            string trackName = TrackName;

            if (string.IsNullOrEmpty(trackName))
            {
                // Generate a unique name (GUID)
                trackName = Guid.NewGuid().ToString();
                TrackName = trackName;
            }
            SdpTokenAttribute.Validate(trackName, allowEmpty: false);

            // Create the external source
            //< TODO - Better abstraction
            if (typeof(T) == typeof(I420AVideoFrameStorage))
            {
                Source = ExternalVideoTrackSource.CreateFromI420ACallback(OnFrameRequested);
            }
            else if (typeof(T) == typeof(Argb32VideoFrameStorage))
            {
                Source = ExternalVideoTrackSource.CreateFromArgb32Callback(OnFrameRequested);
            }
            else
            {
                throw new NotSupportedException("This frame storage is not supported. Use I420AVideoFrameStorage or Argb32VideoFrameStorage.");
            }
            if (Source == null)
            {
                throw new Exception("Failed to create external video track source.");
            }

            // Create the local video track
            Track = LocalVideoTrack.CreateFromExternalSource(trackName, Source);
            if (Track == null)
            {
                throw new Exception("Failed ot create webcam video track.");
            }

            // Synchronize the track status with the Unity component status
            Track.Enabled = enabled;

            // This implementation is fast, so executes synchronously.
            return(Task.CompletedTask);
        }
        protected virtual void OnEnable()
        {
            Debug.Assert(Source == null);

            // Create the external source
            //< TODO - Better abstraction
            if (typeof(T) == typeof(I420AVideoFrameStorage))
            {
                AttachSource(ExternalVideoTrackSource.CreateFromI420ACallback(OnFrameRequested));
            }
            else if (typeof(T) == typeof(Argb32VideoFrameStorage))
            {
                AttachSource(ExternalVideoTrackSource.CreateFromArgb32Callback(OnFrameRequested));
            }
            else
            {
                throw new NotSupportedException("This frame storage is not supported. Use I420AVideoFrameStorage or Argb32VideoFrameStorage.");
            }
        }
        /// <summary>
        /// Create a new local video track backed by an existing external video source.
        /// The track can be added to a peer connection by setting the <see cref="Transceiver.LocalVideoTrack"/>
        /// property.
        /// </summary>
        /// <param name="trackName">Name of the new local video track.</param>
        /// <param name="source">External video track source providing some video frames to the track.</param>
        /// <returns>The newly created local video track.</returns>
        /// <seealso cref="Transceiver.LocalVideoTrack"/>
        public static LocalVideoTrack CreateFromExternalSource(string trackName, ExternalVideoTrackSource source)
        {
            if (string.IsNullOrEmpty(trackName))
            {
                trackName = Guid.NewGuid().ToString();
            }

            // Create interop wrappers
            var track = new LocalVideoTrack(trackName, source);

            // Parse settings
            var config = new PeerConnectionInterop.LocalVideoTrackFromExternalSourceInteropInitConfig(trackName, source);

            // Create native implementation objects
            uint res = LocalVideoTrackInterop.LocalVideoTrack_CreateFromExternalSource(config, out LocalVideoTrackHandle trackHandle);

            Utils.ThrowOnErrorCode(res);
            track.SetHandle(trackHandle);
            return(track);
        }
Beispiel #10
0
        /// <summary>
        /// Add a new track to the peer connection and start the video track playback.
        /// </summary>
        public void StartTrack()
        {
            // Ensure the track has a valid name
            string trackName = TrackName;

            if (trackName == null || trackName.Length == 0)
            {
                // Generate a unique name (GUID)
                trackName = Guid.NewGuid().ToString();
                TrackName = trackName;
            }
            //SdpTokenAttribute.Validate(trackName, allowEmpty: false);

            // Create the external source
            var nativePeer = PeerConnection;

            //< TODO - Better abstraction
            if (typeof(T) == typeof(I420AVideoFrameStorage))
            {
                Source = ExternalVideoTrackSource.CreateFromI420ACallback(OnFrameRequested);
            }
            else if (typeof(T) == typeof(Argb32VideoFrameStorage))
            {
                Source = ExternalVideoTrackSource.CreateFromArgb32Callback(OnFrameRequested);
            }
            else
            {
                throw new NotSupportedException("");
            }

            // Create the local video track
            if (Source != null)
            {
                Track = nativePeer.AddCustomLocalVideoTrack(trackName, Source);
                if (Track != null)
                {
                    NotifyVideoStreamStarted();
                }
            }
        }
Beispiel #11
0
        public async Task Init(IceServerModel[] iceServers)
        {
            Logger.Debug("Starting WebRTC connection.");

            IceServers = iceServers;

            PeerSession = new PeerConnection();

            var iceList = IceServers.Select(x => new IceServer()
            {
                Urls         = { x.Url },
                TurnPassword = x.TurnPassword ?? string.Empty,
                TurnUserName = x.TurnUsername ?? string.Empty
            }).ToList();

            var config = new PeerConnectionConfiguration()
            {
                IceServers = iceList
            };

            await PeerSession.InitializeAsync(config);

            PeerSession.LocalSdpReadytoSend     += PeerSession_LocalSdpReadytoSend;;
            PeerSession.Connected               += PeerConnection_Connected;
            PeerSession.IceStateChanged         += PeerConnection_IceStateChanged;
            PeerSession.IceCandidateReadytoSend += PeerSession_IceCandidateReadytoSend;;

            CaptureChannel = await PeerSession.AddDataChannelAsync("ScreenCapture", true, true);

            CaptureChannel.BufferingChanged += DataChannel_BufferingChanged;
            CaptureChannel.MessageReceived  += CaptureChannel_MessageReceived;
            CaptureChannel.StateChanged     += CaptureChannel_StateChanged;

            VideoSource = ExternalVideoTrackSource.CreateFromArgb32Callback(GetCaptureFrame);
            Transceiver = PeerSession.AddTransceiver(MediaKind.Video);

            PeerSession.CreateOffer();
        }
 // Constructor for interop-based creation; SetHandle() will be called later.
 // Constructor for standalone track not associated to a peer connection.
 internal LocalVideoTrack(string trackName, ExternalVideoTrackSource source = null) : base(null, trackName)
 {
     Transceiver = null;
     Source      = source;
     source?.OnTrackAddedToSource(this);
 }
Beispiel #13
0
 public FrameRequestEventArgs(FrameRequest fr)
 {
     Source      = fr.Source;
     RequestId   = fr.RequestId;
     TimestampMs = fr.TimestampMs;
 }
Beispiel #14
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());
        }
 // Constructor for interop-based creation; SetHandle() will be called later
 // Constructor for a track associated with a peer connection.
 internal LocalVideoTrack(PeerConnection peer, string trackName, ExternalVideoTrackSource source = null)
 {
     PeerConnection = peer;
     Name           = trackName;
     Source         = source;
 }
Beispiel #16
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);
        }
 /// <summary>
 /// Constructor for creating a local video track from a wrapper and an existing external source.
 /// </summary>
 /// <param name="track">The newly created track wrapper.</param>
 /// <param name="source">The external source to use with the newly created native track.</param>
 /// <seealso cref="PeerConnection.AddCustomLocalVideoTrack(string, ExternalVideoTrackSource)"/>
 public LocalVideoTrackFromExternalSourceInteropInitConfig(LocalVideoTrack track, ExternalVideoTrackSource source)
 {
     LocalVideoTrackWrapperHandle = Utils.MakeWrapperRef(track);
 }
Beispiel #18
0
        public CallHandler(ICall statefulCall, PeerConnection peerConnection)
            : base(TimeSpan.FromMinutes(10), statefulCall?.GraphLogger)
        {
            this.Call = statefulCall;

            this.callHandlerVideo = new CallHandlerVideo(this.Call);
            this.callHandlerAudio = new CallHandlerAudio(this.Call);

            this.peerConnection = peerConnection;

            this.peerConnection.VideoTrackAdded   += this.callHandlerVideo.OnClientVideoTrackAdded;
            this.peerConnection.VideoTrackRemoved += this.callHandlerVideo.OnClientVideoTrackRemoved;

            this.peerConnection.AudioTrackAdded   += this.callHandlerAudio.OnClientAudioTrackAdded;
            this.peerConnection.AudioTrackRemoved += this.callHandlerAudio.OnClientAudioTrackRemoved;

            TransceiverInitSettings transceiverInitSettings = new TransceiverInitSettings();

            transceiverInitSettings.InitialDesiredDirection = Transceiver.Direction.Inactive;

            if (this.peerConnection.AssociatedTransceivers.ToList().Count != 0)
            {
                this.teamsAudioTransceiver             = this.peerConnection.AssociatedTransceivers.ToList()[0];
                this.callHandlerAudio.clientAudioTrack = this.peerConnection.AssociatedTransceivers.ToList()[0].RemoteAudioTrack;
                this.callHandlerAudio.clientAudioTrack.AudioFrameReady += this.callHandlerAudio.OnClientAudioReceived;

                this.teamsVideoTransceiver             = this.peerConnection.AssociatedTransceivers.ToList()[1];
                this.callHandlerVideo.clientVideoTrack = this.peerConnection.AssociatedTransceivers.ToList()[1].RemoteVideoTrack;
                this.callHandlerVideo.clientVideoTrack.I420AVideoFrameReady += this.callHandlerVideo.OnClientVideoReceived;
            }
            else
            {
                this.teamsAudioTransceiver = this.peerConnection.AddTransceiver(MediaKind.Audio, transceiverInitSettings);
                this.teamsVideoTransceiver = this.peerConnection.AddTransceiver(MediaKind.Video, transceiverInitSettings);
            }

            LocalVideoTrack teamsVideoTrack = LocalVideoTrack.CreateFromExternalSource("TeamsVideoTrack",
                                                                                       ExternalVideoTrackSource.CreateFromI420ACallback(this.callHandlerVideo.CustomI420AFrameCallback));

            this.teamsVideoTransceiver.LocalVideoTrack = teamsVideoTrack;

            this.teamsVideoTransceiver.DesiredDirection = Transceiver.Direction.SendReceive;

            LocalAudioTrack teamsAudioTrack = LocalAudioTrack.CreateFromExternalSource("TeamsAudioTrack",
                                                                                       ExternalAudioTrackSource.CreateFromCallback(this.callHandlerAudio.CustomAudioFrameCallback));

            this.teamsAudioTransceiver.LocalAudioTrack = teamsAudioTrack;

            this.teamsAudioTransceiver.DesiredDirection = Transceiver.Direction.SendReceive;

            this.Call.OnUpdated += this.OnCallUpdated;
            if (this.Call.GetLocalMediaSession() != null)
            {
                this.Call.GetLocalMediaSession().AudioSocket.DominantSpeakerChanged += this.OnDominantSpeakerChanged;
                this.Call.GetLocalMediaSession().VideoSocket.VideoMediaReceived     += this.callHandlerVideo.OnTeamsVideoReceived;
                this.Call.GetLocalMediaSession().AudioSocket.AudioMediaReceived     += this.callHandlerAudio.OnTeamsAudioReceived;
            }

            this.Call.Participants.OnUpdated += this.OnParticipantsUpdated;
            this.endCallTimer           = new Timer(CallHandler.WaitForMs);
            this.endCallTimer.Enabled   = false;
            this.endCallTimer.AutoReset = false;
            this.endCallTimer.Elapsed  += this.OnTimerElapsed;
        }