/*
* 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.Diagnostics;
using System.Linq;
using static Interop;
namespace Tizen.Multimedia.Remoting
{
///
/// Represents a media packet source.
///
///
///
/// 9
public sealed class MediaPacketSource : MediaSource
{
private readonly MediaFormat _audioMediaFormat;
private readonly MediaFormat _videoMediaFormat;
private static List _supportedAudioFormats;
private static List _supportedVideoFormats;
///
/// Gets all supported audio types.
///
/// 9
public static IEnumerable SupportedAudioTypes
{
get
{
GetSupportedTypes();
return _supportedAudioFormats.AsReadOnly();
}
}
///
/// Gets all supported video types.
///
/// 9
public static IEnumerable SupportedVideoTypes
{
get
{
GetSupportedTypes();
return _supportedVideoFormats.AsReadOnly();
}
}
private static void GetSupportedTypes()
{
if (_supportedAudioFormats != null || _supportedVideoFormats != null)
{
return;
}
// Currentely, supported formats are fixed in native fw but I'll keep this for future use.
_supportedAudioFormats = new List()
{
MediaFormatAudioMimeType.Vorbis,
MediaFormatAudioMimeType.Opus,
MediaFormatAudioMimeType.Pcm
};
_supportedVideoFormats = new List()
{
MediaFormatVideoMimeType.H264SP,
MediaFormatVideoMimeType.H264MP,
MediaFormatVideoMimeType.H264HP,
MediaFormatVideoMimeType.MJpeg,
MediaFormatVideoMimeType.Vp8,
MediaFormatVideoMimeType.Vp9,
MediaFormatVideoMimeType.I420,
MediaFormatVideoMimeType.NV12
};
}
private MediaPacketSourceConfiguration CreateAudioConfiguration(AudioMediaFormat format)
{
if (format == null)
{
return null;
}
if (!SupportedAudioTypes.Contains(format.MimeType))
{
throw new ArgumentException($"The audio format is not supported, Type : {format.MimeType}.");
}
return new MediaPacketSourceConfiguration(this);
}
private MediaPacketSourceConfiguration CreateVideoConfiguration(VideoMediaFormat format)
{
if (format == null)
{
return null;
}
if (!SupportedVideoTypes.Contains(format.MimeType))
{
throw new ArgumentException($"The video format is not supported, Type : {format.MimeType}.");
}
return new MediaPacketSourceConfiguration(this);
}
///
/// Initializes a new instance of the MediaPacketSource class with the specified .
///
/// The for this source.
/// is null.
/// is not supported.
///
/// 9
public MediaPacketSource(AudioMediaFormat audioMediaFormat) : base(MediaType.Audio)
{
_audioMediaFormat = audioMediaFormat ?? throw new ArgumentNullException(nameof(audioMediaFormat));
AudioConfiguration = CreateAudioConfiguration(audioMediaFormat);
}
///
/// Initializes a new instance of the MediaPacketSource class with the specified .
///
/// The for this source.
/// is null.
/// is not supported.
///
/// 9
public MediaPacketSource(VideoMediaFormat videoMediaFormat) : base(MediaType.Video)
{
_videoMediaFormat = videoMediaFormat ?? throw new ArgumentNullException(nameof(videoMediaFormat));
VideoConfiguration = CreateVideoConfiguration(videoMediaFormat);
}
///
/// Gets the audio configuration, or null if no AudioMediaFormat is specified in the constructor.
///
/// 9
public MediaPacketSourceConfiguration AudioConfiguration { get; }
///
/// Gets the video configuration, or null if no VideoMediaFormat is specified in the constructor.
///
/// 9
public MediaPacketSourceConfiguration VideoConfiguration { get; }
///
/// Pushes elementary stream to decode audio or video.
///
///
/// This source must be set as a source to a WebRTC and the WebRTC must be in the
/// or state
///
/// The to decode.
///
/// This source is not set as a source to a WebRTC.
/// -or-
/// The WebRTC is not in the valid state.
///
/// is null.
/// has been disposed.
///
/// is neither video nor audio type.
/// -or-
/// The format of packet is not matched with the specified format in the constructor.
///
///
///
///
/// 9
public void Push(MediaPacket packet)
{
if (WebRtc == null)
{
Log.Error(WebRTCLog.Tag, "The source is not set as a source to a WebRTC yet.");
throw new InvalidOperationException("The source is not set as a source to a WebRTC yet.");
}
if (packet == null)
{
Log.Error(WebRTCLog.Tag, "packet is null");
throw new ArgumentNullException(nameof(packet));
}
if (packet.IsDisposed)
{
Log.Error(WebRTCLog.Tag, "packet is disposed");
throw new ObjectDisposedException(nameof(packet));
}
if (packet.Format.Type == MediaFormatType.Text || packet.Format.Type == MediaFormatType.Container)
{
Log.Error(WebRTCLog.Tag, "The format of the packet is invalid : " + packet.Format.Type);
throw new ArgumentException($"The format of the packet is invalid : {packet.Format.Type}.");
}
if (!packet.Format.Equals(_audioMediaFormat) && !packet.Format.Equals(_videoMediaFormat))
{
Log.Error(WebRTCLog.Tag, "The format of the packet is invalid : Unmatched format.");
throw new ArgumentException("The format of the packet is invalid : Unmatched format.");
}
if (packet.Format.Type == MediaFormatType.Video && _videoMediaFormat == null)
{
Log.Error(WebRTCLog.Tag, "Video is not configured with the current source.");
throw new ArgumentException("Video is not configured with the current source.");
}
if (packet.Format.Type == MediaFormatType.Audio && _audioMediaFormat == null)
{
Log.Error(WebRTCLog.Tag, "Audio is not configured with the current source.");
throw new ArgumentException("Audio is not configured with the current source.");
}
NativeWebRTC.PushMediaPacket(WebRtc.Handle, SourceId.Value, packet.GetHandle()).
ThrowIfFailed("Failed to push the packet to the WebRTC");
}
private void SetMediaStreamInfo(MediaFormat mediaFormat)
{
if (mediaFormat == null)
{
return;
}
IntPtr ptr = IntPtr.Zero;
try
{
ptr = mediaFormat.AsNativeHandle();
NativeWebRTC.SetMediaPacketSourceInfo(WebRtc.Handle, SourceId.Value, ptr).
ThrowIfFailed("Failed to set the media stream info");
}
finally
{
MediaFormat.ReleaseNativeHandle(ptr);
}
}
internal override void OnAttached(WebRTC webRtc)
{
Debug.Assert(webRtc != null);
if (WebRtc != null)
{
Log.Error(WebRTCLog.Tag, "The source is has already been assigned to another WebRTC.");
throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
}
NativeWebRTC.AddMediaSource(webRtc.Handle, MediaSourceType.MediaPacket, out uint sourceId).
ThrowIfFailed("Failed to add MediaPacketSource.");
WebRtc = webRtc;
SourceId = sourceId;
AudioConfiguration?.OnWebRTCSet();
VideoConfiguration?.OnWebRTCSet();
SetMediaStreamInfo(_audioMediaFormat);
SetMediaStreamInfo(_videoMediaFormat);
}
internal override void OnDetached(WebRTC webRtc)
{
NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
ThrowIfFailed("Failed to remove MediaPacketSource.");
AudioConfiguration?.OnWebRTCUnset();
VideoConfiguration?.OnWebRTCUnset();
WebRtc = null;
}
}
}