[MediaPlayer] add API to set/get replaygain (#318)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.MediaPlayer / Player / Player.Properties.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 using System;
17 using System.Threading.Tasks;
18 using System.Runtime.InteropServices;
19 using System.Diagnostics;
20 using System.IO;
21 using System.Threading;
22 using NativeDisplay = Interop.Display;
23 using static Interop;
24
25 namespace Tizen.Multimedia
26 {
27     /// <summary>
28     /// Represents properties for streaming buffering time
29     /// </summary>
30     /// <since_tizen> 5 </since_tizen>
31     public struct PlayerBufferingTime
32     {
33         /// <summary>
34         /// Initializes a new instance of the PlayerBufferingTime struct.
35         /// </summary>
36         /// <param name="preBufferMillisecond">A duration of buffering data that must be prerolled to start playback.</param>
37         /// <param name="reBufferMillisecond">A duration of buffering data that must be prerolled to resume playback
38         /// if player enters pause state for buffering.</param>
39         /// <since_tizen> 5 </since_tizen>
40         public PlayerBufferingTime(int preBufferMillisecond, int reBufferMillisecond)
41         {
42             PreBufferMillisecond = preBufferMillisecond;
43             ReBufferMillisecond = reBufferMillisecond;
44         }
45
46         /// <summary>
47         /// Gets or sets the duration of buffering data that must be prerolled to start playback
48         /// </summary>
49         /// <since_tizen> 5 </since_tizen>
50         public int PreBufferMillisecond
51         {
52             get;
53             set;
54         }
55
56         /// <summary>
57         /// Gets or sets the duration of buffering data that must be prerolled to resume playback
58         /// if player enters pause state for buffering.
59         /// </summary>
60         /// <since_tizen> 5 </since_tizen>
61         public int ReBufferMillisecond
62         {
63             get;
64             set;
65         }
66     }
67     /// <since_tizen> 3 </since_tizen>
68     public partial class Player
69     {
70         /// <summary>
71         /// Gets the native handle of the player.
72         /// </summary>
73         /// <value>An IntPtr that contains the native handle of the player.</value>
74         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
75         /// <since_tizen> 3 </since_tizen>
76         public IntPtr Handle
77         {
78             get
79             {
80                 ValidateNotDisposed();
81                 return _handle.DangerousGetHandle();
82             }
83         }
84
85         #region Network configuration
86         private string _cookie = "";
87         private string _userAgent = "";
88
89         /// <summary>
90         /// Gets or sets the cookie for streaming playback.
91         /// </summary>
92         /// <remarks>To set, the player must be in the <see cref="PlayerState.Idle"/> state.</remarks>
93         /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
94         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
95         /// <exception cref="ArgumentNullException">The value to set is null.</exception>
96         /// <since_tizen> 3 </since_tizen>
97         public string Cookie
98         {
99             get
100             {
101                 return _cookie;
102             }
103             set
104             {
105                 ValidatePlayerState(PlayerState.Idle);
106
107                 if (value == null)
108                 {
109                     throw new ArgumentNullException(nameof(value), "Cookie can't be null.");
110                 }
111
112                 NativePlayer.SetStreamingCookie(Handle, value, value.Length).
113                     ThrowIfFailed(this, "Failed to set the cookie to the player");
114
115                 _cookie = value;
116             }
117         }
118
119         /// <summary>
120         /// Gets or sets the user agent for streaming playback.
121         /// </summary>
122         /// <remarks>To set, the player must be in the <see cref="PlayerState.Idle"/> state.</remarks>
123         /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
124         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
125         /// <exception cref="ArgumentNullException">The value to set is null.</exception>
126         /// <since_tizen> 3 </since_tizen>
127         public string UserAgent
128         {
129             get
130             {
131                 return _userAgent;
132             }
133             set
134             {
135                 ValidatePlayerState(PlayerState.Idle);
136
137                 if (value == null)
138                 {
139                     throw new ArgumentNullException(nameof(value), "UserAgent can't be null.");
140                 }
141
142                 NativePlayer.SetStreamingUserAgent(Handle, value, value.Length).
143                     ThrowIfFailed(this, "Failed to set the user agent to the player");
144
145                 _userAgent = value;
146             }
147         }
148
149         /// <summary>
150         /// Gets or sets the streaming buffering time.
151         /// </summary>
152         /// <remarks>To set, the player must be in the <see cref="PlayerState.Idle"/> state.</remarks>
153         /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
154         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
155         /// <exception cref="ArgumentOutOfRangeException">
156         ///     <pramref name="PreBufferMillisecond"/> is less than 0.<br/>
157         ///     -or-<br/>
158         ///     <pramref name="ReBufferMillisecond"/> is less than 0.<br/>
159         /// </exception>
160         /// <seealso cref="PlayerBufferingTime"/>
161         /// <since_tizen> 5 </since_tizen>
162         public PlayerBufferingTime BufferingTime
163         {
164             get
165             {
166                 ValidateNotDisposed();
167
168                 NativePlayer.GetStreamingBufferingTime(Handle, out var PreBuffMillisecond, out var ReBuffMillisecond).
169                         ThrowIfFailed(this, "Failed to get the buffering time of the player");
170
171                 return new PlayerBufferingTime(PreBuffMillisecond, ReBuffMillisecond);
172             }
173             set
174             {
175                 ValidatePlayerState(PlayerState.Idle);
176
177                 if (value.PreBufferMillisecond < 0 || value.ReBufferMillisecond < 0)
178                 {
179                     throw new ArgumentOutOfRangeException("invalid range");
180                 }
181
182                 NativePlayer.SetStreamingBufferingTime(Handle, value.PreBufferMillisecond, value.ReBufferMillisecond).
183                     ThrowIfFailed(this, "Failed to set the buffering time of the player");
184             }
185         }
186         #endregion
187
188         /// <summary>
189         /// Gets the state of the player.
190         /// </summary>
191         /// <value>The current state of the player.</value>
192         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
193         /// <since_tizen> 3 </since_tizen>
194         public PlayerState State
195         {
196             get
197             {
198                 ValidateNotDisposed();
199
200                 if (IsPreparing())
201                 {
202                     return PlayerState.Preparing;
203                 }
204
205                 NativePlayer.GetState(Handle, out var state).
206                     ThrowIfFailed(this, "Failed to retrieve the state of the player");
207
208                 Debug.Assert(Enum.IsDefined(typeof(PlayerState), state));
209
210                 return (PlayerState)state;
211             }
212         }
213
214         /// <summary>
215         /// Gets or sets the audio latency mode.
216         /// </summary>
217         /// <value>A <see cref="AudioLatencyMode"/> that specifies the mode. The default is <see cref="AudioLatencyMode.Mid"/>.</value>
218         /// <remarks>
219         /// If the mode is <see cref="AudioLatencyMode.High"/>,
220         /// audio output interval can be increased, so it can keep more audio data to play.
221         /// But, state transition like pause or resume can be more slower than default(<see cref="AudioLatencyMode.Mid"/>).
222         /// </remarks>
223         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
224         /// <exception cref="ArgumentException">The value is not valid.</exception>
225         /// <since_tizen> 3 </since_tizen>
226         public AudioLatencyMode AudioLatencyMode
227         {
228             get
229             {
230                 NativePlayer.GetAudioLatencyMode(Handle, out var value).
231                     ThrowIfFailed(this, "Failed to get the audio latency mode of the player");
232
233                 return value;
234             }
235             set
236             {
237                 ValidateNotDisposed();
238
239                 ValidationUtil.ValidateEnum(typeof(AudioLatencyMode), value, nameof(value));
240
241                 NativePlayer.SetAudioLatencyMode(Handle, value).
242                     ThrowIfFailed(this, "Failed to set the audio latency mode of the player");
243             }
244         }
245
246         /// <summary>
247         /// Gets or sets the looping state.
248         /// </summary>
249         /// <value>true if the playback is looping; otherwise, false. The default value is false.</value>
250         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
251         /// <since_tizen> 3 </since_tizen>
252         public bool IsLooping
253         {
254             get
255             {
256                 NativePlayer.IsLooping(Handle, out var value).
257                     ThrowIfFailed(this, "Failed to get the looping state of the player");
258
259                 return value;
260             }
261             set
262             {
263                 ValidateNotDisposed();
264
265                 NativePlayer.SetLooping(Handle, value).
266                     ThrowIfFailed(this, "Failed to set the looping state of the player");
267             }
268         }
269
270         #region Display methods
271
272         private PlayerDisplaySettings _displaySettings;
273
274         /// <summary>
275         /// Gets the display settings.
276         /// </summary>
277         /// <value>A <see cref="PlayerDisplaySettings"/> that specifies the display settings.</value>
278         /// <since_tizen> 3 </since_tizen>
279         public PlayerDisplaySettings DisplaySettings => _displaySettings;
280
281         private Display _display;
282
283         private PlayerErrorCode SetDisplay(Display display)
284         {
285             if (display == null)
286             {
287                 return NativeDisplay.SetDisplay(Handle, PlayerDisplayType.None, IntPtr.Zero);
288             }
289
290             return display.ApplyTo(this);
291         }
292
293         private void ReplaceDisplay(Display newDisplay)
294         {
295             _display?.SetOwner(null);
296             _display = newDisplay;
297             _display?.SetOwner(this);
298         }
299
300         /// <summary>
301         /// Gets or sets the display.
302         /// </summary>
303         /// <value>A <see cref="Multimedia.Display"/> that specifies the display.</value>
304         /// <remarks>
305         ///     The player must be in the <see cref="PlayerState.Idle"/> state.<br/>
306         ///     The raw video feature(http://tizen.org/feature/multimedia.raw_video) is required if
307         ///     the display is created with <see cref="MediaView"/>.
308         /// </remarks>
309         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
310         /// <exception cref="ArgumentException">The value has already been assigned to another player.</exception>
311         /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
312         /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
313         /// <since_tizen> 3 </since_tizen>
314         public Display Display
315         {
316             get
317             {
318                 return _display;
319             }
320             set
321             {
322                 ValidatePlayerState(PlayerState.Idle);
323
324                 if (value != null && value.HasMediaView)
325                 {
326                     ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo);
327                 }
328
329                 if (value?.Owner != null)
330                 {
331                     if (ReferenceEquals(this, value.Owner))
332                     {
333                         return;
334                     }
335
336                     throw new ArgumentException("The display has already been assigned to another.");
337                 }
338
339                 SetDisplay(value).ThrowIfFailed(this, "Failed to configure display of the player");
340
341                 ReplaceDisplay(value);
342             }
343         }
344
345         PlayerErrorCode IDisplayable<PlayerErrorCode>.ApplyEvasDisplay(DisplayType type, ElmSharp.EvasObject evasObject)
346         {
347             Debug.Assert(IsDisposed == false);
348
349             Debug.Assert(Enum.IsDefined(typeof(DisplayType), type));
350             Debug.Assert(type != DisplayType.None);
351
352             return NativeDisplay.SetDisplay(Handle,
353                 type == DisplayType.Overlay ? PlayerDisplayType.Overlay : PlayerDisplayType.Evas, evasObject);
354         }
355
356         PlayerErrorCode IDisplayable<PlayerErrorCode>.ApplyEcoreWindow(IntPtr windowHandle)
357         {
358             Debug.Assert(IsDisposed == false);
359
360             return NativeDisplay.SetEcoreDisplay(Handle, PlayerDisplayType.Overlay, windowHandle);
361         }
362         #endregion
363
364         private PlayerTrackInfo _audioTrack;
365
366         /// <summary>
367         /// Gets the track info for the audio.
368         /// </summary>
369         /// <value>A <see cref="PlayerTrackInfo"/> for audio.</value>
370         /// <since_tizen> 3 </since_tizen>
371         public PlayerTrackInfo AudioTrackInfo
372         {
373             get
374             {
375                 if (_audioTrack == null)
376                 {
377                     _audioTrack = new PlayerTrackInfo(this, StreamType.Audio);
378                 }
379                 return _audioTrack;
380             }
381         }
382
383         private PlayerTrackInfo _subtitleTrackInfo;
384
385         /// <summary>
386         /// Gets the track info for the subtitle.
387         /// </summary>
388         /// <value>A <see cref="PlayerTrackInfo"/> for the subtitle.</value>
389         /// <since_tizen> 3 </since_tizen>
390         public PlayerTrackInfo SubtitleTrackInfo
391         {
392             get
393             {
394                 if (_subtitleTrackInfo == null)
395                 {
396                     _subtitleTrackInfo = new PlayerTrackInfo(this, StreamType.Text);
397                 }
398                 return _subtitleTrackInfo;
399             }
400         }
401
402         private StreamInfo _streamInfo;
403
404         /// <summary>
405         /// Gets the stream information.
406         /// </summary>
407         /// <value>A <see cref="StreamInfo"/> for this player.</value>
408         /// <since_tizen> 3 </since_tizen>
409         public StreamInfo StreamInfo
410         {
411             get
412             {
413                 if (_streamInfo == null)
414                 {
415                     _streamInfo = new StreamInfo(this);
416                 }
417                 return _streamInfo;
418             }
419         }
420
421         private AudioEffect _audioEffect;
422
423         /// <summary>
424         /// Gets the audio effect.
425         /// </summary>
426         /// <feature>http://tizen.org/feature/multimedia.custom_audio_effect</feature>
427         /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
428         /// <since_tizen> 3 </since_tizen>
429         public AudioEffect AudioEffect
430         {
431             get
432             {
433                 if (_audioEffect == null)
434                 {
435                     throw new NotSupportedException($"The feature({PlayerFeatures.AudioEffect}) is not supported.");
436                 }
437
438                 return _audioEffect;
439             }
440         }
441
442         /// <summary>
443         /// Gets or sets the mute state.
444         /// </summary>
445         /// <value>true if the player is muted; otherwise, false.</value>
446         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
447         /// <since_tizen> 3 </since_tizen>
448         public bool Muted
449         {
450             get
451             {
452                 NativePlayer.IsMuted(Handle, out var value).
453                     ThrowIfFailed(this, "Failed to get the mute state of the player");
454
455                 Log.Info(PlayerLog.Tag, "get mute : " + value);
456
457                 return value;
458             }
459             set
460             {
461                 NativePlayer.SetMute(Handle, value).ThrowIfFailed(this, "Failed to set the mute state of the player");
462             }
463         }
464
465         /// <summary>
466         /// Gets or sets the current volume.
467         /// </summary>
468         /// <remarks>Valid volume range is from 0 to 1.0, inclusive.</remarks>
469         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
470         /// <exception cref="ArgumentOutOfRangeException">
471         ///     <paramref name="value"/> is less than zero.<br/>
472         ///     -or-<br/>
473         ///     <paramref name="value"/> is greater than 1.0.
474         /// </exception>
475         /// <since_tizen> 3 </since_tizen>
476         public float Volume
477         {
478             get
479             {
480                 float value = 0.0F;
481                 NativePlayer.GetVolume(Handle, out value, out value).
482                     ThrowIfFailed(this, "Failed to get the volume of the player");
483
484                 return value;
485             }
486             set
487             {
488                 if (value < 0F || 1.0F < value)
489                 {
490                     throw new ArgumentOutOfRangeException(nameof(value), value,
491                         $"Valid volume range is 0 <= value <= 1.0, but got { value }.");
492                 }
493
494                 NativePlayer.SetVolume(Handle, value, value).
495                     ThrowIfFailed(this, "Failed to set the volume of the player");
496             }
497         }
498
499         /// <summary>
500         /// Gets or sets the audio-only state.
501         /// </summary>
502         /// <value>true if the playback is audio-only mode; otherwise, false. The default value is false.</value>
503         /// The <see cref="Player"/> must be in the <see cref="PlayerState.Ready"/>,
504         /// <see cref="PlayerState.Playing"/>, or <see cref="PlayerState.Paused"/> state.
505         /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
506         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
507         /// <since_tizen> 5 </since_tizen>
508         public bool IsAudioOnly
509         {
510             get
511             {
512                 ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
513                 NativePlayer.IsAudioOnly(Handle, out var value).
514                     ThrowIfFailed(this, "Failed to get the audio-only state of the player");
515                 return value;
516             }
517             set
518             {
519                 ValidateNotDisposed();
520                 ValidatePlayerState(PlayerState.Ready, PlayerState.Playing, PlayerState.Paused);
521                 NativePlayer.SetAudioOnly(Handle, value).
522                     ThrowIfFailed(this, "Failed to set the audio-only state of the player");
523             }
524         }
525
526         /// <summary>
527         /// Gets or sets the player's replaygain state.
528         /// </summary>
529         /// <value>If the replaygain status is true, replaygain is applied (if contents has a replaygain tag);
530         /// otherwise, the replaygain isn't affected by tag and properties.</value>
531         /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
532         /// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
533         /// <since_tizen> 5 </since_tizen>
534         public bool ReplayGain
535         {
536             get
537             {
538                 ValidateNotDisposed();
539                 NativePlayer.IsReplayGain(Handle, out var value).
540                     ThrowIfFailed(this, "Failed to get the replaygain of the player");
541                 return value;
542             }
543             set
544             {
545                 ValidateNotDisposed();
546                 NativePlayer.SetReplayGain(Handle, value).
547                     ThrowIfFailed(this, "Failed to set the replaygain of the player");
548             }
549         }
550
551         private SphericalVideo _sphericalVideo;
552
553         /// <summary>
554         /// Gets the spherical video settings.
555         /// </summary>
556         /// <since_tizen> 5 </since_tizen>
557         public SphericalVideo SphericalVideo
558         {
559             get
560             {
561                 if (_sphericalVideo == null)
562                 {
563                     _sphericalVideo = new SphericalVideo(this);
564                 }
565
566                 return _sphericalVideo;
567             }
568         }
569
570         private AdaptiveVariants _adaptiveVariants;
571
572         /// <summary>
573         /// Gets the adaptive variants settings.
574         /// </summary>
575         /// <since_tizen> 5 </since_tizen>
576         public AdaptiveVariants AdaptiveVariants
577         {
578             get
579             {
580                 if (_adaptiveVariants == null)
581                 {
582                     _adaptiveVariants = new AdaptiveVariants(this);
583                 }
584
585                 return _adaptiveVariants;
586             }
587         }
588     }
589 }