1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "PlayerOperations.h"
10 
11 #include "Application.h"
12 #include "AudioLibrary.h"
13 #include "FileItem.h"
14 #include "GUIInfoManager.h"
15 #include "GUIUserMessages.h"
16 #include "PartyModeManager.h"
17 #include "PlayListPlayer.h"
18 #include "SeekHandler.h"
19 #include "Util.h"
20 #include "VideoLibrary.h"
21 #include "cores/IPlayer.h"
22 #include "cores/playercorefactory/PlayerCoreFactory.h"
23 #include "guilib/GUIWindowManager.h"
24 #include "input/Key.h"
25 #include "interfaces/builtins/Builtins.h"
26 #include "messaging/ApplicationMessenger.h"
27 #include "music/MusicDatabase.h"
28 #include "pictures/GUIWindowSlideShow.h"
29 #include "pvr/PVRManager.h"
30 #include "pvr/PVRPlaybackState.h"
31 #include "pvr/channels/PVRChannel.h"
32 #include "pvr/channels/PVRChannelGroupsContainer.h"
33 #include "pvr/epg/EpgInfoTag.h"
34 #include "pvr/guilib/PVRGUIActions.h"
35 #include "pvr/recordings/PVRRecordings.h"
36 #include "settings/DisplaySettings.h"
37 #include "utils/Variant.h"
38 #include "video/VideoDatabase.h"
39 
40 #include <map>
41 #include <tuple>
42 
43 using namespace JSONRPC;
44 using namespace PLAYLIST;
45 using namespace PVR;
46 using namespace KODI::MESSAGING;
47 
48 namespace
49 {
50 
AppendAudioStreamFlagsAsBooleans(CVariant & list,StreamFlags flags)51 void AppendAudioStreamFlagsAsBooleans(CVariant& list, StreamFlags flags)
52 {
53   list["isdefault"] = ((flags & StreamFlags::FLAG_DEFAULT) != 0);
54   list["isoriginal"] = ((flags & StreamFlags::FLAG_ORIGINAL) != 0);
55   list["isimpaired"] = ((flags & StreamFlags::FLAG_VISUAL_IMPAIRED) != 0);
56 }
57 
AppendSubtitleStreamFlagsAsBooleans(CVariant & list,StreamFlags flags)58 void AppendSubtitleStreamFlagsAsBooleans(CVariant& list, StreamFlags flags)
59 {
60   list["isdefault"] = ((flags & StreamFlags::FLAG_DEFAULT) != 0);
61   list["isforced"] = ((flags & StreamFlags::FLAG_FORCED) != 0);
62   list["isimpaired"] = ((flags & StreamFlags::FLAG_HEARING_IMPAIRED) != 0);
63 }
64 
65 } // namespace
66 
GetActivePlayers(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)67 JSONRPC_STATUS CPlayerOperations::GetActivePlayers(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
68 {
69   int activePlayers = GetActivePlayers();
70   result = CVariant(CVariant::VariantTypeArray);
71 
72   std::string strPlayerType = "internal";
73   if (activePlayers & External)
74     strPlayerType = "external";
75   else if (activePlayers & Remote)
76     strPlayerType = "remote";
77 
78   if (activePlayers & Video)
79   {
80     CVariant video = CVariant(CVariant::VariantTypeObject);
81     video["playerid"] = GetPlaylist(Video);
82     video["type"] = "video";
83     video["playertype"] = strPlayerType;
84     result.append(video);
85   }
86   if (activePlayers & Audio)
87   {
88     CVariant audio = CVariant(CVariant::VariantTypeObject);
89     audio["playerid"] = GetPlaylist(Audio);
90     audio["type"] = "audio";
91     audio["playertype"] = strPlayerType;
92     result.append(audio);
93   }
94   if (activePlayers & Picture)
95   {
96     CVariant picture = CVariant(CVariant::VariantTypeObject);
97     picture["playerid"] = GetPlaylist(Picture);
98     picture["type"] = "picture";
99     picture["playertype"] = "internal";
100     result.append(picture);
101   }
102 
103   return OK;
104 }
105 
GetPlayers(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)106 JSONRPC_STATUS CPlayerOperations::GetPlayers(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
107 {
108   const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
109 
110   std::string media = parameterObject["media"].asString();
111   result = CVariant(CVariant::VariantTypeArray);
112   std::vector<std::string> players;
113 
114   if (media == "all")
115   {
116     playerCoreFactory.GetPlayers(players);
117   }
118   else
119   {
120     bool video = false;
121     if (media == "video")
122       video = true;
123     playerCoreFactory.GetPlayers(players, true, video);
124   }
125 
126   for (const auto& playername : players)
127   {
128     CVariant player(CVariant::VariantTypeObject);
129     player["name"] = playername;
130 
131     player["playsvideo"] = playerCoreFactory.PlaysVideo(playername);
132     player["playsaudio"] = playerCoreFactory.PlaysAudio(playername);
133     player["type"] = playerCoreFactory.GetPlayerType(playername);
134 
135     result.push_back(player);
136   }
137 
138   return OK;
139 }
140 
GetProperties(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)141 JSONRPC_STATUS CPlayerOperations::GetProperties(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
142 {
143   PlayerType player = GetPlayer(parameterObject["playerid"]);
144 
145   CVariant properties = CVariant(CVariant::VariantTypeObject);
146   for (unsigned int index = 0; index < parameterObject["properties"].size(); index++)
147   {
148     std::string propertyName = parameterObject["properties"][index].asString();
149     CVariant property;
150     JSONRPC_STATUS ret;
151     if ((ret = GetPropertyValue(player, propertyName, property)) != OK)
152       return ret;
153 
154     properties[propertyName] = property;
155   }
156 
157   result = properties;
158 
159   return OK;
160 }
161 
GetItem(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)162 JSONRPC_STATUS CPlayerOperations::GetItem(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
163 {
164   PlayerType player = GetPlayer(parameterObject["playerid"]);
165   CFileItemPtr fileItem;
166 
167   switch (player)
168   {
169     case Video:
170     case Audio:
171     {
172       fileItem = std::make_shared<CFileItem>(g_application.CurrentFileItem());
173       if (IsPVRChannel())
174         break;
175 
176       if (player == Video)
177       {
178         if (!CVideoLibrary::FillFileItem(fileItem->GetPath(), fileItem, parameterObject))
179         {
180           // Fallback to item details held by GUI but ensure path unchanged
181           //! @todo  remove this once there is no route to playback that updates
182           // GUI item without also updating app item e.g. start playback of a
183           // non-library item via JSON
184           const CVideoInfoTag *currentVideoTag = CServiceBroker::GetGUI()->GetInfoManager().GetCurrentMovieTag();
185           if (currentVideoTag != NULL)
186           {
187             std::string originalLabel = fileItem->GetLabel();
188             std::string originalPath = fileItem->GetPath();
189             fileItem->SetFromVideoInfoTag(*currentVideoTag);
190             if (fileItem->GetLabel().empty())
191               fileItem->SetLabel(originalLabel);
192             fileItem->SetPath(originalPath);   // Ensure path unchanged
193           }
194         }
195 
196         bool additionalInfo = false;
197         for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
198         {
199           std::string fieldValue = itr->asString();
200           if (fieldValue == "cast" || fieldValue == "set" || fieldValue == "setid" || fieldValue == "showlink" || fieldValue == "resume" ||
201              (fieldValue == "streamdetails" && !fileItem->GetVideoInfoTag()->m_streamDetails.HasItems()))
202             additionalInfo = true;
203         }
204 
205         CVideoDatabase videodatabase;
206         if ((additionalInfo) &&
207             videodatabase.Open())
208         {
209           if (additionalInfo)
210           {
211             switch (fileItem->GetVideoContentType())
212             {
213               case VIDEODB_CONTENT_MOVIES:
214                 videodatabase.GetMovieInfo("", *(fileItem->GetVideoInfoTag()), fileItem->GetVideoInfoTag()->m_iDbId);
215                 break;
216 
217               case VIDEODB_CONTENT_MUSICVIDEOS:
218                 videodatabase.GetMusicVideoInfo("", *(fileItem->GetVideoInfoTag()), fileItem->GetVideoInfoTag()->m_iDbId);
219                 break;
220 
221               case VIDEODB_CONTENT_EPISODES:
222                 videodatabase.GetEpisodeInfo("", *(fileItem->GetVideoInfoTag()), fileItem->GetVideoInfoTag()->m_iDbId);
223                 break;
224 
225               case VIDEODB_CONTENT_TVSHOWS:
226               case VIDEODB_CONTENT_MOVIE_SETS:
227               default:
228                 break;
229             }
230           }
231 
232           videodatabase.Close();
233         }
234       }
235       else // Audio
236       {
237         if (!CAudioLibrary::FillFileItem(fileItem->GetPath(), fileItem, parameterObject))
238         {
239           // Fallback to item details held by GUI but ensure path unchanged
240           //! @todo  remove this once there is no route to playback that updates
241           // GUI item without also updating app item e.g. start playback of a
242           // non-library item via JSON
243           const MUSIC_INFO::CMusicInfoTag* currentMusicTag =
244               CServiceBroker::GetGUI()->GetInfoManager().GetCurrentSongTag();
245           if (currentMusicTag != NULL)
246           {
247             std::string originalLabel = fileItem->GetLabel();
248             std::string originalPath = fileItem->GetPath();
249             fileItem->SetFromMusicInfoTag(*currentMusicTag);
250             if (fileItem->GetLabel().empty())
251               fileItem->SetLabel(originalLabel);
252             fileItem->SetPath(originalPath); // Ensure path unchanged
253           }
254         }
255 
256         if (fileItem->IsMusicDb())
257         {
258           CMusicDatabase musicdb;
259           CFileItemList items;
260           items.Add(fileItem);
261           CAudioLibrary::GetAdditionalSongDetails(parameterObject, items, musicdb);
262         }
263       }
264       break;
265     }
266 
267     case Picture:
268     {
269       CGUIWindowSlideShow *slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
270       if (!slideshow)
271         return FailedToExecute;
272 
273       CFileItemList slides;
274       slideshow->GetSlideShowContents(slides);
275       fileItem = slides[slideshow->CurrentSlide() - 1];
276       break;
277     }
278 
279     case None:
280     default:
281       return FailedToExecute;
282   }
283 
284   HandleFileItem("id", !IsPVRChannel(), "item", fileItem, parameterObject, parameterObject["properties"], result, false);
285   return OK;
286 }
287 
PlayPause(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)288 JSONRPC_STATUS CPlayerOperations::PlayPause(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
289 {
290   CGUIWindowSlideShow *slideshow = NULL;
291   switch (GetPlayer(parameterObject["playerid"]))
292   {
293     case Video:
294     case Audio:
295       if (!g_application.GetAppPlayer().CanPause())
296         return FailedToExecute;
297 
298       if (parameterObject["play"].isString())
299         CBuiltins::GetInstance().Execute("playercontrol(play)");
300       else
301       {
302         if (parameterObject["play"].asBoolean())
303         {
304           if (g_application.GetAppPlayer().IsPausedPlayback())
305             CApplicationMessenger::GetInstance().SendMsg(TMSG_MEDIA_PAUSE);
306           else if (g_application.GetAppPlayer().GetPlaySpeed() != 1)
307             g_application.GetAppPlayer().SetPlaySpeed(1);
308         }
309         else if (!g_application.GetAppPlayer().IsPausedPlayback())
310           CApplicationMessenger::GetInstance().SendMsg(TMSG_MEDIA_PAUSE);
311       }
312       result["speed"] = g_application.GetAppPlayer().IsPausedPlayback() ? 0 : (int)lrint(g_application.GetAppPlayer().GetPlaySpeed());
313       return OK;
314 
315     case Picture:
316       slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
317       if (slideshow && slideshow->IsPlaying() &&
318          (parameterObject["play"].isString() ||
319          (parameterObject["play"].isBoolean() && parameterObject["play"].asBoolean() == slideshow->IsPaused())))
320         SendSlideshowAction(ACTION_PAUSE);
321 
322       if (slideshow && slideshow->IsPlaying() && !slideshow->IsPaused())
323         result["speed"] = slideshow->GetDirection();
324       else
325         result["speed"] = 0;
326       return OK;
327 
328     case None:
329     default:
330       return FailedToExecute;
331   }
332 }
333 
Stop(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)334 JSONRPC_STATUS CPlayerOperations::Stop(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
335 {
336   switch (GetPlayer(parameterObject["playerid"]))
337   {
338     case Video:
339     case Audio:
340       CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_STOP, static_cast<int>(parameterObject["playerid"].asInteger()));
341       return ACK;
342 
343     case Picture:
344       SendSlideshowAction(ACTION_STOP);
345       return ACK;
346 
347     case None:
348     default:
349       return FailedToExecute;
350   }
351 }
352 
SetSpeed(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)353 JSONRPC_STATUS CPlayerOperations::SetSpeed(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
354 {
355   switch (GetPlayer(parameterObject["playerid"]))
356   {
357     case Video:
358     case Audio:
359       if (parameterObject["speed"].isInteger())
360       {
361         int speed = (int)parameterObject["speed"].asInteger();
362         if (speed != 0)
363         {
364           // If the player is paused we first need to unpause
365           if (g_application.GetAppPlayer().IsPausedPlayback())
366             g_application.GetAppPlayer().Pause();
367           g_application.GetAppPlayer().SetPlaySpeed(speed);
368         }
369         else
370           g_application.GetAppPlayer().Pause();
371       }
372       else if (parameterObject["speed"].isString())
373       {
374         if (parameterObject["speed"].asString().compare("increment") == 0)
375           CBuiltins::GetInstance().Execute("playercontrol(forward)");
376         else
377           CBuiltins::GetInstance().Execute("playercontrol(rewind)");
378       }
379       else
380         return InvalidParams;
381 
382       result["speed"] = g_application.GetAppPlayer().IsPausedPlayback() ? 0 : (int)lrint(g_application.GetAppPlayer().GetPlaySpeed());
383       return OK;
384 
385     case Picture:
386     case None:
387     default:
388       return FailedToExecute;
389   }
390 }
391 
Seek(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)392 JSONRPC_STATUS CPlayerOperations::Seek(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
393 {
394   PlayerType player = GetPlayer(parameterObject["playerid"]);
395   switch (player)
396   {
397     case Video:
398     case Audio:
399     {
400       if (!g_application.GetAppPlayer().CanSeek())
401         return FailedToExecute;
402 
403       const CVariant& value = parameterObject["value"];
404       if (value.isMember("percentage"))
405         g_application.SeekPercentage(value["percentage"].asFloat());
406       else if (value.isMember("step"))
407       {
408         std::string step = value["step"].asString();
409         if (step == "smallforward")
410           CBuiltins::GetInstance().Execute("playercontrol(smallskipforward)");
411         else if (step == "smallbackward")
412           CBuiltins::GetInstance().Execute("playercontrol(smallskipbackward)");
413         else if (step == "bigforward")
414           CBuiltins::GetInstance().Execute("playercontrol(bigskipforward)");
415         else if (step == "bigbackward")
416           CBuiltins::GetInstance().Execute("playercontrol(bigskipbackward)");
417         else
418           return InvalidParams;
419       }
420       else if (value.isMember("seconds"))
421         g_application.GetAppPlayer().GetSeekHandler().SeekSeconds(static_cast<int>(value["seconds"].asInteger()));
422       else if (value.isMember("time"))
423         g_application.SeekTime(ParseTimeInSeconds(value["time"]));
424       else
425         return InvalidParams;
426 
427       GetPropertyValue(player, "percentage", result["percentage"]);
428       GetPropertyValue(player, "time", result["time"]);
429       GetPropertyValue(player, "totaltime", result["totaltime"]);
430       return OK;
431     }
432 
433     case Picture:
434     case None:
435     default:
436       return FailedToExecute;
437   }
438 }
439 
Move(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)440 JSONRPC_STATUS CPlayerOperations::Move(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
441 {
442   std::string direction = parameterObject["direction"].asString();
443   switch (GetPlayer(parameterObject["playerid"]))
444   {
445     case Picture:
446       if (direction == "left")
447         SendSlideshowAction(ACTION_MOVE_LEFT);
448       else if (direction == "right")
449         SendSlideshowAction(ACTION_MOVE_RIGHT);
450       else if (direction == "up")
451         SendSlideshowAction(ACTION_MOVE_UP);
452       else if (direction == "down")
453         SendSlideshowAction(ACTION_MOVE_DOWN);
454       else
455         return InvalidParams;
456 
457       return ACK;
458 
459     case Video:
460     case Audio:
461       if (direction == "left" || direction == "up")
462         CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_PREV_ITEM)));
463       else if (direction == "right" || direction == "down")
464         CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_NEXT_ITEM)));
465       else
466         return InvalidParams;
467 
468       return ACK;
469 
470     case None:
471     default:
472       return FailedToExecute;
473   }
474 }
475 
Zoom(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)476 JSONRPC_STATUS CPlayerOperations::Zoom(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
477 {
478   CVariant zoom = parameterObject["zoom"];
479   switch (GetPlayer(parameterObject["playerid"]))
480   {
481     case Picture:
482       if (zoom.isInteger())
483         SendSlideshowAction(ACTION_ZOOM_LEVEL_NORMAL + ((int)zoom.asInteger() - 1));
484       else if (zoom.isString())
485       {
486         std::string strZoom = zoom.asString();
487         if (strZoom == "in")
488           SendSlideshowAction(ACTION_ZOOM_IN);
489         else if (strZoom == "out")
490           SendSlideshowAction(ACTION_ZOOM_OUT);
491         else
492           return InvalidParams;
493       }
494       else
495         return InvalidParams;
496 
497       return ACK;
498 
499     case Video:
500     case Audio:
501     case None:
502     default:
503       return FailedToExecute;
504   }
505 }
506 
507 // Matching pairs of values from JSON type "Player.ViewMode" and C++ enum ViewMode
508 // Additions to enum ViewMode need to be added here and in the JSON type
509 std::map<std::string, ViewMode> viewModes =
510 {
511   {"normal", ViewModeNormal},
512   {"zoom", ViewModeZoom},
513   {"stretch4x3", ViewModeStretch4x3},
514   {"widezoom", ViewModeWideZoom, },
515   {"stretch16x9", ViewModeStretch16x9},
516   {"original", ViewModeOriginal},
517   {"stretch16x9nonlin", ViewModeStretch16x9Nonlin},
518   {"zoom120width", ViewModeZoom120Width},
519   {"zoom110width", ViewModeZoom110Width}
520 };
521 
GetStringFromViewMode(ViewMode viewMode)522 std::string GetStringFromViewMode(ViewMode viewMode)
523 {
524   std::string result = "custom";
525 
526   auto it = find_if(viewModes.begin(), viewModes.end(), [viewMode](const std::pair<std::string, ViewMode> & p)
527   {
528     return p.second == viewMode;
529   });
530 
531   if (it != viewModes.end())
532   {
533     std::pair<std::string, ViewMode> value = *it;
534     result = value.first;
535   }
536 
537   return result;
538 }
539 
GetNewValueForViewModeParameter(const CVariant & parameter,float stepSize,float minValue,float maxValue,float & result)540 void GetNewValueForViewModeParameter(const CVariant &parameter, float stepSize, float minValue, float maxValue, float &result)
541 {
542   if (parameter.isDouble())
543   {
544     result = parameter.asDouble();
545   }
546   else if (parameter.isString())
547   {
548     std::string parameterStr = parameter.asString();
549     if (parameter == "decrease")
550     {
551       stepSize *= -1;
552     }
553 
554     result += stepSize;
555   }
556 
557   result = std::max(minValue, std::min(result, maxValue));
558 }
559 
SetViewMode(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)560 JSONRPC_STATUS CPlayerOperations::SetViewMode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
561 {
562   JSONRPC_STATUS jsonStatus = InvalidParams;
563   // init with current values from settings
564   CVideoSettings vs = g_application.GetAppPlayer().GetVideoSettings();
565   ViewMode mode = ViewModeNormal;
566 
567   CVariant viewMode = parameterObject["viewmode"];
568   if (viewMode.isString())
569   {
570     std::string modestr = viewMode.asString();
571     if (viewModes.find(modestr) != viewModes.end())
572     {
573       mode = viewModes[modestr];
574       jsonStatus = ACK;
575     }
576   }
577   else if (viewMode.isObject())
578   {
579     mode = ViewModeCustom;
580     CVariant zoom = viewMode["zoom"];
581     CVariant pixelRatio = viewMode["pixelratio"];
582     CVariant verticalShift = viewMode["verticalshift"];
583     CVariant stretch = viewMode["nonlinearstretch"];
584 
585     if (!zoom.isNull())
586     {
587       GetNewValueForViewModeParameter(zoom, 0.01f, 0.5f, 2.f, vs.m_CustomZoomAmount);
588       jsonStatus = ACK;
589     }
590 
591     if (!pixelRatio.isNull())
592     {
593       GetNewValueForViewModeParameter(pixelRatio, 0.01f, 0.5f, 2.f, vs.m_CustomPixelRatio);
594       jsonStatus = ACK;
595     }
596 
597     if (!verticalShift.isNull())
598     {
599       GetNewValueForViewModeParameter(verticalShift, -0.01f, -2.f, 2.f, vs.m_CustomVerticalShift);
600       jsonStatus = ACK;
601     }
602 
603     if (stretch.isBoolean())
604     {
605       vs.m_CustomNonLinStretch = stretch.asBoolean();
606       jsonStatus = ACK;
607     }
608   }
609 
610   if (jsonStatus == ACK)
611   {
612     g_application.GetAppPlayer().SetRenderViewMode(static_cast<int>(mode),
613       vs.m_CustomZoomAmount, vs.m_CustomPixelRatio, vs.m_CustomVerticalShift,
614       vs.m_CustomNonLinStretch);
615   }
616 
617   return jsonStatus;
618 }
619 
GetViewMode(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)620 JSONRPC_STATUS CPlayerOperations::GetViewMode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
621 {
622   int mode = g_application.GetAppPlayer().GetVideoSettings().m_ViewMode;
623 
624   result["viewmode"] = GetStringFromViewMode(static_cast<ViewMode>(mode));
625 
626   result["zoom"] = CDisplaySettings::GetInstance().GetZoomAmount();
627   result["pixelratio"] = CDisplaySettings::GetInstance().GetPixelRatio();
628   result["verticalshift"] = CDisplaySettings::GetInstance().GetVerticalShift();
629   result["nonlinearstretch"] = CDisplaySettings::GetInstance().IsNonLinearStretched();
630   return OK;
631 }
632 
Rotate(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)633 JSONRPC_STATUS CPlayerOperations::Rotate(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
634 {
635   switch (GetPlayer(parameterObject["playerid"]))
636   {
637     case Picture:
638       if (parameterObject["value"].asString().compare("clockwise") == 0)
639         SendSlideshowAction(ACTION_ROTATE_PICTURE_CW);
640       else
641         SendSlideshowAction(ACTION_ROTATE_PICTURE_CCW);
642       return ACK;
643 
644     case Video:
645     case Audio:
646     case None:
647     default:
648       return FailedToExecute;
649   }
650 }
651 
Open(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)652 JSONRPC_STATUS CPlayerOperations::Open(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
653 {
654   CVariant options = parameterObject["options"];
655   CVariant optionShuffled = options["shuffled"];
656   CVariant optionRepeat = options["repeat"];
657   CVariant optionResume = options["resume"];
658   CVariant optionPlayer = options["playername"];
659 
660   if (parameterObject["item"].isMember("playlistid"))
661   {
662     int playlistid = (int)parameterObject["item"]["playlistid"].asInteger();
663 
664     if (playlistid < PLAYLIST_PICTURE)
665     {
666       // Apply the "shuffled" option if available
667       if (optionShuffled.isBoolean())
668         CServiceBroker::GetPlaylistPlayer().SetShuffle(playlistid, optionShuffled.asBoolean(), false);
669       // Apply the "repeat" option if available
670       if (!optionRepeat.isNull())
671         CServiceBroker::GetPlaylistPlayer().SetRepeat(playlistid, (REPEAT_STATE)ParseRepeatState(optionRepeat), false);
672     }
673 
674     int playlistStartPosition = (int)parameterObject["item"]["position"].asInteger();
675 
676     switch (playlistid)
677     {
678       case PLAYLIST_MUSIC:
679       case PLAYLIST_VIDEO:
680         CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_PLAY, playlistid, playlistStartPosition);
681         break;
682 
683       case PLAYLIST_PICTURE:
684       {
685         std::string firstPicturePath;
686         if (playlistStartPosition > 0)
687         {
688           CGUIWindowSlideShow *slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
689           if (slideshow != NULL)
690           {
691             CFileItemList list;
692             slideshow->GetSlideShowContents(list);
693             if (playlistStartPosition < list.Size())
694               firstPicturePath = list.Get(playlistStartPosition)->GetPath();
695           }
696         }
697 
698         return StartSlideshow("", false, optionShuffled.isBoolean() && optionShuffled.asBoolean(), firstPicturePath);
699         break;
700       }
701     }
702 
703     return ACK;
704   }
705   else if (parameterObject["item"].isMember("path"))
706   {
707     bool random = (optionShuffled.isBoolean() && optionShuffled.asBoolean()) ||
708                   (!optionShuffled.isBoolean() && parameterObject["item"]["random"].asBoolean());
709     return StartSlideshow(parameterObject["item"]["path"].asString(), parameterObject["item"]["recursive"].asBoolean(), random);
710   }
711   else if (parameterObject["item"].isObject() && parameterObject["item"].isMember("partymode"))
712   {
713     if (g_partyModeManager.IsEnabled())
714       g_partyModeManager.Disable();
715     CApplicationMessenger::GetInstance().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, "playercontrol(partymode(" + parameterObject["item"]["partymode"].asString() + "))");
716     return ACK;
717   }
718   else if (parameterObject["item"].isMember("broadcastid"))
719   {
720     const std::shared_ptr<CPVREpgInfoTag> epgTag =
721         CServiceBroker::GetPVRManager().EpgContainer().GetTagByDatabaseId(
722             static_cast<unsigned int>(parameterObject["item"]["broadcastid"].asInteger()));
723 
724     if (!epgTag || !epgTag->IsPlayable())
725       return InvalidParams;
726 
727     if (!CServiceBroker::GetPVRManager().GUIActions()->PlayEpgTag(
728             std::make_shared<CFileItem>(epgTag)))
729       return FailedToExecute;
730 
731     return ACK;
732   }
733   else if (parameterObject["item"].isMember("channelid"))
734   {
735     const std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
736     if (!channelGroupContainer)
737       return FailedToExecute;
738 
739     const std::shared_ptr<CPVRChannel> channel = channelGroupContainer->GetChannelById(static_cast<int>(parameterObject["item"]["channelid"].asInteger()));
740     if (!channel)
741       return InvalidParams;
742 
743     if (!CServiceBroker::GetPVRManager().GUIActions()->PlayMedia(std::make_shared<CFileItem>(channel)))
744       return FailedToExecute;
745 
746     return ACK;
747   }
748   else if (parameterObject["item"].isMember("recordingid"))
749   {
750     const std::shared_ptr<CPVRRecordings> recordingsContainer = CServiceBroker::GetPVRManager().Recordings();
751     if (!recordingsContainer)
752       return FailedToExecute;
753 
754     const std::shared_ptr<CPVRRecording> recording = recordingsContainer->GetById(static_cast<int>(parameterObject["item"]["recordingid"].asInteger()));
755     if (!recording)
756       return InvalidParams;
757 
758     if (!CServiceBroker::GetPVRManager().GUIActions()->PlayMedia(std::make_shared<CFileItem>(recording)))
759       return FailedToExecute;
760 
761     return ACK;
762   }
763   else
764   {
765     CFileItemList list;
766     if (FillFileItemList(parameterObject["item"], list) && list.Size() > 0)
767     {
768       bool slideshow = true;
769       for (int index = 0; index < list.Size(); index++)
770       {
771         if (!list[index]->IsPicture())
772         {
773           slideshow = false;
774           break;
775         }
776       }
777 
778       if (slideshow)
779       {
780         CGUIWindowSlideShow *slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
781         if (!slideshow)
782           return FailedToExecute;
783 
784         SendSlideshowAction(ACTION_STOP);
785         slideshow->Reset();
786         for (int index = 0; index < list.Size(); index++)
787           slideshow->Add(list[index].get());
788 
789         return StartSlideshow("", false, optionShuffled.isBoolean() && optionShuffled.asBoolean());
790       }
791       else
792       {
793         std::string playername;
794         // Handle the "playerid" option
795         if (!optionPlayer.isNull())
796         {
797           if (optionPlayer.isString())
798           {
799             playername = optionPlayer.asString();
800 
801             if (playername != "default")
802             {
803               const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
804 
805               // check if the there's actually a player with the given name
806               if (playerCoreFactory.GetPlayerType(playername).empty())
807                 return InvalidParams;
808 
809               // check if the player can handle at least the first item in the list
810               std::vector<std::string> possiblePlayers;
811               playerCoreFactory.GetPlayers(*list.Get(0).get(), possiblePlayers);
812 
813               bool match = false;
814               for (const auto& entry : possiblePlayers)
815               {
816                 if (StringUtils::EqualsNoCase(entry, playername))
817                 {
818                   match = true;
819                   break;
820                 }
821               }
822               if (!match)
823                 return InvalidParams;
824             }
825           }
826           else
827             return InvalidParams;
828         }
829 
830         // Handle "shuffled" option
831         if (optionShuffled.isBoolean())
832           list.SetProperty("shuffled", optionShuffled);
833         // Handle "repeat" option
834         if (!optionRepeat.isNull())
835           list.SetProperty("repeat", ParseRepeatState(optionRepeat));
836         // Handle "resume" option
837         if (list.Size() == 1)
838         {
839           if (optionResume.isBoolean() && optionResume.asBoolean())
840             list[0]->m_lStartOffset = STARTOFFSET_RESUME;
841           else if (optionResume.isDouble())
842             list[0]->SetProperty("StartPercent", optionResume);
843           else if (optionResume.isObject())
844             list[0]->m_lStartOffset = CUtil::ConvertSecsToMilliSecs(ParseTimeInSeconds(optionResume));
845         }
846 
847         auto l = new CFileItemList(); //don't delete
848         l->Copy(list);
849         CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_PLAY, -1, -1, static_cast<void*>(l), playername);
850       }
851 
852       return ACK;
853     }
854     else
855       return InvalidParams;
856   }
857 
858   return InvalidParams;
859 }
860 
GoTo(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)861 JSONRPC_STATUS CPlayerOperations::GoTo(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
862 {
863   CVariant to = parameterObject["to"];
864   switch (GetPlayer(parameterObject["playerid"]))
865   {
866     case Video:
867     case Audio:
868       if (to.isString())
869       {
870         std::string strTo = to.asString();
871         int actionID;
872         if (strTo == "previous")
873           actionID = ACTION_PREV_ITEM;
874         else if (strTo == "next")
875           actionID = ACTION_NEXT_ITEM;
876         else
877           return InvalidParams;
878 
879         CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(actionID)));
880       }
881       else if (to.isInteger())
882       {
883         if (IsPVRChannel())
884           CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(
885             new CAction(ACTION_CHANNEL_SWITCH, static_cast<float>(to.asInteger()))));
886         else
887           CApplicationMessenger::GetInstance().SendMsg(TMSG_PLAYLISTPLAYER_PLAY, static_cast<int>(to.asInteger()));
888       }
889       else
890         return InvalidParams;
891       break;
892 
893     case Picture:
894       if (to.isString())
895       {
896         std::string strTo = to.asString();
897         int actionID;
898         if (strTo == "previous")
899           actionID = ACTION_PREV_PICTURE;
900         else if (strTo == "next")
901           actionID = ACTION_NEXT_PICTURE;
902         else
903           return InvalidParams;
904 
905         SendSlideshowAction(actionID);
906       }
907       else
908         return FailedToExecute;
909       break;
910 
911     case None:
912     default:
913       return FailedToExecute;
914   }
915 
916   return ACK;
917 }
918 
SetShuffle(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)919 JSONRPC_STATUS CPlayerOperations::SetShuffle(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
920 {
921   CGUIWindowSlideShow *slideshow = NULL;
922   CVariant shuffle = parameterObject["shuffle"];
923   switch (GetPlayer(parameterObject["playerid"]))
924   {
925     case Video:
926     case Audio:
927     {
928       if (IsPVRChannel())
929         return FailedToExecute;
930 
931       int playlistid = GetPlaylist(GetPlayer(parameterObject["playerid"]));
932       if (CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistid))
933       {
934         if ((shuffle.isBoolean() && !shuffle.asBoolean()) ||
935             (shuffle.isString() && shuffle.asString() == "toggle"))
936         {
937           CApplicationMessenger::GetInstance().SendMsg(TMSG_PLAYLISTPLAYER_SHUFFLE, playlistid, 0);
938         }
939       }
940       else
941       {
942         if ((shuffle.isBoolean() && shuffle.asBoolean()) ||
943             (shuffle.isString() && shuffle.asString() == "toggle"))
944         {
945           CApplicationMessenger::GetInstance().SendMsg(TMSG_PLAYLISTPLAYER_SHUFFLE, playlistid, 1);
946         }
947       }
948       break;
949     }
950 
951     case Picture:
952       slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
953       if (slideshow == NULL)
954         return FailedToExecute;
955       if (slideshow->IsShuffled())
956       {
957         if ((shuffle.isBoolean() && !shuffle.asBoolean()) ||
958             (shuffle.isString() && shuffle.asString() == "toggle"))
959           return FailedToExecute;
960       }
961       else
962       {
963         if ((shuffle.isBoolean() && shuffle.asBoolean()) ||
964             (shuffle.isString() && shuffle.asString() == "toggle"))
965           slideshow->Shuffle();
966       }
967       break;
968 
969     default:
970       return FailedToExecute;
971   }
972   return ACK;
973 }
974 
SetRepeat(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)975 JSONRPC_STATUS CPlayerOperations::SetRepeat(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
976 {
977   switch (GetPlayer(parameterObject["playerid"]))
978   {
979     case Video:
980     case Audio:
981     {
982       if (IsPVRChannel())
983         return FailedToExecute;
984 
985       REPEAT_STATE repeat = REPEAT_NONE;
986       int playlistid = GetPlaylist(GetPlayer(parameterObject["playerid"]));
987       if (parameterObject["repeat"].asString() == "cycle")
988       {
989         REPEAT_STATE repeatPrev = CServiceBroker::GetPlaylistPlayer().GetRepeat(playlistid);
990         if (repeatPrev == REPEAT_NONE)
991           repeat = REPEAT_ALL;
992         else if (repeatPrev == REPEAT_ALL)
993           repeat = REPEAT_ONE;
994         else
995           repeat = REPEAT_NONE;
996       }
997       else
998         repeat = (REPEAT_STATE)ParseRepeatState(parameterObject["repeat"]);
999 
1000       CApplicationMessenger::GetInstance().SendMsg(TMSG_PLAYLISTPLAYER_REPEAT, playlistid, repeat);
1001       break;
1002     }
1003 
1004     case Picture:
1005     default:
1006       return FailedToExecute;
1007   }
1008 
1009   return ACK;
1010 }
1011 
SetPartymode(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)1012 JSONRPC_STATUS CPlayerOperations::SetPartymode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
1013 {
1014   PlayerType player = GetPlayer(parameterObject["playerid"]);
1015   switch (player)
1016   {
1017     case Video:
1018     case Audio:
1019     {
1020       if (IsPVRChannel())
1021         return FailedToExecute;
1022 
1023       bool change = false;
1024       PartyModeContext context = PARTYMODECONTEXT_UNKNOWN;
1025       std::string strContext;
1026       if (player == Video)
1027       {
1028         context = PARTYMODECONTEXT_VIDEO;
1029         strContext = "video";
1030       }
1031       else if (player == Audio)
1032       {
1033         context = PARTYMODECONTEXT_MUSIC;
1034         strContext = "music";
1035       }
1036 
1037       bool toggle = parameterObject["partymode"].isString();
1038       if (g_partyModeManager.IsEnabled())
1039       {
1040         if (g_partyModeManager.GetType() != context)
1041           return InvalidParams;
1042 
1043         if (toggle || parameterObject["partymode"].asBoolean() == false)
1044           change = true;
1045       }
1046       else
1047       {
1048         if (toggle || parameterObject["partymode"].asBoolean() == true)
1049           change = true;
1050       }
1051 
1052       if (change)
1053         CApplicationMessenger::GetInstance().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, "playercontrol(partymode(" + strContext + "))");
1054       break;
1055     }
1056 
1057     case Picture:
1058     default:
1059       return FailedToExecute;
1060   }
1061 
1062   return ACK;
1063 }
1064 
SetAudioStream(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)1065 JSONRPC_STATUS CPlayerOperations::SetAudioStream(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
1066 {
1067   switch (GetPlayer(parameterObject["playerid"]))
1068   {
1069     case Video:
1070       if (g_application.GetAppPlayer().HasPlayer())
1071       {
1072         int index = -1;
1073         if (parameterObject["stream"].isString())
1074         {
1075           std::string action = parameterObject["stream"].asString();
1076           if (action.compare("previous") == 0)
1077           {
1078             index = g_application.GetAppPlayer().GetAudioStream() - 1;
1079             if (index < 0)
1080               index = g_application.GetAppPlayer().GetAudioStreamCount() - 1;
1081           }
1082           else if (action.compare("next") == 0)
1083           {
1084             index = g_application.GetAppPlayer().GetAudioStream() + 1;
1085             if (index >= g_application.GetAppPlayer().GetAudioStreamCount())
1086               index = 0;
1087           }
1088           else
1089             return InvalidParams;
1090         }
1091         else if (parameterObject["stream"].isInteger())
1092           index = (int)parameterObject["stream"].asInteger();
1093 
1094         if (index < 0 || g_application.GetAppPlayer().GetAudioStreamCount() <= index)
1095           return InvalidParams;
1096 
1097         g_application.GetAppPlayer().SetAudioStream(index);
1098       }
1099       else
1100         return FailedToExecute;
1101       break;
1102 
1103     case Audio:
1104     case Picture:
1105     default:
1106       return FailedToExecute;
1107   }
1108 
1109   return ACK;
1110 }
1111 
AddSubtitle(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)1112 JSONRPC_STATUS CPlayerOperations::AddSubtitle(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
1113 {
1114   if (GetPlayer(parameterObject["playerid"]) != Video)
1115     return FailedToExecute;
1116 
1117   if (!g_application.GetAppPlayer().HasPlayer())
1118     return FailedToExecute;
1119 
1120   if (!parameterObject["subtitle"].isString())
1121     return FailedToExecute;
1122 
1123   std::string sub = parameterObject["subtitle"].asString();
1124   g_application.GetAppPlayer().AddSubtitle(sub);
1125   return ACK;
1126 }
1127 
SetSubtitle(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)1128 JSONRPC_STATUS CPlayerOperations::SetSubtitle(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
1129 {
1130   switch (GetPlayer(parameterObject["playerid"]))
1131   {
1132     case Video:
1133       if (g_application.GetAppPlayer().HasPlayer())
1134       {
1135         int index = -1;
1136         if (parameterObject["subtitle"].isString())
1137         {
1138           std::string action = parameterObject["subtitle"].asString();
1139           if (action.compare("previous") == 0)
1140           {
1141             index = g_application.GetAppPlayer().GetSubtitle() - 1;
1142             if (index < 0)
1143               index = g_application.GetAppPlayer().GetSubtitleCount() - 1;
1144           }
1145           else if (action.compare("next") == 0)
1146           {
1147             index = g_application.GetAppPlayer().GetSubtitle() + 1;
1148             if (index >= g_application.GetAppPlayer().GetSubtitleCount())
1149               index = 0;
1150           }
1151           else if (action.compare("off") == 0)
1152           {
1153             g_application.GetAppPlayer().SetSubtitleVisible(false);
1154             return ACK;
1155           }
1156           else if (action.compare("on") == 0)
1157           {
1158             g_application.GetAppPlayer().SetSubtitleVisible(true);
1159             return ACK;
1160           }
1161           else
1162             return InvalidParams;
1163         }
1164         else if (parameterObject["subtitle"].isInteger())
1165           index = (int)parameterObject["subtitle"].asInteger();
1166 
1167         if (index < 0 || g_application.GetAppPlayer().GetSubtitleCount() <= index)
1168           return InvalidParams;
1169 
1170         g_application.GetAppPlayer().SetSubtitle(index);
1171 
1172         // Check if we need to enable subtitles to be displayed
1173         if (parameterObject["enable"].asBoolean() && !g_application.GetAppPlayer().GetSubtitleVisible())
1174           g_application.GetAppPlayer().SetSubtitleVisible(true);
1175       }
1176       else
1177         return FailedToExecute;
1178       break;
1179 
1180     case Audio:
1181     case Picture:
1182     default:
1183       return FailedToExecute;
1184   }
1185 
1186   return ACK;
1187 }
1188 
SetVideoStream(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)1189 JSONRPC_STATUS CPlayerOperations::SetVideoStream(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
1190 {
1191   switch (GetPlayer(parameterObject["playerid"]))
1192   {
1193   case Video:
1194   {
1195     int streamCount = g_application.GetAppPlayer().GetVideoStreamCount();
1196     if (streamCount > 0)
1197     {
1198       int index = g_application.GetAppPlayer().GetVideoStream();
1199       if (parameterObject["stream"].isString())
1200       {
1201         std::string action = parameterObject["stream"].asString();
1202         if (action.compare("previous") == 0)
1203         {
1204           index--;
1205           if (index < 0)
1206             index = streamCount - 1;
1207         }
1208         else if (action.compare("next") == 0)
1209         {
1210           index++;
1211           if (index >= streamCount)
1212             index = 0;
1213         }
1214         else
1215           return InvalidParams;
1216       }
1217       else if (parameterObject["stream"].isInteger())
1218         index = (int)parameterObject["stream"].asInteger();
1219 
1220       if (index < 0 || streamCount <= index)
1221         return InvalidParams;
1222 
1223       g_application.GetAppPlayer().SetVideoStream(index);
1224     }
1225     else
1226       return FailedToExecute;
1227     break;
1228   }
1229   case Audio:
1230   case Picture:
1231   default:
1232     return FailedToExecute;
1233   }
1234 
1235   return ACK;
1236 }
1237 
GetActivePlayers()1238 int CPlayerOperations::GetActivePlayers()
1239 {
1240   int activePlayers = 0;
1241 
1242   if (g_application.GetAppPlayer().IsPlayingVideo() ||
1243       CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV() ||
1244       CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRecording())
1245     activePlayers |= Video;
1246   if (g_application.GetAppPlayer().IsPlayingAudio() ||
1247       CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio())
1248     activePlayers |= Audio;
1249   if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_SLIDESHOW))
1250     activePlayers |= Picture;
1251   if (g_application.GetAppPlayer().IsExternalPlaying())
1252     activePlayers |= External;
1253   if (g_application.GetAppPlayer().IsRemotePlaying())
1254     activePlayers |= Remote;
1255 
1256   return activePlayers;
1257 }
1258 
GetPlayer(const CVariant & player)1259 PlayerType CPlayerOperations::GetPlayer(const CVariant &player)
1260 {
1261   int iPlayer = (int)player.asInteger();
1262   PlayerType playerID;
1263 
1264   switch (iPlayer)
1265   {
1266     case PLAYLIST_VIDEO:
1267       playerID = Video;
1268       break;
1269 
1270     case PLAYLIST_MUSIC:
1271       playerID = Audio;
1272       break;
1273 
1274     case PLAYLIST_PICTURE:
1275       playerID = Picture;
1276       break;
1277 
1278     default:
1279       playerID = None;
1280       break;
1281   }
1282 
1283   if (GetPlaylist(playerID) == iPlayer)
1284     return playerID;
1285   else
1286     return None;
1287 }
1288 
GetPlaylist(PlayerType player)1289 int CPlayerOperations::GetPlaylist(PlayerType player)
1290 {
1291   int playlist = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
1292   if (playlist == PLAYLIST_NONE) // No active playlist, try guessing
1293     playlist = g_application.GetAppPlayer().GetPreferredPlaylist();
1294 
1295   switch (player)
1296   {
1297     case Video:
1298       return playlist == PLAYLIST_NONE ? PLAYLIST_VIDEO : playlist;
1299 
1300     case Audio:
1301       return playlist == PLAYLIST_NONE ? PLAYLIST_MUSIC : playlist;
1302 
1303     case Picture:
1304       return PLAYLIST_PICTURE;
1305 
1306     default:
1307       return playlist;
1308   }
1309 }
1310 
StartSlideshow(const std::string & path,bool recursive,bool random,const std::string & firstPicturePath)1311 JSONRPC_STATUS CPlayerOperations::StartSlideshow(const std::string& path, bool recursive, bool random, const std::string &firstPicturePath /* = "" */)
1312 {
1313   int flags = 0;
1314   if (recursive)
1315     flags |= 1;
1316   if (random)
1317     flags |= 2;
1318   else
1319     flags |= 4;
1320 
1321   std::vector<std::string> params;
1322   params.push_back(path);
1323   if (!firstPicturePath.empty())
1324     params.push_back(firstPicturePath);
1325 
1326   // Reset screensaver when started from JSON only to avoid potential conflict with slideshow screensavers
1327   g_application.ResetScreenSaver();
1328   g_application.WakeUpScreenSaverAndDPMS();
1329   CGUIMessage msg(GUI_MSG_START_SLIDESHOW, 0, 0, flags);
1330   msg.SetStringParams(params);
1331   CApplicationMessenger::GetInstance().SendGUIMessage(msg, WINDOW_SLIDESHOW);
1332 
1333   return ACK;
1334 }
1335 
SendSlideshowAction(int actionID)1336 void CPlayerOperations::SendSlideshowAction(int actionID)
1337 {
1338   CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1, static_cast<void*>(new CAction(actionID)));
1339 }
1340 
GetPropertyValue(PlayerType player,const std::string & property,CVariant & result)1341 JSONRPC_STATUS CPlayerOperations::GetPropertyValue(PlayerType player, const std::string &property, CVariant &result)
1342 {
1343   if (player == None)
1344     return FailedToExecute;
1345 
1346   int playlist = GetPlaylist(player);
1347 
1348   if (property == "type")
1349   {
1350     switch (player)
1351     {
1352       case Video:
1353         result = "video";
1354         break;
1355 
1356       case Audio:
1357         result = "audio";
1358         break;
1359 
1360       case Picture:
1361         result = "picture";
1362         break;
1363 
1364       default:
1365         return FailedToExecute;
1366     }
1367   }
1368   else if (property == "partymode")
1369   {
1370     switch (player)
1371     {
1372       case Video:
1373       case Audio:
1374         if (IsPVRChannel())
1375         {
1376           result = false;
1377           break;
1378         }
1379 
1380         result = g_partyModeManager.IsEnabled();
1381         break;
1382 
1383       case Picture:
1384         result = false;
1385         break;
1386 
1387       default:
1388         return FailedToExecute;
1389     }
1390   }
1391   else if (property == "speed")
1392   {
1393     CGUIWindowSlideShow *slideshow = NULL;
1394     switch (player)
1395     {
1396       case Video:
1397       case Audio:
1398         result = g_application.GetAppPlayer().IsPausedPlayback() ? 0 : (int)lrint(g_application.GetAppPlayer().GetPlaySpeed());
1399         break;
1400 
1401       case Picture:
1402         slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
1403         if (slideshow && slideshow->IsPlaying() && !slideshow->IsPaused())
1404           result = slideshow->GetDirection();
1405         else
1406           result = 0;
1407         break;
1408 
1409       default:
1410         return FailedToExecute;
1411     }
1412   }
1413   else if (property == "time")
1414   {
1415     switch (player)
1416     {
1417       case Video:
1418       case Audio:
1419       {
1420         int ms = 0;
1421         if (!IsPVRChannel())
1422           ms = (int)(g_application.GetTime() * 1000.0);
1423         else
1424         {
1425           std::shared_ptr<CPVREpgInfoTag> epg(GetCurrentEpg());
1426           if (epg)
1427             ms = epg->Progress() * 1000;
1428         }
1429 
1430         MillisecondsToTimeObject(ms, result);
1431         break;
1432       }
1433 
1434       case Picture:
1435         MillisecondsToTimeObject(0, result);
1436         break;
1437 
1438       default:
1439         return FailedToExecute;
1440     }
1441   }
1442   else if (property == "percentage")
1443   {
1444     CGUIWindowSlideShow *slideshow = NULL;
1445     switch (player)
1446     {
1447       case Video:
1448       case Audio:
1449       {
1450         if (!IsPVRChannel())
1451           result = g_application.GetPercentage();
1452         else
1453         {
1454           std::shared_ptr<CPVREpgInfoTag> epg(GetCurrentEpg());
1455           if (epg)
1456             result = epg->ProgressPercentage();
1457           else
1458             result = 0;
1459         }
1460         break;
1461       }
1462 
1463       case Picture:
1464         slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
1465         if (slideshow && slideshow->NumSlides() > 0)
1466           result = (double)slideshow->CurrentSlide() / slideshow->NumSlides();
1467         else
1468           result = 0.0;
1469         break;
1470 
1471       default:
1472         return FailedToExecute;
1473     }
1474   }
1475   else if (property == "cachepercentage")
1476   {
1477     switch (player)
1478     {
1479       case Video:
1480       case Audio:
1481       {
1482         result = g_application.GetCachePercentage();
1483         break;
1484       }
1485 
1486       case Picture:
1487       {
1488         result = 0.0;
1489         break;
1490       }
1491 
1492       default:
1493         return FailedToExecute;
1494     }
1495   }
1496   else if (property == "totaltime")
1497   {
1498     switch (player)
1499     {
1500       case Video:
1501       case Audio:
1502       {
1503         int ms = 0;
1504         if (!IsPVRChannel())
1505           ms = (int)(g_application.GetTotalTime() * 1000.0);
1506         else
1507         {
1508           std::shared_ptr<CPVREpgInfoTag> epg(GetCurrentEpg());
1509           if (epg)
1510             ms = epg->GetDuration() * 1000;
1511         }
1512 
1513         MillisecondsToTimeObject(ms, result);
1514         break;
1515       }
1516 
1517       case Picture:
1518         MillisecondsToTimeObject(0, result);
1519         break;
1520 
1521       default:
1522         return FailedToExecute;
1523     }
1524   }
1525   else if (property == "playlistid")
1526   {
1527     result = playlist;
1528   }
1529   else if (property == "position")
1530   {
1531     CGUIWindowSlideShow *slideshow = NULL;
1532     switch (player)
1533     {
1534       case Video:
1535       case Audio: /* Return the position of current item if there is an active playlist */
1536         if (!IsPVRChannel() && CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == playlist)
1537           result = CServiceBroker::GetPlaylistPlayer().GetCurrentSong();
1538         else
1539           result = -1;
1540         break;
1541 
1542       case Picture:
1543         slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
1544         if (slideshow && slideshow->IsPlaying())
1545           result = slideshow->CurrentSlide() - 1;
1546         else
1547           result = -1;
1548         break;
1549 
1550       default:
1551         result = -1;
1552         break;
1553     }
1554   }
1555   else if (property == "repeat")
1556   {
1557     switch (player)
1558     {
1559       case Video:
1560       case Audio:
1561         if (IsPVRChannel())
1562         {
1563           result = "off";
1564           break;
1565         }
1566 
1567         switch (CServiceBroker::GetPlaylistPlayer().GetRepeat(playlist))
1568         {
1569         case REPEAT_ONE:
1570           result = "one";
1571           break;
1572         case REPEAT_ALL:
1573           result = "all";
1574           break;
1575         default:
1576           result = "off";
1577           break;
1578         }
1579         break;
1580 
1581       case Picture:
1582       default:
1583         result = "off";
1584         break;
1585     }
1586   }
1587   else if (property == "shuffled")
1588   {
1589     CGUIWindowSlideShow *slideshow = NULL;
1590     switch (player)
1591     {
1592       case Video:
1593       case Audio:
1594         if (IsPVRChannel())
1595         {
1596           result = false;
1597           break;
1598         }
1599 
1600         result = CServiceBroker::GetPlaylistPlayer().IsShuffled(playlist);
1601         break;
1602 
1603       case Picture:
1604         slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
1605         if (slideshow && slideshow->IsPlaying())
1606           result = slideshow->IsShuffled();
1607         else
1608           result = -1;
1609         break;
1610 
1611       default:
1612         result = -1;
1613         break;
1614     }
1615   }
1616   else if (property == "canseek")
1617   {
1618     switch (player)
1619     {
1620       case Video:
1621       case Audio:
1622         result = g_application.GetAppPlayer().CanSeek();
1623         break;
1624 
1625       case Picture:
1626       default:
1627         result = false;
1628         break;
1629     }
1630   }
1631   else if (property == "canchangespeed")
1632   {
1633     switch (player)
1634     {
1635       case Video:
1636       case Audio:
1637         result = !IsPVRChannel();
1638         break;
1639 
1640       case Picture:
1641       default:
1642         result = false;
1643         break;
1644     }
1645   }
1646   else if (property == "canmove")
1647   {
1648     switch (player)
1649     {
1650       case Picture:
1651         result = true;
1652         break;
1653 
1654       case Video:
1655       case Audio:
1656       default:
1657         result = false;
1658         break;
1659     }
1660   }
1661   else if (property == "canzoom")
1662   {
1663     switch (player)
1664     {
1665       case Picture:
1666         result = true;
1667         break;
1668 
1669       case Video:
1670       case Audio:
1671       default:
1672         result = false;
1673         break;
1674     }
1675   }
1676   else if (property == "canrotate")
1677   {
1678     switch (player)
1679     {
1680       case Picture:
1681         result = true;
1682         break;
1683 
1684       case Video:
1685       case Audio:
1686       default:
1687         result = false;
1688         break;
1689     }
1690   }
1691   else if (property == "canshuffle")
1692   {
1693     switch (player)
1694     {
1695       case Video:
1696       case Audio:
1697       case Picture:
1698         result = !IsPVRChannel();
1699         break;
1700 
1701       default:
1702         result = false;
1703         break;
1704     }
1705   }
1706   else if (property == "canrepeat")
1707   {
1708     switch (player)
1709     {
1710       case Video:
1711       case Audio:
1712         result = !IsPVRChannel();
1713         break;
1714 
1715       case Picture:
1716       default:
1717         result = false;
1718         break;
1719     }
1720   }
1721   else if (property == "currentaudiostream")
1722   {
1723     switch (player)
1724     {
1725       case Video:
1726       case Audio:
1727         if (g_application.GetAppPlayer().HasPlayer())
1728         {
1729           result = CVariant(CVariant::VariantTypeObject);
1730           int index = g_application.GetAppPlayer().GetAudioStream();
1731           if (index >= 0)
1732           {
1733             AudioStreamInfo info;
1734             g_application.GetAppPlayer().GetAudioStreamInfo(index, info);
1735 
1736             result["index"] = index;
1737             result["name"] = info.name;
1738             result["language"] = info.language;
1739             result["codec"] = info.codecName;
1740             result["bitrate"] = info.bitrate;
1741             result["channels"] = info.channels;
1742             result["samplerate"] = info.samplerate;
1743             AppendAudioStreamFlagsAsBooleans(result, info.flags);
1744           }
1745         }
1746         else
1747           result = CVariant(CVariant::VariantTypeNull);
1748         break;
1749 
1750       case Picture:
1751       default:
1752         result = CVariant(CVariant::VariantTypeNull);
1753         break;
1754     }
1755   }
1756   else if (property == "audiostreams")
1757   {
1758     result = CVariant(CVariant::VariantTypeArray);
1759     switch (player)
1760     {
1761       case Video:
1762       case Audio:
1763         if (g_application.GetAppPlayer().HasPlayer())
1764         {
1765           for (int index = 0; index < g_application.GetAppPlayer().GetAudioStreamCount(); index++)
1766           {
1767             AudioStreamInfo info;
1768             g_application.GetAppPlayer().GetAudioStreamInfo(index, info);
1769 
1770             CVariant audioStream(CVariant::VariantTypeObject);
1771             audioStream["index"] = index;
1772             audioStream["name"] = info.name;
1773             audioStream["language"] = info.language;
1774             audioStream["codec"] = info.codecName;
1775             audioStream["bitrate"] = info.bitrate;
1776             audioStream["channels"] = info.channels;
1777             audioStream["samplerate"] = info.samplerate;
1778             AppendAudioStreamFlagsAsBooleans(audioStream, info.flags);
1779 
1780             result.append(audioStream);
1781           }
1782         }
1783         break;
1784 
1785       case Picture:
1786       default:
1787         break;
1788     }
1789   }
1790   else if (property == "currentvideostream")
1791   {
1792     switch (player)
1793     {
1794     case Video:
1795     {
1796       int index = g_application.GetAppPlayer().GetVideoStream();
1797       if (index >= 0)
1798       {
1799         result = CVariant(CVariant::VariantTypeObject);
1800         VideoStreamInfo info;
1801         g_application.GetAppPlayer().GetVideoStreamInfo(index, info);
1802 
1803         result["index"] = index;
1804         result["name"] = info.name;
1805         result["language"] = info.language;
1806         result["codec"] = info.codecName;
1807         result["width"] = info.width;
1808         result["height"] = info.height;
1809       }
1810       else
1811         result = CVariant(CVariant::VariantTypeNull);
1812       break;
1813     }
1814     case Audio:
1815     case Picture:
1816     default:
1817       result = CVariant(CVariant::VariantTypeNull);
1818       break;
1819     }
1820   }
1821   else if (property == "videostreams")
1822   {
1823     result = CVariant(CVariant::VariantTypeArray);
1824     switch (player)
1825     {
1826     case Video:
1827     {
1828       int streamCount = g_application.GetAppPlayer().GetVideoStreamCount();
1829       if (streamCount >= 0)
1830       {
1831         for (int index = 0; index < streamCount; ++index)
1832         {
1833           VideoStreamInfo info;
1834           g_application.GetAppPlayer().GetVideoStreamInfo(index, info);
1835 
1836           CVariant videoStream(CVariant::VariantTypeObject);
1837           videoStream["index"] = index;
1838           videoStream["name"] = info.name;
1839           videoStream["language"] = info.language;
1840           videoStream["codec"] = info.codecName;
1841           videoStream["width"] = info.width;
1842           videoStream["height"] = info.height;
1843 
1844           result.append(videoStream);
1845         }
1846       }
1847       break;
1848     }
1849     case Audio:
1850     case Picture:
1851     default:
1852       break;
1853     }
1854   }
1855   else if (property == "subtitleenabled")
1856   {
1857     switch (player)
1858     {
1859       case Video:
1860         result = g_application.GetAppPlayer().GetSubtitleVisible();
1861         break;
1862 
1863       case Audio:
1864       case Picture:
1865       default:
1866         result = false;
1867         break;
1868     }
1869   }
1870   else if (property == "currentsubtitle")
1871   {
1872     switch (player)
1873     {
1874       case Video:
1875         if (g_application.GetAppPlayer().HasPlayer())
1876         {
1877           result = CVariant(CVariant::VariantTypeObject);
1878           int index = g_application.GetAppPlayer().GetSubtitle();
1879           if (index >= 0)
1880           {
1881             SubtitleStreamInfo info;
1882             g_application.GetAppPlayer().GetSubtitleStreamInfo(index, info);
1883 
1884             result["index"] = index;
1885             result["name"] = info.name;
1886             result["language"] = info.language;
1887             AppendSubtitleStreamFlagsAsBooleans(result, info.flags);
1888           }
1889         }
1890         else
1891           result = CVariant(CVariant::VariantTypeNull);
1892         break;
1893 
1894       case Audio:
1895       case Picture:
1896       default:
1897         result = CVariant(CVariant::VariantTypeNull);
1898         break;
1899     }
1900   }
1901   else if (property == "subtitles")
1902   {
1903     result = CVariant(CVariant::VariantTypeArray);
1904     switch (player)
1905     {
1906       case Video:
1907         if (g_application.GetAppPlayer().HasPlayer())
1908         {
1909           for (int index = 0; index < g_application.GetAppPlayer().GetSubtitleCount(); index++)
1910           {
1911             SubtitleStreamInfo info;
1912             g_application.GetAppPlayer().GetSubtitleStreamInfo(index, info);
1913 
1914             CVariant subtitle(CVariant::VariantTypeObject);
1915             subtitle["index"] = index;
1916             subtitle["name"] = info.name;
1917             subtitle["language"] = info.language;
1918             AppendSubtitleStreamFlagsAsBooleans(subtitle, info.flags);
1919 
1920             result.append(subtitle);
1921           }
1922         }
1923         break;
1924 
1925       case Audio:
1926       case Picture:
1927       default:
1928         break;
1929     }
1930   }
1931   else if (property == "live")
1932     result = IsPVRChannel();
1933   else
1934     return InvalidParams;
1935 
1936   return OK;
1937 }
1938 
ParseRepeatState(const CVariant & repeat)1939 int CPlayerOperations::ParseRepeatState(const CVariant &repeat)
1940 {
1941   REPEAT_STATE state = REPEAT_NONE;
1942   std::string strState = repeat.asString();
1943 
1944   if (strState.compare("one") == 0)
1945     state = REPEAT_ONE;
1946   else if (strState.compare("all") == 0)
1947     state = REPEAT_ALL;
1948 
1949   return state;
1950 }
1951 
ParseTimeInSeconds(const CVariant & time)1952 double CPlayerOperations::ParseTimeInSeconds(const CVariant &time)
1953 {
1954   double seconds = 0.0;
1955   if (time.isMember("hours"))
1956     seconds += time["hours"].asInteger() * 60 * 60;
1957   if (time.isMember("minutes"))
1958     seconds += time["minutes"].asInteger() * 60;
1959   if (time.isMember("seconds"))
1960     seconds += time["seconds"].asInteger();
1961   if (time.isMember("milliseconds"))
1962     seconds += time["milliseconds"].asDouble() / 1000.0;
1963 
1964   return seconds;
1965 }
1966 
IsPVRChannel()1967 bool CPlayerOperations::IsPVRChannel()
1968 {
1969   const std::shared_ptr<CPVRPlaybackState> state = CServiceBroker::GetPVRManager().PlaybackState();
1970   return state->IsPlayingTV() || state->IsPlayingRadio();
1971 }
1972 
GetCurrentEpg()1973 std::shared_ptr<CPVREpgInfoTag> CPlayerOperations::GetCurrentEpg()
1974 {
1975   const std::shared_ptr<CPVRPlaybackState> state = CServiceBroker::GetPVRManager().PlaybackState();
1976   if (!state->IsPlayingTV() && !state->IsPlayingRadio())
1977     return {};
1978 
1979   const std::shared_ptr<CPVRChannel> currentChannel = state->GetPlayingChannel();
1980   if (!currentChannel)
1981     return {};
1982 
1983   return currentChannel->GetEPGNow();
1984 }
1985