X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2FTizen.Multimedia.MediaPlayer%2FPlayer%2FPlayer.cs;h=ac1b78f64a1aa2ad67a94eb0becc0bbd52a861a3;hb=2b5864e3b7d3aac4d4d6f768bfdff518a16d388d;hp=ffb066b1d127e822cc793ca0be415a46403c6253;hpb=a99858b098030631e516a16861db163acaa61c5d;p=platform%2Fcore%2Fcsapi%2Ftizenfx.git diff --git a/src/Tizen.Multimedia.MediaPlayer/Player/Player.cs b/src/Tizen.Multimedia.MediaPlayer/Player/Player.cs index ffb066b..ac1b78f 100644 --- a/src/Tizen.Multimedia.MediaPlayer/Player/Player.cs +++ b/src/Tizen.Multimedia.MediaPlayer/Player/Player.cs @@ -1,5 +1,5 @@ /* - * 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. @@ -13,14 +13,15 @@ * 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 { @@ -39,7 +40,7 @@ namespace Tizen.Multimedia /// public partial class Player : IDisposable, IDisplayable { - private PlayerHandle _handle; + private readonly PlayerHandle _handle; /// /// Initializes a new instance of the class. @@ -47,23 +48,62 @@ namespace Tizen.Multimedia /// 3 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)) + /// + /// Initializes a new instance of the 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. + /// + /// The handle for the media player. + /// The handle for occuring error. + /// + /// This supports the product infrastructure and is not intended to be used directly from application code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected Player(IntPtr handle, Action 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; + + /// + /// This supports the product infrastructure and is not intended to be used directly from application code. + /// + [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) @@ -92,11 +132,24 @@ namespace Tizen.Multimedia public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + /// + /// Releases the unmanaged resources used by the . + /// + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void Dispose(bool disposing) { - if (!_disposed) + if (_disposed) + { + return; + } + + if (disposing) { ReplaceDisplay(null); @@ -105,19 +158,19 @@ namespace Tizen.Multimedia 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; } } @@ -139,7 +192,8 @@ namespace Tizen.Multimedia /// Gets the streaming download progress. /// /// The containing current download progress. - /// The player must be in the or state. + /// The player must be in the , , + /// or state. /// /// The player is not streaming.
/// -or-
@@ -149,14 +203,14 @@ namespace Tizen.Multimedia /// 3 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); } @@ -164,6 +218,7 @@ namespace Tizen.Multimedia /// /// Sets the subtitle path for playback. /// + /// The absolute path of the subtitle file, it can be NULL in the state. /// Only MicroDVD/SubViewer(*.sub), SAMI(*.smi), and SubRip(*.srt) subtitle formats are supported. /// 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. @@ -193,7 +248,7 @@ namespace Tizen.Multimedia } 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"); } /// @@ -208,7 +263,7 @@ namespace Tizen.Multimedia 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"); } /// @@ -235,12 +290,12 @@ namespace Tizen.Multimedia 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"); } /// @@ -249,7 +304,6 @@ namespace Tizen.Multimedia /// 3 protected virtual void OnPreparing() { - RegisterEvents(); } /// @@ -261,6 +315,7 @@ namespace Tizen.Multimedia /// No source is set. /// The player has already been disposed of. /// The player is not in the valid state. + /// /// 3 public virtual Task PrepareAsync() { @@ -271,30 +326,92 @@ namespace Tizen.Multimedia ValidatePlayerState(PlayerState.Idle); - SetDisplay(_display).ThrowIfFailed("Failed to configure display of the player"); - OnPreparing(); - var completionSource = new TaskCompletionSource(); - 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); + } + + /// + /// Prepares the cancellable media player for playback, asynchronously. + /// + /// The cancellation token to cancel preparing. + /// + /// A task that represents the asynchronous prepare operation. + /// To prepare the player, the player must be in the state, + /// and a source must be set. + /// The state must be to cancel preparing. + /// When preparing is cancelled, a state will be changed to from . + /// The player has already been disposed of. + /// + /// Operation failed; internal error. + /// -or-
+ /// The player is not in the valid state. + ///
+ /// + /// + /// 6 + public virtual async Task PrepareAsync(CancellationToken cancellationToken) + { + ValidateNotDisposed(); + + var taskCompletionSource = new TaskCompletionSource(); + + 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(); + } } /// @@ -320,7 +437,7 @@ namespace Tizen.Multimedia } 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(); } @@ -340,10 +457,15 @@ namespace Tizen.Multimedia /// Starts or resumes playback. /// /// - /// The player must be in the or state. - /// It has no effect if the player is already in the state.
- ///
- /// Sound can be mixed with other sounds if you don't control the stream focus using . + /// Sound can be mixed with other sounds if you don't control the stream focus using .
+ /// Before Tizen 5.0, The player must be in the or state. + /// It has no effect if the player is already in the state. + /// Since Tizen 5.0, The player must be in the , , + /// or state.
+ /// 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 will be stopped.
+ /// In other cases, the player will keep playing without returning error.
///
/// The player has already been disposed of. /// The player is not in the valid state. @@ -352,17 +474,13 @@ namespace Tizen.Multimedia /// /// /// + /// /// 3 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"); } /// @@ -386,7 +504,7 @@ namespace Tizen.Multimedia } ValidatePlayerState(PlayerState.Paused, PlayerState.Playing); - NativePlayer.Stop(Handle).ThrowIfFailed("Failed to stop the player"); + NativePlayer.Stop(Handle).ThrowIfFailed(this, "Failed to stop the player"); } /// @@ -410,12 +528,19 @@ namespace Tizen.Multimedia 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; /// + /// Determines whether MediaSource has set. + /// This supports the product infrastructure and is not intended to be used directly from application code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected bool HasSource => _source != null; + + /// /// Sets a media source for the player. /// /// A that specifies the source for playback. @@ -457,7 +582,7 @@ namespace Tizen.Multimedia /// 3 public async Task CaptureVideoAsync() { - ValidationUtil.ValidateFeatureSupported(Features.RawVideo); + ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo); ValidatePlayerState(PlayerState.Playing, PlayerState.Paused); @@ -476,7 +601,7 @@ namespace Tizen.Multimedia 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; } @@ -485,11 +610,14 @@ namespace Tizen.Multimedia /// /// Gets the play position in milliseconds. /// + /// The current position in milliseconds. /// The player must be in the , , /// or state. /// The player has already been disposed of. /// The player is not in the valid state. /// + /// + /// /// 3 public int GetPlayPosition() { @@ -498,29 +626,51 @@ namespace Tizen.Multimedia 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(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; + } } /// @@ -528,6 +678,7 @@ namespace Tizen.Multimedia /// /// The value indicating a desired position in milliseconds. /// The value indicating whether the operation performs with accuracy. + /// A task that represents the asynchronous operation. /// /// The player must be in the , , /// or state. @@ -535,30 +686,71 @@ namespace Tizen.Multimedia /// but this might be considerably slow. If false, the play position will be a nearest keyframe position. /// /// The player has already been disposed of. - /// The player is not in the valid state. + /// The player is not in the valid state.
+ /// -or-
+ /// In case of non-seekable content, the player will return error and keep playing without changing the play position.
/// The specified position is not valid. + /// /// + /// /// 3 public async Task SetPlayPositionAsync(int position, bool accurate) { ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused); - var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + await SetPlayPosition(position, accurate, false); + } - bool immediateResult = _source is MediaStreamSource; + /// + /// Gets the play position in nanoseconds. + /// + /// The current position in nanoseconds. + /// The player must be in the , , + /// or state. + /// The player has already been disposed of. + /// The player is not in the valid state. + /// + /// + /// + /// 5 + 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; + } + + /// + /// Sets the seek position in nanoseconds for playback, asynchronously. + /// + /// The value indicating a desired position in nanoseconds. + /// The value indicating whether the operation performs with accuracy. + /// A task that represents the asynchronous operation. + /// + /// The player must be in the , , + /// or state. + /// If the is true, the play position will be adjusted as the specified value, + /// but this might be considerably slow. If false, the play position will be a nearest keyframe position. + /// + /// The player has already been disposed of. + /// The player is not in the valid state.
+ /// -or-
+ /// In case of non-seekable content, the player will return error and keep playing without changing the play position.
+ /// The specified position is not valid. + /// + /// + /// + /// 5 + public async Task SetPlayPositionNanosecondsAsync(long position, bool accurate) + { + ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused); + + await SetPlayPosition(position, accurate, true); } /// @@ -577,7 +769,7 @@ namespace Tizen.Multimedia /// Streaming playback. /// /// - /// is less than 5.0.
+ /// is less than -5.0.
/// -or-
/// is greater than 5.0.
/// -or-
@@ -593,7 +785,7 @@ namespace Tizen.Multimedia 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."); } /// @@ -617,12 +809,17 @@ namespace Tizen.Multimedia /// The player is not in the valid state. /// is null. /// - /// of is not supported by . + /// The required feature is not supported.
+ /// -or-
+ /// of is not supported on the current platform. ///
/// + /// http://tizen.org/feature/multimedia.player.stream_info /// 3 public void ApplyAudioStreamPolicy(AudioStreamPolicy policy) { + ValidationUtil.ValidateFeatureSupported("http://tizen.org/feature/multimedia.player.stream_info"); + if (policy == null) { throw new ArgumentNullException(nameof(policy)); @@ -630,8 +827,103 @@ namespace Tizen.Multimedia 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"); + } + + /// + /// 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. + /// + /// The containing the ROI area information. + /// + /// 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 + /// + /// The player has already been disposed of. + /// + /// Operation failed; internal error. + /// -or-
+ /// The is not set to . + ///
+ /// + /// is less than 0.0 or greater than 1.0.
+ /// -or-
+ /// is less than 0.0 or greater than 1.0.
+ /// -or-
+ /// is less than or equal to 0.0 or greater than 1.0.
+ /// -or-
+ /// is less than or equal to 0.0 or greater than 1.0. + ///
+ /// + /// + /// + /// + /// 5 + 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."); + } + + /// + /// Get the relative ROI (Region Of Interest) area as a decimal fraction based on the video source. + /// + /// The containing the ROI area information. + /// The specified ROI area is valid only in . + /// The player has already been disposed of. + /// + /// Operation failed; internal error. + /// + /// + /// + /// + /// 5 + 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); + } + + /// + /// This supports the product infrastructure and is not intended to be used directly from application code. + /// + [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 @@ -644,24 +936,90 @@ namespace Tizen.Multimedia return Interlocked.CompareExchange(ref _isPreparing, 1, 1) == 1; } - private void SetPreparing() + /// + /// This supports the product infrastructure and is not intended to be used directly from application code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected void SetPreparing() { Interlocked.Exchange(ref _isPreparing, 1); } - private void ClearPreparing() + /// + /// This supports the product infrastructure and is not intended to be used directly from application code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected void ClearPreparing() { Interlocked.Exchange(ref _isPreparing, 0); } - #endregion /// - /// 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. /// - /// 4 - [EditorBrowsable(EditorBrowsableState.Never)] - protected static Exception GetException(int errorCode, string message) => - ((PlayerErrorCode)errorCode).GetException(message); + /// The media format handle required to audio PCM specification. + /// The format has to include , + /// and . + /// If the format is NULL, the original PCM format or platform default PCM format will be applied. + /// The audio extract option. + /// The player must be in the state. + /// A event is called in a separate thread(not in the main loop). + /// The audio PCM data can be retrieved using a event as a media packet + /// and it is available until it's destroyed by . + /// The packet has to be destroyed as quickly as possible after rendering the data + /// and all the packets have to be destroyed before is called. + /// The player has already been disposed of. + /// The value is not valid. + /// + /// Operation failed; internal error. + /// -or-
+ /// The player is not in the valid state. + ///
+ /// + /// + /// 6 + 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"); + } + + /// + /// Disable to decode an audio data. + /// + /// The player must be in the or + /// state. + /// The player has already been disposed of. + /// The player is not in the valid state. + /// + /// 6 + public void DisableExportingAudioData() + { + ValidatePlayerState(PlayerState.Idle, PlayerState.Ready); + + NativePlayer.UnsetAudioFrameDecodedCb(Handle). + ThrowIfFailed(this, "Failed to unset the AudioFrameDecoded"); + + _audioFrameDecodedCallback = null; + } } }