Skip to content

Commit

Permalink
[MediaPlayer] add API about playback time to set/get in nonoseconds (S…
Browse files Browse the repository at this point in the history
…amsung#337)

* [MediaPlayer] add API about playback time to set/get in nonoseconds
  • Loading branch information
aferin authored Jul 27, 2018
1 parent f30f1c4 commit dc40f54
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 17 deletions.
10 changes: 10 additions & 0 deletions src/Tizen.Multimedia.MediaPlayer/Interop/Interop.Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ internal static partial class NativePlayer
internal static extern PlayerErrorCode SetPlayPosition(IntPtr player, int millisecond,
bool accurate, SeekCompletedCallback cb, IntPtr userData = default(IntPtr));

[DllImport(Libraries.Player, EntryPoint = "player_get_play_position_nsec")]
internal static extern PlayerErrorCode GetPlayPositionNanoseconds(IntPtr player, out long nanoseconds);

[DllImport(Libraries.Player, EntryPoint = "player_set_play_position_nsec")]
internal static extern PlayerErrorCode SetPlayPositionNanoseconds(IntPtr player, long nanoseconds,
bool accurate, SeekCompletedCallback cb, IntPtr userData = default(IntPtr));

[DllImport(Libraries.Player, EntryPoint = "player_set_mute")]
internal static extern PlayerErrorCode SetMute(IntPtr player, bool muted);

Expand Down Expand Up @@ -243,6 +250,9 @@ internal static extern PlayerErrorCode SetMediaStreamSeekCb(IntPtr player, Strea
[DllImport(Libraries.Player, EntryPoint = "player_get_duration")]
internal static extern PlayerErrorCode GetDuration(IntPtr player, out int duration);

[DllImport(Libraries.Player, EntryPoint = "player_get_duration_nsec")]
internal static extern PlayerErrorCode GetDurationNanoseconds(IntPtr player, out long duration);

[DllImport(Libraries.Player, EntryPoint = "player_set_subtitle_path")]
internal static extern PlayerErrorCode SetSubtitlePath(IntPtr player, string path);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ internal void NotifyError(int errorCode, string message)
/// <summary>
/// This method supports the product infrastructure and is not intended to be used directly from application code.
/// </summary>
/// <param name="errorCode">The error code according to the exception.</param>
/// <param name="message">The error message to show user.</param>
/// <since_tizen> 4 </since_tizen>
[EditorBrowsable(EditorBrowsableState.Never)]
protected static Exception GetException(int errorCode, string message) =>
Expand Down
102 changes: 86 additions & 16 deletions src/Tizen.Multimedia.MediaPlayer/Player/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public Player()
/// 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>
Expand Down Expand Up @@ -209,6 +211,7 @@ public DownloadProgress GetDownloadProgress()
/// <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>
Expand Down Expand Up @@ -529,11 +532,14 @@ public async Task<CapturedFrame> CaptureVideoAsync()
/// <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()
{
Expand All @@ -549,15 +555,17 @@ public int GetPlayPosition()
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)
Expand All @@ -567,42 +575,104 @@ private void SetPlayPosition(int milliseconds, bool accurate,
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, cb);
if (immediateResult)
{
taskCompletionSource.TrySetResult(true);
}

await taskCompletionSource.Task;
}
}

/// <summary>
/// Sets the seek position for playback, asynchronously.
/// </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>
/// <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.</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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class PlayerDisplaySettings
/// <summary>
/// This constructor supports the product infrastructure and is not intended to be used directly from application code.
/// </summary>
/// <param name="player"> The handle for the media player </param>
/// <since_tizen> 4 </since_tizen>
[EditorBrowsable(EditorBrowsableState.Never)]
protected PlayerDisplaySettings(Player player)
Expand Down
1 change: 1 addition & 0 deletions src/Tizen.Multimedia.MediaPlayer/Player/PlayerTrackInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public int GetCount()
/// <summary>
/// Gets the language code for the specified index, or null if the language is undefined.
/// </summary>
/// <param name="index">The index of track.</param>
/// <returns>The number of tracks.</returns>
/// <remarks>
/// <para>The <see cref="Player"/> that owns this instance must be in the <see cref="PlayerState.Ready"/>,
Expand Down
30 changes: 29 additions & 1 deletion src/Tizen.Multimedia.MediaPlayer/Player/StreamInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ public string GetVideoCodec()
/// </exception>
/// <exception cref="InvalidOperationException">
/// The <see cref="Multimedia.Player"/> that this instance belongs to is not in the valid state.
/// </exception>
/// </exception>\
/// <seealso cref="GetDurationNanoseconds"/>
/// <since_tizen> 3 </since_tizen>
public int GetDuration()
{
Expand All @@ -273,6 +274,33 @@ public int GetDuration()
return duration;
}

/// <summary>
/// Gets the duration in nanoseconds.
/// </summary>
/// <returns>The duration of the stream.</returns>
/// <remarks>
/// The <see cref="Multimedia.Player"/> that owns this instance must be in the <see cref="PlayerState.Ready"/>,
/// <see cref="PlayerState.Playing"/>, or <see cref="PlayerState.Paused"/> state.
/// </remarks>
/// <exception cref="ObjectDisposedException">
/// The <see cref="Multimedia.Player"/> that this instance belongs to has been disposed of.
/// </exception>
/// <exception cref="InvalidOperationException">
/// The <see cref="Multimedia.Player"/> that this instance belongs to is not in the valid state.
/// </exception>
/// <seealso cref="GetDuration"/>
/// <since_tizen> 5 </since_tizen>
public long GetDurationNanoseconds()
{
Player.ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);

NativePlayer.GetDurationNanoseconds(Player.Handle, out var duration).
ThrowIfFailed(Player, "Failed to get the duration in nanoseconds");

Log.Info(PlayerLog.Tag, "get duration(nsec) : " + duration);
return duration;
}

/// <summary>
/// Gets the properties of the audio.
/// </summary>
Expand Down

0 comments on commit dc40f54

Please sign in to comment.