summaryrefslogtreecommitdiffstats
path: root/xbmc/network/upnp/UPnPRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/network/upnp/UPnPRenderer.cpp')
-rw-r--r--xbmc/network/upnp/UPnPRenderer.cpp758
1 files changed, 758 insertions, 0 deletions
diff --git a/xbmc/network/upnp/UPnPRenderer.cpp b/xbmc/network/upnp/UPnPRenderer.cpp
new file mode 100644
index 0000000..d296471
--- /dev/null
+++ b/xbmc/network/upnp/UPnPRenderer.cpp
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2012-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 "UPnPRenderer.h"
+
+#include "FileItem.h"
+#include "GUIInfoManager.h"
+#include "GUIUserMessages.h"
+#include "PlayListPlayer.h"
+#include "ServiceBroker.h"
+#include "TextureDatabase.h"
+#include "ThumbLoader.h"
+#include "UPnP.h"
+#include "UPnPInternal.h"
+#include "URL.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "application/ApplicationVolumeHandling.h"
+#include "filesystem/SpecialProtocol.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/guiinfo/GUIInfoLabels.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "interfaces/AnnouncementManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "network/Network.h"
+#include "pictures/GUIWindowSlideShow.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "xbmc/interfaces/AnnouncementManager.h"
+
+#include <inttypes.h>
+#include <mutex>
+
+#include <Platinum/Source/Platinum/Platinum.h>
+
+NPT_SET_LOCAL_LOGGER("xbmc.upnp.renderer")
+
+namespace UPNP
+{
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::CUPnPRenderer
++---------------------------------------------------------------------*/
+CUPnPRenderer::CUPnPRenderer(const char* friendly_name, bool show_ip /*= false*/,
+ const char* uuid /*= NULL*/, unsigned int port /*= 0*/)
+ : PLT_MediaRenderer(friendly_name, show_ip, uuid, port)
+{
+ CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::~CUPnPRenderer
++---------------------------------------------------------------------*/
+CUPnPRenderer::~CUPnPRenderer()
+{
+ CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::SetupServices
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::SetupServices()
+{
+ NPT_CHECK(PLT_MediaRenderer::SetupServices());
+
+ // update what we can play
+ PLT_Service* service = NULL;
+ NPT_CHECK_FATAL(FindServiceByType("urn:schemas-upnp-org:service:ConnectionManager:1", service));
+ service->SetStateVariable("SinkProtocolInfo"
+ ,"http-get:*:*:*"
+ ",xbmc-get:*:*:*"
+ ",http-get:*:audio/mkv:*"
+ ",http-get:*:audio/mpegurl:*"
+ ",http-get:*:audio/mpeg:*"
+ ",http-get:*:audio/mpeg3:*"
+ ",http-get:*:audio/mp3:*"
+ ",http-get:*:audio/mp4:*"
+ ",http-get:*:audio/basic:*"
+ ",http-get:*:audio/midi:*"
+ ",http-get:*:audio/ulaw:*"
+ ",http-get:*:audio/ogg:*"
+ ",http-get:*:audio/DVI4:*"
+ ",http-get:*:audio/G722:*"
+ ",http-get:*:audio/G723:*"
+ ",http-get:*:audio/G726-16:*"
+ ",http-get:*:audio/G726-24:*"
+ ",http-get:*:audio/G726-32:*"
+ ",http-get:*:audio/G726-40:*"
+ ",http-get:*:audio/G728:*"
+ ",http-get:*:audio/G729:*"
+ ",http-get:*:audio/G729D:*"
+ ",http-get:*:audio/G729E:*"
+ ",http-get:*:audio/GSM:*"
+ ",http-get:*:audio/GSM-EFR:*"
+ ",http-get:*:audio/L8:*"
+ ",http-get:*:audio/L16:*"
+ ",http-get:*:audio/LPC:*"
+ ",http-get:*:audio/MPA:*"
+ ",http-get:*:audio/PCMA:*"
+ ",http-get:*:audio/PCMU:*"
+ ",http-get:*:audio/QCELP:*"
+ ",http-get:*:audio/RED:*"
+ ",http-get:*:audio/VDVI:*"
+ ",http-get:*:audio/ac3:*"
+ ",http-get:*:audio/webm:*"
+ ",http-get:*:audio/vorbis:*"
+ ",http-get:*:audio/speex:*"
+ ",http-get:*:audio/flac:*"
+ ",http-get:*:audio/x-flac:*"
+ ",http-get:*:audio/x-aiff:*"
+ ",http-get:*:audio/x-pn-realaudio:*"
+ ",http-get:*:audio/x-realaudio:*"
+ ",http-get:*:audio/x-wav:*"
+ ",http-get:*:audio/x-matroska:*"
+ ",http-get:*:audio/x-ms-wma:*"
+ ",http-get:*:audio/x-mpegurl:*"
+ ",http-get:*:application/x-shockwave-flash:*"
+ ",http-get:*:application/ogg:*"
+ ",http-get:*:application/sdp:*"
+ ",http-get:*:image/gif:*"
+ ",http-get:*:image/jpeg:*"
+ ",http-get:*:image/ief:*"
+ ",http-get:*:image/png:*"
+ ",http-get:*:image/tiff:*"
+ ",http-get:*:image/webp:*"
+ ",http-get:*:video/avi:*"
+ ",http-get:*:video/divx:*"
+ ",http-get:*:video/mpeg:*"
+ ",http-get:*:video/fli:*"
+ ",http-get:*:video/flv:*"
+ ",http-get:*:video/quicktime:*"
+ ",http-get:*:video/vnd.vivo:*"
+ ",http-get:*:video/vc1:*"
+ ",http-get:*:video/ogg:*"
+ ",http-get:*:video/mp4:*"
+ ",http-get:*:video/mkv:*"
+ ",http-get:*:video/BT656:*"
+ ",http-get:*:video/CelB:*"
+ ",http-get:*:video/JPEG:*"
+ ",http-get:*:video/H261:*"
+ ",http-get:*:video/H263:*"
+ ",http-get:*:video/H263-1998:*"
+ ",http-get:*:video/H263-2000:*"
+ ",http-get:*:video/MPV:*"
+ ",http-get:*:video/MP2T:*"
+ ",http-get:*:video/MP1S:*"
+ ",http-get:*:video/MP2P:*"
+ ",http-get:*:video/BMPEG:*"
+ ",http-get:*:video/webm:*"
+ ",http-get:*:video/xvid:*"
+ ",http-get:*:video/x-divx:*"
+ ",http-get:*:video/x-matroska:*"
+ ",http-get:*:video/x-mkv:*"
+ ",http-get:*:video/x-ms-wmv:*"
+ ",http-get:*:video/x-ms-avi:*"
+ ",http-get:*:video/x-flv:*"
+ ",http-get:*:video/x-fli:*"
+ ",http-get:*:video/x-ms-asf:*"
+ ",http-get:*:video/x-ms-asx:*"
+ ",http-get:*:video/x-ms-wmx:*"
+ ",http-get:*:video/x-ms-wvx:*"
+ ",http-get:*:video/x-msvideo:*"
+ ",http-get:*:video/x-xvid:*"
+ );
+
+ NPT_CHECK_FATAL(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+ service->SetStateVariable("NextAVTransportURI", "");
+ service->SetStateVariable("NextAVTransportURIMetadata", "");
+
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::ProcessHttpRequest
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::ProcessHttpGetRequest(NPT_HttpRequest& request,
+ const NPT_HttpRequestContext& context,
+ NPT_HttpResponse& response)
+{
+ // get the address of who sent us some data back
+ NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
+ NPT_String method = request.GetMethod();
+ NPT_String protocol = request.GetProtocol();
+ NPT_HttpUrl url = request.GetUrl();
+
+ if (url.GetPath() == "/thumb") {
+ NPT_HttpUrlQuery query(url.GetQuery());
+ NPT_String filepath = query.GetField("path");
+ if (!filepath.IsEmpty()) {
+ NPT_HttpEntity* entity = response.GetEntity();
+ if (entity == NULL) return NPT_ERROR_INVALID_STATE;
+
+ // check the method
+ if (request.GetMethod() != NPT_HTTP_METHOD_GET &&
+ request.GetMethod() != NPT_HTTP_METHOD_HEAD) {
+ response.SetStatus(405, "Method Not Allowed");
+ return NPT_SUCCESS;
+ }
+
+ // prevent hackers from accessing files outside of our root
+ if ((filepath.Find("/..") >= 0) || (filepath.Find("\\..") >=0)) {
+ return NPT_FAILURE;
+ }
+
+ // open the file
+ std::string path (CURL::Decode((const char*) filepath));
+ NPT_File file(path.c_str());
+ NPT_Result result = file.Open(NPT_FILE_OPEN_MODE_READ);
+ if (NPT_FAILED(result)) {
+ response.SetStatus(404, "Not Found");
+ return NPT_SUCCESS;
+ }
+ NPT_InputStreamReference stream;
+ file.GetInputStream(stream);
+ entity->SetContentType(GetMimeType(filepath));
+ entity->SetInputStream(stream, true);
+
+ return NPT_SUCCESS;
+ }
+ }
+
+ return PLT_MediaRenderer::ProcessHttpGetRequest(request, context, response);
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::Announce
++---------------------------------------------------------------------*/
+void CUPnPRenderer::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
+ const std::string& sender,
+ const std::string& message,
+ const CVariant& data)
+{
+ if (sender != ANNOUNCEMENT::CAnnouncementManager::ANNOUNCEMENT_SENDER)
+ return;
+
+ NPT_AutoLock lock(m_state);
+ PLT_Service *avt, *rct;
+
+ if (flag == ANNOUNCEMENT::Player)
+ {
+ if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))
+ return;
+
+ if (message == "OnPlay" || message == "OnResume")
+ {
+ avt->SetStateVariable("AVTransportURI", g_application.CurrentFile().c_str());
+ avt->SetStateVariable("CurrentTrackURI", g_application.CurrentFile().c_str());
+
+ NPT_String meta;
+ if (NPT_SUCCEEDED(GetMetadata(meta)))
+ {
+ avt->SetStateVariable("CurrentTrackMetadata", meta);
+ avt->SetStateVariable("AVTransportURIMetaData", meta);
+ }
+
+ avt->SetStateVariable("TransportPlaySpeed",
+ NPT_String::FromInteger(data["player"]["speed"].asInteger()));
+ avt->SetStateVariable("TransportState", "PLAYING");
+
+ /* this could be a transition to next track, so clear next */
+ avt->SetStateVariable("NextAVTransportURI", "");
+ avt->SetStateVariable("NextAVTransportURIMetaData", "");
+ }
+ else if (message == "OnPause")
+ {
+ int64_t speed = data["player"]["speed"].asInteger();
+ avt->SetStateVariable("TransportPlaySpeed", NPT_String::FromInteger(speed != 0 ? speed : 1));
+ avt->SetStateVariable("TransportState", "PAUSED_PLAYBACK");
+ }
+ else if (message == "OnSpeedChanged")
+ {
+ avt->SetStateVariable("TransportPlaySpeed",
+ NPT_String::FromInteger(data["player"]["speed"].asInteger()));
+ }
+ }
+ else if (flag == ANNOUNCEMENT::Application && message == "OnVolumeChanged")
+ {
+ if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:RenderingControl:1", rct)))
+ return;
+
+ std::string buffer;
+
+ buffer = std::to_string(data["volume"].asInteger());
+ rct->SetStateVariable("Volume", buffer.c_str());
+
+ buffer = std::to_string(256 * (data["volume"].asInteger() * 60 - 60) / 100);
+ rct->SetStateVariable("VolumeDb", buffer.c_str());
+
+ rct->SetStateVariable("Mute", data["muted"].asBoolean() ? "1" : "0");
+ }
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::UpdateState
++---------------------------------------------------------------------*/
+void
+CUPnPRenderer::UpdateState()
+{
+ NPT_AutoLock lock(m_state);
+
+ PLT_Service *avt;
+
+ if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))
+ return;
+
+ /* don't update state while transitioning */
+ NPT_String state;
+ avt->GetStateVariableValue("TransportState", state);
+ if(state == "TRANSITIONING")
+ return;
+
+ avt->SetStateVariable("TransportStatus", "OK");
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->IsPlaying() || appPlayer->IsPausedPlayback())
+ {
+ avt->SetStateVariable("NumberOfTracks", "1");
+ avt->SetStateVariable("CurrentTrack", "1");
+
+ // get elapsed time
+ std::string buffer = StringUtils::SecondsToTimeString(std::lrint(g_application.GetTime()),
+ TIME_FORMAT_HH_MM_SS);
+ avt->SetStateVariable("RelativeTimePosition", buffer.c_str());
+ avt->SetStateVariable("AbsoluteTimePosition", buffer.c_str());
+
+ // get duration
+ buffer = StringUtils::SecondsToTimeString(std::lrint(g_application.GetTotalTime()),
+ TIME_FORMAT_HH_MM_SS);
+ if (buffer.length() > 0)
+ {
+ avt->SetStateVariable("CurrentTrackDuration", buffer.c_str());
+ avt->SetStateVariable("CurrentMediaDuration", buffer.c_str());
+ }
+ else
+ {
+ avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
+ avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
+ }
+ }
+ else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW)
+ {
+ avt->SetStateVariable("TransportState", "PLAYING");
+
+ const std::string filePath = CServiceBroker::GetGUI()->GetInfoManager().GetLabel(
+ SLIDESHOW_FILE_PATH, INFO::DEFAULT_CONTEXT);
+ avt->SetStateVariable("AVTransportURI", filePath.c_str());
+ avt->SetStateVariable("CurrentTrackURI", filePath.c_str());
+ avt->SetStateVariable("TransportPlaySpeed", "1");
+
+ CGUIWindowSlideShow* slideshow =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(
+ WINDOW_SLIDESHOW);
+ if (slideshow)
+ {
+ std::string index;
+ index = std::to_string(slideshow->NumSlides());
+ avt->SetStateVariable("NumberOfTracks", index.c_str());
+ index = std::to_string(slideshow->CurrentSlide());
+ avt->SetStateVariable("CurrentTrack", index.c_str());
+ }
+
+ avt->SetStateVariable("CurrentTrackMetadata", "");
+ avt->SetStateVariable("AVTransportURIMetaData", "");
+ }
+ else
+ {
+ avt->SetStateVariable("TransportState", "STOPPED");
+ avt->SetStateVariable("TransportPlaySpeed", "1");
+ avt->SetStateVariable("NumberOfTracks", "0");
+ avt->SetStateVariable("CurrentTrack", "0");
+ avt->SetStateVariable("RelativeTimePosition", "00:00:00");
+ avt->SetStateVariable("AbsoluteTimePosition", "00:00:00");
+ avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
+ avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
+ avt->SetStateVariable("NextAVTransportURI", "");
+ avt->SetStateVariable("NextAVTransportURIMetaData", "");
+ }
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::SetupIcons
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::SetupIcons()
+{
+ NPT_String file_root = CSpecialProtocol::TranslatePath("special://xbmc/media/").c_str();
+ AddIcon(
+ PLT_DeviceIcon("image/png", 256, 256, 8, "/icon256x256.png"),
+ file_root);
+ AddIcon(
+ PLT_DeviceIcon("image/png", 120, 120, 8, "/icon120x120.png"),
+ file_root);
+ AddIcon(
+ PLT_DeviceIcon("image/png", 48, 48, 8, "/icon48x48.png"),
+ file_root);
+ AddIcon(
+ PLT_DeviceIcon("image/png", 32, 32, 8, "/icon32x32.png"),
+ file_root);
+ AddIcon(
+ PLT_DeviceIcon("image/png", 16, 16, 8, "/icon16x16.png"),
+ file_root);
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::GetMetadata
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::GetMetadata(NPT_String& meta)
+{
+ NPT_Result res = NPT_FAILURE;
+ CFileItem item(g_application.CurrentFileItem());
+ NPT_String file_path, tmp;
+
+ // we pass an empty CThumbLoader reference, as it can't be used
+ // without CUPnPServer enabled
+ NPT_Reference<CThumbLoader> thumb_loader;
+ PLT_MediaObject* object = BuildObject(item, file_path, false, thumb_loader, NULL, NULL, UPnPRenderer);
+ if (object) {
+ // fetch the item's artwork
+ std::string thumb;
+ if (object->m_ObjectClass.type == "object.item.audioItem.musicTrack")
+ thumb = CServiceBroker::GetGUI()->GetInfoManager().GetImage(MUSICPLAYER_COVER, -1);
+ else
+ thumb = CServiceBroker::GetGUI()->GetInfoManager().GetImage(VIDEOPLAYER_COVER, -1);
+
+ thumb = CTextureUtils::GetWrappedImageURL(thumb);
+
+ NPT_String ip;
+ if (CServiceBroker::GetNetwork().GetFirstConnectedInterface()) {
+ ip = CServiceBroker::GetNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
+ }
+ // build url, use the internal device http server to serv the image
+ NPT_HttpUrlQuery query;
+ query.AddField("path", thumb.c_str());
+ PLT_AlbumArtInfo art;
+ art.uri = NPT_HttpUrl(
+ ip,
+ m_URLDescription.GetPort(),
+ "/thumb",
+ query.ToString()).ToString();
+ // Set DLNA profileID by extension, defaulting to JPEG.
+ if (URIUtils::HasExtension(item.GetArt("thumb"), ".png")) {
+ art.dlna_profile = "PNG_TN";
+ } else {
+ art.dlna_profile = "JPEG_TN";
+ }
+ object->m_ExtraInfo.album_arts.Add(art);
+
+ res = PLT_Didl::ToDidl(*object, "*", tmp);
+ meta = didl_header + tmp + didl_footer;
+ delete object;
+ }
+ return res;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnNext
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnNext(PLT_ActionReference& action)
+{
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW) {
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1,
+ static_cast<void*>(new CAction(ACTION_NEXT_PICTURE)));
+ } else {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_NEXT);
+ }
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnPause
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnPause(PLT_ActionReference& action)
+{
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW) {
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1,
+ static_cast<void*>(new CAction(ACTION_NEXT_PICTURE)));
+ }
+ else
+ {
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!appPlayer->IsPausedPlayback())
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_PAUSE);
+ }
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnPlay
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnPlay(PLT_ActionReference& action)
+{
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW)
+ return NPT_SUCCESS;
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->IsPausedPlayback())
+ {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_PAUSE);
+ }
+ else if (appPlayer && !appPlayer->IsPlaying())
+ {
+ NPT_String uri, meta;
+ PLT_Service* service;
+ // look for value set previously by SetAVTransportURI
+ NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+ NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURI", uri));
+ NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURIMetaData", meta));
+
+ // if not set, use the current file being played
+ PlayMedia(uri, meta);
+ }
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnPrevious
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnPrevious(PLT_ActionReference& action)
+{
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW) {
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1,
+ static_cast<void*>(new CAction(ACTION_PREV_PICTURE)));
+ } else {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_PREV);
+ }
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnStop
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnStop(PLT_ActionReference& action)
+{
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW) {
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1,
+ static_cast<void*>(new CAction(ACTION_NEXT_PICTURE)));
+ } else {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_STOP);
+ }
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnSetAVTransportURI
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSetAVTransportURI(PLT_ActionReference& action)
+{
+ NPT_String uri, meta;
+ PLT_Service* service;
+ NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+
+ NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURI", uri));
+ NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURIMetaData", meta));
+
+ // if not playing already, just keep around uri & metadata
+ // and wait for play command
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!appPlayer->IsPlaying() &&
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_SLIDESHOW)
+ {
+ service->SetStateVariable("TransportState", "STOPPED");
+ service->SetStateVariable("TransportStatus", "OK");
+ service->SetStateVariable("TransportPlaySpeed", "1");
+ service->SetStateVariable("AVTransportURI", uri);
+ service->SetStateVariable("AVTransportURIMetaData", meta);
+ service->SetStateVariable("NextAVTransportURI", "");
+ service->SetStateVariable("NextAVTransportURIMetaData", "");
+
+ NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
+ return NPT_SUCCESS;
+ }
+
+ return PlayMedia(uri, meta, action.AsPointer());
+}
+
+/*----------------------------------------------------------------------
+ | CUPnPRenderer::OnSetAVTransportURI
+ +---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSetNextAVTransportURI(PLT_ActionReference& action)
+{
+ NPT_String uri, meta;
+ PLT_Service* service;
+ NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+
+ NPT_CHECK_SEVERE(action->GetArgumentValue("NextURI", uri));
+ NPT_CHECK_SEVERE(action->GetArgumentValue("NextURIMetaData", meta));
+
+ CFileItemPtr item = GetFileItem(uri, meta);
+ if (!item) {
+ return NPT_FAILURE;
+ }
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->IsPlaying())
+ {
+
+ PLAYLIST::Id playlistId = PLAYLIST::TYPE_MUSIC;
+ if (item->IsVideo())
+ playlistId = PLAYLIST::TYPE_VIDEO;
+
+ {
+ std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
+ CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
+ CServiceBroker::GetPlaylistPlayer().Add(playlistId, item);
+
+ CServiceBroker::GetPlaylistPlayer().SetCurrentSong(-1);
+ CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
+ }
+
+ CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+
+
+ service->SetStateVariable("NextAVTransportURI", uri);
+ service->SetStateVariable("NextAVTransportURIMetaData", meta);
+
+ NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
+
+ return NPT_SUCCESS;
+ }
+ else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW)
+ {
+ return NPT_FAILURE;
+ }
+ else
+ {
+ return NPT_FAILURE;
+ }
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::PlayMedia
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::PlayMedia(const NPT_String& uri, const NPT_String& meta, PLT_Action* action)
+{
+ PLT_Service* service;
+ NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+
+ { NPT_AutoLock lock(m_state);
+ service->SetStateVariable("TransportState", "TRANSITIONING");
+ service->SetStateVariable("TransportStatus", "OK");
+ }
+
+ CFileItemPtr item = GetFileItem(uri, meta);
+ if (!item) {
+ return NPT_FAILURE;
+ }
+
+ if (item->IsPicture()) {
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_PICTURE_SHOW, -1, -1, nullptr,
+ item->GetPath());
+ } else {
+ CFileItemList *l = new CFileItemList; //don't delete,
+ l->Add(std::make_shared<CFileItem>(*item));
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, -1, -1, static_cast<void*>(l));
+ }
+
+ // just return success because the play actions are asynchronous
+ NPT_AutoLock lock(m_state);
+ service->SetStateVariable("TransportState", "PLAYING");
+ service->SetStateVariable("TransportStatus", "OK");
+ service->SetStateVariable("AVTransportURI", uri);
+ service->SetStateVariable("AVTransportURIMetaData", meta);
+
+ service->SetStateVariable("NextAVTransportURI", "");
+ service->SetStateVariable("NextAVTransportURIMetaData", "");
+
+ if (action) {
+ NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
+ }
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnSetVolume
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSetVolume(PLT_ActionReference& action)
+{
+ NPT_String volume;
+ NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredVolume", volume));
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
+ appVolume->SetVolume((float)strtod((const char*)volume, NULL));
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnSetMute
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSetMute(PLT_ActionReference& action)
+{
+ NPT_String mute;
+ NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredMute",mute));
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
+ if ((mute == "1") ^ appVolume->IsMuted())
+ appVolume->ToggleMute();
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CUPnPRenderer::OnSeek
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSeek(PLT_ActionReference& action)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (!appPlayer->IsPlaying())
+ return NPT_ERROR_INVALID_STATE;
+
+ NPT_String unit, target;
+ NPT_CHECK_SEVERE(action->GetArgumentValue("Unit", unit));
+ NPT_CHECK_SEVERE(action->GetArgumentValue("Target", target));
+
+ if (!unit.Compare("REL_TIME"))
+ {
+ // converts target to seconds
+ NPT_UInt32 seconds;
+ NPT_CHECK_SEVERE(PLT_Didl::ParseTimeStamp(target, seconds));
+ g_application.SeekTime(seconds);
+ }
+
+ return NPT_SUCCESS;
+}
+
+} /* namespace UPNP */
+