2 * UPnP Support for XBMC
3 * Copyright (c) 2006 c0diq (Sylvain Rebaud)
4 * Portions Copyright (c) by the authors of libPlatinum
6 * http://www.plutinosoft.com/blog/category/platinum/
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "threads/SystemClock.h"
25 #include "UPnPInternal.h"
26 #include "UPnPRenderer.h"
27 #include "UPnPServer.h"
28 #include "UPnPSettings.h"
29 #include "utils/URIUtils.h"
30 #include "Application.h"
31 #include "ApplicationMessenger.h"
32 #include "network/Network.h"
33 #include "utils/log.h"
36 #include "profiles/ProfilesManager.h"
37 #include "settings/GUISettings.h"
38 #include "GUIUserMessages.h"
40 #include "guilib/GUIWindowManager.h"
41 #include "GUIInfoManager.h"
42 #include "utils/TimeUtils.h"
43 #include "guilib/Key.h"
49 NPT_SET_LOCAL_LOGGER("xbmc.upnp")
51 #define UPNP_DEFAULT_MAX_RETURNED_ITEMS 200
52 #define UPNP_DEFAULT_MIN_RETURNED_ITEMS 30
58 DLNA_ORG_PS = 'DLNA.ORG_PS'
61 # Convertion Indicator
64 DLNA_ORG_CI = 'DLNA.ORG_CI'
68 # 00 not time seek range, not range
70 # 10 time seek range supported
72 DLNA_ORG_OP = 'DLNA.ORG_OP'
73 DLNA_ORG_OP_VAL = '01'
76 # senderPaced 80000000 31
77 # lsopTimeBasedSeekSupported 40000000 30
78 # lsopByteBasedSeekSupported 20000000 29
79 # playcontainerSupported 10000000 28
80 # s0IncreasingSupported 08000000 27
81 # sNIncreasingSupported 04000000 26
82 # rtspPauseSupported 02000000 25
83 # streamingTransferModeSupported 01000000 24
84 # interactiveTransferModeSupported 00800000 23
85 # backgroundTransferModeSupported 00400000 22
86 # connectionStallingSupported 00200000 21
87 # dlnaVersion15Supported 00100000 20
88 DLNA_ORG_FLAGS = 'DLNA.ORG_FLAGS'
89 DLNA_ORG_FLAGS_VAL = '01500000000000000000000000000000'
92 /*----------------------------------------------------------------------
94 +---------------------------------------------------------------------*/
96 NPT_Console::Output(const char* message)
98 CLog::Log(LOGDEBUG, "%s", message);
104 /*----------------------------------------------------------------------
106 +---------------------------------------------------------------------*/
107 CUPnP* CUPnP::upnp = NULL;
109 /*----------------------------------------------------------------------
110 | CDeviceHostReferenceHolder class
111 +---------------------------------------------------------------------*/
112 class CDeviceHostReferenceHolder
115 PLT_DeviceHostReference m_Device;
118 /*----------------------------------------------------------------------
119 | CCtrlPointReferenceHolder class
120 +---------------------------------------------------------------------*/
121 class CCtrlPointReferenceHolder
124 PLT_CtrlPointReference m_CtrlPoint;
127 /*----------------------------------------------------------------------
129 +---------------------------------------------------------------------*/
130 class CUPnPCleaner : public NPT_Thread
133 CUPnPCleaner(CUPnP* upnp) : NPT_Thread(true), m_UPnP(upnp) {}
141 /*----------------------------------------------------------------------
142 | CMediaBrowser class
143 +---------------------------------------------------------------------*/
144 class CMediaBrowser : public PLT_SyncMediaBrowser,
145 public PLT_MediaContainerChangesListener
148 CMediaBrowser(PLT_CtrlPointReference& ctrlPoint)
149 : PLT_SyncMediaBrowser(ctrlPoint, true)
151 SetContainerListener(this);
154 // PLT_MediaBrowser methods
155 virtual bool OnMSAdded(PLT_DeviceDataReference& device)
157 CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
158 message.SetStringParam("upnp://");
159 g_windowManager.SendThreadMessage(message);
161 return PLT_SyncMediaBrowser::OnMSAdded(device);
163 virtual void OnMSRemoved(PLT_DeviceDataReference& device)
165 PLT_SyncMediaBrowser::OnMSRemoved(device);
167 CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
168 message.SetStringParam("upnp://");
169 g_windowManager.SendThreadMessage(message);
171 PLT_SyncMediaBrowser::OnMSRemoved(device);
174 // PLT_MediaContainerChangesListener methods
175 virtual void OnContainerChanged(PLT_DeviceDataReference& device,
177 const char* update_id)
179 NPT_String path = "upnp://"+device->GetUUID()+"/";
180 if (!NPT_StringsEqual(item_id, "0")) {
181 CStdString id = item_id;
183 URIUtils::AddSlashAtEnd(id);
187 CLog::Log(LOGDEBUG, "UPNP: notfified container update %s", (const char*)path);
188 CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
189 message.SetStringParam(path.GetChars());
190 g_windowManager.SendThreadMessage(message);
195 /*----------------------------------------------------------------------
196 | CMediaController class
197 +---------------------------------------------------------------------*/
198 class CMediaController
199 : public PLT_MediaControllerDelegate
200 , public PLT_MediaController
203 CMediaController(PLT_CtrlPointReference& ctrl_point)
204 : PLT_MediaController(ctrl_point)
206 PLT_MediaController::SetDelegate(this);
213 virtual void OnStopResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
214 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnStopResult(res, device, userdata); }
216 virtual void OnSetPlayModeResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
217 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetPlayModeResult(res, device, userdata); }
219 virtual void OnSetAVTransportURIResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
220 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetAVTransportURIResult(res, device, userdata); }
222 virtual void OnSeekResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
223 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSeekResult(res, device, userdata); }
225 virtual void OnPreviousResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
226 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPreviousResult(res, device, userdata); }
228 virtual void OnPlayResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
229 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPlayResult(res, device, userdata); }
231 virtual void OnPauseResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
232 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPauseResult(res, device, userdata); }
234 virtual void OnNextResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
235 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnNextResult(res, device, userdata); }
237 virtual void OnGetMediaInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_MediaInfo* info, void* userdata)
238 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetMediaInfoResult(res, device, info, userdata); }
240 virtual void OnGetPositionInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_PositionInfo* info, void* userdata)
241 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetPositionInfoResult(res, device, info, userdata); }
243 virtual void OnGetTransportInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_TransportInfo* info, void* userdata)
244 { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetTransportInfoResult(res, device, info, userdata); }
247 virtual bool OnMRAdded(PLT_DeviceDataReference& device )
249 CPlayerCoreFactory::Get().OnPlayerDiscovered((const char*)device->GetUUID()
250 ,(const char*)device->GetFriendlyName()
255 virtual void OnMRRemoved(PLT_DeviceDataReference& device )
257 CPlayerCoreFactory::Get().OnPlayerRemoved((const char*)device->GetUUID());
261 /*----------------------------------------------------------------------
263 +---------------------------------------------------------------------*/
265 m_MediaBrowser(NULL),
266 m_MediaController(NULL),
267 m_ServerHolder(new CDeviceHostReferenceHolder()),
268 m_RendererHolder(new CRendererReferenceHolder()),
269 m_CtrlPointHolder(new CCtrlPointReferenceHolder())
271 // initialize upnp context
272 m_UPnP = new PLT_UPnP();
274 // keep main IP around
275 if (g_application.getNetwork().GetFirstConnectedInterface()) {
276 m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
278 NPT_List<NPT_IpAddress> list;
279 if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list)) && list.GetItemCount()) {
280 m_IP = (*(list.GetFirstItem())).ToString();
282 else if(m_IP.IsEmpty())
285 // start upnp monitoring
289 /*----------------------------------------------------------------------
291 +---------------------------------------------------------------------*/
299 delete m_ServerHolder;
300 delete m_RendererHolder;
301 delete m_CtrlPointHolder;
304 /*----------------------------------------------------------------------
306 +---------------------------------------------------------------------*/
317 /*----------------------------------------------------------------------
318 | CUPnP::ReleaseInstance
319 +---------------------------------------------------------------------*/
321 CUPnP::ReleaseInstance(bool bWait)
330 // since it takes a while to clean up
331 // starts a detached thread to do this
332 CUPnPCleaner* cleaner = new CUPnPCleaner(_upnp);
338 /*----------------------------------------------------------------------
340 +---------------------------------------------------------------------*/
341 CUPnPServer* CUPnP::GetServer()
344 return (CUPnPServer*)upnp->m_ServerHolder->m_Device.AsPointer();
348 /*----------------------------------------------------------------------
350 +---------------------------------------------------------------------*/
354 if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
356 // create controlpoint
357 m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint();
360 m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
363 m_MediaBrowser = new CMediaBrowser(m_CtrlPointHolder->m_CtrlPoint);
366 if (g_guiSettings.GetBool("services.upnpcontroller")) {
367 m_MediaController = new CMediaController(m_CtrlPointHolder->m_CtrlPoint);
371 /*----------------------------------------------------------------------
373 +---------------------------------------------------------------------*/
377 if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
379 m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
380 m_CtrlPointHolder->m_CtrlPoint = NULL;
382 delete m_MediaBrowser;
383 m_MediaBrowser = NULL;
384 delete m_MediaController;
385 m_MediaController = NULL;
388 /*----------------------------------------------------------------------
389 | CUPnP::CreateServer
390 +---------------------------------------------------------------------*/
392 CUPnP::CreateServer(int port /* = 0 */)
394 CUPnPServer* device =
395 new CUPnPServer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
396 CUPnPSettings::Get().GetServerUUID().length() ? CUPnPSettings::Get().GetServerUUID().c_str() : NULL,
399 // trying to set optional upnp values for XP UPnP UI Icons to detect us
400 // but it doesn't work anyways as it requires multicast for XP to detect us
401 device->m_PresentationURL =
403 atoi(g_guiSettings.GetString("services.webserverport")),
406 device->m_ModelName = "XBMC Media Center";
407 device->m_ModelNumber = g_infoManager.GetVersion().c_str();
408 device->m_ModelDescription = "XBMC Media Center - Media Server";
409 device->m_ModelURL = "http://www.xbmc.org/";
410 device->m_Manufacturer = "Team XBMC";
411 device->m_ManufacturerURL = "http://www.xbmc.org/";
413 device->SetDelegate(device);
417 /*----------------------------------------------------------------------
419 +---------------------------------------------------------------------*/
423 if (!m_ServerHolder->m_Device.IsNull()) return;
425 // load upnpserver.xml so that g_settings.m_vecUPnPMusiCMediaSources, etc.. are loaded
427 URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml", filename);
428 CUPnPSettings::Get().Load(filename);
430 // create the server with a XBox compatible friendlyname and UUID from upnpserver.xml if found
431 m_ServerHolder->m_Device = CreateServer(CUPnPSettings::Get().GetServerPort());
434 NPT_Result res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
435 if (NPT_FAILED(res)) {
436 // if the upnp device port was not 0, it could have failed because
437 // of port being in used, so restart with a random port
438 if (CUPnPSettings::Get().GetServerPort() > 0) m_ServerHolder->m_Device = CreateServer(0);
440 res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
443 // save port but don't overwrite saved settings if port was random
444 if (NPT_SUCCEEDED(res)) {
445 if (CUPnPSettings::Get().GetServerPort() == 0) {
446 CUPnPSettings::Get().SetServerPort(m_ServerHolder->m_Device->GetPort());
448 CUPnPServer::m_MaxReturnedItems = UPNP_DEFAULT_MAX_RETURNED_ITEMS;
449 if (CUPnPSettings::Get().GetMaximumReturnedItems() > 0) {
450 // must be > UPNP_DEFAULT_MIN_RETURNED_ITEMS
451 CUPnPServer::m_MaxReturnedItems = max(UPNP_DEFAULT_MIN_RETURNED_ITEMS, CUPnPSettings::Get().GetMaximumReturnedItems());
453 CUPnPSettings::Get().SetMaximumReturnedItems(CUPnPServer::m_MaxReturnedItems);
457 CUPnPSettings::Get().SetServerUUID(m_ServerHolder->m_Device->GetUUID().GetChars());
458 CUPnPSettings::Get().Save(filename);
461 /*----------------------------------------------------------------------
463 +---------------------------------------------------------------------*/
467 if (m_ServerHolder->m_Device.IsNull()) return;
469 m_UPnP->RemoveDevice(m_ServerHolder->m_Device);
470 m_ServerHolder->m_Device = NULL;
473 /*----------------------------------------------------------------------
474 | CUPnP::CreateRenderer
475 +---------------------------------------------------------------------*/
477 CUPnP::CreateRenderer(int port /* = 0 */)
479 CUPnPRenderer* device =
480 new CUPnPRenderer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
482 (CUPnPSettings::Get().GetRendererUUID().length() ? CUPnPSettings::Get().GetRendererUUID().c_str() : NULL),
485 device->m_PresentationURL =
487 atoi(g_guiSettings.GetString("services.webserverport")),
489 device->m_ModelName = "XBMC Media Center";
490 device->m_ModelNumber = g_infoManager.GetVersion().c_str();
491 device->m_ModelDescription = "XBMC Media Center - Media Renderer";
492 device->m_ModelURL = "http://www.xbmc.org/";
493 device->m_Manufacturer = "Team XBMC";
494 device->m_ManufacturerURL = "http://www.xbmc.org/";
499 /*----------------------------------------------------------------------
500 | CUPnP::StartRenderer
501 +---------------------------------------------------------------------*/
502 void CUPnP::StartRenderer()
504 if (!m_RendererHolder->m_Device.IsNull()) return;
507 URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml", filename);
508 CUPnPSettings::Get().Load(filename);
510 m_RendererHolder->m_Device = CreateRenderer(CUPnPSettings::Get().GetRendererPort());
512 NPT_Result res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
514 // failed most likely because port is in use, try again with random port now
515 if (NPT_FAILED(res) && CUPnPSettings::Get().GetRendererPort() != 0) {
516 m_RendererHolder->m_Device = CreateRenderer(0);
518 res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
521 // save port but don't overwrite saved settings if random
522 if (NPT_SUCCEEDED(res) && CUPnPSettings::Get().GetRendererPort() == 0) {
523 CUPnPSettings::Get().SetRendererPort(m_RendererHolder->m_Device->GetPort());
527 CUPnPSettings::Get().SetRendererUUID(m_RendererHolder->m_Device->GetUUID().GetChars());
528 CUPnPSettings::Get().Save(filename);
531 /*----------------------------------------------------------------------
532 | CUPnP::StopRenderer
533 +---------------------------------------------------------------------*/
534 void CUPnP::StopRenderer()
536 if (m_RendererHolder->m_Device.IsNull()) return;
538 m_UPnP->RemoveDevice(m_RendererHolder->m_Device);
539 m_RendererHolder->m_Device = NULL;
542 /*----------------------------------------------------------------------
544 +---------------------------------------------------------------------*/
545 void CUPnP::UpdateState()
547 if (!m_RendererHolder->m_Device.IsNull())
548 ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();
551 } /* namespace UPNP */