/// <summary> /// Create a video track from a local video capture device (webcam). /// /// The video track receives its video data from an underlying hidden source associated with /// the track and producing video frames by capturing them from a capture device accessible /// from the local host machine, generally a USB webcam or built-in device camera. /// /// The underlying video source initially starts in the capturing state, and will remain live /// for as long as the track is alive. It can be added to a peer connection by assigning it to /// the <see cref="Transceiver.LocalVideoTrack"/> property of a video transceiver of that peer /// connection. Once attached to the peer connection, it can temporarily be disabled and re-enabled /// (see <see cref="Enabled"/>) while remaining attached to it. /// /// Note that disabling the track does not release the device; the source retains exclusive access to it. /// Therefore in general multiple tracks cannot be created using a single video capture device. /// </summary> /// <param name="settings">Video capture settings for configuring the capture device associated with /// the underlying video track source.</param> /// <returns>This returns a task which, upon successful completion, provides an instance of /// <see cref="LocalVideoTrack"/> representing the newly created video track.</returns> /// <remarks> /// On UWP this requires the "webcam" capability. /// See <see href="https://docs.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations"/> /// for more details. /// /// The video capture device may be accessed several times during the initializing process, /// generally once for listing and validating the capture format, and once for actually starting /// the video capture. /// /// Note that the capture device must support a capture format with the given constraints of profile /// ID or kind, capture resolution, and framerate, otherwise the call will fail. That is, there is no /// fallback mechanism selecting a closest match. Developers should use /// <see cref="PeerConnection.GetVideoCaptureFormatsAsync(string)"/> to list the supported formats ahead /// of calling <see cref="CreateFromDeviceAsync(LocalVideoTrackSettings)"/>, and can build their own /// fallback mechanism on top of this call if needed. /// </remarks> /// <exception xref="InvalidOperationException">The peer connection is not intialized.</exception> /// <example> /// Create a video track called "MyTrack", with Mixed Reality Capture (MRC) enabled. /// This assumes that the platform supports MRC. Note that if MRC is not available /// the call will still succeed, but will return a track without MRC enabled. /// <code> /// var settings = new LocalVideoTrackSettings /// { /// trackName = "MyTrack", /// enableMrc = true /// }; /// var videoTrack = await LocalVideoTrack.CreateFromDeviceAsync(settings); /// </code> /// Create a video track from a local webcam, asking for a capture format suited for video conferencing, /// and a target framerate of 30 frames per second (FPS). The implementation will select an appropriate /// capture resolution. This assumes that the device supports video profiles, and has at least one capture /// format supporting 30 FPS capture associated with the VideoConferencing profile. Otherwise the call /// will fail. /// <code> /// var settings = new LocalVideoTrackSettings /// { /// videoProfileKind = VideoProfileKind.VideoConferencing, /// framerate = 30.0 /// }; /// var videoTrack = await LocalVideoTrack.CreateFromDeviceAsync(settings); /// </code> /// </example> /// <seealso cref="Transceiver.LocalVideoTrack"/> public static Task <LocalVideoTrack> CreateFromDeviceAsync(LocalVideoTrackSettings settings = null) { return(Task.Run(() => { // On UWP this cannot be called from the main UI thread, so always call it from // a background worker thread. string trackName = settings?.trackName; if (string.IsNullOrEmpty(trackName)) { trackName = Guid.NewGuid().ToString(); } // Create interop wrappers var track = new LocalVideoTrack(trackName); // Parse settings var config = new PeerConnectionInterop.LocalVideoTrackInteropInitConfig(track, settings); // Create native implementation objects uint res = LocalVideoTrackInterop.LocalVideoTrack_CreateFromDevice(in config, trackName, out LocalVideoTrackHandle trackHandle); Utils.ThrowOnErrorCode(res); track.SetHandle(trackHandle); return track; })); }
/// <summary> /// Internal callback when a track stops using this source. /// </summary> /// <param name="track">The track not using this source anymore.</param> internal void OnTrackRemovedFromSource(LocalVideoTrack track) { Debug.Assert(!_nativeHandle.IsClosed); bool removed = _tracks.Remove(track); Debug.Assert(removed); }
/// <summary> /// Create a video track from an existing video track source. /// /// This does not add the track to any peer connection. Instead, the track must be added manually to /// a video transceiver to be attached to a peer connection and transmitted to a remote peer. /// </summary> /// <param name="source">The track source which provides the raw video frames to the newly created track.</param> /// <param name="initConfig">Configuration to initialize the track being created.</param> /// <returns>Asynchronous task completed once the track is created.</returns> public static LocalVideoTrack CreateFromSource(VideoTrackSource source, LocalVideoTrackInitConfig initConfig) { if (source == null) { throw new ArgumentNullException(); } // Parse and marshal the settings string trackName = initConfig?.trackName; if (string.IsNullOrEmpty(trackName)) { trackName = Guid.NewGuid().ToString(); } var config = new LocalVideoTrackInterop.TrackInitConfig { TrackName = trackName }; // Create interop wrappers var track = new LocalVideoTrack(trackName); // Create native implementation objects uint res = LocalVideoTrackInterop.LocalVideoTrack_CreateFromSource(in config, source._nativeHandle, out LocalVideoTrackHandle trackHandle); Utils.ThrowOnErrorCode(res); // Finish creating the track, and bind it to the source track.FinishCreate(trackHandle, source); return(track); }
/// <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); }
/// <summary> /// Create a video track from an existing video track source. /// /// This does not add the track to any peer connection. Instead, the track must be added manually to /// a video transceiver to be attached to a peer connection and transmitted to a remote peer. /// </summary> /// <param name="source">The track source which provides the raw video frames to the newly created track.</param> /// <param name="initConfig">Configuration to initialize the track being created.</param> /// <returns>Asynchronous task completed once the track is created.</returns> public static LocalVideoTrack CreateFromSource(VideoTrackSource source, LocalVideoTrackInitConfig initConfig) { if (source == null) { throw new ArgumentNullException(); } // Parse and marshal the settings string trackName = initConfig?.trackName; if (string.IsNullOrEmpty(trackName)) { trackName = Guid.NewGuid().ToString(); } var config = new LocalVideoTrackInterop.TrackInitConfig { TrackName = trackName }; // Create interop wrappers var track = new LocalVideoTrack(trackName); // Create native implementation objects uint res = LocalVideoTrackInterop.LocalVideoTrack_CreateFromSource(in config, source._nativeHandle, out LocalVideoTrackHandle trackHandle); Utils.ThrowOnErrorCode(res); // Finish creating the track, and bind it to the source Debug.Assert(!trackHandle.IsClosed); track._nativeHandle = trackHandle; track.Source = source; source.OnTrackAddedToSource(track); // Note that this prevents the object from being garbage-collected until it is disposed. track._selfHandle = Utils.MakeWrapperRef(track); return(track); }
/// <summary> /// Internal callback when a track starts using this source. /// </summary> /// <param name="track">The track using this source.</param> internal void OnTrackAddedToSource(LocalVideoTrack track) { Debug.Assert(!_nativeHandle.IsClosed); Debug.Assert(!_tracks.Contains(track)); _tracks.Add(track); }