summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/json-rpc/PlayerOperations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/interfaces/json-rpc/PlayerOperations.cpp')
-rw-r--r--xbmc/interfaces/json-rpc/PlayerOperations.cpp2158
1 files changed, 2158 insertions, 0 deletions
diff --git a/xbmc/interfaces/json-rpc/PlayerOperations.cpp b/xbmc/interfaces/json-rpc/PlayerOperations.cpp
new file mode 100644
index 0000000..9912a72
--- /dev/null
+++ b/xbmc/interfaces/json-rpc/PlayerOperations.cpp
@@ -0,0 +1,2158 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PlayerOperations.h"
+
+#include "AudioLibrary.h"
+#include "FileItem.h"
+#include "GUIInfoManager.h"
+#include "GUIUserMessages.h"
+#include "PartyModeManager.h"
+#include "PlayListPlayer.h"
+#include "SeekHandler.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "VideoLibrary.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "application/ApplicationPowerHandling.h"
+#include "cores/playercorefactory/PlayerCoreFactory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "interfaces/builtins/Builtins.h"
+#include "messaging/ApplicationMessenger.h"
+#include "music/MusicDatabase.h"
+#include "pictures/GUIWindowSlideShow.h"
+#include "pvr/PVRManager.h"
+#include "pvr/PVRPlaybackState.h"
+#include "pvr/channels/PVRChannel.h"
+#include "pvr/channels/PVRChannelGroupMember.h"
+#include "pvr/channels/PVRChannelGroupsContainer.h"
+#include "pvr/epg/EpgInfoTag.h"
+#include "pvr/guilib/PVRGUIActionsChannels.h"
+#include "pvr/guilib/PVRGUIActionsPlayback.h"
+#include "pvr/recordings/PVRRecordings.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/DisplaySettings.h"
+#include "settings/MediaSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/MathUtils.h"
+#include "utils/Variant.h"
+#include "video/VideoDatabase.h"
+
+#include <map>
+#include <tuple>
+
+using namespace JSONRPC;
+using namespace PVR;
+
+namespace
+{
+
+void AppendAudioStreamFlagsAsBooleans(CVariant& list, StreamFlags flags)
+{
+ list["isdefault"] = ((flags & StreamFlags::FLAG_DEFAULT) != 0);
+ list["isoriginal"] = ((flags & StreamFlags::FLAG_ORIGINAL) != 0);
+ list["isimpaired"] = ((flags & StreamFlags::FLAG_VISUAL_IMPAIRED) != 0);
+}
+
+void AppendSubtitleStreamFlagsAsBooleans(CVariant& list, StreamFlags flags)
+{
+ list["isdefault"] = ((flags & StreamFlags::FLAG_DEFAULT) != 0);
+ list["isforced"] = ((flags & StreamFlags::FLAG_FORCED) != 0);
+ list["isimpaired"] = ((flags & StreamFlags::FLAG_HEARING_IMPAIRED) != 0);
+}
+
+} // namespace
+
+JSONRPC_STATUS CPlayerOperations::GetActivePlayers(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ int activePlayers = GetActivePlayers();
+ result = CVariant(CVariant::VariantTypeArray);
+
+ std::string strPlayerType = "internal";
+ if (activePlayers & External)
+ strPlayerType = "external";
+ else if (activePlayers & Remote)
+ strPlayerType = "remote";
+
+ if (activePlayers & Video)
+ {
+ CVariant video = CVariant(CVariant::VariantTypeObject);
+ video["playerid"] = GetPlaylist(Video);
+ video["type"] = "video";
+ video["playertype"] = strPlayerType;
+ result.append(video);
+ }
+ if (activePlayers & Audio)
+ {
+ CVariant audio = CVariant(CVariant::VariantTypeObject);
+ audio["playerid"] = GetPlaylist(Audio);
+ audio["type"] = "audio";
+ audio["playertype"] = strPlayerType;
+ result.append(audio);
+ }
+ if (activePlayers & Picture)
+ {
+ CVariant picture = CVariant(CVariant::VariantTypeObject);
+ picture["playerid"] = GetPlaylist(Picture);
+ picture["type"] = "picture";
+ picture["playertype"] = "internal";
+ result.append(picture);
+ }
+
+ return OK;
+}
+
+JSONRPC_STATUS CPlayerOperations::GetPlayers(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
+
+ std::string media = parameterObject["media"].asString();
+ result = CVariant(CVariant::VariantTypeArray);
+ std::vector<std::string> players;
+
+ if (media == "all")
+ {
+ playerCoreFactory.GetPlayers(players);
+ }
+ else
+ {
+ bool video = false;
+ if (media == "video")
+ video = true;
+ playerCoreFactory.GetPlayers(players, true, video);
+ }
+
+ for (const auto& playername : players)
+ {
+ CVariant player(CVariant::VariantTypeObject);
+ player["name"] = playername;
+
+ player["playsvideo"] = playerCoreFactory.PlaysVideo(playername);
+ player["playsaudio"] = playerCoreFactory.PlaysAudio(playername);
+ player["type"] = playerCoreFactory.GetPlayerType(playername);
+
+ result.push_back(player);
+ }
+
+ return OK;
+}
+
+JSONRPC_STATUS CPlayerOperations::GetProperties(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ PlayerType player = GetPlayer(parameterObject["playerid"]);
+
+ CVariant properties = CVariant(CVariant::VariantTypeObject);
+ for (unsigned int index = 0; index < parameterObject["properties"].size(); index++)
+ {
+ std::string propertyName = parameterObject["properties"][index].asString();
+ CVariant property;
+ JSONRPC_STATUS ret;
+ if ((ret = GetPropertyValue(player, propertyName, property)) != OK)
+ return ret;
+
+ properties[propertyName] = property;
+ }
+
+ result = properties;
+
+ return OK;
+}
+
+JSONRPC_STATUS CPlayerOperations::GetItem(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ PlayerType player = GetPlayer(parameterObject["playerid"]);
+ CFileItemPtr fileItem;
+
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ fileItem = std::make_shared<CFileItem>(g_application.CurrentFileItem());
+ if (IsPVRChannel())
+ break;
+
+ if (player == Video)
+ {
+ if (!CVideoLibrary::FillFileItem(fileItem->GetPath(), fileItem, parameterObject))
+ {
+ // Fallback to item details held by GUI but ensure path unchanged
+ //! @todo remove this once there is no route to playback that updates
+ // GUI item without also updating app item e.g. start playback of a
+ // non-library item via JSON
+ const CVideoInfoTag *currentVideoTag = CServiceBroker::GetGUI()->GetInfoManager().GetCurrentMovieTag();
+ if (currentVideoTag != NULL)
+ {
+ std::string originalLabel = fileItem->GetLabel();
+ std::string originalPath = fileItem->GetPath();
+ fileItem->SetFromVideoInfoTag(*currentVideoTag);
+ if (fileItem->GetLabel().empty())
+ fileItem->SetLabel(originalLabel);
+ fileItem->SetPath(originalPath); // Ensure path unchanged
+ }
+ }
+
+ bool additionalInfo = false;
+ for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array();
+ itr != parameterObject["properties"].end_array(); ++itr)
+ {
+ std::string fieldValue = itr->asString();
+ if (fieldValue == "cast" || fieldValue == "set" || fieldValue == "setid" || fieldValue == "showlink" || fieldValue == "resume" ||
+ (fieldValue == "streamdetails" && !fileItem->GetVideoInfoTag()->m_streamDetails.HasItems()))
+ additionalInfo = true;
+ }
+
+ CVideoDatabase videodatabase;
+ if ((additionalInfo) &&
+ videodatabase.Open())
+ {
+ switch (fileItem->GetVideoContentType())
+ {
+ case VideoDbContentType::MOVIES:
+ videodatabase.GetMovieInfo("", *(fileItem->GetVideoInfoTag()),
+ fileItem->GetVideoInfoTag()->m_iDbId);
+ break;
+
+ case VideoDbContentType::MUSICVIDEOS:
+ videodatabase.GetMusicVideoInfo("", *(fileItem->GetVideoInfoTag()),
+ fileItem->GetVideoInfoTag()->m_iDbId);
+ break;
+
+ case VideoDbContentType::EPISODES:
+ videodatabase.GetEpisodeInfo("", *(fileItem->GetVideoInfoTag()),
+ fileItem->GetVideoInfoTag()->m_iDbId);
+ break;
+
+ case VideoDbContentType::TVSHOWS:
+ case VideoDbContentType::MOVIE_SETS:
+ default:
+ break;
+ }
+
+ videodatabase.Close();
+ }
+ }
+ else // Audio
+ {
+ if (!CAudioLibrary::FillFileItem(fileItem->GetPath(), fileItem, parameterObject))
+ {
+ // Fallback to item details held by GUI but ensure path unchanged
+ //! @todo remove this once there is no route to playback that updates
+ // GUI item without also updating app item e.g. start playback of a
+ // non-library item via JSON
+ const MUSIC_INFO::CMusicInfoTag* currentMusicTag =
+ CServiceBroker::GetGUI()->GetInfoManager().GetCurrentSongTag();
+ if (currentMusicTag != NULL)
+ {
+ std::string originalLabel = fileItem->GetLabel();
+ std::string originalPath = fileItem->GetPath();
+ fileItem->SetFromMusicInfoTag(*currentMusicTag);
+ if (fileItem->GetLabel().empty())
+ fileItem->SetLabel(originalLabel);
+ fileItem->SetPath(originalPath); // Ensure path unchanged
+ }
+ }
+
+ if (fileItem->IsMusicDb())
+ {
+ CMusicDatabase musicdb;
+ CFileItemList items;
+ items.Add(fileItem);
+ CAudioLibrary::GetAdditionalSongDetails(parameterObject, items, musicdb);
+ }
+ }
+ break;
+ }
+
+ case Picture:
+ {
+ CGUIWindowSlideShow *slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (!slideshow)
+ return FailedToExecute;
+
+ CFileItemList slides;
+ slideshow->GetSlideShowContents(slides);
+ fileItem = slides[slideshow->CurrentSlide() - 1];
+ break;
+ }
+
+ case None:
+ default:
+ return FailedToExecute;
+ }
+
+ HandleFileItem("id", !IsPVRChannel(), "item", fileItem, parameterObject, parameterObject["properties"], result, false);
+ return OK;
+}
+
+JSONRPC_STATUS CPlayerOperations::PlayPause(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ CGUIWindowSlideShow *slideshow = NULL;
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ case Audio:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!appPlayer->CanPause())
+ return FailedToExecute;
+
+ if (parameterObject["play"].isString())
+ CBuiltins::GetInstance().Execute("playercontrol(play)");
+ else
+ {
+ if (parameterObject["play"].asBoolean())
+ {
+ if (appPlayer->IsPausedPlayback())
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_PAUSE);
+ else if (appPlayer->GetPlaySpeed() != 1)
+ appPlayer->SetPlaySpeed(1);
+ }
+ else if (!appPlayer->IsPausedPlayback())
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_PAUSE);
+ }
+ result["speed"] = appPlayer->IsPausedPlayback() ? 0 : (int)lrint(appPlayer->GetPlaySpeed());
+ return OK;
+ }
+
+ case Picture:
+ slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (slideshow && slideshow->IsPlaying() &&
+ (parameterObject["play"].isString() ||
+ (parameterObject["play"].isBoolean() && parameterObject["play"].asBoolean() == slideshow->IsPaused())))
+ SendSlideshowAction(ACTION_PAUSE);
+
+ if (slideshow && slideshow->IsPlaying() && !slideshow->IsPaused())
+ result["speed"] = slideshow->GetDirection();
+ else
+ result["speed"] = 0;
+ return OK;
+
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::Stop(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ case Audio:
+ CServiceBroker::GetAppMessenger()->PostMsg(
+ TMSG_MEDIA_STOP, static_cast<int>(parameterObject["playerid"].asInteger()));
+ return ACK;
+
+ case Picture:
+ SendSlideshowAction(ACTION_STOP);
+ return ACK;
+
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::GetAudioDelay(const std::string& method,
+ ITransportLayer* transport,
+ IClient* client,
+ const CVariant& parameterObject,
+ CVariant& result)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ result["offset"] = appPlayer->GetVideoSettings().m_AudioDelay;
+ return OK;
+}
+
+JSONRPC_STATUS CPlayerOperations::SetAudioDelay(const std::string& method,
+ ITransportLayer* transport,
+ IClient* client,
+ const CVariant& parameterObject,
+ CVariant& result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ float videoAudioDelayRange =
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAudioDelayRange;
+
+ if (parameterObject["offset"].isDouble())
+ {
+ float offset = static_cast<float>(parameterObject["offset"].asDouble());
+ offset = MathUtils::RoundF(offset, AUDIO_DELAY_STEP);
+ if (offset > videoAudioDelayRange)
+ offset = videoAudioDelayRange;
+ else if (offset < -videoAudioDelayRange)
+ offset = -videoAudioDelayRange;
+
+ appPlayer->SetAVDelay(offset);
+ }
+ else if (parameterObject["offset"].isString())
+ {
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ if (parameterObject["offset"].asString().compare("increment") == 0)
+ {
+ vs.m_AudioDelay += AUDIO_DELAY_STEP;
+ if (vs.m_AudioDelay > videoAudioDelayRange)
+ vs.m_AudioDelay = videoAudioDelayRange;
+ appPlayer->SetAVDelay(vs.m_AudioDelay);
+ }
+ else
+ {
+ vs.m_AudioDelay -= AUDIO_DELAY_STEP;
+ if (vs.m_AudioDelay < -videoAudioDelayRange)
+ vs.m_AudioDelay = -videoAudioDelayRange;
+ appPlayer->SetAVDelay(vs.m_AudioDelay);
+ }
+ }
+ else
+ return InvalidParams;
+
+ result["offset"] = appPlayer->GetVideoSettings().m_AudioDelay;
+ return OK;
+ }
+ case Audio:
+ case Picture:
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::SetSpeed(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ case Audio:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (parameterObject["speed"].isInteger())
+ {
+ int speed = (int)parameterObject["speed"].asInteger();
+ if (speed != 0)
+ {
+ // If the player is paused we first need to unpause
+ if (appPlayer->IsPausedPlayback())
+ appPlayer->Pause();
+ appPlayer->SetPlaySpeed(speed);
+ }
+ else
+ appPlayer->Pause();
+ }
+ else if (parameterObject["speed"].isString())
+ {
+ if (parameterObject["speed"].asString().compare("increment") == 0)
+ CBuiltins::GetInstance().Execute("playercontrol(forward)");
+ else
+ CBuiltins::GetInstance().Execute("playercontrol(rewind)");
+ }
+ else
+ return InvalidParams;
+
+ result["speed"] = appPlayer->IsPausedPlayback() ? 0 : (int)lrint(appPlayer->GetPlaySpeed());
+ return OK;
+ }
+
+ case Picture:
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::Seek(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ PlayerType player = GetPlayer(parameterObject["playerid"]);
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!appPlayer->CanSeek())
+ return FailedToExecute;
+
+ const CVariant& value = parameterObject["value"];
+ if (value.isMember("percentage"))
+ g_application.SeekPercentage(value["percentage"].asFloat());
+ else if (value.isMember("step"))
+ {
+ std::string step = value["step"].asString();
+ if (step == "smallforward")
+ CBuiltins::GetInstance().Execute("playercontrol(smallskipforward)");
+ else if (step == "smallbackward")
+ CBuiltins::GetInstance().Execute("playercontrol(smallskipbackward)");
+ else if (step == "bigforward")
+ CBuiltins::GetInstance().Execute("playercontrol(bigskipforward)");
+ else if (step == "bigbackward")
+ CBuiltins::GetInstance().Execute("playercontrol(bigskipbackward)");
+ else
+ return InvalidParams;
+ }
+ else if (value.isMember("seconds"))
+ appPlayer->GetSeekHandler().SeekSeconds(static_cast<int>(value["seconds"].asInteger()));
+ else if (value.isMember("time"))
+ g_application.SeekTime(ParseTimeInSeconds(value["time"]));
+ else
+ return InvalidParams;
+
+ GetPropertyValue(player, "percentage", result["percentage"]);
+ GetPropertyValue(player, "time", result["time"]);
+ GetPropertyValue(player, "totaltime", result["totaltime"]);
+ return OK;
+ }
+
+ case Picture:
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::Move(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ std::string direction = parameterObject["direction"].asString();
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Picture:
+ if (direction == "left")
+ SendSlideshowAction(ACTION_MOVE_LEFT);
+ else if (direction == "right")
+ SendSlideshowAction(ACTION_MOVE_RIGHT);
+ else if (direction == "up")
+ SendSlideshowAction(ACTION_MOVE_UP);
+ else if (direction == "down")
+ SendSlideshowAction(ACTION_MOVE_DOWN);
+ else
+ return InvalidParams;
+
+ return ACK;
+
+ case Video:
+ case Audio:
+ if (direction == "left" || direction == "up")
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_PREV_ITEM)));
+ else if (direction == "right" || direction == "down")
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_NEXT_ITEM)));
+ else
+ return InvalidParams;
+
+ return ACK;
+
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::Zoom(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ CVariant zoom = parameterObject["zoom"];
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Picture:
+ if (zoom.isInteger())
+ SendSlideshowAction(ACTION_ZOOM_LEVEL_NORMAL + ((int)zoom.asInteger() - 1));
+ else if (zoom.isString())
+ {
+ std::string strZoom = zoom.asString();
+ if (strZoom == "in")
+ SendSlideshowAction(ACTION_ZOOM_IN);
+ else if (strZoom == "out")
+ SendSlideshowAction(ACTION_ZOOM_OUT);
+ else
+ return InvalidParams;
+ }
+ else
+ return InvalidParams;
+
+ return ACK;
+
+ case Video:
+ case Audio:
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+// Matching pairs of values from JSON type "Player.ViewMode" and C++ enum ViewMode
+// Additions to enum ViewMode need to be added here and in the JSON type
+std::map<std::string, ViewMode> viewModes =
+{
+ {"normal", ViewModeNormal},
+ {"zoom", ViewModeZoom},
+ {"stretch4x3", ViewModeStretch4x3},
+ {"widezoom", ViewModeWideZoom, },
+ {"stretch16x9", ViewModeStretch16x9},
+ {"original", ViewModeOriginal},
+ {"stretch16x9nonlin", ViewModeStretch16x9Nonlin},
+ {"zoom120width", ViewModeZoom120Width},
+ {"zoom110width", ViewModeZoom110Width}
+};
+
+std::string GetStringFromViewMode(ViewMode viewMode)
+{
+ std::string result = "custom";
+
+ auto it = find_if(viewModes.begin(), viewModes.end(), [viewMode](const std::pair<std::string, ViewMode> & p)
+ {
+ return p.second == viewMode;
+ });
+
+ if (it != viewModes.end())
+ {
+ std::pair<std::string, ViewMode> value = *it;
+ result = value.first;
+ }
+
+ return result;
+}
+
+void GetNewValueForViewModeParameter(const CVariant &parameter, float stepSize, float minValue, float maxValue, float &result)
+{
+ if (parameter.isDouble())
+ {
+ result = parameter.asDouble();
+ }
+ else if (parameter.isString())
+ {
+ if (parameter == "decrease")
+ {
+ stepSize *= -1;
+ }
+
+ result += stepSize;
+ }
+
+ result = std::max(minValue, std::min(result, maxValue));
+}
+
+JSONRPC_STATUS CPlayerOperations::SetViewMode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ JSONRPC_STATUS jsonStatus = InvalidParams;
+ // init with current values from settings
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ CVideoSettings vs = appPlayer->GetVideoSettings();
+ ViewMode mode = ViewModeNormal;
+
+ CVariant viewMode = parameterObject["viewmode"];
+ if (viewMode.isString())
+ {
+ std::string modestr = viewMode.asString();
+ if (viewModes.find(modestr) != viewModes.end())
+ {
+ mode = viewModes[modestr];
+ jsonStatus = ACK;
+ }
+ }
+ else if (viewMode.isObject())
+ {
+ mode = ViewModeCustom;
+ CVariant zoom = viewMode["zoom"];
+ CVariant pixelRatio = viewMode["pixelratio"];
+ CVariant verticalShift = viewMode["verticalshift"];
+ CVariant stretch = viewMode["nonlinearstretch"];
+
+ if (!zoom.isNull())
+ {
+ GetNewValueForViewModeParameter(zoom, 0.01f, 0.5f, 2.f, vs.m_CustomZoomAmount);
+ jsonStatus = ACK;
+ }
+
+ if (!pixelRatio.isNull())
+ {
+ GetNewValueForViewModeParameter(pixelRatio, 0.01f, 0.5f, 2.f, vs.m_CustomPixelRatio);
+ jsonStatus = ACK;
+ }
+
+ if (!verticalShift.isNull())
+ {
+ GetNewValueForViewModeParameter(verticalShift, -0.01f, -2.f, 2.f, vs.m_CustomVerticalShift);
+ jsonStatus = ACK;
+ }
+
+ if (stretch.isBoolean())
+ {
+ vs.m_CustomNonLinStretch = stretch.asBoolean();
+ jsonStatus = ACK;
+ }
+ }
+
+ if (jsonStatus == ACK)
+ {
+ appPlayer->SetRenderViewMode(static_cast<int>(mode), vs.m_CustomZoomAmount,
+ vs.m_CustomPixelRatio, vs.m_CustomVerticalShift,
+ vs.m_CustomNonLinStretch);
+ }
+
+ return jsonStatus;
+}
+
+JSONRPC_STATUS CPlayerOperations::GetViewMode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ int mode = appPlayer->GetVideoSettings().m_ViewMode;
+
+ result["viewmode"] = GetStringFromViewMode(static_cast<ViewMode>(mode));
+
+ result["zoom"] = CDisplaySettings::GetInstance().GetZoomAmount();
+ result["pixelratio"] = CDisplaySettings::GetInstance().GetPixelRatio();
+ result["verticalshift"] = CDisplaySettings::GetInstance().GetVerticalShift();
+ result["nonlinearstretch"] = CDisplaySettings::GetInstance().IsNonLinearStretched();
+ return OK;
+}
+
+JSONRPC_STATUS CPlayerOperations::Rotate(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Picture:
+ if (parameterObject["value"].asString().compare("clockwise") == 0)
+ SendSlideshowAction(ACTION_ROTATE_PICTURE_CW);
+ else
+ SendSlideshowAction(ACTION_ROTATE_PICTURE_CCW);
+ return ACK;
+
+ case Video:
+ case Audio:
+ case None:
+ default:
+ return FailedToExecute;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::Open(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ CVariant options = parameterObject["options"];
+ CVariant optionShuffled = options["shuffled"];
+ CVariant optionRepeat = options["repeat"];
+ CVariant optionResume = options["resume"];
+ CVariant optionPlayer = options["playername"];
+
+ if (parameterObject["item"].isMember("playlistid"))
+ {
+ PLAYLIST::Id playlistid = parameterObject["item"]["playlistid"].asInteger();
+
+ if (playlistid == PLAYLIST::TYPE_MUSIC || playlistid == PLAYLIST::TYPE_VIDEO)
+ {
+ // Apply the "shuffled" option if available
+ if (optionShuffled.isBoolean())
+ CServiceBroker::GetPlaylistPlayer().SetShuffle(playlistid, optionShuffled.asBoolean(), false);
+ // Apply the "repeat" option if available
+ if (!optionRepeat.isNull())
+ CServiceBroker::GetPlaylistPlayer().SetRepeat(playlistid, ParseRepeatState(optionRepeat),
+ false);
+ }
+
+ int playlistStartPosition = (int)parameterObject["item"]["position"].asInteger();
+
+ switch (playlistid)
+ {
+ case PLAYLIST::TYPE_MUSIC:
+ case PLAYLIST::TYPE_VIDEO:
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, playlistid,
+ playlistStartPosition);
+ break;
+
+ case PLAYLIST::TYPE_PICTURE:
+ {
+ std::string firstPicturePath;
+ if (playlistStartPosition > 0)
+ {
+ CGUIWindowSlideShow *slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (slideshow != NULL)
+ {
+ CFileItemList list;
+ slideshow->GetSlideShowContents(list);
+ if (playlistStartPosition < list.Size())
+ firstPicturePath = list.Get(playlistStartPosition)->GetPath();
+ }
+ }
+
+ return StartSlideshow("", false, optionShuffled.isBoolean() && optionShuffled.asBoolean(), firstPicturePath);
+ break;
+ }
+ }
+
+ return ACK;
+ }
+ else if (parameterObject["item"].isMember("path"))
+ {
+ bool random = (optionShuffled.isBoolean() && optionShuffled.asBoolean()) ||
+ (!optionShuffled.isBoolean() && parameterObject["item"]["random"].asBoolean());
+ return StartSlideshow(parameterObject["item"]["path"].asString(), parameterObject["item"]["recursive"].asBoolean(), random);
+ }
+ else if (parameterObject["item"].isObject() && parameterObject["item"].isMember("partymode"))
+ {
+ if (g_partyModeManager.IsEnabled())
+ g_partyModeManager.Disable();
+ CServiceBroker::GetAppMessenger()->PostMsg(
+ TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
+ "playercontrol(partymode(" + parameterObject["item"]["partymode"].asString() + "))");
+ return ACK;
+ }
+ else if (parameterObject["item"].isMember("broadcastid"))
+ {
+ const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ CServiceBroker::GetPVRManager().EpgContainer().GetTagByDatabaseId(
+ static_cast<unsigned int>(parameterObject["item"]["broadcastid"].asInteger()));
+
+ if (!epgTag || !epgTag->IsPlayable())
+ return InvalidParams;
+
+ if (!CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayEpgTag(CFileItem(epgTag)))
+ return FailedToExecute;
+
+ return ACK;
+ }
+ else if (parameterObject["item"].isMember("channelid"))
+ {
+ const std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
+ if (!channelGroupContainer)
+ return FailedToExecute;
+
+ const std::shared_ptr<CPVRChannel> channel = channelGroupContainer->GetChannelById(static_cast<int>(parameterObject["item"]["channelid"].asInteger()));
+ if (!channel)
+ return InvalidParams;
+
+ const std::shared_ptr<CPVRChannelGroupMember> groupMember =
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().GetChannelGroupMember(channel);
+ if (!groupMember)
+ return InvalidParams;
+
+ if (!CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayMedia(
+ CFileItem(groupMember)))
+ return FailedToExecute;
+
+ return ACK;
+ }
+ else if (parameterObject["item"].isMember("recordingid"))
+ {
+ const std::shared_ptr<CPVRRecordings> recordingsContainer = CServiceBroker::GetPVRManager().Recordings();
+ if (!recordingsContainer)
+ return FailedToExecute;
+
+ const std::shared_ptr<CPVRRecording> recording = recordingsContainer->GetById(static_cast<int>(parameterObject["item"]["recordingid"].asInteger()));
+ if (!recording)
+ return InvalidParams;
+
+ if (!CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayMedia(CFileItem(recording)))
+ return FailedToExecute;
+
+ return ACK;
+ }
+ else
+ {
+ CFileItemList list;
+ if (FillFileItemList(parameterObject["item"], list) && list.Size() > 0)
+ {
+ bool slideshow = true;
+ for (int index = 0; index < list.Size(); index++)
+ {
+ if (!list[index]->IsPicture())
+ {
+ slideshow = false;
+ break;
+ }
+ }
+
+ if (slideshow)
+ {
+ CGUIWindowSlideShow *slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (!slideshow)
+ return FailedToExecute;
+
+ SendSlideshowAction(ACTION_STOP);
+ slideshow->Reset();
+ for (int index = 0; index < list.Size(); index++)
+ slideshow->Add(list[index].get());
+
+ return StartSlideshow("", false, optionShuffled.isBoolean() && optionShuffled.asBoolean());
+ }
+ else
+ {
+ std::string playername;
+ // Handle the "playerid" option
+ if (!optionPlayer.isNull())
+ {
+ if (optionPlayer.isString())
+ {
+ playername = optionPlayer.asString();
+
+ if (playername != "default")
+ {
+ const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
+
+ // check if the there's actually a player with the given name
+ if (playerCoreFactory.GetPlayerType(playername).empty())
+ return InvalidParams;
+
+ // check if the player can handle at least the first item in the list
+ std::vector<std::string> possiblePlayers;
+ playerCoreFactory.GetPlayers(*list.Get(0).get(), possiblePlayers);
+
+ bool match = false;
+ for (const auto& entry : possiblePlayers)
+ {
+ if (StringUtils::EqualsNoCase(entry, playername))
+ {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ return InvalidParams;
+ }
+ }
+ else
+ return InvalidParams;
+ }
+
+ // Handle "shuffled" option
+ if (optionShuffled.isBoolean())
+ list.SetProperty("shuffled", optionShuffled);
+ // Handle "repeat" option
+ if (!optionRepeat.isNull())
+ list.SetProperty("repeat", static_cast<int>(ParseRepeatState(optionRepeat)));
+ // Handle "resume" option
+ if (list.Size() == 1)
+ {
+ if (optionResume.isBoolean() && optionResume.asBoolean())
+ list[0]->SetStartOffset(STARTOFFSET_RESUME);
+ else if (optionResume.isDouble())
+ list[0]->SetProperty("StartPercent", optionResume);
+ else if (optionResume.isObject())
+ list[0]->SetStartOffset(
+ CUtil::ConvertSecsToMilliSecs(ParseTimeInSeconds(optionResume)));
+ }
+
+ auto l = new CFileItemList(); //don't delete
+ l->Copy(list);
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, -1, -1, static_cast<void*>(l),
+ playername);
+ }
+
+ return ACK;
+ }
+ else
+ return InvalidParams;
+ }
+
+ return InvalidParams;
+}
+
+JSONRPC_STATUS CPlayerOperations::GoTo(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ CVariant to = parameterObject["to"];
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ case Audio:
+ if (to.isString())
+ {
+ std::string strTo = to.asString();
+ int actionID;
+ if (strTo == "previous")
+ actionID = ACTION_PREV_ITEM;
+ else if (strTo == "next")
+ actionID = ACTION_NEXT_ITEM;
+ else
+ return InvalidParams;
+
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
+ static_cast<void*>(new CAction(actionID)));
+ }
+ else if (to.isInteger())
+ {
+ if (IsPVRChannel())
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_INVALID, -1,
+ static_cast<void*>(
+ new CAction(ACTION_CHANNEL_SWITCH, static_cast<float>(to.asInteger()))));
+ else
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_PLAY,
+ static_cast<int>(to.asInteger()));
+ }
+ else
+ return InvalidParams;
+ break;
+
+ case Picture:
+ if (to.isString())
+ {
+ std::string strTo = to.asString();
+ int actionID;
+ if (strTo == "previous")
+ actionID = ACTION_PREV_PICTURE;
+ else if (strTo == "next")
+ actionID = ACTION_NEXT_PICTURE;
+ else
+ return InvalidParams;
+
+ SendSlideshowAction(actionID);
+ }
+ else
+ return FailedToExecute;
+ break;
+
+ case None:
+ default:
+ return FailedToExecute;
+ }
+
+ return ACK;
+}
+
+JSONRPC_STATUS CPlayerOperations::SetShuffle(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ CGUIWindowSlideShow *slideshow = NULL;
+ CVariant shuffle = parameterObject["shuffle"];
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ case Audio:
+ {
+ if (IsPVRChannel())
+ return FailedToExecute;
+
+ PLAYLIST::Id playlistid = GetPlaylist(GetPlayer(parameterObject["playerid"]));
+ if (CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistid))
+ {
+ if ((shuffle.isBoolean() && !shuffle.asBoolean()) ||
+ (shuffle.isString() && shuffle.asString() == "toggle"))
+ {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_SHUFFLE, playlistid, 0);
+ }
+ }
+ else
+ {
+ if ((shuffle.isBoolean() && shuffle.asBoolean()) ||
+ (shuffle.isString() && shuffle.asString() == "toggle"))
+ {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_SHUFFLE, playlistid, 1);
+ }
+ }
+ break;
+ }
+
+ case Picture:
+ slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (slideshow == NULL)
+ return FailedToExecute;
+ if (slideshow->IsShuffled())
+ {
+ if ((shuffle.isBoolean() && !shuffle.asBoolean()) ||
+ (shuffle.isString() && shuffle.asString() == "toggle"))
+ return FailedToExecute;
+ }
+ else
+ {
+ if ((shuffle.isBoolean() && shuffle.asBoolean()) ||
+ (shuffle.isString() && shuffle.asString() == "toggle"))
+ slideshow->Shuffle();
+ }
+ break;
+
+ default:
+ return FailedToExecute;
+ }
+ return ACK;
+}
+
+JSONRPC_STATUS CPlayerOperations::SetRepeat(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ case Audio:
+ {
+ if (IsPVRChannel())
+ return FailedToExecute;
+
+ PLAYLIST::RepeatState repeat = PLAYLIST::RepeatState::NONE;
+ PLAYLIST::Id playlistid = GetPlaylist(GetPlayer(parameterObject["playerid"]));
+ if (parameterObject["repeat"].asString() == "cycle")
+ {
+ PLAYLIST::RepeatState repeatPrev =
+ CServiceBroker::GetPlaylistPlayer().GetRepeat(playlistid);
+ if (repeatPrev == PLAYLIST::RepeatState::NONE)
+ repeat = PLAYLIST::RepeatState::ALL;
+ else if (repeatPrev == PLAYLIST::RepeatState::ALL)
+ repeat = PLAYLIST::RepeatState::ONE;
+ else
+ repeat = PLAYLIST::RepeatState::NONE;
+ }
+ else
+ repeat = ParseRepeatState(parameterObject["repeat"]);
+
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_REPEAT, playlistid,
+ static_cast<int>(repeat));
+ break;
+ }
+
+ case Picture:
+ default:
+ return FailedToExecute;
+ }
+
+ return ACK;
+}
+
+JSONRPC_STATUS CPlayerOperations::SetPartymode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ PlayerType player = GetPlayer(parameterObject["playerid"]);
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ if (IsPVRChannel())
+ return FailedToExecute;
+
+ bool change = false;
+ PartyModeContext context = PARTYMODECONTEXT_UNKNOWN;
+ std::string strContext;
+ if (player == Video)
+ {
+ context = PARTYMODECONTEXT_VIDEO;
+ strContext = "video";
+ }
+ else if (player == Audio)
+ {
+ context = PARTYMODECONTEXT_MUSIC;
+ strContext = "music";
+ }
+
+ bool toggle = parameterObject["partymode"].isString();
+ if (g_partyModeManager.IsEnabled())
+ {
+ if (g_partyModeManager.GetType() != context)
+ return InvalidParams;
+
+ if (toggle || parameterObject["partymode"].asBoolean() == false)
+ change = true;
+ }
+ else
+ {
+ if (toggle || parameterObject["partymode"].asBoolean() == true)
+ change = true;
+ }
+
+ if (change)
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
+ "playercontrol(partymode(" + strContext + "))");
+ break;
+ }
+
+ case Picture:
+ default:
+ return FailedToExecute;
+ }
+
+ return ACK;
+}
+
+JSONRPC_STATUS CPlayerOperations::SetAudioStream(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ int index = -1;
+ if (parameterObject["stream"].isString())
+ {
+ std::string action = parameterObject["stream"].asString();
+ if (action.compare("previous") == 0)
+ {
+ index = appPlayer->GetAudioStream() - 1;
+ if (index < 0)
+ index = appPlayer->GetAudioStreamCount() - 1;
+ }
+ else if (action.compare("next") == 0)
+ {
+ index = appPlayer->GetAudioStream() + 1;
+ if (index >= appPlayer->GetAudioStreamCount())
+ index = 0;
+ }
+ else
+ return InvalidParams;
+ }
+ else if (parameterObject["stream"].isInteger())
+ index = (int)parameterObject["stream"].asInteger();
+
+ if (index < 0 || appPlayer->GetAudioStreamCount() <= index)
+ return InvalidParams;
+
+ appPlayer->SetAudioStream(index);
+ }
+ else
+ return FailedToExecute;
+ break;
+ }
+
+ case Audio:
+ case Picture:
+ default:
+ return FailedToExecute;
+ }
+
+ return ACK;
+}
+
+JSONRPC_STATUS CPlayerOperations::AddSubtitle(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ if (GetPlayer(parameterObject["playerid"]) != Video)
+ return FailedToExecute;
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ if (!appPlayer->HasPlayer())
+ return FailedToExecute;
+
+ if (!parameterObject["subtitle"].isString())
+ return FailedToExecute;
+
+ std::string sub = parameterObject["subtitle"].asString();
+ appPlayer->AddSubtitle(sub);
+ return ACK;
+}
+
+JSONRPC_STATUS CPlayerOperations::SetSubtitle(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ int index = -1;
+ if (parameterObject["subtitle"].isString())
+ {
+ std::string action = parameterObject["subtitle"].asString();
+ if (action.compare("previous") == 0)
+ {
+ index = appPlayer->GetSubtitle() - 1;
+ if (index < 0)
+ index = appPlayer->GetSubtitleCount() - 1;
+ }
+ else if (action.compare("next") == 0)
+ {
+ index = appPlayer->GetSubtitle() + 1;
+ if (index >= appPlayer->GetSubtitleCount())
+ index = 0;
+ }
+ else if (action.compare("off") == 0)
+ {
+ appPlayer->SetSubtitleVisible(false);
+ return ACK;
+ }
+ else if (action.compare("on") == 0)
+ {
+ appPlayer->SetSubtitleVisible(true);
+ return ACK;
+ }
+ else
+ return InvalidParams;
+ }
+ else if (parameterObject["subtitle"].isInteger())
+ index = (int)parameterObject["subtitle"].asInteger();
+
+ if (index < 0 || appPlayer->GetSubtitleCount() <= index)
+ return InvalidParams;
+
+ appPlayer->SetSubtitle(index);
+
+ // Check if we need to enable subtitles to be displayed
+ if (parameterObject["enable"].asBoolean() && !appPlayer->GetSubtitleVisible())
+ appPlayer->SetSubtitleVisible(true);
+ }
+ else
+ return FailedToExecute;
+ break;
+ }
+
+ case Audio:
+ case Picture:
+ default:
+ return FailedToExecute;
+ }
+
+ return ACK;
+}
+
+JSONRPC_STATUS CPlayerOperations::SetVideoStream(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
+{
+ switch (GetPlayer(parameterObject["playerid"]))
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ int streamCount = appPlayer->GetVideoStreamCount();
+ if (streamCount > 0)
+ {
+ int index = appPlayer->GetVideoStream();
+ if (parameterObject["stream"].isString())
+ {
+ std::string action = parameterObject["stream"].asString();
+ if (action.compare("previous") == 0)
+ {
+ index--;
+ if (index < 0)
+ index = streamCount - 1;
+ }
+ else if (action.compare("next") == 0)
+ {
+ index++;
+ if (index >= streamCount)
+ index = 0;
+ }
+ else
+ return InvalidParams;
+ }
+ else if (parameterObject["stream"].isInteger())
+ index = (int)parameterObject["stream"].asInteger();
+
+ if (index < 0 || streamCount <= index)
+ return InvalidParams;
+
+ appPlayer->SetVideoStream(index);
+ }
+ else
+ return FailedToExecute;
+ break;
+ }
+ case Audio:
+ case Picture:
+ default:
+ return FailedToExecute;
+ }
+
+ return ACK;
+}
+
+int CPlayerOperations::GetActivePlayers()
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ int activePlayers = 0;
+ if (appPlayer->IsPlayingVideo() ||
+ CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV() ||
+ CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRecording())
+ activePlayers |= Video;
+ if (appPlayer->IsPlayingAudio() ||
+ CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio())
+ activePlayers |= Audio;
+ if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_SLIDESHOW))
+ activePlayers |= Picture;
+ if (appPlayer->IsExternalPlaying())
+ activePlayers |= External;
+ if (appPlayer->IsRemotePlaying())
+ activePlayers |= Remote;
+
+ return activePlayers;
+}
+
+PlayerType CPlayerOperations::GetPlayer(const CVariant &player)
+{
+ PLAYLIST::Id playerPlaylistId = player.asInteger();
+ PlayerType playerID;
+
+ switch (playerPlaylistId)
+ {
+ case PLAYLIST::TYPE_VIDEO:
+ playerID = Video;
+ break;
+
+ case PLAYLIST::TYPE_MUSIC:
+ playerID = Audio;
+ break;
+
+ case PLAYLIST::TYPE_PICTURE:
+ playerID = Picture;
+ break;
+
+ default:
+ playerID = None;
+ break;
+ }
+
+ if (GetPlaylist(playerID) == playerPlaylistId)
+ return playerID;
+ else
+ return None;
+}
+
+PLAYLIST::Id CPlayerOperations::GetPlaylist(PlayerType player)
+{
+ PLAYLIST::Id playlistId = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
+ if (playlistId == PLAYLIST::TYPE_NONE) // No active playlist, try guessing
+ {
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ playlistId = appPlayer->GetPreferredPlaylist();
+ }
+
+ switch (player)
+ {
+ case Video:
+ return playlistId == PLAYLIST::TYPE_NONE ? PLAYLIST::TYPE_VIDEO : playlistId;
+
+ case Audio:
+ return playlistId == PLAYLIST::TYPE_NONE ? PLAYLIST::TYPE_MUSIC : playlistId;
+
+ case Picture:
+ return PLAYLIST::TYPE_PICTURE;
+
+ default:
+ return playlistId;
+ }
+}
+
+JSONRPC_STATUS CPlayerOperations::StartSlideshow(const std::string& path, bool recursive, bool random, const std::string &firstPicturePath /* = "" */)
+{
+ int flags = 0;
+ if (recursive)
+ flags |= 1;
+ if (random)
+ flags |= 2;
+ else
+ flags |= 4;
+
+ std::vector<std::string> params;
+ params.push_back(path);
+ if (!firstPicturePath.empty())
+ params.push_back(firstPicturePath);
+
+ // Reset screensaver when started from JSON only to avoid potential conflict with slideshow screensavers
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->ResetScreenSaver();
+ appPower->WakeUpScreenSaverAndDPMS();
+ CGUIMessage msg(GUI_MSG_START_SLIDESHOW, 0, 0, flags);
+ msg.SetStringParams(params);
+ CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, WINDOW_SLIDESHOW);
+
+ return ACK;
+}
+
+void CPlayerOperations::SendSlideshowAction(int actionID)
+{
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1,
+ static_cast<void*>(new CAction(actionID)));
+}
+
+JSONRPC_STATUS CPlayerOperations::GetPropertyValue(PlayerType player, const std::string &property, CVariant &result)
+{
+ if (player == None)
+ return FailedToExecute;
+
+ PLAYLIST::Id playlistId = GetPlaylist(player);
+
+ if (property == "type")
+ {
+ switch (player)
+ {
+ case Video:
+ result = "video";
+ break;
+
+ case Audio:
+ result = "audio";
+ break;
+
+ case Picture:
+ result = "picture";
+ break;
+
+ default:
+ return FailedToExecute;
+ }
+ }
+ else if (property == "partymode")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ if (IsPVRChannel())
+ {
+ result = false;
+ break;
+ }
+
+ result = g_partyModeManager.IsEnabled();
+ break;
+
+ case Picture:
+ result = false;
+ break;
+
+ default:
+ return FailedToExecute;
+ }
+ }
+ else if (property == "speed")
+ {
+ CGUIWindowSlideShow *slideshow = NULL;
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ result = appPlayer->IsPausedPlayback() ? 0 : (int)lrint(appPlayer->GetPlaySpeed());
+ break;
+ }
+
+ case Picture:
+ slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (slideshow && slideshow->IsPlaying() && !slideshow->IsPaused())
+ result = slideshow->GetDirection();
+ else
+ result = 0;
+ break;
+
+ default:
+ return FailedToExecute;
+ }
+ }
+ else if (property == "time")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ int ms = 0;
+ if (!IsPVRChannel())
+ ms = (int)(g_application.GetTime() * 1000.0);
+ else
+ {
+ std::shared_ptr<CPVREpgInfoTag> epg(GetCurrentEpg());
+ if (epg)
+ ms = epg->Progress() * 1000;
+ }
+
+ MillisecondsToTimeObject(ms, result);
+ break;
+ }
+
+ case Picture:
+ MillisecondsToTimeObject(0, result);
+ break;
+
+ default:
+ return FailedToExecute;
+ }
+ }
+ else if (property == "percentage")
+ {
+ CGUIWindowSlideShow *slideshow = NULL;
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ if (!IsPVRChannel())
+ result = g_application.GetPercentage();
+ else
+ {
+ std::shared_ptr<CPVREpgInfoTag> epg(GetCurrentEpg());
+ if (epg)
+ result = epg->ProgressPercentage();
+ else
+ result = 0;
+ }
+ break;
+ }
+
+ case Picture:
+ slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (slideshow && slideshow->NumSlides() > 0)
+ result = (double)slideshow->CurrentSlide() / slideshow->NumSlides();
+ else
+ result = 0.0;
+ break;
+
+ default:
+ return FailedToExecute;
+ }
+ }
+ else if (property == "cachepercentage")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ result = g_application.GetCachePercentage();
+ break;
+ }
+
+ case Picture:
+ {
+ result = 0.0;
+ break;
+ }
+
+ default:
+ return FailedToExecute;
+ }
+ }
+ else if (property == "totaltime")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ int ms = 0;
+ if (!IsPVRChannel())
+ ms = (int)(g_application.GetTotalTime() * 1000.0);
+ else
+ {
+ std::shared_ptr<CPVREpgInfoTag> epg(GetCurrentEpg());
+ if (epg)
+ ms = epg->GetDuration() * 1000;
+ }
+
+ MillisecondsToTimeObject(ms, result);
+ break;
+ }
+
+ case Picture:
+ MillisecondsToTimeObject(0, result);
+ break;
+
+ default:
+ return FailedToExecute;
+ }
+ }
+ else if (property == "playlistid")
+ {
+ result = playlistId;
+ }
+ else if (property == "position")
+ {
+ CGUIWindowSlideShow *slideshow = NULL;
+ switch (player)
+ {
+ case Video:
+ case Audio: /* Return the position of current item if there is an active playlist */
+ if (!IsPVRChannel() &&
+ CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == playlistId)
+ {
+ result = CServiceBroker::GetPlaylistPlayer().GetCurrentSong();
+ }
+ else
+ result = -1;
+ break;
+
+ case Picture:
+ slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (slideshow && slideshow->IsPlaying())
+ result = slideshow->CurrentSlide() - 1;
+ else
+ result = -1;
+ break;
+
+ default:
+ result = -1;
+ break;
+ }
+ }
+ else if (property == "repeat")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ if (IsPVRChannel())
+ {
+ result = "off";
+ break;
+ }
+
+ switch (CServiceBroker::GetPlaylistPlayer().GetRepeat(playlistId))
+ {
+ case PLAYLIST::RepeatState::ONE:
+ result = "one";
+ break;
+ case PLAYLIST::RepeatState::ALL:
+ result = "all";
+ break;
+ default:
+ result = "off";
+ break;
+ }
+ break;
+
+ case Picture:
+ default:
+ result = "off";
+ break;
+ }
+ }
+ else if (property == "shuffled")
+ {
+ CGUIWindowSlideShow *slideshow = NULL;
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ if (IsPVRChannel())
+ {
+ result = false;
+ break;
+ }
+
+ result = CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId);
+ break;
+
+ case Picture:
+ slideshow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (slideshow && slideshow->IsPlaying())
+ result = slideshow->IsShuffled();
+ else
+ result = -1;
+ break;
+
+ default:
+ result = -1;
+ break;
+ }
+ }
+ else if (property == "canseek")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ result = appPlayer->CanSeek();
+ break;
+ }
+
+ case Picture:
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "canchangespeed")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ result = !IsPVRChannel();
+ break;
+
+ case Picture:
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "canmove")
+ {
+ switch (player)
+ {
+ case Picture:
+ result = true;
+ break;
+
+ case Video:
+ case Audio:
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "canzoom")
+ {
+ switch (player)
+ {
+ case Picture:
+ result = true;
+ break;
+
+ case Video:
+ case Audio:
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "canrotate")
+ {
+ switch (player)
+ {
+ case Picture:
+ result = true;
+ break;
+
+ case Video:
+ case Audio:
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "canshuffle")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ case Picture:
+ result = !IsPVRChannel();
+ break;
+
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "canrepeat")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ result = !IsPVRChannel();
+ break;
+
+ case Picture:
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "currentaudiostream")
+ {
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ result = CVariant(CVariant::VariantTypeObject);
+ int index = appPlayer->GetAudioStream();
+ if (index >= 0)
+ {
+ AudioStreamInfo info;
+ appPlayer->GetAudioStreamInfo(index, info);
+
+ result["index"] = index;
+ result["name"] = info.name;
+ result["language"] = info.language;
+ result["codec"] = info.codecName;
+ result["bitrate"] = info.bitrate;
+ result["channels"] = info.channels;
+ result["samplerate"] = info.samplerate;
+ AppendAudioStreamFlagsAsBooleans(result, info.flags);
+ }
+ }
+ else
+ result = CVariant(CVariant::VariantTypeNull);
+ break;
+ }
+
+ case Picture:
+ default:
+ result = CVariant(CVariant::VariantTypeNull);
+ break;
+ }
+ }
+ else if (property == "audiostreams")
+ {
+ result = CVariant(CVariant::VariantTypeArray);
+ switch (player)
+ {
+ case Video:
+ case Audio:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ for (int index = 0; index < appPlayer->GetAudioStreamCount(); index++)
+ {
+ AudioStreamInfo info;
+ appPlayer->GetAudioStreamInfo(index, info);
+
+ CVariant audioStream(CVariant::VariantTypeObject);
+ audioStream["index"] = index;
+ audioStream["name"] = info.name;
+ audioStream["language"] = info.language;
+ audioStream["codec"] = info.codecName;
+ audioStream["bitrate"] = info.bitrate;
+ audioStream["channels"] = info.channels;
+ audioStream["samplerate"] = info.samplerate;
+ AppendAudioStreamFlagsAsBooleans(audioStream, info.flags);
+
+ result.append(audioStream);
+ }
+ }
+ break;
+ }
+
+ case Picture:
+ default:
+ break;
+ }
+ }
+ else if (property == "currentvideostream")
+ {
+ switch (player)
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ int index = appPlayer->GetVideoStream();
+ if (index >= 0)
+ {
+ result = CVariant(CVariant::VariantTypeObject);
+ VideoStreamInfo info;
+ appPlayer->GetVideoStreamInfo(index, info);
+
+ result["index"] = index;
+ result["name"] = info.name;
+ result["language"] = info.language;
+ result["codec"] = info.codecName;
+ result["width"] = info.width;
+ result["height"] = info.height;
+ }
+ else
+ result = CVariant(CVariant::VariantTypeNull);
+ break;
+ }
+ case Audio:
+ case Picture:
+ default:
+ result = CVariant(CVariant::VariantTypeNull);
+ break;
+ }
+ }
+ else if (property == "videostreams")
+ {
+ result = CVariant(CVariant::VariantTypeArray);
+ switch (player)
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ int streamCount = appPlayer->GetVideoStreamCount();
+ if (streamCount >= 0)
+ {
+ for (int index = 0; index < streamCount; ++index)
+ {
+ VideoStreamInfo info;
+ appPlayer->GetVideoStreamInfo(index, info);
+
+ CVariant videoStream(CVariant::VariantTypeObject);
+ videoStream["index"] = index;
+ videoStream["name"] = info.name;
+ videoStream["language"] = info.language;
+ videoStream["codec"] = info.codecName;
+ videoStream["width"] = info.width;
+ videoStream["height"] = info.height;
+
+ result.append(videoStream);
+ }
+ }
+ break;
+ }
+ case Audio:
+ case Picture:
+ default:
+ break;
+ }
+ }
+ else if (property == "subtitleenabled")
+ {
+ switch (player)
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ result = appPlayer->GetSubtitleVisible();
+ break;
+ }
+
+ case Audio:
+ case Picture:
+ default:
+ result = false;
+ break;
+ }
+ }
+ else if (property == "currentsubtitle")
+ {
+ switch (player)
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ result = CVariant(CVariant::VariantTypeObject);
+ int index = appPlayer->GetSubtitle();
+ if (index >= 0)
+ {
+ SubtitleStreamInfo info;
+ appPlayer->GetSubtitleStreamInfo(index, info);
+
+ result["index"] = index;
+ result["name"] = info.name;
+ result["language"] = info.language;
+ AppendSubtitleStreamFlagsAsBooleans(result, info.flags);
+ }
+ }
+ else
+ result = CVariant(CVariant::VariantTypeNull);
+ break;
+ }
+
+ case Audio:
+ case Picture:
+ default:
+ result = CVariant(CVariant::VariantTypeNull);
+ break;
+ }
+ }
+ else if (property == "subtitles")
+ {
+ result = CVariant(CVariant::VariantTypeArray);
+ switch (player)
+ {
+ case Video:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->HasPlayer())
+ {
+ for (int index = 0; index < appPlayer->GetSubtitleCount(); index++)
+ {
+ SubtitleStreamInfo info;
+ appPlayer->GetSubtitleStreamInfo(index, info);
+
+ CVariant subtitle(CVariant::VariantTypeObject);
+ subtitle["index"] = index;
+ subtitle["name"] = info.name;
+ subtitle["language"] = info.language;
+ AppendSubtitleStreamFlagsAsBooleans(subtitle, info.flags);
+
+ result.append(subtitle);
+ }
+ }
+ break;
+ }
+
+ case Audio:
+ case Picture:
+ default:
+ break;
+ }
+ }
+ else if (property == "live")
+ result = IsPVRChannel();
+ else
+ return InvalidParams;
+
+ return OK;
+}
+
+PLAYLIST::RepeatState CPlayerOperations::ParseRepeatState(const CVariant& repeat)
+{
+ PLAYLIST::RepeatState state = PLAYLIST::RepeatState::NONE;
+ std::string strState = repeat.asString();
+
+ if (strState.compare("one") == 0)
+ state = PLAYLIST::RepeatState::ONE;
+ else if (strState.compare("all") == 0)
+ state = PLAYLIST::RepeatState::ALL;
+
+ return state;
+}
+
+double CPlayerOperations::ParseTimeInSeconds(const CVariant &time)
+{
+ double seconds = 0.0;
+ if (time.isMember("hours"))
+ seconds += time["hours"].asInteger() * 60 * 60;
+ if (time.isMember("minutes"))
+ seconds += time["minutes"].asInteger() * 60;
+ if (time.isMember("seconds"))
+ seconds += time["seconds"].asInteger();
+ if (time.isMember("milliseconds"))
+ seconds += time["milliseconds"].asDouble() / 1000.0;
+
+ return seconds;
+}
+
+bool CPlayerOperations::IsPVRChannel()
+{
+ const std::shared_ptr<CPVRPlaybackState> state = CServiceBroker::GetPVRManager().PlaybackState();
+ return state->IsPlayingTV() || state->IsPlayingRadio();
+}
+
+std::shared_ptr<CPVREpgInfoTag> CPlayerOperations::GetCurrentEpg()
+{
+ const std::shared_ptr<CPVRPlaybackState> state = CServiceBroker::GetPVRManager().PlaybackState();
+ if (!state->IsPlayingTV() && !state->IsPlayingRadio())
+ return {};
+
+ const std::shared_ptr<CPVRChannel> currentChannel = state->GetPlayingChannel();
+ if (!currentChannel)
+ return {};
+
+ return currentChannel->GetEPGNow();
+}