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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meter, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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