2 * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.ComponentModel;
20 using System.Diagnostics;
22 using System.Runtime.InteropServices;
23 using System.Threading;
24 using System.Threading.Tasks;
26 namespace Tizen.Multimedia
28 internal static class PlayerLog
30 internal const string Tag = "Tizen.Multimedia.Player";
34 /// Provides the ability to control media playback.
37 /// The player provides functions to play a media content.
38 /// It also provides functions to adjust the configurations of the player such as playback rate, volume, looping etc.
39 /// Note that only one video player can be played at one time.
41 public partial class Player : IDisposable, IDisplayable<PlayerErrorCode>
43 private readonly PlayerHandle _handle;
46 /// Initializes a new instance of the <see cref="Player"/> class.
48 /// <since_tizen> 3 </since_tizen>
51 NativePlayer.Create(out _handle).ThrowIfFailed(null, "Failed to create player");
53 Debug.Assert(_handle != null);
59 /// Initializes a new instance of the <see cref="Player"/> class with a native handle.
60 /// The class takes care of the life cycle of the handle.
61 /// Thus, it should not be closed/destroyed in another location.
63 /// <param name="handle">The handle for the media player.</param>
64 /// <param name="errorHandler">The handle for occuring error.</param>
66 /// This supports the product infrastructure and is not intended to be used directly from application code.
68 [EditorBrowsable(EditorBrowsableState.Never)]
69 protected Player(IntPtr handle, Action<int, string> errorHandler)
71 // This constructor is to support TV product player.
72 // Be careful with 'handle'. It must be wrapped in safe handle, first.
73 _handle = handle != IntPtr.Zero ? new PlayerHandle(handle) :
74 throw new ArgumentException("Handle is invalid.", nameof(handle));
76 _errorHandler = errorHandler;
79 private bool _initialized;
82 /// This supports the product infrastructure and is not intended to be used directly from application code.
84 [EditorBrowsable(EditorBrowsableState.Never)]
85 protected void Initialize()
89 throw new InvalidOperationException("It has already been initialized.");
92 if (Features.IsSupported(PlayerFeatures.AudioEffect))
94 _audioEffect = new AudioEffect(this);
99 _displaySettings = PlayerDisplaySettings.Create(this);
104 internal void ValidatePlayerState(params PlayerState[] desiredStates)
106 Debug.Assert(desiredStates.Length > 0);
108 ValidateNotDisposed();
110 var curState = State;
111 if (curState.IsAnyOf(desiredStates))
116 throw new InvalidOperationException($"The player is not in a valid state. " +
117 $"Current State : { curState }, Valid State : { string.Join(", ", desiredStates) }.");
120 #region Dispose support
121 private bool _disposed;
124 /// Releases all resources used by the current instance.
126 /// <since_tizen> 3 </since_tizen>
127 public void Dispose()
130 GC.SuppressFinalize(this);
134 /// Releases the unmanaged resources used by the <see cref="Player"/>.
136 /// <param name="disposing">
137 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
139 [EditorBrowsable(EditorBrowsableState.Never)]
140 protected virtual void Dispose(bool disposing)
149 ReplaceDisplay(null);
155 _source.DetachFrom(this);
160 Log.Error(PlayerLog.Tag, e.ToString());
172 internal void ValidateNotDisposed()
176 Log.Warn(PlayerLog.Tag, "player was disposed");
177 throw new ObjectDisposedException(nameof(Player));
181 internal bool IsDisposed => _disposed;
187 /// Gets the streaming download progress.
189 /// <returns>The <see cref="DownloadProgress"/> containing current download progress.</returns>
190 /// <remarks>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
191 /// or <see cref="PlayerState.Paused"/> state.</remarks>
192 /// <exception cref="InvalidOperationException">
193 /// The player is not streaming.<br/>
195 /// The player is not in the valid state.
197 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
198 /// <since_tizen> 3 </since_tizen>
199 public DownloadProgress GetDownloadProgress()
201 ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
205 NativePlayer.GetStreamingDownloadProgress(Handle, out start, out current).
206 ThrowIfFailed(this, "Failed to get download progress");
208 Log.Info(PlayerLog.Tag, $"get download progress : {start}, {current}");
210 return new DownloadProgress(start, current);
214 /// Sets the subtitle path for playback.
216 /// <param name="path">The absolute path of the subtitle file, it can be NULL in the <see cref="PlayerState.Idle"/> state.</param>
217 /// <remarks>Only MicroDVD/SubViewer(*.sub), SAMI(*.smi), and SubRip(*.srt) subtitle formats are supported.
218 /// <para>The mediastorage privilege(http://tizen.org/privilege/mediastorage) must be added if any files are used to play located in the internal storage.
219 /// The externalstorage privilege(http://tizen.org/privilege/externalstorage) must be added if any files are used to play located in the external storage.</para>
221 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
222 /// <exception cref="ArgumentException"><paramref name="path"/> is an empty string.</exception>
223 /// <exception cref="FileNotFoundException">The specified path does not exist.</exception>
224 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
225 /// <since_tizen> 3 </since_tizen>
226 public void SetSubtitle(string path)
228 ValidateNotDisposed();
232 throw new ArgumentNullException(nameof(path));
235 if (path.Length == 0)
237 throw new ArgumentException("The path is empty.", nameof(path));
240 if (!File.Exists(path))
242 throw new FileNotFoundException($"The specified file does not exist.", path);
245 NativePlayer.SetSubtitlePath(Handle, path).
246 ThrowIfFailed(this, "Failed to set the subtitle path to the player");
250 /// Removes the subtitle path.
252 /// <remarks>The player must be in the <see cref="PlayerState.Idle"/> state.</remarks>
253 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
254 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
255 /// <since_tizen> 3 </since_tizen>
256 public void ClearSubtitle()
258 ValidatePlayerState(PlayerState.Idle);
260 NativePlayer.SetSubtitlePath(Handle, null).
261 ThrowIfFailed(this, "Failed to clear the subtitle of the player");
265 /// Sets the offset for the subtitle.
267 /// <param name="offset">The value indicating a desired offset in milliseconds.</param>
268 /// <remarks>The player must be in the <see cref="PlayerState.Playing"/> or <see cref="PlayerState.Paused"/> state.</remarks>
269 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
270 /// <exception cref="InvalidOperationException">
271 /// The player is not in the valid state.<br/>
273 /// No subtitle is set.
275 /// <seealso cref="SetSubtitle(string)"/>
276 /// <since_tizen> 3 </since_tizen>
277 public void SetSubtitleOffset(int offset)
279 ValidatePlayerState(PlayerState.Playing, PlayerState.Paused);
281 var err = NativePlayer.SetSubtitlePositionOffset(Handle, offset);
283 if (err == PlayerErrorCode.FeatureNotSupported)
285 throw new InvalidOperationException("No subtitle set");
288 err.ThrowIfFailed(this, "Failed to the subtitle offset of the player");
291 private void Prepare()
293 NativePlayer.Prepare(Handle).ThrowIfFailed(this, "Failed to prepare the player");
297 /// Called when the <see cref="Prepare"/> is invoked.
299 /// <since_tizen> 3 </since_tizen>
300 protected virtual void OnPreparing()
305 /// Prepares the media player for playback, asynchronously.
307 /// <returns>A task that represents the asynchronous prepare operation.</returns>
308 /// <remarks>To prepare the player, the player must be in the <see cref="PlayerState.Idle"/> state,
309 /// and a source must be set.</remarks>
310 /// <exception cref="InvalidOperationException">No source is set.</exception>
311 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
312 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
313 /// <seealso cref="PlayerState.Preparing"/>
314 /// <since_tizen> 3 </since_tizen>
315 public virtual Task PrepareAsync()
319 throw new InvalidOperationException("No source is set.");
322 ValidatePlayerState(PlayerState.Idle);
328 return Task.Factory.StartNew(() =>
338 }, CancellationToken.None,
339 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
340 TaskScheduler.Default);
344 /// Prepares the cancelable media player for playback, asynchronously.
346 /// <param name="cancellationToken">The cancellation token to cancel preparing.</param>
347 /// <seealso cref="CancellationToken"/>
348 /// <returns>The task that represents the asynchronous prepare operation.</returns>
349 /// <remarks>To prepare the player, the player must be in the <see cref="PlayerState.Idle"/> state,
350 /// and a source must be set.
351 /// The state must be <see cref="PlayerState.Preparing"/> to cancel preparing.
352 /// When preparing is cancelled, a state will be changed to <see cref="PlayerState.Idle"/> from <see cref="PlayerState.Preparing"/>.</remarks>
353 /// <exception cref="ObjectDisposedException">The player has already been disposed.</exception>
354 /// <exception cref="InvalidOperationException">
355 /// Operation failed; internal error.
357 /// The player is not in the valid state.
359 /// <seealso cref="PrepareAsync()"/>
360 /// <seealso cref="Unprepare()"/>
361 /// <since_tizen> 6 </since_tizen>
362 public virtual async Task PrepareAsync(CancellationToken cancellationToken)
364 ValidateNotDisposed();
366 var taskCompletionSource = new TaskCompletionSource<bool>();
370 throw new InvalidOperationException("No source is set.");
373 ValidatePlayerState(PlayerState.Idle);
379 // register a callback to handle cancellation token anytime it occurs
380 cancellationToken.Register(() =>
382 ValidatePlayerState(PlayerState.Preparing);
384 // a user can get the state before finally block is called.
387 Log.Warn(PlayerLog.Tag, $"preparing will be cancelled.");
388 NativePlayer.Unprepare(Handle).ThrowIfFailed(this, "Failed to unprepare the player");
390 taskCompletionSource.TrySetCanceled();
393 _prepareCallback = _ =>
395 Log.Warn(PlayerLog.Tag, $"prepared callback is called.");
396 taskCompletionSource.TrySetResult(true);
401 NativePlayer.PrepareAsync(Handle, _prepareCallback, IntPtr.Zero).
402 ThrowIfFailed(this, "Failed to prepare the player");
404 await taskCompletionSource.Task.ConfigureAwait(false);
413 /// Unprepares the player.
416 /// The most recently used source is reset and is no longer associated with the player. Playback is no longer possible.
417 /// If you want to use the player again, you have to set a source and call <see cref="PrepareAsync"/> again.
419 /// The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>, or <see cref="PlayerState.Paused"/> state.
420 /// It has no effect if the player is already in the <see cref="PlayerState.Idle"/> state.
423 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
424 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
425 /// <since_tizen> 3 </since_tizen>
426 public virtual void Unprepare()
428 if (State == PlayerState.Idle)
430 Log.Warn(PlayerLog.Tag, "idle state already");
433 ValidatePlayerState(PlayerState.Ready, PlayerState.Paused, PlayerState.Playing);
435 NativePlayer.Unprepare(Handle).ThrowIfFailed(this, "Failed to unprepare the player");
441 /// Called after the <see cref="Player"/> is unprepared.
443 /// <seealso cref="Unprepare"/>
444 /// <since_tizen> 3 </since_tizen>
445 protected virtual void OnUnprepared()
447 _source?.DetachFrom(this);
452 /// Starts or resumes playback.
455 /// Sound can be mixed with other sounds if you don't control the stream focus using <see cref="ApplyAudioStreamPolicy"/>.<br/>
456 /// <para>Before Tizen 5.0, The player must be in the <see cref="PlayerState.Ready"/> or <see cref="PlayerState.Paused"/> state.
457 /// It has no effect if the player is already in the <see cref="PlayerState.Playing"/> state.</para>
458 /// <para>Since Tizen 5.0, The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
459 /// or <see cref="PlayerState.Paused"/> state.<br/>
460 /// In case of HTTP streaming playback, the player could be internally paused for buffering.
461 /// If the application calls this function during the buffering, the playback will be resumed by force
462 /// and the buffering message posting by <see cref="BufferingProgressChanged"/> will be stopped.<br/>
463 /// In other cases, the player will keep playing without returning error.</para>
465 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
466 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
467 /// <seealso cref="PrepareAsync"/>
468 /// <seealso cref="Stop"/>
469 /// <seealso cref="Pause"/>
470 /// <seealso cref="PlaybackCompleted"/>
471 /// <seealso cref="ApplyAudioStreamPolicy"/>
472 /// <seealso cref="BufferingProgressChanged"/>
473 /// <since_tizen> 3 </since_tizen>
474 public virtual void Start()
476 ValidatePlayerState(PlayerState.Ready, PlayerState.Paused, PlayerState.Playing);
478 NativePlayer.Start(Handle).ThrowIfFailed(this, "Failed to start the player");
482 /// Stops playing the media content.
485 /// The player must be in the <see cref="PlayerState.Playing"/> or <see cref="PlayerState.Paused"/> state.
486 /// It has no effect if the player is already in the <see cref="PlayerState.Ready"/> state.
488 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
489 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
490 /// <seealso cref="Start"/>
491 /// <seealso cref="Pause"/>
492 /// <since_tizen> 3 </since_tizen>
493 public virtual void Stop()
495 if (State == PlayerState.Ready)
497 Log.Warn(PlayerLog.Tag, "ready state already");
500 ValidatePlayerState(PlayerState.Paused, PlayerState.Playing);
502 NativePlayer.Stop(Handle).ThrowIfFailed(this, "Failed to stop the player");
506 /// Pauses the player.
509 /// The player must be in the <see cref="PlayerState.Playing"/> state.
510 /// It has no effect if the player is already in the <see cref="PlayerState.Paused"/> state.
512 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
513 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
514 /// <seealso cref="Start"/>
515 /// <since_tizen> 3 </since_tizen>
516 public virtual void Pause()
518 if (State == PlayerState.Paused)
520 Log.Warn(PlayerLog.Tag, "pause state already");
524 ValidatePlayerState(PlayerState.Playing);
526 NativePlayer.Pause(Handle).ThrowIfFailed(this, "Failed to pause the player");
529 private MediaSource _source;
532 /// Determines whether MediaSource has set.
533 /// This supports the product infrastructure and is not intended to be used directly from application code.
535 [EditorBrowsable(EditorBrowsableState.Never)]
536 protected bool HasSource => _source != null;
539 /// Sets a media source for the player.
541 /// <param name="source">A <see cref="MediaSource"/> that specifies the source for playback.</param>
542 /// <remarks>The player must be in the <see cref="PlayerState.Idle"/> state.</remarks>
543 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
544 /// <exception cref="InvalidOperationException">
545 /// The player is not in the valid state.<br/>
547 /// It is not able to assign the source to the player.
549 /// <seealso cref="PrepareAsync"/>
550 /// <since_tizen> 3 </since_tizen>
551 public void SetSource(MediaSource source)
553 ValidatePlayerState(PlayerState.Idle);
557 source.AttachTo(this);
562 _source.DetachFrom(this);
569 /// Captures a video frame, asynchronously.
571 /// <returns>A task that represents the asynchronous capture operation.</returns>
572 /// <feature>http://tizen.org/feature/multimedia.raw_video</feature>
573 /// <remarks>The player must be in the <see cref="PlayerState.Playing"/> or <see cref="PlayerState.Paused"/> state.</remarks>
574 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
575 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
576 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
577 /// <since_tizen> 3 </since_tizen>
578 public async Task<CapturedFrame> CaptureVideoAsync()
580 ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo);
582 ValidatePlayerState(PlayerState.Playing, PlayerState.Paused);
584 TaskCompletionSource<CapturedFrame> t = new TaskCompletionSource<CapturedFrame>();
586 NativePlayer.VideoCaptureCallback cb = (data, width, height, size, _) =>
588 Debug.Assert(size <= int.MaxValue);
590 byte[] buf = new byte[size];
591 Marshal.Copy(data, buf, 0, (int)size);
593 t.TrySetResult(new CapturedFrame(buf, width, height));
596 using (var cbKeeper = ObjectKeeper.Get(cb))
598 NativePlayer.CaptureVideo(Handle, cb)
599 .ThrowIfFailed(this, "Failed to capture the video");
606 /// Gets the play position in milliseconds.
608 /// <returns>The current position in milliseconds.</returns>
609 /// <remarks>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
610 /// or <see cref="PlayerState.Paused"/> state.</remarks>
611 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
612 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
613 /// <seealso cref="SetPlayPositionAsync(int, bool)"/>
614 /// <seealso cref="SetPlayPositionNanosecondsAsync(long, bool)"/>
615 /// <seealso cref="GetPlayPositionNanoseconds"/>
616 /// <since_tizen> 3 </since_tizen>
617 public int GetPlayPosition()
619 ValidatePlayerState(PlayerState.Ready, PlayerState.Paused, PlayerState.Playing);
621 int playPosition = 0;
623 NativePlayer.GetPlayPosition(Handle, out playPosition).
624 ThrowIfFailed(this, "Failed to get the play position of the player");
626 Log.Info(PlayerLog.Tag, $"get play position : {playPosition}");
631 private void NativeSetPlayPosition(long position, bool accurate, bool nanoseconds,
632 NativePlayer.SeekCompletedCallback cb)
634 //Check if it is nanoseconds or milliseconds.
635 var ret = !nanoseconds ? NativePlayer.SetPlayPosition(Handle, (int)position, accurate, cb, IntPtr.Zero) :
636 NativePlayer.SetPlayPositionNanoseconds(Handle, position, accurate, cb, IntPtr.Zero);
638 //Note that we assume invalid param error is returned only when the position value is invalid.
639 if (ret == PlayerErrorCode.InvalidArgument)
641 throw new ArgumentOutOfRangeException(nameof(position), position,
642 "The position is not valid.");
644 if (ret != PlayerErrorCode.None)
646 Log.Error(PlayerLog.Tag, "Failed to set play position, " + (PlayerError)ret);
648 ret.ThrowIfFailed(this, "Failed to set play position");
651 private async Task SetPlayPosition(long position, bool accurate, bool nanoseconds)
653 var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
655 bool immediateResult = _source is MediaStreamSource;
657 NativePlayer.SeekCompletedCallback cb = _ => taskCompletionSource.TrySetResult(true);
659 using (var cbKeeper = ObjectKeeper.Get(cb))
661 NativeSetPlayPosition(position, accurate, nanoseconds, immediateResult ? null : cb);
665 taskCompletionSource.TrySetResult(true);
667 await taskCompletionSource.Task;
672 /// Sets the seek position for playback, asynchronously.
674 /// <param name="position">The value indicating a desired position in milliseconds.</param>
675 /// <param name="accurate">The value indicating whether the operation performs with accuracy.</param>
676 /// <returns>A task that represents the asynchronous operation.</returns>
678 /// <para>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
679 /// or <see cref="PlayerState.Paused"/> state.</para>
680 /// <para>If the <paramref name="accurate"/> is true, the play position will be adjusted as the specified <paramref name="position"/> value,
681 /// but this might be considerably slow. If false, the play position will be a nearest keyframe position.</para>
683 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
684 /// <exception cref="InvalidOperationException">The player is not in the valid state.<br/>
686 /// In case of non-seekable content, the player will return error and keep playing without changing the play position.</exception>
687 /// <exception cref="ArgumentOutOfRangeException">The specified position is not valid.</exception>
688 /// <seealso cref="SetPlayPositionNanosecondsAsync(long, bool)"/>
689 /// <seealso cref="GetPlayPosition"/>
690 /// <seealso cref="GetPlayPositionNanoseconds"/>
691 /// <since_tizen> 3 </since_tizen>
692 public async Task SetPlayPositionAsync(int position, bool accurate)
694 ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
696 await SetPlayPosition(position, accurate, false);
700 /// Gets the play position in nanoseconds.
702 /// <returns>The current position in nanoseconds.</returns>
703 /// <remarks>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
704 /// or <see cref="PlayerState.Paused"/> state.</remarks>
705 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
706 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
707 /// <seealso cref="SetPlayPositionAsync(int, bool)"/>
708 /// <seealso cref="SetPlayPositionNanosecondsAsync(long, bool)"/>
709 /// <seealso cref="GetPlayPosition"/>
710 /// <since_tizen> 5 </since_tizen>
711 public long GetPlayPositionNanoseconds()
713 ValidatePlayerState(PlayerState.Ready, PlayerState.Paused, PlayerState.Playing);
715 NativePlayer.GetPlayPositionNanoseconds(Handle, out long playPosition).
716 ThrowIfFailed(this, "Failed to get the play position(nsec) of the player");
718 Log.Info(PlayerLog.Tag, $"get play position(nsec) : {playPosition}");
724 /// Sets the seek position in nanoseconds for playback, asynchronously.
726 /// <param name="position">The value indicating a desired position in nanoseconds.</param>
727 /// <param name="accurate">The value indicating whether the operation performs with accuracy.</param>
728 /// <returns>A task that represents the asynchronous operation.</returns>
730 /// <para>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
731 /// or <see cref="PlayerState.Paused"/> state.</para>
732 /// <para>If the <paramref name="accurate"/> is true, the play position will be adjusted as the specified <paramref name="position"/> value,
733 /// but this might be considerably slow. If false, the play position will be a nearest keyframe position.</para>
735 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
736 /// <exception cref="InvalidOperationException">The player is not in the valid state.<br/>
738 /// In case of non-seekable content, the player will return error and keep playing without changing the play position.</exception>
739 /// <exception cref="ArgumentOutOfRangeException">The specified position is not valid.</exception>
740 /// <seealso cref="SetPlayPositionAsync(int, bool)"/>
741 /// <seealso cref="GetPlayPosition"/>
742 /// <seealso cref="GetPlayPositionNanoseconds"/>
743 /// <since_tizen> 5 </since_tizen>
744 public async Task SetPlayPositionNanosecondsAsync(long position, bool accurate)
746 ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
748 await SetPlayPosition(position, accurate, true);
752 /// Sets the playback rate.
754 /// <param name="rate">The value for the playback rate. Valid range is -5.0 to 5.0, inclusive.</param>
756 /// <para>The player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/>,
757 /// or <see cref="PlayerState.Paused"/> state.</para>
758 /// <para>The sound will be muted, when the playback rate is under 0.0 or over 2.0.</para>
760 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
761 /// <exception cref="InvalidOperationException">
762 /// The player is not in the valid state.<br/>
764 /// Streaming playback.
766 /// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
768 /// <exception cref="ArgumentOutOfRangeException">
769 /// <paramref name="rate"/> is less than -5.0.<br/>
771 /// <paramref name="rate"/> is greater than 5.0.<br/>
773 /// <paramref name="rate"/> is zero.
775 /// <since_tizen> 3 </since_tizen>
776 public void SetPlaybackRate(float rate)
778 if (rate < -5.0F || 5.0F < rate || rate == 0.0F)
780 throw new ArgumentOutOfRangeException(nameof(rate), rate, "Valid range is -5.0 to 5.0 (except 0.0)");
783 AudioOffload.CheckDisabled();
784 ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
786 NativePlayer.SetPlaybackRate(Handle, rate).ThrowIfFailed(this, "Failed to set the playback rate.");
790 /// Applies the audio stream policy.
792 /// <param name="policy">The <see cref="AudioStreamPolicy"/> to apply.</param>
794 /// The player must be in the <see cref="PlayerState.Idle"/> state.<br/>
796 /// <see cref="Player"/> does not support all <see cref="AudioStreamType"/>.<br/>
797 /// Supported types are <see cref="AudioStreamType.Media"/>, <see cref="AudioStreamType.System"/>,
798 /// <see cref="AudioStreamType.Alarm"/>, <see cref="AudioStreamType.Notification"/>,
799 /// <see cref="AudioStreamType.Emergency"/>, <see cref="AudioStreamType.VoiceInformation"/>,
800 /// <see cref="AudioStreamType.RingtoneVoip"/> and <see cref="AudioStreamType.MediaExternalOnly"/>.
802 /// <exception cref="ObjectDisposedException">
803 /// The player has already been disposed of.<br/>
805 /// <paramref name="policy"/> has already been disposed of.
807 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
808 /// <exception cref="ArgumentNullException"><paramref name="policy"/> is null.</exception>
809 /// <exception cref="NotSupportedException">
810 /// The required feature is not supported.<br/>
812 /// <see cref="AudioStreamType"/> of <paramref name="policy"/> is not supported on the current platform.
814 /// <seealso cref="AudioStreamPolicy"/>
815 /// <feature>http://tizen.org/feature/multimedia.player.stream_info</feature>
816 /// <since_tizen> 3 </since_tizen>
817 public void ApplyAudioStreamPolicy(AudioStreamPolicy policy)
819 ValidationUtil.ValidateFeatureSupported("http://tizen.org/feature/multimedia.player.stream_info");
823 throw new ArgumentNullException(nameof(policy));
826 ValidatePlayerState(PlayerState.Idle);
828 var ret = NativePlayer.SetAudioPolicyInfo(Handle, policy.Handle);
830 if (ret == PlayerErrorCode.InvalidArgument)
832 throw new NotSupportedException("The specified policy is not supported on the current system.");
835 ret.ThrowIfFailed(this, "Failed to set the audio stream policy to the player");
839 /// Set the relative ROI (Region Of Interest) area as a decimal fraction based on the video source.
840 /// It can be regarded as zooming operation because the specified video area will be rendered to fit to the display.
842 /// <param name="scaleRectangle">The containing the ROI area information.</param>
844 /// This function requires the ratio of the each coordinate and size to the video resolution size
845 /// to guarantee of showing the same area for the dynamic resolution video content.
846 /// This function have to be called after setting <see cref="Display"/>
848 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
849 /// <exception cref="InvalidOperationException">
850 /// Operation failed; internal error.
852 /// The <see cref="PlayerDisplayType"/> is not set to <see cref="PlayerDisplayType.Overlay"/>.
854 /// <exception cref="ArgumentOutOfRangeException">
855 /// <paramref name="scaleRectangle.ScaleX"/> is less than 0.0 or greater than 1.0.<br/>
857 /// <paramref name="scaleRectangle.ScaleY"/> is less than 0.0 or greater than 1.0.<br/>
859 /// <paramref name="scaleRectangle.ScaleWidth"/> is less than or equal to 0.0 or greater than 1.0.<br/>
861 /// <paramref name="scaleRectangle.ScaleHeight"/> is less than or equal to 0.0 or greater than 1.0.
863 /// <seealso cref="ScaleRectangle"/>
864 /// <seealso cref="Display"/>
865 /// <seealso cref="StreamInfo.GetVideoProperties"/>
866 /// <seealso cref="GetVideoRoi"/>
867 /// <since_tizen> 5 </since_tizen>
868 public void SetVideoRoi(ScaleRectangle scaleRectangle)
870 ValidateNotDisposed();
872 if (scaleRectangle.ScaleX < 0 || scaleRectangle.ScaleX > 1)
874 throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleX), scaleRectangle.ScaleX, "Valid range is 0 to 1.0");
876 if (scaleRectangle.ScaleY < 0 || scaleRectangle.ScaleY > 1)
878 throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleY), scaleRectangle.ScaleY, "Valid range is 0 to 1.0");
880 if (scaleRectangle.ScaleWidth <= 0 || scaleRectangle.ScaleWidth > 1)
882 throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleWidth), scaleRectangle.ScaleWidth, "Valid range is 0 to 1.0 (except 0.0)");
884 if (scaleRectangle.ScaleHeight <= 0 || scaleRectangle.ScaleHeight > 1)
886 throw new ArgumentOutOfRangeException(nameof(scaleRectangle.ScaleHeight), scaleRectangle.ScaleHeight, "Valid range is 0 to 1.0 (except 0.0)");
889 NativePlayer.SetVideoRoi(Handle, scaleRectangle.ScaleX, scaleRectangle.ScaleY, scaleRectangle.ScaleWidth, scaleRectangle.ScaleHeight).ThrowIfFailed(this, "Failed to set the video roi area.");
893 /// Get the relative ROI (Region Of Interest) area as a decimal fraction based on the video source.
895 /// <returns>The <see cref="ScaleRectangle"/> containing the ROI area information.</returns>
896 /// <remarks>The specified ROI area is valid only in <see cref="PlayerDisplayType.Overlay"/>.</remarks>
897 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
898 /// <exception cref="InvalidOperationException">
899 /// Operation failed; internal error.
901 /// <seealso cref="Display"/>
902 /// <seealso cref="StreamInfo.GetVideoProperties"/>
903 /// <seealso cref="SetVideoRoi"/>
904 /// <since_tizen> 5 </since_tizen>
905 public ScaleRectangle GetVideoRoi()
907 ValidateNotDisposed();
909 NativePlayer.GetVideoRoi(Handle, out var scaleX, out var scaleY,
910 out var scaleWidth, out var scaleHeight).ThrowIfFailed(this, "Failed to get the video roi area");
912 return new ScaleRectangle(scaleX, scaleY, scaleWidth, scaleHeight);
916 /// This supports the product infrastructure and is not intended to be used directly from application code.
918 [EditorBrowsable(EditorBrowsableState.Never)]
919 protected MediaPacket GetMediaPacket(IntPtr handle)
921 MediaPacket mediaPacket = handle != IntPtr.Zero ? MediaPacket.From(handle) :
922 throw new ArgumentException("MediaPacket handle is invalid.", nameof(handle));
928 #region Preparing state
930 private int _isPreparing;
932 private bool IsPreparing()
934 return Interlocked.CompareExchange(ref _isPreparing, 1, 1) == 1;
938 /// This supports the product infrastructure and is not intended to be used directly from application code.
940 [EditorBrowsable(EditorBrowsableState.Never)]
941 protected void SetPreparing()
943 Interlocked.Exchange(ref _isPreparing, 1);
947 /// This supports the product infrastructure and is not intended to be used directly from application code.
949 [EditorBrowsable(EditorBrowsableState.Never)]
950 protected void ClearPreparing()
952 Interlocked.Exchange(ref _isPreparing, 0);
957 /// Enable to decode an audio data for exporting PCM from a data.
959 /// <param name="format">The media format handle required to audio PCM specification.
960 /// The format has to include <see cref="AudioMediaFormat.MimeType"/>,
961 /// <see cref="AudioMediaFormat.Channel"/> and <see cref="AudioMediaFormat.SampleRate"/>.
962 /// If the format is NULL, the original PCM format or platform default PCM format will be applied.</param>
963 /// <param name="option">The audio extract option.</param>
964 /// <remarks><para>The player must be in the <see cref="PlayerState.Idle"/> state.</para>
965 /// <para>A <see cref="AudioDataDecoded"/> event is called in a separate thread(not in the main loop).</para>
966 /// <para>The audio PCM data can be retrieved using a <see cref="AudioDataDecoded"/> event as a media packet
967 /// and it is available until it's destroyed by <see cref="MediaPacket.Dispose()"/>.
968 /// The packet has to be destroyed as quickly as possible after rendering the data
969 /// and all the packets have to be destroyed before <see cref="Unprepare"/> is called.</para></remarks>
970 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
971 /// <exception cref="ArgumentException">The value is not valid.</exception>
972 /// <exception cref="InvalidOperationException">
973 /// Operation failed; internal error.
975 /// The player is not in the valid state.
977 /// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
979 /// <seealso cref="PlayerAudioExtractOption"/>
980 /// <seealso cref="DisableExportingAudioData"/>
981 /// <since_tizen> 6 </since_tizen>
982 public void EnableExportingAudioData(AudioMediaFormat format, PlayerAudioExtractOption option)
984 ValidationUtil.ValidateEnum(typeof(PlayerAudioExtractOption), option, nameof(option));
985 AudioOffload.CheckDisabled();
986 ValidatePlayerState(PlayerState.Idle);
988 _audioFrameDecodedCallback = (IntPtr packetHandle, IntPtr userData) =>
990 var handler = AudioDataDecoded;
993 Log.Debug(PlayerLog.Tag, "packet : " + packetHandle.ToString());
995 new AudioDataDecodedEventArgs(MediaPacket.From(packetHandle)));
999 MediaPacket.From(packetHandle).Dispose();
1003 NativePlayer.SetAudioFrameDecodedCb(Handle, format == null ? IntPtr.Zero : format.AsNativeHandle(), option,
1004 _audioFrameDecodedCallback, IntPtr.Zero).ThrowIfFailed(this, "Failed to register the _audioFrameDecoded");
1008 /// Disable to decode an audio data.
1010 /// <remarks>The player must be in the <see cref="PlayerState.Idle"/> or <see cref="PlayerState.Ready"/>
1011 /// state.</remarks>
1012 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
1013 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
1014 /// <seealso cref="EnableExportingAudioData"/>
1015 /// <since_tizen> 6 </since_tizen>
1016 public void DisableExportingAudioData()
1018 ValidatePlayerState(PlayerState.Idle, PlayerState.Ready);
1020 NativePlayer.UnsetAudioFrameDecodedCb(Handle).
1021 ThrowIfFailed(this, "Failed to unset the AudioFrameDecoded");
1023 _audioFrameDecodedCallback = null;
1027 /// Enables to decode a video data for every frame.
1029 /// <remarks><para>The player must be in the <see cref="PlayerState.Idle"/> state,
1030 /// but <see cref="Multimedia.Display"/> must not be set.</para>
1031 /// <para>A <see cref="VideoFrameDecoded"/> event is called in a separate thread, not called in the main loop.</para>
1032 /// <para>The video frame can be retrieved using a <see cref="VideoFrameDecoded"/> event with a media packet parameter.
1033 /// If you change the media packet in the <see cref="VideoFrameDecoded"/> event, it will be displayed on the device.
1034 /// The callback function holds the same buffer that is drawn on the display device.
1035 /// and the <see cref="MediaPacket"/> is available until it is destroyed by <see cref="MediaPacket.Dispose()"/>.
1036 /// It is recommended to destroy the packet as quickly as possible after the decoded data is rendered on the display.
1037 /// All the packets have to be destroyed before <see cref="Unprepare"/> is called.</para></remarks>
1038 /// <feature>http://tizen.org/feature/multimedia.raw_video</feature>
1039 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1040 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
1041 /// <exception cref="InvalidOperationException">
1042 /// Operation failed; internal error.
1044 /// The player is not in the valid state.
1046 /// <seealso cref="DisableExportingVideoFrame"/>
1047 /// <since_tizen> 6 </since_tizen>
1048 public void EnableExportingVideoFrame()
1050 ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo);
1051 ValidatePlayerState(PlayerState.Idle);
1053 if (Display != null)
1055 throw new InvalidOperationException("Display must be none.");
1058 _videoFrameDecodedCallback = (packetHandle, _) =>
1060 var handler = VideoFrameDecoded;
1061 if (handler != null)
1063 Log.Debug(PlayerLog.Tag, "packet : " + packetHandle);
1064 handler.Invoke(this,
1065 new VideoFrameDecodedEventArgs(MediaPacket.From(packetHandle)));
1069 MediaPacket.From(packetHandle).Dispose();
1073 NativePlayer.SetVideoFrameDecodedCb(Handle, _videoFrameDecodedCallback).
1074 ThrowIfFailed(this, "Failed to register the VideoFrameDecoded");
1078 /// Disables to decode a video data.
1080 /// <remarks>The player must be in the <see cref="PlayerState.Idle"/> or <see cref="PlayerState.Ready"/>
1081 /// state.</remarks>
1082 /// <feature>http://tizen.org/feature/multimedia.raw_video</feature>
1083 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1084 /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
1085 /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
1086 /// <seealso cref="EnableExportingVideoFrame"/>
1087 /// <since_tizen> 6 </since_tizen>
1088 public void DisableExportingVideoFrame()
1090 ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo);
1091 ValidatePlayerState(PlayerState.Idle, PlayerState.Ready);
1093 NativePlayer.UnsetVideoFrameDecodedCb(Handle).
1094 ThrowIfFailed(this, "Failed to unset the VideoFrameDecoded");
1096 _videoFrameDecodedCallback = null;