/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2018 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+using static Interop;
using System;
-using System.Threading.Tasks;
-using System.Runtime.InteropServices;
+using System.ComponentModel;
using System.Diagnostics;
using System.IO;
+using System.Runtime.InteropServices;
using System.Threading;
-using static Interop;
-using System.ComponentModel;
+using System.Threading.Tasks;
namespace Tizen.Multimedia
{
/// </remarks>
public partial class Player : IDisposable, IDisplayable<PlayerErrorCode>
{
- private PlayerHandle _handle;
+ private readonly PlayerHandle _handle;
/// <summary>
/// Initializes a new instance of the <see cref="Player"/> class.
/// <since_tizen> 3 </since_tizen>
public Player()
{
- NativePlayer.Create(out _handle).ThrowIfFailed("Failed to create player");
+ NativePlayer.Create(out _handle).ThrowIfFailed(null, "Failed to create player");
Debug.Assert(_handle != null);
- RetrieveProperties();
+ Initialize();
+ }
- if (Features.IsSupported(Features.AudioEffect))
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Player"/> class with a native handle.
+ /// The class takes care of the life cycle of the handle.
+ /// Thus, it should not be closed/destroyed in another location.
+ /// </summary>
+ /// <param name="handle">The handle for the media player.</param>
+ /// <param name="errorHandler">The handle for occuring error.</param>
+ /// <remarks>
+ /// This supports the product infrastructure and is not intended to be used directly from application code.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected Player(IntPtr handle, Action<int, string> errorHandler)
+ {
+ // This constructor is to support TV product player.
+ // Be careful with 'handle'. It must be wrapped in safe handle, first.
+ _handle = handle != IntPtr.Zero ? new PlayerHandle(handle) :
+ throw new ArgumentException("Handle is invalid.", nameof(handle));
+
+ _errorHandler = errorHandler;
+ }
+
+ private bool _initialized;
+
+ /// <summary>
+ /// This supports the product infrastructure and is not intended to be used directly from application code.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected void Initialize()
+ {
+ if (_initialized)
+ {
+ throw new InvalidOperationException("It has already been initialized.");
+ }
+
+ if (Features.IsSupported(PlayerFeatures.AudioEffect))
{
_audioEffect = new AudioEffect(this);
}
- if (Features.IsSupported(Features.RawVideo))
+ if (Features.IsSupported(PlayerFeatures.RawVideo))
{
RegisterVideoFrameDecodedCallback();
}
- DisplaySettings = PlayerDisplaySettings.Create(this);
+ RegisterEvents();
+
+ _displaySettings = PlayerDisplaySettings.Create(this);
+
+ _initialized = true;
}
internal void ValidatePlayerState(params PlayerState[] desiredStates)
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
- private void Dispose(bool disposing)
+ /// <summary>
+ /// Releases the unmanaged resources used by the <see cref="Player"/>.
+ /// </summary>
+ /// <param name="disposing">
+ /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// </param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected virtual void Dispose(bool disposing)
{
- if (!_disposed)
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
{
ReplaceDisplay(null);
try
{
_source.DetachFrom(this);
+ _source = null;
}
catch (Exception e)
{
Log.Error(PlayerLog.Tag, e.ToString());
}
}
- _source = null;
if (_handle != null)
{
_handle.Dispose();
+ _disposed = true;
}
- _disposed = true;
}
}
/// Gets the streaming download progress.
/// </summary>
/// <returns>The <see cref="DownloadProgress"/> containing current download progress.</returns>
- /// <remarks>The player must be in the <see cref="PlayerState.Playing"/> or <see cref="PlayerState.Paused"/> state.</remarks>
+ /// <remarks>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
+ /// or <see cref="PlayerState.Paused"/> state.</remarks>
/// <exception cref="InvalidOperationException">
/// The player is not streaming.<br/>
/// -or-<br/>
/// <since_tizen> 3 </since_tizen>
public DownloadProgress GetDownloadProgress()
{
- ValidatePlayerState(PlayerState.Playing, PlayerState.Paused);
+ ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
int start = 0;
int current = 0;
NativePlayer.GetStreamingDownloadProgress(Handle, out start, out current).
- ThrowIfFailed("Failed to get download progress");
+ ThrowIfFailed(this, "Failed to get download progress");
- Log.Info(PlayerLog.Tag, "get download progress : " + start + ", " + current);
+ Log.Info(PlayerLog.Tag, $"get download progress : {start}, {current}");
return new DownloadProgress(start, current);
}
/// <summary>
/// Sets the subtitle path for playback.
/// </summary>
+ /// <param name="path">The absolute path of the subtitle file, it can be NULL in the <see cref="PlayerState.Idle"/> state.</param>
/// <remarks>Only MicroDVD/SubViewer(*.sub), SAMI(*.smi), and SubRip(*.srt) subtitle formats are supported.
/// <para>The mediastorage privilege(http://tizen.org/privilege/mediastorage) must be added if any files are used to play located in the internal storage.
/// The externalstorage privilege(http://tizen.org/privilege/externalstorage) must be added if any files are used to play located in the external storage.</para>
}
NativePlayer.SetSubtitlePath(Handle, path).
- ThrowIfFailed("Failed to set the subtitle path to the player");
+ ThrowIfFailed(this, "Failed to set the subtitle path to the player");
}
/// <summary>
ValidatePlayerState(PlayerState.Idle);
NativePlayer.SetSubtitlePath(Handle, null).
- ThrowIfFailed("Failed to clear the subtitle of the player");
+ ThrowIfFailed(this, "Failed to clear the subtitle of the player");
}
/// <summary>
throw new InvalidOperationException("No subtitle set");
}
- err.ThrowIfFailed("Failed to the subtitle offset of the player");
+ err.ThrowIfFailed(this, "Failed to the subtitle offset of the player");
}
private void Prepare()
{
- NativePlayer.Prepare(Handle).ThrowIfFailed("Failed to prepare the player");
+ NativePlayer.Prepare(Handle).ThrowIfFailed(this, "Failed to prepare the player");
}
/// <summary>
/// <since_tizen> 3 </since_tizen>
protected virtual void OnPreparing()
{
- RegisterEvents();
}
/// <summary>
/// <exception cref="InvalidOperationException">No source is set.</exception>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
/// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
+ /// <seealso cref="PlayerState.Preparing"/>
/// <since_tizen> 3 </since_tizen>
public virtual Task PrepareAsync()
{
ValidatePlayerState(PlayerState.Idle);
- SetDisplay(_display).ThrowIfFailed("Failed to configure display of the player");
-
OnPreparing();
- var completionSource = new TaskCompletionSource<bool>();
-
SetPreparing();
- Task.Run(() =>
+ return Task.Factory.StartNew(() =>
{
try
{
Prepare();
- ClearPreparing();
- completionSource.SetResult(true);
}
- catch (Exception e)
+ finally
{
ClearPreparing();
- completionSource.TrySetException(e);
}
+ }, CancellationToken.None,
+ TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
+ TaskScheduler.Default);
+ }
+
+ /// <summary>
+ /// Prepares the cancellable media player for playback, asynchronously.
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token to cancel preparing.</param>
+ /// <seealso cref="CancellationToken"/>
+ /// <returns>A task that represents the asynchronous prepare operation.</returns>
+ /// <remarks>To prepare the player, the player must be in the <see cref="PlayerState.Idle"/> state,
+ /// and a source must be set.
+ /// The state must be <see cref="PlayerState.Preparing"/> to cancel preparing.
+ /// When preparing is cancelled, a state will be changed to <see cref="PlayerState.Idle"/> from <see cref="PlayerState.Preparing"/>.</remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="InvalidOperationException">
+ /// Operation failed; internal error.
+ /// -or-<br/>
+ /// The player is not in the valid state.
+ /// </exception>
+ /// <seealso cref="PrepareAsync()"/>
+ /// <seealso cref="Unprepare()"/>
+ /// <since_tizen> 6 </since_tizen>
+ public virtual async Task PrepareAsync(CancellationToken cancellationToken)
+ {
+ ValidateNotDisposed();
+
+ var taskCompletionSource = new TaskCompletionSource<bool>();
+
+ if (_source == null)
+ {
+ throw new InvalidOperationException("No source is set.");
+ }
+
+ ValidatePlayerState(PlayerState.Idle);
+
+ OnPreparing();
+
+ SetPreparing();
+
+ // register a callback to handle cancellation token anytime it occurs
+ cancellationToken.Register(() =>
+ {
+ ValidatePlayerState(PlayerState.Preparing);
+
+ // a user can get the state before finally block is called.
+ ClearPreparing();
+
+ Log.Warn(PlayerLog.Tag, $"preparing will be cancelled.");
+ NativePlayer.Unprepare(Handle).ThrowIfFailed(this, "Failed to unprepare the player");
+
+ taskCompletionSource.TrySetCanceled();
});
- return completionSource.Task;
+ _prepareCallback = _ =>
+ {
+ Log.Warn(PlayerLog.Tag, $"prepared callback is called.");
+ taskCompletionSource.TrySetResult(true);
+ };
+
+ try
+ {
+ NativePlayer.PrepareAsync(Handle, _prepareCallback, IntPtr.Zero).
+ ThrowIfFailed(this, "Failed to prepare the player");
+
+ await taskCompletionSource.Task.ConfigureAwait(false);
+ }
+ finally
+ {
+ ClearPreparing();
+ }
}
/// <summary>
}
ValidatePlayerState(PlayerState.Ready, PlayerState.Paused, PlayerState.Playing);
- NativePlayer.Unprepare(Handle).ThrowIfFailed("Failed to unprepare the player");
+ NativePlayer.Unprepare(Handle).ThrowIfFailed(this, "Failed to unprepare the player");
OnUnprepared();
}
/// Starts or resumes playback.
/// </summary>
/// <remarks>
- /// The player must be in the <see cref="PlayerState.Ready"/> or <see cref="PlayerState.Paused"/> state.
- /// It has no effect if the player is already in the <see cref="PlayerState.Playing"/> state.<br/>
- /// <br/>
- /// Sound can be mixed with other sounds if you don't control the stream focus using <see cref="ApplyAudioStreamPolicy"/>.
+ /// Sound can be mixed with other sounds if you don't control the stream focus using <see cref="ApplyAudioStreamPolicy"/>.<br/>
+ /// <para>Before Tizen 5.0, The player must be in the <see cref="PlayerState.Ready"/> or <see cref="PlayerState.Paused"/> state.
+ /// It has no effect if the player is already in the <see cref="PlayerState.Playing"/> state.</para>
+ /// <para>Since Tizen 5.0, The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
+ /// or <see cref="PlayerState.Paused"/> state.<br/>
+ /// In case of HTTP streaming playback, the player could be internally paused for buffering.
+ /// If the application calls this function during the buffering, the playback will be resumed by force
+ /// and the buffering message posting by <see cref="BufferingProgressChanged"/> will be stopped.<br/>
+ /// In other cases, the player will keep playing without returning error.</para>
/// </remarks>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
/// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
/// <seealso cref="Pause"/>
/// <seealso cref="PlaybackCompleted"/>
/// <seealso cref="ApplyAudioStreamPolicy"/>
+ /// <seealso cref="BufferingProgressChanged"/>
/// <since_tizen> 3 </since_tizen>
public virtual void Start()
{
- if (State == PlayerState.Playing)
- {
- Log.Warn(PlayerLog.Tag, "playing state already");
- return;
- }
- ValidatePlayerState(PlayerState.Ready, PlayerState.Paused);
+ ValidatePlayerState(PlayerState.Ready, PlayerState.Paused, PlayerState.Playing);
- NativePlayer.Start(Handle).ThrowIfFailed("Failed to start the player");
+ NativePlayer.Start(Handle).ThrowIfFailed(this, "Failed to start the player");
}
/// <summary>
}
ValidatePlayerState(PlayerState.Paused, PlayerState.Playing);
- NativePlayer.Stop(Handle).ThrowIfFailed("Failed to stop the player");
+ NativePlayer.Stop(Handle).ThrowIfFailed(this, "Failed to stop the player");
}
/// <summary>
ValidatePlayerState(PlayerState.Playing);
- NativePlayer.Pause(Handle).ThrowIfFailed("Failed to pause the player");
+ NativePlayer.Pause(Handle).ThrowIfFailed(this, "Failed to pause the player");
}
private MediaSource _source;
/// <summary>
+ /// Determines whether MediaSource has set.
+ /// This supports the product infrastructure and is not intended to be used directly from application code.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected bool HasSource => _source != null;
+
+ /// <summary>
/// Sets a media source for the player.
/// </summary>
/// <param name="source">A <see cref="MediaSource"/> that specifies the source for playback.</param>
/// <since_tizen> 3 </since_tizen>
public async Task<CapturedFrame> CaptureVideoAsync()
{
- ValidationUtil.ValidateFeatureSupported(Features.RawVideo);
+ ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo);
ValidatePlayerState(PlayerState.Playing, PlayerState.Paused);
using (var cbKeeper = ObjectKeeper.Get(cb))
{
NativePlayer.CaptureVideo(Handle, cb)
- .ThrowIfFailed("Failed to capture the video");
+ .ThrowIfFailed(this, "Failed to capture the video");
return await t.Task;
}
/// <summary>
/// Gets the play position in milliseconds.
/// </summary>
+ /// <returns>The current position in milliseconds.</returns>
/// <remarks>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
/// or <see cref="PlayerState.Paused"/> state.</remarks>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
/// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
/// <seealso cref="SetPlayPositionAsync(int, bool)"/>
+ /// <seealso cref="SetPlayPositionNanosecondsAsync(long, bool)"/>
+ /// <seealso cref="GetPlayPositionNanoseconds"/>
/// <since_tizen> 3 </since_tizen>
public int GetPlayPosition()
{
int playPosition = 0;
NativePlayer.GetPlayPosition(Handle, out playPosition).
- ThrowIfFailed("Failed to get the play position of the player");
+ ThrowIfFailed(this, "Failed to get the play position of the player");
- Log.Info(PlayerLog.Tag, "get play position : " + playPosition);
+ Log.Info(PlayerLog.Tag, $"get play position : {playPosition}");
return playPosition;
}
- private void SetPlayPosition(int milliseconds, bool accurate,
+ private void NativeSetPlayPosition(long position, bool accurate, bool nanoseconds,
NativePlayer.SeekCompletedCallback cb)
{
- var ret = NativePlayer.SetPlayPosition(Handle, milliseconds, accurate, cb, IntPtr.Zero);
+ //Check if it is nanoseconds or milliseconds.
+ var ret = !nanoseconds ? NativePlayer.SetPlayPosition(Handle, (int)position, accurate, cb, IntPtr.Zero) :
+ NativePlayer.SetPlayPositionNanoseconds(Handle, position, accurate, cb, IntPtr.Zero);
//Note that we assume invalid param error is returned only when the position value is invalid.
if (ret == PlayerErrorCode.InvalidArgument)
{
- throw new ArgumentOutOfRangeException(nameof(milliseconds), milliseconds,
+ throw new ArgumentOutOfRangeException(nameof(position), position,
"The position is not valid.");
}
if (ret != PlayerErrorCode.None)
{
Log.Error(PlayerLog.Tag, "Failed to set play position, " + (PlayerError)ret);
}
- ret.ThrowIfFailed("Failed to set play position");
+ ret.ThrowIfFailed(this, "Failed to set play position");
+ }
+
+ private async Task SetPlayPosition(long position, bool accurate, bool nanoseconds)
+ {
+ var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ bool immediateResult = _source is MediaStreamSource;
+
+ NativePlayer.SeekCompletedCallback cb = _ => taskCompletionSource.TrySetResult(true);
+
+ using (var cbKeeper = ObjectKeeper.Get(cb))
+ {
+ NativeSetPlayPosition(position, accurate, nanoseconds, immediateResult ? null : cb);
+
+ if (immediateResult)
+ {
+ taskCompletionSource.TrySetResult(true);
+ }
+ await taskCompletionSource.Task;
+ }
}
/// <summary>
/// </summary>
/// <param name="position">The value indicating a desired position in milliseconds.</param>
/// <param name="accurate">The value indicating whether the operation performs with accuracy.</param>
+ /// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
/// or <see cref="PlayerState.Paused"/> state.</para>
/// but this might be considerably slow. If false, the play position will be a nearest keyframe position.</para>
/// </remarks>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
- /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
+ /// <exception cref="InvalidOperationException">The player is not in the valid state.<br/>
+ /// -or-<br/>
+ /// In case of non-seekable content, the player will return error and keep playing without changing the play position.</exception>
/// <exception cref="ArgumentOutOfRangeException">The specified position is not valid.</exception>
+ /// <seealso cref="SetPlayPositionNanosecondsAsync(long, bool)"/>
/// <seealso cref="GetPlayPosition"/>
+ /// <seealso cref="GetPlayPositionNanoseconds"/>
/// <since_tizen> 3 </since_tizen>
public async Task SetPlayPositionAsync(int position, bool accurate)
{
ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
- var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
+ await SetPlayPosition(position, accurate, false);
+ }
- bool immediateResult = _source is MediaStreamSource;
+ /// <summary>
+ /// Gets the play position in nanoseconds.
+ /// </summary>
+ /// <returns>The current position in nanoseconds.</returns>
+ /// <remarks>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
+ /// or <see cref="PlayerState.Paused"/> state.</remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
+ /// <seealso cref="SetPlayPositionAsync(int, bool)"/>
+ /// <seealso cref="SetPlayPositionNanosecondsAsync(long, bool)"/>
+ /// <seealso cref="GetPlayPosition"/>
+ /// <since_tizen> 5 </since_tizen>
+ public long GetPlayPositionNanoseconds()
+ {
+ ValidatePlayerState(PlayerState.Ready, PlayerState.Paused, PlayerState.Playing);
- NativePlayer.SeekCompletedCallback cb = _ => taskCompletionSource.TrySetResult(true);
+ NativePlayer.GetPlayPositionNanoseconds(Handle, out long playPosition).
+ ThrowIfFailed(this, "Failed to get the play position(nsec) of the player");
- using (var cbKeeper = ObjectKeeper.Get(cb))
- {
- SetPlayPosition(position, accurate, cb);
- if (immediateResult)
- {
- taskCompletionSource.TrySetResult(true);
- }
+ Log.Info(PlayerLog.Tag, $"get play position(nsec) : {playPosition}");
- await taskCompletionSource.Task;
- }
+ return playPosition;
+ }
+
+ /// <summary>
+ /// Sets the seek position in nanoseconds for playback, asynchronously.
+ /// </summary>
+ /// <param name="position">The value indicating a desired position in nanoseconds.</param>
+ /// <param name="accurate">The value indicating whether the operation performs with accuracy.</param>
+ /// <returns>A task that represents the asynchronous operation.</returns>
+ /// <remarks>
+ /// <para>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
+ /// or <see cref="PlayerState.Paused"/> state.</para>
+ /// <para>If the <paramref name="accurate"/> is true, the play position will be adjusted as the specified <paramref name="position"/> value,
+ /// but this might be considerably slow. If false, the play position will be a nearest keyframe position.</para>
+ /// </remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="InvalidOperationException">The player is not in the valid state.<br/>
+ /// -or-<br/>
+ /// In case of non-seekable content, the player will return error and keep playing without changing the play position.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">The specified position is not valid.</exception>
+ /// <seealso cref="SetPlayPositionAsync(int, bool)"/>
+ /// <seealso cref="GetPlayPosition"/>
+ /// <seealso cref="GetPlayPositionNanoseconds"/>
+ /// <since_tizen> 5 </since_tizen>
+ public async Task SetPlayPositionNanosecondsAsync(long position, bool accurate)
+ {
+ ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
+
+ await SetPlayPosition(position, accurate, true);
}
/// <summary>
/// Streaming playback.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
- /// <paramref name="rate"/> is less than 5.0.<br/>
+ /// <paramref name="rate"/> is less than -5.0.<br/>
/// -or-<br/>
/// <paramref name="rate"/> is greater than 5.0.<br/>
/// -or-<br/>
ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
- NativePlayer.SetPlaybackRate(Handle, rate).ThrowIfFailed("Failed to set the playback rate.");
+ NativePlayer.SetPlaybackRate(Handle, rate).ThrowIfFailed(this, "Failed to set the playback rate.");
}
/// <summary>
/// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
/// <exception cref="ArgumentNullException"><paramref name="policy"/> is null.</exception>
/// <exception cref="NotSupportedException">
- /// <see cref="AudioStreamType"/> of <paramref name="policy"/> is not supported by <see cref="Player"/>.
+ /// The required feature is not supported.<br/>
+ /// -or-<br/>
+ /// <see cref="AudioStreamType"/> of <paramref name="policy"/> is not supported on the current platform.
/// </exception>
/// <seealso cref="AudioStreamPolicy"/>
+ /// <feature>http://tizen.org/feature/multimedia.player.stream_info</feature>
/// <since_tizen> 3 </since_tizen>
public void ApplyAudioStreamPolicy(AudioStreamPolicy policy)
{
+ ValidationUtil.ValidateFeatureSupported("http://tizen.org/feature/multimedia.player.stream_info");
+
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
ValidatePlayerState(PlayerState.Idle);
- NativePlayer.SetAudioPolicyInfo(Handle, policy.Handle).
- ThrowIfFailed("Failed to set the audio stream policy to the player");
+ var ret = NativePlayer.SetAudioPolicyInfo(Handle, policy.Handle);
+
+ if (ret == PlayerErrorCode.InvalidArgument)
+ {
+ throw new NotSupportedException("The specified policy is not supported on the current system.");
+ }
+
+ ret.ThrowIfFailed(this, "Failed to set the audio stream policy to the player");
+ }
+
+ /// <summary>
+ /// Set the relative ROI (Region Of Interest) area as a decimal fraction based on the video source.
+ /// It can be regarded as zooming operation because the specified video area will be rendered to fit to the display.
+ /// </summary>
+ /// <param name="scaleRectangle">The containing the ROI area information.</param>
+ /// <remarks>
+ /// This function requires the ratio of the each coordinate and size to the video resolution size
+ /// to guarantee of showing the same area for the dynamic resolution video content.
+ /// This function have to be called after setting <see cref="Display"/>
+ /// </remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="InvalidOperationException">
+ /// Operation failed; internal error.
+ /// -or-<br/>
+ /// The <see cref="PlayerDisplayType"/> is not set to <see cref="PlayerDisplayType.Overlay"/>.
+ /// </exception>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="scaleRectangle.ScaleX"/> is less than 0.0 or greater than 1.0.<br/>
+ /// -or-<br/>
+ /// <paramref name="scaleRectangle.ScaleY"/> is less than 0.0 or greater than 1.0.<br/>
+ /// -or-<br/>
+ /// <paramref name="scaleRectangle.ScaleWidth"/> is less than or equal to 0.0 or greater than 1.0.<br/>
+ /// -or-<br/>
+ /// <paramref name="scaleRectangle.ScaleHeight"/> is less than or equal to 0.0 or greater than 1.0.
+ /// </exception>
+ /// <seealso cref="ScaleRectangle"/>
+ /// <seealso cref="Display"/>
+ /// <seealso cref="StreamInfo.GetVideoProperties"/>
+ /// <seealso cref="GetVideoRoi"/>
+ /// <since_tizen> 5 </since_tizen>
+ public void SetVideoRoi(ScaleRectangle scaleRectangle)
+ {
+ ValidateNotDisposed();
+
+ if (scaleRectangle.ScaleX < 0 || scaleRectangle.ScaleX > 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleX), scaleRectangle.ScaleX, "Valid range is 0 to 1.0");
+ }
+ if (scaleRectangle.ScaleY < 0 || scaleRectangle.ScaleY > 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleY), scaleRectangle.ScaleY, "Valid range is 0 to 1.0");
+ }
+ if (scaleRectangle.ScaleWidth <= 0 || scaleRectangle.ScaleWidth > 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleWidth), scaleRectangle.ScaleWidth, "Valid range is 0 to 1.0 (except 0.0)");
+ }
+ if (scaleRectangle.ScaleHeight <= 0 || scaleRectangle.ScaleHeight > 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleHeight), scaleRectangle.ScaleHeight, "Valid range is 0 to 1.0 (except 0.0)");
+ }
+
+ NativePlayer.SetVideoRoi(Handle, scaleRectangle.ScaleX, scaleRectangle.ScaleY, scaleRectangle.ScaleWidth, scaleRectangle.ScaleHeight).ThrowIfFailed(this, "Failed to set the video roi area.");
+ }
+
+ /// <summary>
+ /// Get the relative ROI (Region Of Interest) area as a decimal fraction based on the video source.
+ /// </summary>
+ /// <returns>The <see cref="ScaleRectangle"/> containing the ROI area information.</returns>
+ /// <remarks>The specified ROI area is valid only in <see cref="PlayerDisplayType.Overlay"/>.</remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="InvalidOperationException">
+ /// Operation failed; internal error.
+ /// </exception>
+ /// <seealso cref="Display"/>
+ /// <seealso cref="StreamInfo.GetVideoProperties"/>
+ /// <seealso cref="SetVideoRoi"/>
+ /// <since_tizen> 5 </since_tizen>
+ public ScaleRectangle GetVideoRoi()
+ {
+ ValidateNotDisposed();
+
+ NativePlayer.GetVideoRoi(Handle, out var scaleX, out var scaleY,
+ out var scaleWidth, out var scaleHeight).ThrowIfFailed(this, "Failed to get the video roi area");
+
+ return new ScaleRectangle(scaleX, scaleY, scaleWidth, scaleHeight);
+ }
+
+ /// <summary>
+ /// This supports the product infrastructure and is not intended to be used directly from application code.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected MediaPacket GetMediaPacket(IntPtr handle)
+ {
+ MediaPacket mediaPacket = handle != IntPtr.Zero ? MediaPacket.From(handle) :
+ throw new ArgumentException("MediaPacket handle is invalid.", nameof(handle));
+
+ return mediaPacket;
}
#endregion
return Interlocked.CompareExchange(ref _isPreparing, 1, 1) == 1;
}
- private void SetPreparing()
+ /// <summary>
+ /// This supports the product infrastructure and is not intended to be used directly from application code.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected void SetPreparing()
{
Interlocked.Exchange(ref _isPreparing, 1);
}
- private void ClearPreparing()
+ /// <summary>
+ /// This supports the product infrastructure and is not intended to be used directly from application code.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected void ClearPreparing()
{
Interlocked.Exchange(ref _isPreparing, 0);
}
-
#endregion
/// <summary>
- /// This method supports the product infrastructure and is not intended to be used directly from application code.
+ /// Enable to decode an audio data for exporting PCM from a data.
/// </summary>
- /// <since_tizen> 4 </since_tizen>
- [EditorBrowsable(EditorBrowsableState.Never)]
- protected static Exception GetException(int errorCode, string message) =>
- ((PlayerErrorCode)errorCode).GetException(message);
+ /// <param name="format">The media format handle required to audio PCM specification.
+ /// The format has to include <see cref="AudioMediaFormat.MimeType"/>,
+ /// <see cref="AudioMediaFormat.Channel"/> and <see cref="AudioMediaFormat.SampleRate"/>.
+ /// If the format is NULL, the original PCM format or platform default PCM format will be applied.</param>
+ /// <param name="option">The audio extract option.</param>
+ /// <remarks><para>The player must be in the <see cref="PlayerState.Idle"/> state.</para>
+ /// <para>A <see cref="AudioDataDecoded"/> event is called in a separate thread(not in the main loop).</para>
+ /// <para>The audio PCM data can be retrieved using a <see cref="AudioDataDecoded"/> event as a media packet
+ /// and it is available until it's destroyed by <see cref="MediaPacket.Dispose()"/>.
+ /// The packet has to be destroyed as quickly as possible after rendering the data
+ /// and all the packets have to be destroyed before <see cref="Unprepare"/> is called.</para></remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="ArgumentException">The value is not valid.</exception>
+ /// <exception cref="InvalidOperationException">
+ /// Operation failed; internal error.
+ /// -or-<br/>
+ /// The player is not in the valid state.
+ /// </exception>
+ /// <seealso cref="PlayerAudioExtractOption"/>
+ /// <seealso cref="DisableExportingAudioData"/>
+ /// <since_tizen> 6 </since_tizen>
+ public void EnableExportingAudioData(AudioMediaFormat format, PlayerAudioExtractOption option)
+ {
+ ValidatePlayerState(PlayerState.Idle);
+ ValidationUtil.ValidateEnum(typeof(PlayerAudioExtractOption), option, nameof(option));
+
+ _audioFrameDecodedCallback = (IntPtr packetHandle, IntPtr userData) =>
+ {
+ var handler = AudioDataDecoded;
+ if (handler != null)
+ {
+ Log.Debug(PlayerLog.Tag, "packet : " + packetHandle.ToString());
+ handler.Invoke(this,
+ new AudioDataDecodedEventArgs(MediaPacket.From(packetHandle)));
+ }
+ else
+ {
+ MediaPacket.From(packetHandle).Dispose();
+ }
+ };
+
+ NativePlayer.SetAudioFrameDecodedCb(Handle, format == null ? IntPtr.Zero : format.AsNativeHandle(), option,
+ _audioFrameDecodedCallback, IntPtr.Zero).ThrowIfFailed(this, "Failed to register the _audioFrameDecoded");
+ }
+
+ /// <summary>
+ /// Disable to decode an audio data.
+ /// </summary>
+ /// <remarks>The player must be in the <see cref="PlayerState.Idle"/> or <see cref="PlayerState.Ready"/>
+ /// state.</remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
+ /// <seealso cref="EnableExportingAudioData"/>
+ /// <since_tizen> 6 </since_tizen>
+ public void DisableExportingAudioData()
+ {
+ ValidatePlayerState(PlayerState.Idle, PlayerState.Ready);
+
+ NativePlayer.UnsetAudioFrameDecodedCb(Handle).
+ ThrowIfFailed(this, "Failed to unset the AudioFrameDecoded");
+
+ _audioFrameDecodedCallback = null;
+ }
}
}