/*
* Copyright (c) 2016 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
{
///
/// Provides the ability to push packets as the source of .
///
/// The source must be set as a source to a player before pushing.
///
/// 3
public sealed class MediaStreamSource : MediaSource
{
private readonly MediaFormat _audioMediaFormat;
private readonly MediaFormat _videoMediaFormat;
private static List _supportedAudioFormats;
private static List _supportedVideoFormats;
///
/// Gets all supported audio types.
///
/// 3
public static IEnumerable SupportedAudioTypes
{
get
{
GetSupportedTypes();
return _supportedAudioFormats.AsReadOnly();
}
}
///
/// Gets all supported video types.
///
/// 3
public static IEnumerable SupportedVideoTypes
{
get
{
GetSupportedTypes();
return _supportedVideoFormats.AsReadOnly();
}
}
private static void GetSupportedTypes()
{
if (_supportedAudioFormats == null && _supportedVideoFormats == null)
{
PlayerHandle _playerHandle;
IntPtr _handle;
NativePlayer.Create(out _playerHandle).ThrowIfFailed(null, "Failed to create player");
_handle = _playerHandle.DangerousGetHandle();
Debug.Assert(_handle != IntPtr.Zero);
try
{
_supportedAudioFormats = new List();
_supportedVideoFormats = new List();
NativePlayer.SupportedMediaFormatCallback callback = (int format, IntPtr userData) =>
{
if (Enum.IsDefined(typeof(MediaFormatAudioMimeType), format))
{
Log.Debug(PlayerLog.Tag, "supported audio : " + ((MediaFormatAudioMimeType)format).ToString());
_supportedAudioFormats.Add((MediaFormatAudioMimeType)format);
}
else if (Enum.IsDefined(typeof(MediaFormatVideoMimeType), format))
{
Log.Debug(PlayerLog.Tag, "supported video : " + ((MediaFormatVideoMimeType)format).ToString());
_supportedVideoFormats.Add((MediaFormatVideoMimeType)format);
}
else
{
Log.Debug(PlayerLog.Tag, "skipped : " + format.ToString());
}
return true;
};
NativePlayer.SupportedMediaStreamFormat(_handle, callback, IntPtr.Zero).
ThrowIfFailed(null, "Failed to get the list");
}
finally
{
_playerHandle.Dispose();
}
}
}
private Player _player;
private MediaStreamConfiguration CreateAudioConfiguration(AudioMediaFormat format)
{
if (format == null)
{
return null;
}
if (!SupportedAudioTypes.Contains(format.MimeType))
{
Log.Error(PlayerLog.Tag, "The audio format is not supported : " + format.MimeType);
throw new ArgumentException($"The audio format is not supported, Type : {format.MimeType}.");
}
return new MediaStreamConfiguration(this, StreamType.Audio);
}
private MediaStreamConfiguration CreateVideoConfiguration(VideoMediaFormat format)
{
if (format == null)
{
return null;
}
if (!SupportedVideoTypes.Contains(format.MimeType))
{
Log.Error(PlayerLog.Tag, "The video format is not supported : " + format.MimeType);
throw new ArgumentException($"The video format is not supported, Type : {format.MimeType}.");
}
return new MediaStreamConfiguration(this, StreamType.Video);
}
///
/// Initializes a new instance of the MediaStreamSource class
/// with the specified and .
///
/// The for this source.
/// The for this source.
/// AAC and H.264 are supported.
/// Both and are null.
///
/// is not supported.
/// -or-
/// is not supported.
///
///
///
/// 3
public MediaStreamSource(AudioMediaFormat audioMediaFormat, VideoMediaFormat videoMediaFormat)
{
if (audioMediaFormat == null && videoMediaFormat == null)
{
throw new ArgumentNullException(string.Concat(nameof(_audioMediaFormat), " and ", nameof(_videoMediaFormat)));
}
_audioMediaFormat = audioMediaFormat;
_videoMediaFormat = videoMediaFormat;
AudioConfiguration = CreateAudioConfiguration(audioMediaFormat);
VideoConfiguration = CreateVideoConfiguration(videoMediaFormat);
}
///
/// Initializes a new instance of the MediaStreamSource class with the specified .
///
/// The for this source.
/// AAC is supported.
/// is null.
/// is not supported.
///
/// 3
public MediaStreamSource(AudioMediaFormat audioMediaFormat)
{
_audioMediaFormat = audioMediaFormat ?? throw new ArgumentNullException(nameof(audioMediaFormat));
AudioConfiguration = CreateAudioConfiguration(audioMediaFormat);
}
///
/// Initializes a new instance of the MediaStreamSource class with the specified .
///
/// H.264 is supported.
/// The for this source.
/// is null.
/// is not supported.
///
/// 3
public MediaStreamSource(VideoMediaFormat videoMediaFormat)
{
_videoMediaFormat = videoMediaFormat ?? throw new ArgumentNullException(nameof(videoMediaFormat));
VideoConfiguration = CreateVideoConfiguration(videoMediaFormat);
}
///
/// Gets the audio configuration, or null if no AudioMediaFormat is specified in the constructor.
///
/// 3
public MediaStreamConfiguration AudioConfiguration { get; }
///
/// Gets the video configuration, or null if no VideoMediaFormat is specified in the constructor.
///
/// 3
public MediaStreamConfiguration VideoConfiguration { get; }
///
/// Pushes elementary stream to decode audio or video.
///
/// This source must be set as a source to a player and the player must be in the ,
/// , or state.
/// The to decode.
///
/// This source is not set as a source to a player.
/// -or-
/// The player is not in the valid state.
///
/// is null.
/// has been disposed of.
///
/// is neither video nor audio type.
/// -or-
/// The format of packet is not matched with the specified format in the constructor.
///
/// The internal buffer has reached its limits.
///
///
///
/// 3
public void Push(MediaPacket packet)
{
if (_player == null)
{
Log.Error(PlayerLog.Tag, "The source is not set as a source to a player yet.");
throw new InvalidOperationException("The source is not set as a source to a player yet.");
}
if (packet == null)
{
Log.Error(PlayerLog.Tag, "packet is null");
throw new ArgumentNullException(nameof(packet));
}
if (packet.IsDisposed)
{
Log.Error(PlayerLog.Tag, "packet is disposed");
throw new ObjectDisposedException(nameof(packet));
}
if (packet.Format.Type == MediaFormatType.Text || packet.Format.Type == MediaFormatType.Container)
{
Log.Error(PlayerLog.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(PlayerLog.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(PlayerLog.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(PlayerLog.Tag, "Audio is not configured with the current source.");
throw new ArgumentException("Audio is not configured with the current source.");
}
_player.ValidatePlayerState(PlayerState.Paused, PlayerState.Playing, PlayerState.Ready);
NativePlayer.PushMediaStream(_player.Handle, packet.GetHandle()).
ThrowIfFailed(_player, "Failed to push the packet to the player");
}
private void SetMediaStreamInfo(StreamType streamType, MediaFormat mediaFormat)
{
if (mediaFormat == null)
{
Log.Warn(PlayerLog.Tag, "invalid media format");
return;
}
IntPtr ptr = IntPtr.Zero;
try
{
ptr = mediaFormat.AsNativeHandle();
NativePlayer.SetMediaStreamInfo(_player.Handle, streamType, ptr).
ThrowIfFailed(_player, "Failed to set the media stream info");
}
finally
{
MediaFormat.ReleaseNativeHandle(ptr);
}
}
internal override void OnAttached(Player player)
{
Debug.Assert(player != null);
if (_player != null)
{
Log.Error(PlayerLog.Tag, "The source is has already been assigned to another player.");
throw new InvalidOperationException("The source is has already been assigned to another player.");
}
AudioConfiguration?.OnPlayerSet(player);
VideoConfiguration?.OnPlayerSet(player);
_player = player;
SetMediaStreamInfo(StreamType.Audio, _audioMediaFormat);
SetMediaStreamInfo(StreamType.Video, _videoMediaFormat);
}
internal override void OnDetached(Player player)
{
base.OnDetached(player);
AudioConfiguration?.OnPlayerUnset(player);
VideoConfiguration?.OnPlayerUnset(player);
_player = null;
}
///
/// Gets the that this source is assigned to as a source, or null if this source is not assigned.
///
///
/// 3
public Player Player => _player;
}
}