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.
18 using System.Collections.Generic;
19 using System.Threading.Tasks;
20 using Tizen.Applications;
21 using Native = Interop.MediaControllerServer;
22 using NativePlaylist = Interop.MediaControllerPlaylist;
24 namespace Tizen.Multimedia.Remoting
27 /// Provides a means to set playback information and metadata and receive commands from clients.
29 /// <seealso cref="MediaControllerManager"/>
30 /// <seealso cref="MediaController"/>
31 /// <since_tizen> 4 </since_tizen>
32 public static partial class MediaControlServer
34 private static IntPtr _handle;
35 private static bool? _isRunning;
36 private static string _serverName;
39 /// Gets a value indicating whether the server is running.
41 /// <value>true if the server has started; otherwise, false.</value>
42 /// <seealso cref="Start"/>
43 /// <seealso cref="Stop"/>
44 /// <since_tizen> 4 </since_tizen>
45 public static bool IsRunning
49 if (_isRunning.HasValue == false)
51 _isRunning = GetRunningState();
54 return _isRunning.Value;
58 private static bool GetRunningState()
60 IntPtr handle = IntPtr.Zero;
63 Native.ConnectDb(out handle).ThrowIfError("Failed to retrieve the running state.");
65 Native.CheckServerExist(handle, Applications.Application.Current.ApplicationInfo.ApplicationId,
66 out var value).ThrowIfError("Failed to retrieve the running state.");
72 if (handle != IntPtr.Zero)
74 Native.DisconnectDb(handle);
79 private static void EnsureInitializedIfRunning()
81 if (_handle != IntPtr.Zero)
86 if (IsRunning == false)
88 throw new InvalidOperationException("The server is not running.");
94 private static IntPtr Handle
98 EnsureInitializedIfRunning();
104 private static void Initialize()
106 Native.Create(out _handle).ThrowIfError("Failed to create media controller server.");
110 RegisterPlaybackActionCommandReceivedEvent();
111 RegisterPlaybackPositionCommandReceivedEvent();
112 RegisterPlaylistCommandReceivedEvent();
113 RegisterShuffleModeCommandReceivedEvent();
114 RegisterRepeatModeCommandReceivedEvent();
115 RegisterCustomCommandReceivedEvent();
116 RegisterCommandCompletedEvent();
117 RegisterSearchCommandReceivedEvent();
119 _serverName = Application.Current.ApplicationInfo.ApplicationId;
124 Native.Destroy(_handle);
125 _playbackCommandCallback = null;
126 _handle = IntPtr.Zero;
133 /// Starts the media control server.
136 /// When the server starts, <see cref="MediaControllerManager.ServerStarted"/> will be raised.
138 /// <privilege>http://tizen.org/privilege/mediacontroller.server</privilege>
139 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
140 /// <exception cref="UnauthorizedAccessException">Caller does not have required privilege.</exception>
141 /// <seealso cref="MediaControllerManager.ServerStarted"/>
142 /// <since_tizen> 4 </since_tizen>
143 public static void Start()
149 /// Stops the media control server.
152 /// When the server stops, <see cref="MediaControllerManager.ServerStopped"/> will be raised.
154 /// <exception cref="InvalidOperationException">
155 /// The server is not running .<br/>
157 /// An internal error occurs.
159 /// <seealso cref="MediaControllerManager.ServerStopped"/>
160 /// <since_tizen> 4 </since_tizen>
161 public static void Stop()
163 EnsureInitializedIfRunning();
165 Native.Destroy(_handle).ThrowIfError("Failed to stop the server.");
167 _handle = IntPtr.Zero;
168 _playbackCommandCallback = null;
173 /// Updates playback state and playback position.</summary>
174 /// <param name="state">The playback state.</param>
175 /// <param name="position">The playback position in milliseconds.</param>
176 /// <exception cref="ArgumentException"><paramref name="state"/> is not valid.</exception>
177 /// <exception cref="ArgumentOutOfRangeException"><paramref name="position"/> is less than zero.</exception>
178 /// <exception cref="InvalidOperationException">
179 /// The server is not running .<br/>
181 /// An internal error occurs.
183 /// <since_tizen> 4 </since_tizen>
184 public static void SetPlaybackState(MediaControlPlaybackState state, long position)
186 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackState), state, nameof(state));
190 throw new ArgumentOutOfRangeException(nameof(position), position, "position can't be less than zero.");
193 Native.SetPlaybackState(Handle, state.ToNative()).ThrowIfError("Failed to set playback state.");
195 Native.SetPlaybackPosition(Handle, (ulong)position).ThrowIfError("Failed to set playback position.");
197 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
200 private static void SetMetadata(MediaControllerNativeAttribute attribute, string value)
204 Native.SetMetadata(Handle, attribute, value).ThrowIfError($"Failed to set metadata({attribute}).");
209 /// Updates metadata information.
211 /// <param name="metadata">The metadata to update.</param>
212 /// <exception cref="ArgumentNullException"><paramref name="metadata"/> is null.</exception>
213 /// <exception cref="InvalidOperationException">
214 /// The server is not running .<br/>
216 /// An internal error occurs.
218 /// <since_tizen> 4 </since_tizen>
219 public static void SetMetadata(MediaControlMetadata metadata)
221 if (metadata == null)
223 throw new ArgumentNullException(nameof(metadata));
226 SetMetadata(MediaControllerNativeAttribute.Title, metadata.Title);
227 SetMetadata(MediaControllerNativeAttribute.Artist, metadata.Artist);
228 SetMetadata(MediaControllerNativeAttribute.Album, metadata.Album);
229 SetMetadata(MediaControllerNativeAttribute.Author, metadata.Author);
230 SetMetadata(MediaControllerNativeAttribute.Genre, metadata.Genre);
231 SetMetadata(MediaControllerNativeAttribute.Duration, metadata.Duration);
232 SetMetadata(MediaControllerNativeAttribute.Date, metadata.Date);
233 SetMetadata(MediaControllerNativeAttribute.Copyright, metadata.Copyright);
234 SetMetadata(MediaControllerNativeAttribute.Description, metadata.Description);
235 SetMetadata(MediaControllerNativeAttribute.TrackNumber, metadata.TrackNumber);
236 SetMetadata(MediaControllerNativeAttribute.Picture, metadata.AlbumArtPath);
237 SetMetadata(MediaControllerNativeAttribute.Season, metadata.EncodedSeason);
238 SetMetadata(MediaControllerNativeAttribute.Episode, metadata.EncodedEpisode);
239 SetMetadata(MediaControllerNativeAttribute.Resolution, metadata.EncodedResolution);
241 Native.UpdateMetadata(Handle).ThrowIfError("Failed to set metadata.");
245 /// Updates the shuffle mode.
247 /// <param name="enabled">A value indicating whether the shuffle mode is enabled.</param>
248 /// <exception cref="InvalidOperationException">
249 /// The server is not running .<br/>
251 /// An internal error occurs.
253 /// <since_tizen> 4 </since_tizen>
254 public static void SetShuffleModeEnabled(bool enabled)
256 Native.UpdateShuffleMode(Handle, enabled ? MediaControllerNativeShuffleMode.On : MediaControllerNativeShuffleMode.Off).
257 ThrowIfError("Failed to set shuffle mode.");
261 /// Updates the repeat mode.
263 /// <param name="mode">A value indicating the repeat mode.</param>
264 /// <exception cref="InvalidOperationException">
265 /// The server is not running .<br/>
267 /// An internal error occurs.
269 /// <exception cref="ArgumentException"><paramref name="mode"/> is invalid.</exception>
270 /// <since_tizen> 4 </since_tizen>
271 public static void SetRepeatMode(MediaControlRepeatMode mode)
273 ValidationUtil.ValidateEnum(typeof(MediaControlRepeatMode), mode, nameof(mode));
275 Native.UpdateRepeatMode(Handle, mode.ToNative()).ThrowIfError("Failed to set repeat mode.");
279 /// Sets the index of current playing media.
281 /// <param name="index">The index of current playing media.</param>
282 /// <exception cref="ArgumentNullException"><paramref name="index"/> is null.</exception>
283 /// <exception cref="InvalidOperationException">
284 /// The server is not running .<br/>
286 /// An internal error occurs.
288 /// <since_tizen> 5 </since_tizen>
289 [Obsolete("Please do not use! This will be deprecated. Please use SetInfoOfCurrentPlayingMedia instead.")]
290 public static void SetIndexOfCurrentPlayingMedia(string index)
294 throw new ArgumentNullException(nameof(index));
297 Native.SetIndexOfCurrentPlayingMedia(Handle, index)
298 .ThrowIfError("Failed to set the index of current playing media");
300 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
304 /// Sets the playlist name and index of current playing media.
306 /// <param name="playlistName">The playlist name of current playing media.</param>
307 /// <param name="index">The index of current playing media.</param>
308 /// <exception cref="ArgumentNullException">
309 /// <paramref name="playlistName"/> or <paramref name="index"/> is null.
311 /// <exception cref="InvalidOperationException">
312 /// The server is not running .<br/>
314 /// An internal error occurs.
316 /// <since_tizen> 5 </since_tizen>
317 public static void SetInfoOfCurrentPlayingMedia(string playlistName, string index)
319 if (playlistName == null)
321 throw new ArgumentNullException(nameof(playlistName));
325 throw new ArgumentNullException(nameof(index));
328 Native.SetInfoOfCurrentPlayingMedia(Handle, playlistName, index)
329 .ThrowIfError("Failed to set the playlist name and index of current playing media");
331 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
337 /// <param name="playlist">The name of playlist.</param>
338 /// <exception cref="ArgumentNullException"><paramref name="playlist"/> is null.</exception>
339 /// <exception cref="InvalidOperationException">
340 /// The server is not running .<br/>
342 /// An internal error occurs.
344 /// <since_tizen> 5 </since_tizen>
345 public static void RemovePlaylist(MediaControlPlaylist playlist)
347 if (playlist == null)
349 throw new ArgumentNullException(nameof(playlist));
352 Native.DeletePlaylist(Handle, playlist.Handle);
356 // Saves the playlist to the persistent storage.
357 internal static void SavePlaylist(IntPtr playlistHandle)
359 Native.SavePlaylist(Handle, playlistHandle).ThrowIfError("Failed to save playlist");
362 // Gets the playlist handle by name.
363 internal static IntPtr GetPlaylistHandle(string name)
365 NativePlaylist.GetPlaylistHandle(_serverName, name, out IntPtr playlistHandle).
366 ThrowIfError("Failed to get playlist handle by name");
368 return playlistHandle;
372 /// Gets the active clients.
374 /// <exception cref="InvalidOperationException">
375 /// The server is not running .<br/>
377 /// An internal error occurs.
379 /// <returns>the activated client ids.</returns>
380 /// <since_tizen> 5 </since_tizen>
381 public static IEnumerable<string> GetActivatedClients()
383 var clientIds = new List<string>();
385 Native.ActivatedClientCallback activatedClientCallback = (name, _) =>
391 Native.ForeachActivatedClient(Handle, activatedClientCallback).
392 ThrowIfError("Failed to get activated client.");
394 return clientIds.AsReadOnly();
398 /// Requests commands to the client.
401 /// The client can request the command to execute <see cref="Command"/>, <br/>
402 /// and then, the server receive the result of each request(command).
404 /// <param name="command">A <see cref="Command"/> class.</param>
405 /// <param name="clientId">The client Id to send command.</param>
406 /// <returns><see cref="Bundle"/> represents the extra data from client and it can be null.</returns>
407 /// <exception cref="ArgumentNullException">
408 /// <paramref name="command"/> or <paramref name="clientId"/> is null.
410 /// <exception cref="InvalidOperationException">
411 /// The server has already been stopped.<br/>
413 /// An internal error occurs.
415 /// <since_tizen> 5 </since_tizen>
416 public static async Task<Bundle> RequestAsync(Command command, string clientId)
420 throw new ArgumentNullException(nameof(command));
422 if (clientId == null)
424 throw new ArgumentNullException(nameof(clientId));
427 command.SetRequestInformation(clientId);
429 var tcs = new TaskCompletionSource<MediaControllerError>();
430 string reqeustId = null;
431 Bundle bundle = null;
433 EventHandler<CommandCompletedEventArgs> eventHandler = (s, e) =>
435 if (e.RequestId == reqeustId)
438 tcs.TrySetResult(e.Result);
444 CommandCompleted += eventHandler;
446 reqeustId = command.Request(Handle);
448 (await tcs.Task).ThrowIfError("Failed to request event.");
454 CommandCompleted -= eventHandler;
459 /// Sends the result of each command.
461 /// <param name="command">The command that return to client.</param>
462 /// <param name="result">The result of <paramref name="command"/>.</param>
463 /// <param name="bundle">The extra data.</param>
464 /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
465 /// <exception cref="InvalidOperationException">
466 /// The server is not running .<br/>
468 /// An internal error occurs.
470 /// <since_tizen> 5 </since_tizen>
471 public static void Response(Command command, int result, Bundle bundle)
475 throw new ArgumentNullException(nameof(command));
478 command.Response(Handle, result, bundle);
482 /// Sends the result of each command.
484 /// <param name="command">The command that return to client.</param>
485 /// <param name="result">The result of <paramref name="command"/>.</param>
486 /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
487 /// <exception cref="InvalidOperationException">
488 /// The server is not running .<br/>
490 /// An internal error occurs.
492 /// <since_tizen> 5 </since_tizen>
493 public static void Response(Command command, int result)
497 throw new ArgumentNullException(nameof(command));
500 command.Response(Handle, result, null);
505 /// Sets the content type of latest played media.
507 /// <param name="type">A value indicating the content type of the latest played media.</param>
508 /// <exception cref="InvalidOperationException">
509 /// The server is not running .<br/>
511 /// An internal error occurs.
513 /// <exception cref="ArgumentException"><paramref name="type"/> is invalid.</exception>
514 /// <since_tizen> 5 </since_tizen>
515 public static void SetPlaybackContentType(MediaControlContentType type)
517 ValidationUtil.ValidateEnum(typeof(MediaControlContentType), type, nameof(type));
519 Native.SetPlaybackContentType(Handle, type).ThrowIfError("Failed to set playback content type.");
521 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
525 /// Sets the path of icon.
527 /// <param name="path">The path of icon.</param>
528 /// <exception cref="InvalidOperationException">
529 /// The server is not running .<br/>
531 /// An internal error occurs.
533 /// <exception cref="ArgumentNullException"><paramref name="path"/> is invalid.</exception>
534 /// <since_tizen> 5 </since_tizen>
535 public static void SetIconPath(string path)
539 throw new ArgumentNullException(nameof(path));
542 Native.SetIconPath(Handle, path).ThrowIfError("Failed to set uri path.");
546 /// Sets the capabilities by <see cref="MediaControlPlaybackCommand"/>.
548 /// <param name="capabilities">The set of <see cref="MediaControlPlaybackCommand"/> and <see cref="MediaControlCapabilitySupport"/>.</param>
549 /// <exception cref="InvalidOperationException">
550 /// The server is not running .<br/>
552 /// An internal error occurs.
554 /// <exception cref="ArgumentException"><paramref name="capabilities"/> is invalid.</exception>
555 /// <since_tizen> 5 </since_tizen>
556 public static void SetPlaybackCapabilities(Dictionary<MediaControlPlaybackCommand, MediaControlCapabilitySupport> capabilities)
558 foreach (var pair in capabilities)
560 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), pair.Key, nameof(pair.Key));
561 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), pair.Value, nameof(pair.Value));
563 SetPlaybackCapability(pair.Key, pair.Value);
564 Native.SetPlaybackCapability(Handle, pair.Key.ToNative(), pair.Value).
565 ThrowIfError("Failed to set playback capability.");
568 Native.SaveAndNotifyPlaybackCapabilityUpdated(Handle).ThrowIfError("Failed to update playback capability.");
572 /// Sets the capabilities by <see cref="MediaControlPlaybackCommand"/>.
574 /// <param name="action">A playback command.</param>
575 /// <param name="support">A value indicating whether the <paramref name="action"/> is supported or not.</param>
576 /// <exception cref="InvalidOperationException">
577 /// The server is not running .<br/>
579 /// An internal error occurs.
581 /// <exception cref="ArgumentException"><paramref name="action"/> or <paramref name="support"/> is invalid.</exception>
582 /// <since_tizen> 5 </since_tizen>
583 public static void SetPlaybackCapability(MediaControlPlaybackCommand action, MediaControlCapabilitySupport support)
585 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), action, nameof(action));
586 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support));
588 Native.SetPlaybackCapability(Handle, action.ToNative(), support).ThrowIfError("Failed to set playback capability.");
590 Native.SaveAndNotifyPlaybackCapabilityUpdated(Handle).ThrowIfError("Failed to update playback capability.");
594 /// Sets the <see cref="MediaControlCapabilitySupport"/> indicating shuffle mode is supported or not.
596 /// <param name="support">A value indicating whether the shuffle mode is supported or not.</param>
597 /// <exception cref="InvalidOperationException">
598 /// The server is not running .<br/>
600 /// An internal error occurs.
602 /// <exception cref="ArgumentException"><paramref name="support"/> is invalid.</exception>
603 /// <since_tizen> 5 </since_tizen>
604 public static void SetShuffleModeCapability(MediaControlCapabilitySupport support)
606 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support));
608 Native.SetSimpleCapability(Handle, MediaControlCapabilityCategory.Shuffle, support).
609 ThrowIfError("Failed to set shuffle mode capability.");
613 /// Sets the content type of latest played media.
615 /// <param name="support">A value indicating whether the <see cref="MediaControlRepeatMode"/> is supported or not.</param>
616 /// <exception cref="InvalidOperationException">
617 /// The server is not running .<br/>
619 /// An internal error occurs.
621 /// <exception cref="ArgumentException"><paramref name="support"/> is invalid.</exception>
622 /// <since_tizen> 5 </since_tizen>
623 public static void SetRepeatModeCapability(MediaControlCapabilitySupport support)
625 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support));
627 Native.SetSimpleCapability(Handle, MediaControlCapabilityCategory.Repeat, support).
628 ThrowIfError("Failed to set repeat mode capability.");
630 #endregion Capabilities
633 /// Sets the age rating of latest played media.
635 /// <param name="ageRating">
636 /// The Age rating of latest played media. The valid range is 0 to 19, inclusive.
637 /// Especially, 0 means that media is suitable for all ages.
639 /// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="ageRating"/> is not valid.</exception>
640 /// <exception cref="InvalidOperationException">
641 /// The server is not running .<br/>
643 /// An internal error occurs.
645 /// <since_tizen> 5 </since_tizen>
646 public static void SetAgeRating(int ageRating)
648 if (ageRating < 0 || ageRating > 19)
650 throw new ArgumentOutOfRangeException(nameof(ageRating));
653 Native.SetAgeRating(Handle, ageRating).ThrowIfError("Failed to set age rating.");
655 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");