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