2 * Copyright (c) 2006 elupus (Joakim Plate)
3 * Copyright (C) 2006-2013 Team XBMC
6 * This Program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This Program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with XBMC; see the file COPYING. If not, see
18 * <http://www.gnu.org/licenses/>.
22 #include "UPnPPlayer.h"
24 #include "UPnPInternal.h"
26 #include "PltMediaController.h"
29 #include "threads/Event.h"
30 #include "utils/log.h"
31 #include "utils/TimeUtils.h"
32 #include "GUIInfoManager.h"
33 #include "ThumbLoader.h"
34 #include "video/VideoThumbLoader.h"
35 #include "music/MusicThumbLoader.h"
36 #include "ApplicationMessenger.h"
37 #include "Application.h"
38 #include "dialogs/GUIDialogBusy.h"
39 #include "guilib/GUIWindowManager.h"
40 #include "guilib/Key.h"
41 #include "dialogs/GUIDialogYesNo.h"
44 NPT_SET_LOCAL_LOGGER("xbmc.upnp.player")
49 class CUPnPPlayerController
50 : public PLT_MediaControllerDelegate
53 CUPnPPlayerController(PLT_MediaController* control, PLT_DeviceDataReference& device, IPlayerCallback& callback)
58 , m_callback(callback)
61 memset(&m_posinfo, 0, sizeof(m_posinfo));
62 m_device->FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", m_transport);
65 virtual void OnSetAVTransportURIResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
68 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer : OnSetAVTransportURIResult failed");
73 virtual void OnPlayResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
76 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer : OnPlayResult failed");
81 virtual void OnStopResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
84 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer : OnStopResult failed");
89 virtual void OnGetMediaInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_MediaInfo* info, void* userdata)
91 if(NPT_FAILED(res) || info == NULL)
92 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer : OnGetMediaInfoResult failed");
95 virtual void OnGetTransportInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_TransportInfo* info, void* userdata)
97 CSingleLock lock(m_section);
101 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer : OnGetTransportInfoResult failed");
102 m_trainfo.cur_speed = "0";
103 m_trainfo.cur_transport_state = "STOPPED";
104 m_trainfo.cur_transport_status = "ERROR_OCCURED";
111 void UpdatePositionInfo()
114 || m_postime > CTimeUtils::GetFrameTime())
117 m_control->GetTransportInfo(m_device, m_instance, this);
118 m_control->GetPositionInfo(m_device, m_instance, this);
122 virtual void OnGetPositionInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_PositionInfo* info, void* userdata)
124 CSingleLock lock(m_section);
126 if(NPT_FAILED(res) || info == NULL)
128 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer : OnGetMediaInfoResult failed");
129 m_posinfo = PLT_PositionInfo();
133 m_postime = CTimeUtils::GetFrameTime() + 500;
138 ~CUPnPPlayerController()
142 PLT_MediaController* m_control;
143 PLT_Service * m_transport;
144 PLT_DeviceDataReference m_device;
145 NPT_UInt32 m_instance;
146 IPlayerCallback& m_callback;
148 NPT_Result m_resstatus;
151 CCriticalSection m_section;
152 unsigned int m_postime;
155 PLT_PositionInfo m_posinfo;
158 PLT_TransportInfo m_trainfo;
161 CUPnPPlayer::CUPnPPlayer(IPlayerCallback& callback, const char* uuid)
166 , m_stopremote(false)
168 m_control = CUPnP::GetInstance()->m_MediaController;
170 PLT_DeviceDataReference device;
171 if(NPT_SUCCEEDED(m_control->FindRenderer(uuid, device)))
173 m_delegate = new CUPnPPlayerController(m_control, device, callback);
174 CUPnP::RegisterUserdata(m_delegate);
177 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer couldn't find device as %s", uuid);
180 CUPnPPlayer::~CUPnPPlayer()
183 CUPnP::UnregisterUserdata(m_delegate);
187 static NPT_Result WaitOnEvent(CEvent& event, XbmcThreads::EndTime& timeout, CGUIDialogBusy*& dialog)
189 if(event.WaitMSec(0))
193 dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
197 g_windowManager.ProcessRenderLoop(false);
200 if(event.WaitMSec(100))
203 g_windowManager.ProcessRenderLoop(false);
205 if(dialog->IsCanceled())
208 } while(!timeout.IsTimePast());
213 int CUPnPPlayer::PlayFile(const CFileItem& file, const CPlayerOptions& options, CGUIDialogBusy*& dialog, XbmcThreads::EndTime& timeout)
215 CFileItem item(file);
216 NPT_Reference<CThumbLoader> thumb_loader;
217 NPT_Reference<PLT_MediaObject> obj;
218 NPT_String path(file.GetPath().c_str());
219 NPT_String tmp, resource;
220 EMediaControllerQuirks quirks = EMEDIACONTROLLERQUIRKS_NONE;
222 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
224 if (file.IsVideoDb())
225 thumb_loader = NPT_Reference<CThumbLoader>(new CVideoThumbLoader());
226 else if (item.IsMusicDb())
227 thumb_loader = NPT_Reference<CThumbLoader>(new CMusicThumbLoader());
229 obj = BuildObject(item, path, false, thumb_loader, NULL, CUPnP::GetServer());
230 if(obj.IsNull()) goto failed;
232 NPT_CHECK_LABEL_SEVERE(PLT_Didl::ToDidl(*obj, "", tmp), failed);
233 tmp.Insert(didl_header, 0);
234 tmp.Append(didl_footer);
236 quirks = GetMediaControllerQuirks(m_delegate->m_device.AsPointer());
237 if (quirks & EMEDIACONTROLLERQUIRKS_X_MKV)
239 for (NPT_Cardinal i=0; i< obj->m_Resources.GetItemCount(); i++) {
240 if (obj->m_Resources[i].m_ProtocolInfo.GetContentType().Compare("video/x-matroska") == 0) {
241 NPT_String protocolInfo = obj->m_Resources[i].m_ProtocolInfo.ToString();
242 protocolInfo.Replace(":video/x-matroska:", ":video/x-mkv:");
243 obj->m_Resources[i].m_ProtocolInfo = PLT_ProtocolInfo(protocolInfo);
248 /* The resource uri's are stored in the Didl. We must choose the best resource
249 * for the playback device */
250 NPT_Cardinal res_index;
251 NPT_CHECK_LABEL_SEVERE(m_control->FindBestResource(m_delegate->m_device, *obj, res_index), failed);
254 timeout.Set(timeout.GetInitialTimeoutValue());
255 /* dlna specifies that a return code of 705 should be returned
256 * if TRANSPORT_STATE is not STOPPED or NO_MEDIA_PRESENT */
257 NPT_CHECK_LABEL_SEVERE(m_control->Stop(m_delegate->m_device
258 , m_delegate->m_instance
259 , m_delegate), failed);
260 NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed);
261 NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed);
264 timeout.Set(timeout.GetInitialTimeoutValue());
265 NPT_CHECK_LABEL_SEVERE(m_control->SetAVTransportURI(m_delegate->m_device
266 , m_delegate->m_instance
267 , obj->m_Resources[res_index].m_Uri
269 , m_delegate), failed);
270 NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed);
271 NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed);
273 timeout.Set(timeout.GetInitialTimeoutValue());
274 NPT_CHECK_LABEL_SEVERE(m_control->Play(m_delegate->m_device
275 , m_delegate->m_instance
277 , m_delegate), failed);
278 NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed);
279 NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed);
282 /* wait for PLAYING state */
283 timeout.Set(timeout.GetInitialTimeoutValue());
285 NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
286 , m_delegate->m_instance
287 , m_delegate), failed);
290 { CSingleLock lock(m_delegate->m_section);
291 if(m_delegate->m_trainfo.cur_transport_state == "PLAYING"
292 || m_delegate->m_trainfo.cur_transport_state == "PAUSED_PLAYBACK")
295 if(m_delegate->m_trainfo.cur_transport_state == "STOPPED"
296 && m_delegate->m_trainfo.cur_transport_status != "OK")
298 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::OpenFile - remote player signalled error %s", file.GetPath().c_str());
303 NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed);
305 } while(!timeout.IsTimePast());
307 if(options.starttime > 0)
309 /* many upnp units won't load file properly until after play (including xbmc) */
310 NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device
311 , m_delegate->m_instance
313 , PLT_Didl::FormatTimeStamp((NPT_UInt32)options.starttime)
314 , m_delegate), failed);
322 bool CUPnPPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options)
324 CGUIDialogBusy* dialog = NULL;
325 XbmcThreads::EndTime timeout(10000);
327 /* if no path we want to attach to a already playing player */
328 if(file.GetPath() == "")
330 NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
331 , m_delegate->m_instance
332 , m_delegate), failed);
334 NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed);
336 /* make sure the attached player is actually playing */
337 { CSingleLock lock(m_delegate->m_section);
338 if(m_delegate->m_trainfo.cur_transport_state != "PLAYING"
339 && m_delegate->m_trainfo.cur_transport_state != "PAUSED_PLAYBACK")
344 NPT_CHECK_LABEL_SEVERE(PlayFile(file, options, dialog, timeout), failed);
348 m_callback.OnPlayBackStarted();
349 NPT_CHECK_LABEL_SEVERE(m_control->GetPositionInfo(m_delegate->m_device
350 , m_delegate->m_instance
351 , m_delegate), failed);
352 NPT_CHECK_LABEL_SEVERE(m_control->GetMediaInfo(m_delegate->m_device
353 , m_delegate->m_instance
354 , m_delegate), failed);
361 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::OpenFile - unable to open file %s", file.GetPath().c_str());
367 bool CUPnPPlayer::QueueNextFile(const CFileItem& file)
369 CFileItem item(file);
370 NPT_Reference<CThumbLoader> thumb_loader;
371 NPT_Reference<PLT_MediaObject> obj;
372 NPT_String path(file.GetPath().c_str());
375 if (file.IsVideoDb())
376 thumb_loader = NPT_Reference<CThumbLoader>(new CVideoThumbLoader());
377 else if (item.IsMusicDb())
378 thumb_loader = NPT_Reference<CThumbLoader>(new CMusicThumbLoader());
381 obj = BuildObject(item, path, 0, thumb_loader, NULL, CUPnP::GetServer());
384 NPT_CHECK_LABEL_SEVERE(PLT_Didl::ToDidl(*obj, "", tmp), failed);
385 tmp.Insert(didl_header, 0);
386 tmp.Append(didl_footer);
389 NPT_CHECK_LABEL_WARNING(m_control->SetNextAVTransportURI(m_delegate->m_device
390 , m_delegate->m_instance
391 , file.GetPath().c_str()
393 , m_delegate), failed);
394 if(!m_delegate->m_resevent.WaitMSec(10000)) goto failed;
395 NPT_CHECK_LABEL_WARNING(m_delegate->m_resstatus, failed);
399 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::QueueNextFile - unable to queue file %s", file.GetPath().c_str());
403 bool CUPnPPlayer::CloseFile(bool reopen)
405 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
408 NPT_CHECK_LABEL(m_control->Stop(m_delegate->m_device
409 , m_delegate->m_instance
410 , m_delegate), failed);
411 if(!m_delegate->m_resevent.WaitMSec(10000)) goto failed;
412 NPT_CHECK_LABEL(m_delegate->m_resstatus, failed);
418 m_callback.OnPlayBackStopped();
423 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::CloseFile - unable to stop playback");
427 void CUPnPPlayer::Pause()
430 NPT_CHECK_LABEL(m_control->Play(m_delegate->m_device
431 , m_delegate->m_instance
433 , m_delegate), failed);
435 NPT_CHECK_LABEL(m_control->Pause(m_delegate->m_device
436 , m_delegate->m_instance
437 , m_delegate), failed);
441 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::CloseFile - unable to pause/unpause playback");
445 void CUPnPPlayer::SeekTime(__int64 ms)
447 NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device
448 , m_delegate->m_instance
449 , "REL_TIME", PLT_Didl::FormatTimeStamp((NPT_UInt32)(ms / 1000))
450 , m_delegate), failed);
452 g_infoManager.SetDisplayAfterSeek();
455 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::SeekTime - unable to seek playback");
458 float CUPnPPlayer::GetPercentage()
460 int64_t tot = GetTotalTime();
462 return 100.0f * GetTime() / tot;
467 void CUPnPPlayer::SeekPercentage(float percent)
469 int64_t tot = GetTotalTime();
471 SeekTime((int64_t)(tot * percent / 100));
474 void CUPnPPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
478 void CUPnPPlayer::DoAudioWork()
481 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
482 m_delegate->UpdatePositionInfo();
485 NPT_String uri, meta;
486 NPT_CHECK_LABEL(m_delegate->m_transport->GetStateVariableValue("CurrentTrackURI", uri), failed);
487 NPT_CHECK_LABEL(m_delegate->m_transport->GetStateVariableValue("CurrentTrackMetadata", meta), failed);
489 if(m_current_uri != (const char*)uri
490 || m_current_meta != (const char*)meta) {
491 m_current_uri = (const char*)uri;
492 m_current_meta = (const char*)meta;
493 CFileItemPtr item = GetFileItem(uri, meta);
494 g_application.CurrentFileItem() = *item;
495 CApplicationMessenger::Get().SetCurrentItem(*item.get());
498 NPT_CHECK_LABEL(m_delegate->m_transport->GetStateVariableValue("TransportState", data), failed);
499 if(data == "STOPPED")
502 m_callback.OnPlayBackEnded();
510 bool CUPnPPlayer::IsPlaying() const
513 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
514 NPT_CHECK_LABEL(m_delegate->m_transport->GetStateVariableValue("TransportState", data), failed);
515 return data != "STOPPED";
520 bool CUPnPPlayer::IsPaused() const
523 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
524 NPT_CHECK_LABEL(m_delegate->m_transport->GetStateVariableValue("TransportState", data), failed);
525 return data == "PAUSED_PLAYBACK";
530 void CUPnPPlayer::SetVolume(float volume)
532 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
533 NPT_CHECK_LABEL(m_control->SetVolume(m_delegate->m_device
534 , m_delegate->m_instance
535 , "Master", (int)(volume * 100)
536 , m_delegate), failed);
539 CLog::Log(LOGERROR, "UPNP: CUPnPPlayer - unable to set volume");
543 int64_t CUPnPPlayer::GetTime()
545 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
546 return m_delegate->m_posinfo.rel_time.ToMillis();
551 int64_t CUPnPPlayer::GetTotalTime()
553 NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
554 return m_delegate->m_posinfo.track_duration.ToMillis();
559 CStdString CUPnPPlayer::GetPlayingTitle()
564 bool CUPnPPlayer::OnAction(const CAction &action)
566 switch (action.GetID())
571 if(CGUIDialogYesNo::ShowAndGetInput(37022, 37023, 0, 0)) /* stop on remote system */
574 m_stopremote = false;
575 return false; /* let normal code handle the action */
582 } /* namespace UPNP */