Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / network / upnp / UPnPRenderer.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 #include "network/Network.h"
21 #include "UPnPRenderer.h"
22 #include "UPnP.h"
23 #include "UPnPInternal.h"
24 #include "Platinum.h"
25 #include "Application.h"
26 #include "ApplicationMessenger.h"
27 #include "FileItem.h"
28 #include "filesystem/SpecialProtocol.h"
29 #include "GUIInfoManager.h"
30 #include "guilib/GUIWindowManager.h"
31 #include "guilib/Key.h"
32 #include "pictures/GUIWindowSlideShow.h"
33 #include "pictures/PictureInfoTag.h"
34 #include "interfaces/AnnouncementManager.h"
35 #include "settings/Settings.h"
36 #include "TextureDatabase.h"
37 #include "ThumbLoader.h"
38 #include "URL.h"
39 #include "utils/URIUtils.h"
40 #include "utils/Variant.h"
41 #include "utils/StringUtils.h"
42 #include "playlists/PlayList.h"
43 #include "GUIUserMessages.h"
44
45 using namespace ANNOUNCEMENT;
46
47 namespace UPNP
48 {
49
50 /*----------------------------------------------------------------------
51 |   CUPnPRenderer::CUPnPRenderer
52 +---------------------------------------------------------------------*/
53 CUPnPRenderer::CUPnPRenderer(const char* friendly_name, bool show_ip /*= false*/,
54                              const char* uuid /*= NULL*/, unsigned int port /*= 0*/)
55     : PLT_MediaRenderer(friendly_name, show_ip, uuid, port)
56 {
57     CAnnouncementManager::AddAnnouncer(this);
58 }
59
60 /*----------------------------------------------------------------------
61 |   CUPnPRenderer::~CUPnPRenderer
62 +---------------------------------------------------------------------*/
63 CUPnPRenderer::~CUPnPRenderer()
64 {
65     CAnnouncementManager::RemoveAnnouncer(this);
66 }
67
68 /*----------------------------------------------------------------------
69 |   CUPnPRenderer::SetupServices
70 +---------------------------------------------------------------------*/
71 NPT_Result
72 CUPnPRenderer::SetupServices()
73 {
74     NPT_CHECK(PLT_MediaRenderer::SetupServices());
75
76     // update what we can play
77     PLT_Service* service = NULL;
78     NPT_CHECK_FATAL(FindServiceByType("urn:schemas-upnp-org:service:ConnectionManager:1", service));
79     service->SetStateVariable("SinkProtocolInfo"
80         ,"http-get:*:*:*"
81         ",xbmc-get:*:*:*"
82         ",http-get:*:audio/mkv:*"
83         ",http-get:*:audio/mpegurl:*"
84         ",http-get:*:audio/mpeg:*"
85         ",http-get:*:audio/mpeg3:*"
86         ",http-get:*:audio/mp3:*"
87         ",http-get:*:audio/mp4:*"
88         ",http-get:*:audio/basic:*"
89         ",http-get:*:audio/midi:*"
90         ",http-get:*:audio/ulaw:*"
91         ",http-get:*:audio/ogg:*"
92         ",http-get:*:audio/DVI4:*"
93         ",http-get:*:audio/G722:*"
94         ",http-get:*:audio/G723:*"
95         ",http-get:*:audio/G726-16:*"
96         ",http-get:*:audio/G726-24:*"
97         ",http-get:*:audio/G726-32:*"
98         ",http-get:*:audio/G726-40:*"
99         ",http-get:*:audio/G728:*"
100         ",http-get:*:audio/G729:*"
101         ",http-get:*:audio/G729D:*"
102         ",http-get:*:audio/G729E:*"
103         ",http-get:*:audio/GSM:*"
104         ",http-get:*:audio/GSM-EFR:*"
105         ",http-get:*:audio/L8:*"
106         ",http-get:*:audio/L16:*"
107         ",http-get:*:audio/LPC:*"
108         ",http-get:*:audio/MPA:*"
109         ",http-get:*:audio/PCMA:*"
110         ",http-get:*:audio/PCMU:*"
111         ",http-get:*:audio/QCELP:*"
112         ",http-get:*:audio/RED:*"
113         ",http-get:*:audio/VDVI:*"
114         ",http-get:*:audio/ac3:*"
115         ",http-get:*:audio/vorbis:*"
116         ",http-get:*:audio/speex:*"
117         ",http-get:*:audio/flac:*"
118         ",http-get:*:audio/x-flac:*"
119         ",http-get:*:audio/x-aiff:*"
120         ",http-get:*:audio/x-pn-realaudio:*"
121         ",http-get:*:audio/x-realaudio:*"
122         ",http-get:*:audio/x-wav:*"
123         ",http-get:*:audio/x-matroska:*"
124         ",http-get:*:audio/x-ms-wma:*"
125         ",http-get:*:audio/x-mpegurl:*"
126         ",http-get:*:application/x-shockwave-flash:*"
127         ",http-get:*:application/ogg:*"
128         ",http-get:*:application/sdp:*"
129         ",http-get:*:image/gif:*"
130         ",http-get:*:image/jpeg:*"
131         ",http-get:*:image/ief:*"
132         ",http-get:*:image/png:*"
133         ",http-get:*:image/tiff:*"
134         ",http-get:*:video/avi:*"
135         ",http-get:*:video/divx:*"
136         ",http-get:*:video/mpeg:*"
137         ",http-get:*:video/fli:*"
138         ",http-get:*:video/flv:*"
139         ",http-get:*:video/quicktime:*"
140         ",http-get:*:video/vnd.vivo:*"
141         ",http-get:*:video/vc1:*"
142         ",http-get:*:video/ogg:*"
143         ",http-get:*:video/mp4:*"
144         ",http-get:*:video/mkv:*"
145         ",http-get:*:video/BT656:*"
146         ",http-get:*:video/CelB:*"
147         ",http-get:*:video/JPEG:*"
148         ",http-get:*:video/H261:*"
149         ",http-get:*:video/H263:*"
150         ",http-get:*:video/H263-1998:*"
151         ",http-get:*:video/H263-2000:*"
152         ",http-get:*:video/MPV:*"
153         ",http-get:*:video/MP2T:*"
154         ",http-get:*:video/MP1S:*"
155         ",http-get:*:video/MP2P:*"
156         ",http-get:*:video/BMPEG:*"
157         ",http-get:*:video/xvid:*"
158         ",http-get:*:video/x-divx:*"
159         ",http-get:*:video/x-matroska:*"
160         ",http-get:*:video/x-mkv:*"
161         ",http-get:*:video/x-ms-wmv:*"
162         ",http-get:*:video/x-ms-avi:*"
163         ",http-get:*:video/x-flv:*"
164         ",http-get:*:video/x-fli:*"
165         ",http-get:*:video/x-ms-asf:*"
166         ",http-get:*:video/x-ms-asx:*"
167         ",http-get:*:video/x-ms-wmx:*"
168         ",http-get:*:video/x-ms-wvx:*"
169         ",http-get:*:video/x-msvideo:*"
170         ",http-get:*:video/x-xvid:*"
171         );
172
173     NPT_CHECK_FATAL(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
174     service->SetStateVariable("NextAVTransportURI", "");
175     service->SetStateVariable("NextAVTransportURIMetadata", "");
176
177     return NPT_SUCCESS;
178 }
179
180 /*----------------------------------------------------------------------
181 |   CUPnPRenderer::ProcessHttpRequest
182 +---------------------------------------------------------------------*/
183 NPT_Result
184 CUPnPRenderer::ProcessHttpGetRequest(NPT_HttpRequest&              request,
185                                   const NPT_HttpRequestContext& context,
186                                   NPT_HttpResponse&             response)
187 {
188     // get the address of who sent us some data back
189     NPT_String  ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
190     NPT_String  method     = request.GetMethod();
191     NPT_String  protocol   = request.GetProtocol();
192     NPT_HttpUrl url        = request.GetUrl();
193
194     if (url.GetPath() == "/thumb") {
195         NPT_HttpUrlQuery query(url.GetQuery());
196         NPT_String filepath = query.GetField("path");
197         if (!filepath.IsEmpty()) {
198             NPT_HttpEntity* entity = response.GetEntity();
199             if (entity == NULL) return NPT_ERROR_INVALID_STATE;
200
201             // check the method
202             if (request.GetMethod() != NPT_HTTP_METHOD_GET &&
203                 request.GetMethod() != NPT_HTTP_METHOD_HEAD) {
204                 response.SetStatus(405, "Method Not Allowed");
205                 return NPT_SUCCESS;
206             }
207
208             // prevent hackers from accessing files outside of our root
209             if ((filepath.Find("/..") >= 0) || (filepath.Find("\\..") >=0)) {
210                 return NPT_FAILURE;
211             }
212
213             // open the file
214             CStdString path (CURL::Decode((const char*) filepath));
215             NPT_File file(path.c_str());
216             NPT_Result result = file.Open(NPT_FILE_OPEN_MODE_READ);
217             if (NPT_FAILED(result)) {
218                 response.SetStatus(404, "Not Found");
219                 return NPT_SUCCESS;
220             }
221             NPT_InputStreamReference stream;
222             file.GetInputStream(stream);
223             entity->SetContentType(GetMimeType(filepath));
224             entity->SetInputStream(stream, true);
225
226             return NPT_SUCCESS;
227         }
228     }
229
230     return PLT_MediaRenderer::ProcessHttpGetRequest(request, context, response);
231 }
232
233 /*----------------------------------------------------------------------
234 |   CUPnPRenderer::Announce
235 +---------------------------------------------------------------------*/
236 void
237 CUPnPRenderer::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
238 {
239     if (strcmp(sender, "xbmc") != 0)
240       return;
241
242     NPT_AutoLock lock(m_state);
243     PLT_Service *avt, *rct;
244
245     if (flag == Player) {
246         if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))
247             return;
248         if (strcmp(message, "OnPlay") == 0) {
249             avt->SetStateVariable("AVTransportURI", g_application.CurrentFile().c_str());
250             avt->SetStateVariable("CurrentTrackURI", g_application.CurrentFile().c_str());
251
252             NPT_String meta;
253             if (NPT_SUCCEEDED(GetMetadata(meta))) {
254                 avt->SetStateVariable("CurrentTrackMetadata", meta);
255                 avt->SetStateVariable("AVTransportURIMetaData", meta);
256             }
257
258             avt->SetStateVariable("TransportPlaySpeed", NPT_String::FromInteger(data["speed"].asInteger()));
259             avt->SetStateVariable("TransportState", "PLAYING");
260
261             /* this could be a transition to next track, so clear next */
262             avt->SetStateVariable("NextAVTransportURI", "");
263             avt->SetStateVariable("NextAVTransportURIMetaData", "");
264         }
265         else if (strcmp(message, "OnPause") == 0) {
266             avt->SetStateVariable("TransportPlaySpeed", NPT_String::FromInteger(data["speed"].asInteger()));
267             avt->SetStateVariable("TransportState", "PAUSED_PLAYBACK");
268         }
269         else if (strcmp(message, "OnSpeedChanged") == 0) {
270             avt->SetStateVariable("TransportPlaySpeed", NPT_String::FromInteger(data["speed"].asInteger()));
271         }
272     }
273     else if (flag == Application && strcmp(message, "OnVolumeChanged") == 0) {
274         if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:RenderingControl:1", rct)))
275             return;
276
277         CStdString buffer;
278
279         buffer = StringUtils::Format("%ld", data["volume"].asInteger());
280         rct->SetStateVariable("Volume", buffer.c_str());
281
282         buffer = StringUtils::Format("%ld", 256 * (data["volume"].asInteger() * 60 - 60) / 100);
283         rct->SetStateVariable("VolumeDb", buffer.c_str());
284
285         rct->SetStateVariable("Mute", data["muted"].asBoolean() ? "1" : "0");
286     }
287 }
288
289 /*----------------------------------------------------------------------
290 |   CUPnPRenderer::UpdateState
291 +---------------------------------------------------------------------*/
292 void
293 CUPnPRenderer::UpdateState()
294 {
295     NPT_AutoLock lock(m_state);
296
297     PLT_Service *avt;
298
299     if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))
300         return;
301
302     /* don't update state while transitioning */
303     NPT_String state;
304     avt->GetStateVariableValue("TransportState", state);
305     if(state == "TRANSITIONING")
306         return;
307
308     avt->SetStateVariable("TransportStatus", "OK");
309
310     if (g_application.m_pPlayer->IsPlaying() || g_application.m_pPlayer->IsPausedPlayback()) {
311         avt->SetStateVariable("NumberOfTracks", "1");
312         avt->SetStateVariable("CurrentTrack", "1");
313
314         CStdString buffer = g_infoManager.GetCurrentPlayTime(TIME_FORMAT_HH_MM_SS);
315         avt->SetStateVariable("RelativeTimePosition", buffer.c_str());
316         avt->SetStateVariable("AbsoluteTimePosition", buffer.c_str());
317
318         buffer = g_infoManager.GetDuration(TIME_FORMAT_HH_MM_SS);
319         if (buffer.length() > 0) {
320           avt->SetStateVariable("CurrentTrackDuration", buffer.c_str());
321           avt->SetStateVariable("CurrentMediaDuration", buffer.c_str());
322         } else {
323           avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
324           avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
325         }
326
327     } else if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
328         avt->SetStateVariable("TransportState", "PLAYING");
329
330         avt->SetStateVariable("AVTransportURI" , g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
331         avt->SetStateVariable("CurrentTrackURI", g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
332         avt->SetStateVariable("TransportPlaySpeed", "1");
333
334         CGUIWindowSlideShow *slideshow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
335         if (slideshow)
336         {
337           CStdString index;
338           index = StringUtils::Format("%d", slideshow->NumSlides());
339           avt->SetStateVariable("NumberOfTracks", index.c_str());
340           index = StringUtils::Format("%d", slideshow->CurrentSlide());
341           avt->SetStateVariable("CurrentTrack", index.c_str());
342
343         }
344
345         avt->SetStateVariable("CurrentTrackMetadata", "");
346         avt->SetStateVariable("AVTransportURIMetaData", "");
347
348     } else {
349         avt->SetStateVariable("TransportState", "STOPPED");
350         avt->SetStateVariable("TransportPlaySpeed", "1");
351         avt->SetStateVariable("NumberOfTracks", "0");
352         avt->SetStateVariable("CurrentTrack", "0");
353         avt->SetStateVariable("RelativeTimePosition", "00:00:00");
354         avt->SetStateVariable("AbsoluteTimePosition", "00:00:00");
355         avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
356         avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
357         avt->SetStateVariable("NextAVTransportURI", "");
358         avt->SetStateVariable("NextAVTransportURIMetaData", "");
359     }
360 }
361
362 /*----------------------------------------------------------------------
363 |   CUPnPRenderer::SetupIcons
364 +---------------------------------------------------------------------*/
365 NPT_Result
366 CUPnPRenderer::SetupIcons()
367 {
368     NPT_String file_root = CSpecialProtocol::TranslatePath("special://xbmc/media/").c_str();
369     AddIcon(
370         PLT_DeviceIcon("image/png", 256, 256, 24, "/icon-flat-256x256.png"),
371         file_root);
372     AddIcon(
373         PLT_DeviceIcon("image/png", 120, 120, 24, "/icon-flat-120x120.png"),
374         file_root);
375     return NPT_SUCCESS;
376 }
377
378 /*----------------------------------------------------------------------
379 |   CUPnPRenderer::GetMetadata
380 +---------------------------------------------------------------------*/
381 NPT_Result
382 CUPnPRenderer::GetMetadata(NPT_String& meta)
383 {
384     NPT_Result res = NPT_FAILURE;
385     CFileItem item(g_application.CurrentFileItem());
386     NPT_String file_path, tmp;
387
388     // we pass an empty CThumbLoader reference, as it can't be used
389     // without CUPnPServer enabled
390     NPT_Reference<CThumbLoader> thumb_loader;
391     PLT_MediaObject* object = BuildObject(item, file_path, false, thumb_loader);
392     if (object) {
393         // fetch the item's artwork
394         CStdString thumb;
395         if (object->m_ObjectClass.type == "object.item.audioItem.musicTrack")
396             thumb = g_infoManager.GetImage(MUSICPLAYER_COVER, -1);
397         else
398             thumb = g_infoManager.GetImage(VIDEOPLAYER_COVER, -1);
399
400         thumb = CTextureUtils::GetWrappedImageURL(thumb);
401
402         NPT_String ip;
403         if (g_application.getNetwork().GetFirstConnectedInterface()) {
404             ip = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
405         }
406         // build url, use the internal device http server to serv the image
407         NPT_HttpUrlQuery query;
408         query.AddField("path", thumb.c_str());
409         PLT_AlbumArtInfo art;
410         art.uri = NPT_HttpUrl(
411             ip,
412             m_URLDescription.GetPort(),
413             "/thumb",
414             query.ToString()).ToString();
415         // Set DLNA profileID by extension, defaulting to JPEG.
416         if (URIUtils::HasExtension(item.GetArt("thumb"), ".png")) {
417             art.dlna_profile = "PNG_TN";
418         } else {
419             art.dlna_profile = "JPEG_TN";
420         }
421         object->m_ExtraInfo.album_arts.Add(art);
422
423         res = PLT_Didl::ToDidl(*object, "*", tmp);
424         meta = didl_header + tmp + didl_footer;
425         delete object;
426     }
427     return res;
428 }
429
430 /*----------------------------------------------------------------------
431 |   CUPnPRenderer::OnNext
432 +---------------------------------------------------------------------*/
433 NPT_Result
434 CUPnPRenderer::OnNext(PLT_ActionReference& action)
435 {
436     if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
437         CAction action(ACTION_NEXT_PICTURE);
438         CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
439     } else {
440         CApplicationMessenger::Get().PlayListPlayerNext();
441     }
442     return NPT_SUCCESS;
443 }
444
445 /*----------------------------------------------------------------------
446 |   CUPnPRenderer::OnPause
447 +---------------------------------------------------------------------*/
448 NPT_Result
449 CUPnPRenderer::OnPause(PLT_ActionReference& action)
450 {
451     if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
452         CAction action(ACTION_PAUSE);
453         CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
454     } else if (!g_application.m_pPlayer->IsPausedPlayback())
455       CApplicationMessenger::Get().MediaPause();
456     return NPT_SUCCESS;
457 }
458
459 /*----------------------------------------------------------------------
460 |   CUPnPRenderer::OnPlay
461 +---------------------------------------------------------------------*/
462 NPT_Result
463 CUPnPRenderer::OnPlay(PLT_ActionReference& action)
464 {
465     if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
466         return NPT_SUCCESS;
467     } else if (g_application.m_pPlayer->IsPausedPlayback()) {
468       CApplicationMessenger::Get().MediaPause();
469     } else if (!g_application.m_pPlayer->IsPlaying()) {
470         NPT_String uri, meta;
471         PLT_Service* service;
472         // look for value set previously by SetAVTransportURI
473         NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
474         NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURI", uri));
475         NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURIMetaData", meta));
476
477         // if not set, use the current file being played
478         PlayMedia(uri, meta);
479     }
480     return NPT_SUCCESS;
481 }
482
483 /*----------------------------------------------------------------------
484 |   CUPnPRenderer::OnPrevious
485 +---------------------------------------------------------------------*/
486 NPT_Result
487 CUPnPRenderer::OnPrevious(PLT_ActionReference& action)
488 {
489     if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
490         CAction action(ACTION_PREV_PICTURE);
491         CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
492     } else {
493         CApplicationMessenger::Get().PlayListPlayerPrevious();
494     }
495     return NPT_SUCCESS;
496 }
497
498 /*----------------------------------------------------------------------
499 |   CUPnPRenderer::OnStop
500 +---------------------------------------------------------------------*/
501 NPT_Result
502 CUPnPRenderer::OnStop(PLT_ActionReference& action)
503 {
504     if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
505         CAction action(ACTION_STOP);
506         CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
507     } else {
508         CApplicationMessenger::Get().MediaStop();
509     }
510     return NPT_SUCCESS;
511 }
512
513 /*----------------------------------------------------------------------
514 |   CUPnPRenderer::OnSetAVTransportURI
515 +---------------------------------------------------------------------*/
516 NPT_Result
517 CUPnPRenderer::OnSetAVTransportURI(PLT_ActionReference& action)
518 {
519     NPT_String uri, meta;
520     PLT_Service* service;
521     NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
522
523     NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURI", uri));
524     NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURIMetaData", meta));
525
526     // if not playing already, just keep around uri & metadata
527     // and wait for play command
528     if (!g_application.m_pPlayer->IsPlaying() && g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW) {
529         service->SetStateVariable("TransportState", "STOPPED");
530         service->SetStateVariable("TransportStatus", "OK");
531         service->SetStateVariable("TransportPlaySpeed", "1");
532         service->SetStateVariable("AVTransportURI", uri);
533         service->SetStateVariable("AVTransportURIMetaData", meta);
534         service->SetStateVariable("NextAVTransportURI", "");
535         service->SetStateVariable("NextAVTransportURIMetaData", "");
536
537         NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
538         return NPT_SUCCESS;
539     }
540
541     return PlayMedia(uri, meta, action.AsPointer());
542 }
543
544 /*----------------------------------------------------------------------
545  |   CUPnPRenderer::OnSetAVTransportURI
546  +---------------------------------------------------------------------*/
547 NPT_Result
548 CUPnPRenderer::OnSetNextAVTransportURI(PLT_ActionReference& action)
549 {
550     NPT_String uri, meta;
551     PLT_Service* service;
552     NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
553
554     NPT_CHECK_SEVERE(action->GetArgumentValue("NextURI", uri));
555     NPT_CHECK_SEVERE(action->GetArgumentValue("NextURIMetaData", meta));
556
557     CFileItemPtr item = GetFileItem(uri, meta);
558     if (!item) {
559         return NPT_FAILURE;
560     }
561
562     if (g_application.m_pPlayer->IsPlaying()) {
563
564         int playlist = PLAYLIST_MUSIC;
565         if(item->IsVideo())
566           playlist = PLAYLIST_VIDEO;
567
568         {   CSingleLock lock(g_graphicsContext);
569             g_playlistPlayer.ClearPlaylist(playlist);
570             g_playlistPlayer.Add(playlist, item);
571
572             g_playlistPlayer.SetCurrentSong(-1);
573             g_playlistPlayer.SetCurrentPlaylist(playlist);
574         }
575
576         CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
577         g_windowManager.SendThreadMessage(msg);
578
579
580         service->SetStateVariable("NextAVTransportURI", uri);
581         service->SetStateVariable("NextAVTransportURIMetaData", meta);
582
583         NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
584
585         return NPT_SUCCESS;
586
587   } else if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
588         return NPT_FAILURE;
589   } else {
590         return NPT_FAILURE;
591   }
592 }
593
594 /*----------------------------------------------------------------------
595 |   CUPnPRenderer::PlayMedia
596 +---------------------------------------------------------------------*/
597 NPT_Result
598 CUPnPRenderer::PlayMedia(const NPT_String& uri, const NPT_String& meta, PLT_Action* action)
599 {
600     PLT_Service* service;
601     NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
602
603     { NPT_AutoLock lock(m_state);
604       service->SetStateVariable("TransportState", "TRANSITIONING");
605       service->SetStateVariable("TransportStatus", "OK");
606     }
607
608     CFileItemPtr item = GetFileItem(uri, meta);
609     if (!item) {
610         return NPT_FAILURE;
611     }
612
613     if (item->IsPicture()) {
614         CApplicationMessenger::Get().PictureShow(item->GetPath());
615     } else {
616         CApplicationMessenger::Get().MediaPlay(*item);
617     }
618
619     if (g_application.m_pPlayer->IsPlaying() || g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
620         NPT_AutoLock lock(m_state);
621         service->SetStateVariable("TransportState", "PLAYING");
622         service->SetStateVariable("TransportStatus", "OK");
623         service->SetStateVariable("AVTransportURI", uri);
624         service->SetStateVariable("AVTransportURIMetaData", meta);
625     } else {
626         NPT_AutoLock lock(m_state);
627         service->SetStateVariable("TransportState", "STOPPED");
628         service->SetStateVariable("TransportStatus", "ERROR_OCCURRED");
629     }
630
631     service->SetStateVariable("NextAVTransportURI", "");
632     service->SetStateVariable("NextAVTransportURIMetaData", "");
633
634     if (action) {
635         NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
636     }
637     return NPT_SUCCESS;
638 }
639
640 /*----------------------------------------------------------------------
641 |   CUPnPRenderer::OnSetVolume
642 +---------------------------------------------------------------------*/
643 NPT_Result
644 CUPnPRenderer::OnSetVolume(PLT_ActionReference& action)
645 {
646     NPT_String volume;
647     NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredVolume", volume));
648     g_application.SetVolume((float)strtod((const char*)volume, NULL));
649     return NPT_SUCCESS;
650 }
651
652 /*----------------------------------------------------------------------
653 |   CUPnPRenderer::OnSetMute
654 +---------------------------------------------------------------------*/
655 NPT_Result
656 CUPnPRenderer::OnSetMute(PLT_ActionReference& action)
657 {
658     NPT_String mute;
659     NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredMute",mute));
660     if((mute == "1") ^ g_application.IsMuted())
661         g_application.ToggleMute();
662     return NPT_SUCCESS;
663 }
664
665 /*----------------------------------------------------------------------
666 |   CUPnPRenderer::OnSeek
667 +---------------------------------------------------------------------*/
668 NPT_Result
669 CUPnPRenderer::OnSeek(PLT_ActionReference& action)
670 {
671     if (!g_application.m_pPlayer->IsPlaying()) return NPT_ERROR_INVALID_STATE;
672
673     NPT_String unit, target;
674     NPT_CHECK_SEVERE(action->GetArgumentValue("Unit", unit));
675     NPT_CHECK_SEVERE(action->GetArgumentValue("Target", target));
676
677     if (!unit.Compare("REL_TIME")) {
678         // converts target to seconds
679         NPT_UInt32 seconds;
680         NPT_CHECK_SEVERE(PLT_Didl::ParseTimeStamp(target, seconds));
681         g_application.SeekTime(seconds);
682     }
683
684     return NPT_SUCCESS;
685 }
686
687 } /* namespace UPNP */
688