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