2 * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections.Generic;
20 using System.Diagnostics;
21 using System.Threading.Tasks;
22 using Tizen.Applications;
23 using Native = Interop.MediaControllerClient;
24 using NativePlaylist = Interop.MediaControllerPlaylist;
26 namespace Tizen.Multimedia.Remoting
29 /// Provides a means to send commands to and handle events from media control server.
31 /// <since_tizen> 4 </since_tizen>
32 public partial class MediaController
34 internal MediaController(MediaControllerManager manager, string serverAppId)
36 Debug.Assert(manager != null);
37 Debug.Assert(serverAppId != null);
40 ServerAppId = serverAppId;
43 private MediaControllerManager Manager { get; }
46 /// Gets the application id of the server.
48 /// <value>The server application id.</value>
49 /// <since_tizen> 4 </since_tizen>
50 public string ServerAppId { get; }
53 /// Gets a value indicating whether the sever has been stopped.
55 /// <value>true if the server has been stopped; otherwise, false.</value>
56 /// <since_tizen> 4 </since_tizen>
63 private void ThrowIfStopped()
67 throw new InvalidOperationException("The server has already been stopped.");
72 #region Get information
74 /// Returns the playback state set by the server.
76 /// <returns>The playback state.</returns>
77 /// <exception cref="InvalidOperationException">
78 /// The server has already been stopped.<br/>
80 /// An internal error occurs.
82 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
83 /// <seealso cref="MediaControlServer.SetPlaybackState(MediaControlPlaybackState, long)"/>
84 /// <since_tizen> 4 </since_tizen>
85 public MediaControlPlaybackState GetPlaybackState()
89 IntPtr playbackHandle = IntPtr.Zero;
93 Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
95 Native.GetPlaybackState(playbackHandle, out var playbackCode).ThrowIfError("Failed to get state.");
97 return playbackCode.ToPublic();
101 if (playbackHandle != IntPtr.Zero)
103 Native.DestroyPlayback(playbackHandle);
109 /// Returns the playback position set by the server.
111 /// <returns>The playback position in milliseconds.</returns>
112 /// <exception cref="InvalidOperationException">
113 /// The server has already been stopped.<br/>
115 /// An internal error occurs.
117 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
118 /// <seealso cref="MediaControlServer.SetPlaybackState(MediaControlPlaybackState, long)"/>
119 /// <since_tizen> 4 </since_tizen>
120 public long GetPlaybackPosition()
124 IntPtr playbackHandle = IntPtr.Zero;
128 Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
130 Native.GetPlaybackPosition(playbackHandle, out var position).ThrowIfError("Failed to get position.");
132 return (long)position;
136 if (playbackHandle != IntPtr.Zero)
138 Native.DestroyPlayback(playbackHandle);
144 /// Returns the metadata set by the server.
146 /// <returns>The metadata.</returns>
147 /// <exception cref="InvalidOperationException">
148 /// The server has already been stopped.<br/>
150 /// An internal error occurs.
152 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
153 /// <seealso cref="MediaControlServer.SetMetadata(MediaControlMetadata)"/>
154 /// <since_tizen> 4 </since_tizen>
155 public MediaControlMetadata GetMetadata()
159 IntPtr metadataHandle = IntPtr.Zero;
163 NativePlaylist.GetServerMetadata(Manager.Handle, ServerAppId, out metadataHandle).
164 ThrowIfError("Failed to get metadata.");
166 return new MediaControlMetadata(metadataHandle);
170 if (metadataHandle != IntPtr.Zero)
172 NativePlaylist.DestroyMetadata(metadataHandle);
178 /// Returns the all playlists.
180 /// <returns>The set of <see cref="MediaControlPlaylist"/>.</returns>
181 /// <exception cref="InvalidOperationException">
182 /// The server has already been stopped.<br/>
184 /// An internal error occurs.
186 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
187 /// <since_tizen> 5 </since_tizen>
188 public IEnumerable<MediaControlPlaylist> GetPlaylists()
192 var playlists = new List<MediaControlPlaylist>();
194 Exception caught = null;
196 NativePlaylist.PlaylistCallback playlistCallback = (handle, _) =>
200 playlists.Add(new MediaControlPlaylist(handle));
210 NativePlaylist.ForeachPlaylist(ServerAppId, playlistCallback, IntPtr.Zero).
211 ThrowIfError("Failed to get playlist.");
218 return playlists.AsReadOnly();
222 /// Returns the playlist name of current playing media.
224 /// <returns>The playlist name.</returns>
225 /// <exception cref="InvalidOperationException">
226 /// The server has already been stopped.<br/>
228 /// An internal error occurs.
230 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
231 /// <since_tizen> 5 </since_tizen>
232 public MediaControlPlaylist GetPlaylistOfCurrentPlayingMedia()
236 IntPtr playbackHandle = IntPtr.Zero;
238 // Get the playlist name of current playing media.
241 Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
243 var (name, index) = NativePlaylist.GetPlaylistInfo(playbackHandle);
245 return GetPlaylists().FirstOrDefault(playlist => playlist.Name == name);
249 if (playbackHandle != IntPtr.Zero)
251 Native.DestroyPlayback(playbackHandle).ThrowIfError("Failed to destroy playback handle.");
257 /// Returns the index of current playing media.
259 /// <returns>The index of current playing media.</returns>
260 /// <exception cref="InvalidOperationException">
261 /// The server has already been stopped.<br/>
263 /// An internal error occurs.
265 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
266 /// <since_tizen> 5 </since_tizen>
267 public string GetIndexOfCurrentPlayingMedia()
271 IntPtr playbackHandle = IntPtr.Zero;
275 Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
277 var (name, index) = NativePlaylist.GetPlaylistInfo(playbackHandle);
282 if (playbackHandle != IntPtr.Zero)
284 Native.DestroyPlayback(playbackHandle).ThrowIfError("Failed to destroy playback handle.");
290 /// Returns whether the shuffle mode is enabled.
292 /// <returns>A value indicating whether the shuffle mode is enabled.</returns>
293 /// <exception cref="InvalidOperationException">
294 /// The server has already been stopped.<br/>
296 /// An internal error occurs.
298 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
299 /// <seealso cref="MediaControlServer.SetShuffleModeEnabled(bool)"/>
300 /// <since_tizen> 4 </since_tizen>
301 public bool IsShuffleModeEnabled()
305 Native.GetServerShuffleMode(Manager.Handle, ServerAppId, out var shuffleMode).
306 ThrowIfError("Failed to get shuffle mode state.");
308 return shuffleMode == MediaControllerNativeShuffleMode.On;
312 /// Returns the repeat mode.
314 /// <returns>A <see cref="MediaControlRepeatMode"/> set by the server.</returns>
315 /// <exception cref="InvalidOperationException">
316 /// The server has already been stopped.<br/>
318 /// An internal error occurs.
320 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
321 /// <seealso cref="MediaControlServer.SetRepeatMode(MediaControlRepeatMode)"/>
322 /// <since_tizen> 4 </since_tizen>
323 public MediaControlRepeatMode GetRepeatMode()
327 Native.GetServerRepeatMode(Manager.Handle, ServerAppId, out var repeatMode).
328 ThrowIfError("Failed to get repeat mode state.");
330 return repeatMode.ToPublic();
334 /// Gets the content type of current playing media.
336 /// <returns>The <see cref="MediaControlContentType"/>.</returns>
337 /// <exception cref="InvalidOperationException">
338 /// The server has already been stopped.<br/>
340 /// An internal error occurs.
342 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
343 /// <since_tizen> 5 </since_tizen>
344 public MediaControlContentType GetContentTypeOfCurrentPlayingMedia()
348 IntPtr playbackHandle = IntPtr.Zero;
352 Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
354 Native.GetPlaybackContentType(playbackHandle, out MediaControlContentType type).
355 ThrowIfError("Failed to get playback content type");
361 if (playbackHandle != IntPtr.Zero)
363 Native.DestroyPlayback(playbackHandle);
369 /// Gets the icon path.
371 /// <returns>The icon path.</returns>
372 /// <exception cref="InvalidOperationException">
373 /// The server has already been stopped.<br/>
375 /// An internal error occurs.
377 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
378 /// <since_tizen> 5 </since_tizen>
379 public string GetIconPath()
383 Native.GetServerIcon(Manager.Handle, ServerAppId, out string uri).
384 ThrowIfError("Failed to get icon path.");
390 /// Gets the age rating of current playing media.
392 /// <returns>The Age rating of current playing media. The range is 0 to 19, inclusive.</returns>
393 /// <exception cref="InvalidOperationException">
394 /// The server has already been stopped.<br/>
396 /// An internal error occurs.
398 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
399 /// <since_tizen> 5 </since_tizen>
400 public int GetAgeRatingOfCurrentPlayingMedia()
404 IntPtr playbackHandle = IntPtr.Zero;
408 Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
410 Native.GetAgeRating(playbackHandle, out int ageRating).ThrowIfError("Failed to get age rating.");
416 if (playbackHandle != IntPtr.Zero)
418 Native.DestroyPlayback(playbackHandle);
424 /// Gets whether the subtitle mode is enabled or not.
426 /// <returns>A value indicating whether the subtitle mode is enabled or not.</returns>
427 /// <value>true if the subtitle mode is enabled; otherwise, false.</value>
428 /// <exception cref="InvalidOperationException">
429 /// The server has already been stopped.<br/>
431 /// An internal error occurs.
433 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
434 /// <since_tizen> 6 </since_tizen>
435 public bool IsSubtitleModeEnabled()
439 Native.IsSubtitleEnabled(Manager.Handle, ServerAppId, out var isEnabled).
440 ThrowIfError("Failed to get subtitle mode state.");
446 /// Gets whether the 360 mode is enabled or not.
448 /// <returns>A value indicating whether the 360 mode is enabled or not.</returns>
449 /// <value>true if the 360 mode is enabled; otherwise, false.</value>
450 /// <exception cref="InvalidOperationException">
451 /// The server has already been stopped.<br/>
453 /// An internal error occurs.
455 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
456 /// <since_tizen> 6 </since_tizen>
457 public bool IsMode360Enabled()
461 Native.IsMode360Enabled(Manager.Handle, ServerAppId, out var isEnabled).
462 ThrowIfError("Failed to get 360 mode state.");
468 /// Gets the current display mode.
470 /// <returns>The <see cref="MediaControlDisplayMode"/>.</returns>
471 /// <exception cref="InvalidOperationException">
472 /// The server has already been stopped.<br/>
474 /// An internal error occurs.
476 /// <exception cref="ObjectDisposedException">
477 /// The <see cref="MediaControllerManager"/> has already been disposed.
479 /// <since_tizen> 6 </since_tizen>
480 public MediaControlDisplayMode GetDisplayMode()
484 Native.GetDisplayMode(Manager.Handle, ServerAppId, out var mode).
485 ThrowIfError("Failed to get display mode state.");
487 return mode.ToPublic();
491 /// Gets the current display rotation.
493 /// <returns>The <see cref="Rotation"/>.</returns>
494 /// <exception cref="InvalidOperationException">
495 /// The server has already been stopped.<br/>
497 /// An internal error occurs.
499 /// <exception cref="ObjectDisposedException">
500 /// The <see cref="MediaControllerManager"/> has already been disposed.
502 /// <since_tizen> 6 </since_tizen>
503 public Rotation GetDisplayRotation()
507 Native.GetDisplayRotation(Manager.Handle, ServerAppId, out var rotation).
508 ThrowIfError("Failed to get display rotation state.");
510 return rotation.ToPublic();
512 #endregion Get information
517 /// Gets the value whether <see cref="MediaControlPlaybackCommand"/> is supported or not.
520 /// the set of <see cref="MediaControlPlaybackCommand"/> and <see cref="MediaControlCapabilitySupport"/>.
522 /// <exception cref="InvalidOperationException">
523 /// The server has already been stopped.<br/>
525 /// An internal error occurs.
527 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
528 /// <since_tizen> 5 </since_tizen>
529 public Dictionary<MediaControlPlaybackCommand, MediaControlCapabilitySupport> GetPlaybackCapabilities()
533 IntPtr playbackCapaHandle = IntPtr.Zero;
535 var playbackCapabilities = new Dictionary<MediaControlPlaybackCommand, MediaControlCapabilitySupport>();
539 Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
540 ThrowIfError("Failed to get playback capability handle.");
542 foreach (MediaControllerNativePlaybackAction action in Enum.GetValues(typeof(MediaControllerNativePlaybackAction)))
544 Native.GetPlaybackCapability(playbackCapaHandle, action, out MediaControlCapabilitySupport support);
545 playbackCapabilities.Add(action.ToPublic(), support);
548 return playbackCapabilities;
552 if (playbackCapaHandle != IntPtr.Zero)
554 Native.DestroyCapability(playbackCapaHandle);
560 /// Gets the value whether <paramref name="action"/> is supported or not.
562 /// <param name="action">A playback command.</param>
563 /// <returns>A <see cref="MediaControlCapabilitySupport"/>.</returns>
564 /// <exception cref="ArgumentException"><paramref name="action"/> is not valid.</exception>
565 /// <exception cref="InvalidOperationException">
566 /// The server has already been stopped.<br/>
568 /// An internal error occurs.
570 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
571 /// <since_tizen> 5 </since_tizen>
572 public MediaControlCapabilitySupport GetPlaybackCapability(MediaControlPlaybackCommand action)
576 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), action, nameof(action));
578 IntPtr playbackCapaHandle = IntPtr.Zero;
582 Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
583 ThrowIfError("Failed to get playback capability handle.");
585 Native.GetPlaybackCapability(playbackCapaHandle, action.ToNative(), out MediaControlCapabilitySupport support);
591 if (playbackCapaHandle != IntPtr.Zero)
593 Native.DestroyCapability(playbackCapaHandle);
599 /// Gets the value whether the shuffle mode is supported or not.
601 /// <returns>A <see cref="MediaControlCapabilitySupport"/>.</returns>
602 /// <exception cref="InvalidOperationException">
603 /// The server has already been stopped.<br/>
605 /// An internal error occurs.
607 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
608 /// <since_tizen> 5 </since_tizen>
609 public MediaControlCapabilitySupport GetShuffleModeCapability()
613 Native.GetSimpleCapability(Manager.Handle, ServerAppId, MediaControlNativeCapabilityCategory.Shuffle, out MediaControlCapabilitySupport support).
614 ThrowIfError("Failed to get shuffle mode capability");
620 /// Gets the value whether the repeat mode is supported or not.
622 /// <returns>A <see cref="MediaControlCapabilitySupport"/>.</returns>
623 /// <exception cref="InvalidOperationException">
624 /// The server has already been stopped.<br/>
626 /// An internal error occurs.
628 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
629 /// <since_tizen> 5 </since_tizen>
630 public MediaControlCapabilitySupport GetRepeatModeCapability()
634 Native.GetSimpleCapability(Manager.Handle, ServerAppId, MediaControlNativeCapabilityCategory.Repeat, out MediaControlCapabilitySupport support).
635 ThrowIfError("Failed to get repeat mode capability");
641 /// Gets the value whether the repeat mode is supported or not.
644 /// If there's no supported display mode by server, it will return null.
645 /// otherwise, it will return the supported list of <see cref="MediaControlDisplayMode"/>.
647 /// <exception cref="InvalidOperationException">
648 /// The server has already been stopped.<br/>
650 /// An internal error occurs.
652 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
653 /// <since_tizen> 6 </since_tizen>
654 public IEnumerable<MediaControlDisplayMode> GetDisplayModeCapability()
658 Native.GetDisplayModeCapability(Manager.Handle, ServerAppId, out uint support).
659 ThrowIfError("Failed to get display mode capability");
661 return support != 0 ? ((MediaControlNativeDisplayMode)support).ToPublicList() : null;
665 /// Gets the value whether the display mode is supported or not.
668 /// If there's no supported display rotation by server, it will return null.
669 /// otherwise, it will return the supported list of <see cref="Rotation"/>.
671 /// <exception cref="InvalidOperationException">
672 /// The server has already been stopped.<br/>
674 /// An internal error occurs.
676 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
677 /// <since_tizen> 6 </since_tizen>
678 public IEnumerable<Rotation> GetDisplayRotationapability()
682 Native.GetDisplayRotationCapability(Manager.Handle, ServerAppId, out uint support).
683 ThrowIfError("Failed to get display mode capability");
685 return support != 0 ? ((MediaControlNativeDisplayRotation)support).ToPublicList() : null;
687 #endregion Capability
692 /// Requests command to the server.
695 /// The client can request the server to execute <see cref="PlaybackCommand"/> or <see cref="ShuffleModeCommand"/> or
696 /// <see cref="RepeatModeCommand"/> or <see cref="CustomCommand"/>, <br/>
697 /// and then, the client receive the result of each request(command).
699 /// <param name="command">A <see cref="Command"/> class.</param>
700 /// <returns><see cref="Bundle"/> represents the extra data from server and it can be null.</returns>
701 /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
702 /// <exception cref="InvalidOperationException">
703 /// The server has already been stopped.<br/>
705 /// An internal error occurs.
707 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
708 /// <since_tizen> 5 </since_tizen>
709 public async Task<Bundle> RequestAsync(Command command)
713 throw new ArgumentNullException(nameof(command));
718 command.SetRequestInformation(ServerAppId);
720 var tcs = new TaskCompletionSource<MediaControllerError>();
721 string reqeustId = null;
722 Bundle bundle = null;
724 EventHandler<CommandCompletedEventArgs> eventHandler = (s, e) =>
726 if (e.RequestId == reqeustId)
729 tcs.TrySetResult(e.Result);
735 CommandCompleted += eventHandler;
737 reqeustId = command.Request(Manager.Handle);
739 (await tcs.Task).ThrowIfError("Failed to request command");
745 CommandCompleted -= eventHandler;
750 /// Sends the result of each command.
752 /// <param name="command">The command that return to client.</param>
753 /// <param name="result">The result of <paramref name="command"/>.</param>
754 /// <param name="bundle">The extra data.</param>
755 /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
756 /// <exception cref="InvalidOperationException">
757 /// The server is not running .<br/>
759 /// An internal error occurs.
761 /// <since_tizen> 5 </since_tizen>
762 public void Response(Command command, int result, Bundle bundle)
766 throw new ArgumentNullException(nameof(command));
769 command.Response(Manager.Handle, result, bundle);
773 /// Sends the result of each command.
775 /// <param name="command">The command that return to client.</param>
776 /// <param name="result">The result of <paramref name="command"/>.</param>
777 /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
778 /// <exception cref="InvalidOperationException">
779 /// The server is not running .<br/>
781 /// An internal error occurs.
783 /// <since_tizen> 5 </since_tizen>
784 public void Response(Command command, int result)
788 throw new ArgumentNullException(nameof(command));
791 command.Response(Manager.Handle, result, null);
795 /// Sends playback command to the server.
797 /// <param name="command">A playback command.</param>
798 /// <exception cref="InvalidOperationException">
799 /// The server has already been stopped.<br/>
801 /// An internal error occurs.
803 /// <exception cref="ArgumentException"><paramref name="command"/> is not valid.</exception>
804 /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed.</exception>
805 /// <seealso cref="MediaControlServer.PlaybackCommandReceived"/>
806 /// <since_tizen> 4 </since_tizen>
807 [Obsolete("Please do not use! This will be deprecated. Please use Request instead.")]
808 public void SendPlaybackCommand(MediaControlPlaybackCommand command)
812 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), command, nameof(command));
814 Native.SendPlaybackActionCommandWithoutReqId(Manager.Handle, ServerAppId, command.ToNative()).
815 ThrowIfError("Failed to send command.");