/* * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using static Interop; namespace Tizen.Multimedia.Remoting { /// /// MediaSource is a base class for sources. /// /// 9 public abstract class MediaSource : IDisplayable { internal WebRTC WebRtc { get; set; } internal uint? SourceId { get; set; } private Display _display; /// /// Gets the type of MediaSource. /// /// /// 9 protected MediaType MediaType { get; } private bool IsDetached {get; set;} = false; /// /// Initializes a new instance of the class. /// /// 9 protected MediaSource(MediaType mediaType) { MediaType = mediaType; } internal void AttachTo(WebRTC webRtc) { if (IsDetached) { throw new InvalidOperationException("MediaSource was already detached."); } OnAttached(webRtc); } internal void DetachFrom(WebRTC webRtc) { OnDetached(webRtc); IsDetached = true; } internal abstract void OnAttached(WebRTC webRtc); internal abstract void OnDetached(WebRTC webRtc); /// /// Gets or sets the transceiver direction of current media source. /// /// A that specifies the transceiver direction. /// MediaSource is not attached yet. /// The WebRTC has already been disposed. /// 9 public TransceiverDirection TransceiverDirection { get { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } NativeWebRTC.GetTransceiverDirection(WebRtc.Handle, SourceId.Value, MediaType, out TransceiverDirection mode). ThrowIfFailed("Failed to get transceiver direction."); return mode; } set { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } NativeWebRTC.SetTransceiverDirection(WebRtc.Handle, SourceId.Value, MediaType, value). ThrowIfFailed("Failed to get transceiver direction."); } } /// /// Gets or sets the transceiver codec of current media source. /// /// /// This API is not supported in , .
/// The WebRTC must be in the state when transceiver codec is set. ///
/// The transceiver codec. /// /// MediaSource is not attached yet.
/// -or-
/// This MediaSource is not supported type of MediaSource.
/// -or-
/// The WebRTC is not in the valid state. ///
/// The WebRTC has already been disposed. /// 10 public TransceiverCodec TransceiverCodec { get { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (this is MediaFileSource || this is MediaPacketSource) { throw new InvalidOperationException($"This property is not supported in {this.GetType()}."); } NativeWebRTC.GetTransceiverCodec(WebRtc.Handle, SourceId.Value, MediaType, out TransceiverCodec codec). ThrowIfFailed("Failed to get transceiver codec"); return codec; } set { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (this is MediaFileSource || this is MediaPacketSource) { throw new InvalidOperationException($"This property is not supported in {this.GetType()}."); } WebRtc.ValidateWebRTCState(WebRTCState.Idle); NativeWebRTC.SetTransceiverCodec(WebRtc.Handle, SourceId.Value, MediaType, value). ThrowIfFailed("Failed to set transceiver codec"); } } /// /// Retrieves the supported transceiver codecs. /// /// /// This API is not supported in , . /// /// The transceiver codecs. /// This MediaSource is not supported type of MediaSource. /// The WebRTC has already been disposed. /// 10 public ReadOnlyCollection SupportedTransceiverCodecs { get { if (this is MediaFileSource || this is MediaPacketSource) { throw new InvalidOperationException($"This property is not supported in {this.GetType()}."); } var codecs = new List(); Exception caught = null; NativeWebRTC.RetrieveTransceiverCodecCallback cb = (codec, _) => { try { codecs.Add(codec); } catch (Exception e) { caught = e; return false; } return true; }; using (var cbKeeper = ObjectKeeper.Get(cb)) { NativeWebRTC.ForeachSupportedTransceiverCodec(WebRtc.Handle, GetMediaSourceType(), MediaType, cb). ThrowIfFailed("failed to retrieve stats"); if (caught != null) { throw caught; } } return new ReadOnlyCollection(codecs); } } private MediaSourceType GetMediaSourceType() { if (this is MediaTestSource) { if (MediaType == MediaType.Audio) { return MediaSourceType.AudioTest; } else { return MediaSourceType.VideoTest; } } else if (this is MediaMicrophoneSource) { return MediaSourceType.Microphone; } else if (this is MediaCameraSource) { return MediaSourceType.Camera; } else if (this is MediaScreenSource) { return MediaSourceType.Screen; } else if (this is MediaFileSource) { return MediaSourceType.File; } else if (this is MediaPacketSource) { return MediaSourceType.MediaPacket; } return MediaSourceType.Null; } /// /// Gets or sets the pause status of current media source. /// /// A value that specifies the pause status. /// MediaSource is not attached yet. /// The WebRTC has already been disposed. /// 9 public bool Pause { get { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } NativeWebRTC.GetPause(WebRtc.Handle, SourceId.Value, MediaType, out bool isPaused). ThrowIfFailed("Failed to get pause"); return isPaused; } set { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } NativeWebRTC.SetPause(WebRtc.Handle, SourceId.Value, MediaType, value). ThrowIfFailed("Failed to set pause"); } } /// /// Gets or sets the mute status of the current media source. /// /// A value that specifies the mute status. /// MediaSource is not attached yet. /// The WebRTC has already been disposed. /// 9 public bool Mute { get { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } NativeWebRTC.GetMute(WebRtc.Handle, SourceId.Value, MediaType, out bool isMuted). ThrowIfFailed("Failed to get mute"); return isMuted; } set { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } NativeWebRTC.SetMute(WebRtc.Handle, SourceId.Value, MediaType, value). ThrowIfFailed("Failed to set mute"); } } /// /// Gets or sets the video resolution of the current media source. /// /// A value that specifies the video resolution. /// /// MediaSource is not attached yet.
/// -or-
/// This MediaSource is not Video. ///
/// The WebRTC has already been disposed. /// 9 public Size VideoResolution { get { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (MediaType != MediaType.Video) { throw new InvalidOperationException("This property is only for video."); } NativeWebRTC.GetVideoResolution(WebRtc.Handle, SourceId.Value, out int width, out int height). ThrowIfFailed("Failed to get video resolution"); return new Size(width, height); } set { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (MediaType != MediaType.Video) { throw new InvalidOperationException("This property is only for video."); } NativeWebRTC.SetVideoResolution(WebRtc.Handle, SourceId.Value, value.Width, value.Height). ThrowIfFailed("Failed to set video resolution"); } } /// /// Gets or sets the video frame rate of the current media source. /// /// /// This API is only supported in video media source, especially , /// , .
///
/// A value that specifies the video frame rate. /// VideoFrameRate is less than or equal to zero. /// /// MediaSource is not attached yet.
/// -or-
/// This MediaSource is not Video. /// -or-
/// This MediaSource is not supported type of MediaSource. ///
/// The WebRTC has already been disposed. /// 10 public int VideoFrameRate { get { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (MediaType != MediaType.Video) { throw new InvalidOperationException("This property is only for video."); } if (this is MediaFileSource || this is MediaPacketSource) { throw new InvalidOperationException($"This property is not supported in {this.GetType()}."); } NativeWebRTC.GetVideoFrameRate(WebRtc.Handle, SourceId.Value, out int frameRate). ThrowIfFailed("Failed to get video frame rate"); return frameRate; } set { if (value <= 0) { throw new ArgumentException($"VideoFrameRate should be greater than zero."); } if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (MediaType != MediaType.Video) { throw new InvalidOperationException("This property is only for video."); } if (this is MediaFileSource || this is MediaPacketSource) { throw new InvalidOperationException($"This property is not supported in {this.GetType()}."); } NativeWebRTC.SetVideoFrameRate(WebRtc.Handle, SourceId.Value, value). ThrowIfFailed("Failed to set video frame rate"); } } /// /// Gets or sets the encoder bitrate of the current media source. /// /// /// This API is not supported in , .
///
/// A value that specifies the encoder bitrate. /// EncoderBitrate is less than or equal to zero. /// /// MediaSource is not attached yet.
/// -or-
/// This MediaSource is not Video. /// -or-
/// This MediaSource is not supported type of MediaSource. ///
/// The WebRTC has already been disposed. /// 10 public int EncoderBitrate { get { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (this is MediaFileSource || this is MediaPacketSource) { throw new InvalidOperationException($"This property is not supported in {this.GetType()}."); } NativeWebRTC.GetEncoderBitrate(WebRtc.Handle, SourceId.Value, MediaType, out int bitrate). ThrowIfFailed("Failed to get encoder bitrate"); return bitrate; } set { if (value <= 0) { throw new ArgumentException($"EncoderBitrate should be greater than zero."); } if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (this is MediaFileSource || this is MediaPacketSource) { throw new InvalidOperationException($"This property is not supported in {this.GetType()}."); } NativeWebRTC.SetEncoderBitrate(WebRtc.Handle, SourceId.Value, MediaType, value). ThrowIfFailed("Failed to set encoder bitrate"); } } /// /// Enables the audio loopback. The local audio will be played with . /// /// The to apply. /// /// does not support all .
/// Supported types are , , /// .
///
/// is null. /// /// MediaSource is not attached yet.
/// -or-
/// This MediaSource is not Audio ///
/// /// of is not supported on the current platform. /// /// /// or WebRTC has already been disposed. /// /// public MediaStreamTrack EnableAudioLoopback(AudioStreamPolicy policy) { if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (policy == null) { throw new ArgumentNullException(nameof(policy)); } if (MediaType != MediaType.Audio) { throw new InvalidOperationException("AudioLoopback is only for Audio MediaSource"); } var ret = NativeWebRTC.SetAudioLoopback(WebRtc.Handle, SourceId.Value, policy.Handle, out uint trackId); if (ret == WebRTCErrorCode.InvalidArgument) { throw new NotSupportedException("The specified policy is not supported on the current system."); } ret.ThrowIfFailed("Failed to set the audio stream policy to the WebRTC"); return new MediaStreamTrack(WebRtc, MediaType, trackId); } private uint SetDisplay(Display display) => display.ApplyTo(this); internal void ReplaceDisplay(Display newDisplay) { _display?.SetOwner(null); _display = newDisplay; _display?.SetOwner(this); } /// /// Enables the video loopback. The local video will be diaplayed in . /// /// The to apply. /// The display has already been assigned to another. /// is null. /// /// MediaSource is not attached yet.
/// -or-
/// This MediaSource is not Video ///
/// The WebRTC has already been disposed. /// public MediaStreamTrack EnableVideoLoopback(Display display) { uint trackId = 0; if (!SourceId.HasValue) { throw new InvalidOperationException("MediaSource is not attached yet. Call AddSource() first."); } if (display == null) { throw new ArgumentNullException(nameof(display), "Display cannot be null."); } if (MediaType != MediaType.Video) { throw new InvalidOperationException("VideoLoopback is only for Video MediaSource"); } if (display?.Owner != null) { if (ReferenceEquals(this, display.Owner)) { throw new ArgumentException("The display has already been assigned to another."); } } else { trackId = SetDisplay(display); ReplaceDisplay(display); } return new MediaStreamTrack(WebRtc, MediaType, trackId); } uint IDisplayable.ApplyEvasDisplay(DisplayType type, ElmSharp.EvasObject evasObject) { Debug.Assert(Enum.IsDefined(typeof(DisplayType), type)); Debug.Assert(type != DisplayType.None); NativeWebRTC.SetVideoLoopback(WebRtc.Handle, SourceId.Value, type == DisplayType.Overlay ? WebRTCDisplayType.Overlay : WebRTCDisplayType.Evas, evasObject, out uint trackId).ThrowIfFailed("Failed to set video loopback"); return trackId; } uint IDisplayable.ApplyEcoreWindow(IntPtr windowHandle) { NativeWebRTC.SetEcoreVideoLoopback(WebRtc.Handle, SourceId.Value, windowHandle, out uint trackId). ThrowIfFailed("Failed to set ecore video loopback"); return trackId; } } }