46692c2c48fbbf2f3815c958136df51f98722210
[vuplus_xbmc] / xbmc / network / upnp / UPnP.cpp
1 /*
2  * UPnP Support for XBMC
3  *      Copyright (c) 2006 c0diq (Sylvain Rebaud)
4  *      Portions Copyright (c) by the authors of libPlatinum
5  *      http://www.plutinosoft.com/blog/category/platinum/
6  *      Copyright (C) 2006-2013 Team XBMC
7  *      http://xbmc.org
8  *
9  *  This Program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2, or (at your option)
12  *  any later version.
13  *
14  *  This Program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with XBMC; see the file COPYING.  If not, see
21  *  <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "threads/SystemClock.h"
26 #include "UPnP.h"
27 #include "UPnPInternal.h"
28 #include "UPnPRenderer.h"
29 #include "UPnPServer.h"
30 #include "UPnPSettings.h"
31 #include "utils/URIUtils.h"
32 #include "Application.h"
33 #include "ApplicationMessenger.h"
34 #include "network/Network.h"
35 #include "utils/log.h"
36 #include "Platinum.h"
37 #include "URL.h"
38 #include "profiles/ProfilesManager.h"
39 #include "settings/Settings.h"
40 #include "GUIUserMessages.h"
41 #include "FileItem.h"
42 #include "guilib/GUIWindowManager.h"
43 #include "GUIInfoManager.h"
44 #include "utils/TimeUtils.h"
45 #include "video/VideoInfoTag.h"
46 #include "guilib/Key.h"
47 #include "Util.h"
48
49 using namespace std;
50 using namespace UPNP;
51
52 NPT_SET_LOCAL_LOGGER("xbmc.upnp")
53
54 #define UPNP_DEFAULT_MAX_RETURNED_ITEMS 200
55 #define UPNP_DEFAULT_MIN_RETURNED_ITEMS 30
56
57 /*
58 # Play speed
59 #    1 normal
60 #    0 invalid
61 DLNA_ORG_PS = 'DLNA.ORG_PS'
62 DLNA_ORG_PS_VAL = '1'
63
64 # Convertion Indicator
65 #    1 transcoded
66 #    0 not transcoded
67 DLNA_ORG_CI = 'DLNA.ORG_CI'
68 DLNA_ORG_CI_VAL = '0'
69
70 # Operations
71 #    00 not time seek range, not range
72 #    01 range supported
73 #    10 time seek range supported
74 #    11 both supported
75 DLNA_ORG_OP = 'DLNA.ORG_OP'
76 DLNA_ORG_OP_VAL = '01'
77
78 # Flags
79 #    senderPaced                      80000000  31
80 #    lsopTimeBasedSeekSupported       40000000  30
81 #    lsopByteBasedSeekSupported       20000000  29
82 #    playcontainerSupported           10000000  28
83 #    s0IncreasingSupported            08000000  27
84 #    sNIncreasingSupported            04000000  26
85 #    rtspPauseSupported               02000000  25
86 #    streamingTransferModeSupported   01000000  24
87 #    interactiveTransferModeSupported 00800000  23
88 #    backgroundTransferModeSupported  00400000  22
89 #    connectionStallingSupported      00200000  21
90 #    dlnaVersion15Supported           00100000  20
91 DLNA_ORG_FLAGS = 'DLNA.ORG_FLAGS'
92 DLNA_ORG_FLAGS_VAL = '01500000000000000000000000000000'
93 */
94
95 /*----------------------------------------------------------------------
96 |   NPT_Console::Output
97 +---------------------------------------------------------------------*/
98 void
99 NPT_Console::Output(const char* message)
100 {
101     CLog::Log(LOGDEBUG, "%s", message);
102 }
103
104 namespace UPNP
105 {
106
107 /*----------------------------------------------------------------------
108 |   static
109 +---------------------------------------------------------------------*/
110 CUPnP* CUPnP::upnp = NULL;
111 static NPT_List<void*> g_UserData;
112 static NPT_Mutex       g_UserDataLock;
113
114 /*----------------------------------------------------------------------
115 |   CDeviceHostReferenceHolder class
116 +---------------------------------------------------------------------*/
117 class CDeviceHostReferenceHolder
118 {
119 public:
120     PLT_DeviceHostReference m_Device;
121 };
122
123 /*----------------------------------------------------------------------
124 |   CCtrlPointReferenceHolder class
125 +---------------------------------------------------------------------*/
126 class CCtrlPointReferenceHolder
127 {
128 public:
129     PLT_CtrlPointReference m_CtrlPoint;
130 };
131
132 /*----------------------------------------------------------------------
133 |   CUPnPCleaner class
134 +---------------------------------------------------------------------*/
135 class CUPnPCleaner : public NPT_Thread
136 {
137 public:
138     CUPnPCleaner(CUPnP* upnp) : NPT_Thread(true), m_UPnP(upnp) {}
139     void Run() {
140         delete m_UPnP;
141     }
142
143     CUPnP* m_UPnP;
144 };
145
146 /*----------------------------------------------------------------------
147 |   CMediaBrowser class
148 +---------------------------------------------------------------------*/
149 class CMediaBrowser : public PLT_SyncMediaBrowser,
150                       public PLT_MediaContainerChangesListener
151 {
152 public:
153     CMediaBrowser(PLT_CtrlPointReference& ctrlPoint)
154         : PLT_SyncMediaBrowser(ctrlPoint, true)
155     {
156         SetContainerListener(this);
157     }
158
159     // PLT_MediaBrowser methods
160     virtual bool OnMSAdded(PLT_DeviceDataReference& device)
161     {
162         CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
163         message.SetStringParam("upnp://");
164         g_windowManager.SendThreadMessage(message);
165
166         return PLT_SyncMediaBrowser::OnMSAdded(device);
167     }
168     virtual void OnMSRemoved(PLT_DeviceDataReference& device)
169     {
170         PLT_SyncMediaBrowser::OnMSRemoved(device);
171
172         CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
173         message.SetStringParam("upnp://");
174         g_windowManager.SendThreadMessage(message);
175
176         PLT_SyncMediaBrowser::OnMSRemoved(device);
177     }
178
179     // PLT_MediaContainerChangesListener methods
180     virtual void OnContainerChanged(PLT_DeviceDataReference& device,
181                                     const char*              item_id,
182                                     const char*              update_id)
183     {
184         NPT_String path = "upnp://"+device->GetUUID()+"/";
185         if (!NPT_StringsEqual(item_id, "0")) {
186             CStdString id = item_id;
187             CURL::Encode(id);
188             URIUtils::AddSlashAtEnd(id);
189             path += id.c_str();
190         }
191
192         CLog::Log(LOGDEBUG, "UPNP: notfified container update %s", (const char*)path);
193         CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
194         message.SetStringParam(path.GetChars());
195         g_windowManager.SendThreadMessage(message);
196     }
197
198     bool MarkWatched(const CFileItem& item, const bool watched)
199     {
200         if (watched) {
201             CFileItem temp(item);
202             temp.SetProperty("original_listitem_url", item.GetPath());
203             return SaveFileState(temp, CBookmark(), watched);
204         }
205         else {
206             CLog::Log(LOGDEBUG, "UPNP: Marking video item %s as watched", item.GetPath().c_str());
207             return InvokeUpdateObject(item.GetPath().c_str(), "<upnp:playCount>1</upnp:playCount>", "<upnp:playCount>0</upnp:playCount>");
208         }
209     }
210
211     bool SaveFileState(const CFileItem& item, const CBookmark& bookmark, const bool updatePlayCount)
212     {
213         string path = item.GetProperty("original_listitem_url").asString();
214         if (!item.HasVideoInfoTag() || path.empty())  {
215           return false;
216         }
217
218         NPT_String curr_value;
219         NPT_String new_value;
220
221         if (item.GetVideoInfoTag()->m_resumePoint.timeInSeconds != bookmark.timeInSeconds) {
222             CLog::Log(LOGDEBUG, "UPNP: Updating resume point for item %s", path.c_str());
223             long time = (long)bookmark.timeInSeconds;
224             if (time < 0) time = 0;
225             curr_value.Append(NPT_String::Format("<upnp:lastPlaybackPosition>%ld</upnp:lastPlaybackPosition>",
226                                                  (long)item.GetVideoInfoTag()->m_resumePoint.timeInSeconds));
227             new_value.Append(NPT_String::Format("<upnp:lastPlaybackPosition>%ld</upnp:lastPlaybackPosition>", time));
228         }
229         if (updatePlayCount) {
230             CLog::Log(LOGDEBUG, "UPNP: Marking video item %s as watched", path.c_str());
231             if (!curr_value.IsEmpty()) curr_value.Append(",");
232             if (!new_value.IsEmpty()) new_value.Append(",");
233             curr_value.Append("<upnp:playCount>0</upnp:playCount>");
234             new_value.Append("<upnp:playCount>1</upnp:playCount>");
235         }
236
237         return InvokeUpdateObject(path.c_str(), (const char*)curr_value, (const char*)new_value);
238     }
239
240     bool InvokeUpdateObject(const char* id, const char* curr_value, const char* new_value)
241     {
242         CURL url(id);
243         PLT_DeviceDataReference device;
244         PLT_Service* cds;
245         PLT_ActionReference action;
246
247         CLog::Log(LOGDEBUG, "UPNP: attempting to invoke UpdateObject for %s", id);
248
249         // check this server supports UpdateObject action
250         NPT_CHECK_LABEL(FindServer(url.GetHostName().c_str(), device),failed);
251         NPT_CHECK_LABEL(device->FindServiceById("urn:upnp-org:serviceId:ContentDirectory", cds),failed);
252
253         NPT_CHECK_SEVERE(m_CtrlPoint->CreateAction(
254             device,
255             "urn:schemas-upnp-org:service:ContentDirectory:1",
256             "UpdateObject",
257             action));
258
259         NPT_CHECK_LABEL(action->SetArgumentValue("ObjectID", url.GetFileName().c_str()), failed);
260         NPT_CHECK_LABEL(action->SetArgumentValue("CurrentTagValue", curr_value), failed);
261         NPT_CHECK_LABEL(action->SetArgumentValue("NewTagValue", new_value), failed);
262
263         NPT_CHECK_LABEL(m_CtrlPoint->InvokeAction(action, NULL),failed);
264
265         CLog::Log(LOGDEBUG, "UPNP: invoked UpdateObject successfully");
266         return true;
267
268     failed:
269         CLog::Log(LOGINFO, "UPNP: invoking UpdateObject failed");
270         return false;
271     }
272 };
273
274
275 /*----------------------------------------------------------------------
276 |   CMediaController class
277 +---------------------------------------------------------------------*/
278 class CMediaController
279   : public PLT_MediaControllerDelegate
280   , public PLT_MediaController
281 {
282 public:
283   CMediaController(PLT_CtrlPointReference& ctrl_point)
284     : PLT_MediaController(ctrl_point)
285   {
286     PLT_MediaController::SetDelegate(this);
287   }
288
289   ~CMediaController()
290   {
291   }
292
293 #define CHECK_USERDATA_RETURN(userdata) do {     \
294   if (!g_UserData.Contains(userdata))            \
295       return;                                    \
296   } while(0)
297
298   virtual void OnStopResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
299   { CHECK_USERDATA_RETURN(userdata);
300     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnStopResult(res, device, userdata);
301   }
302
303   virtual void OnSetPlayModeResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
304   { CHECK_USERDATA_RETURN(userdata);
305     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetPlayModeResult(res, device, userdata);
306   }
307
308   virtual void OnSetAVTransportURIResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
309   { CHECK_USERDATA_RETURN(userdata);
310     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetAVTransportURIResult(res, device, userdata);
311   }
312
313   virtual void OnSeekResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
314   { CHECK_USERDATA_RETURN(userdata);
315     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSeekResult(res, device, userdata);
316   }
317
318   virtual void OnPreviousResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
319   { CHECK_USERDATA_RETURN(userdata);
320     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPreviousResult(res, device, userdata);
321   }
322
323   virtual void OnPlayResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
324   { CHECK_USERDATA_RETURN(userdata);
325     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPlayResult(res, device, userdata);
326   }
327
328   virtual void OnPauseResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
329   { CHECK_USERDATA_RETURN(userdata);
330     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPauseResult(res, device, userdata);
331   }
332
333   virtual void OnNextResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
334   { CHECK_USERDATA_RETURN(userdata);
335     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnNextResult(res, device, userdata);
336   }
337
338   virtual void OnGetMediaInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_MediaInfo* info, void* userdata)
339   { CHECK_USERDATA_RETURN(userdata);
340     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetMediaInfoResult(res, device, info, userdata);
341   }
342
343   virtual void OnGetPositionInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_PositionInfo* info, void* userdata)
344   { CHECK_USERDATA_RETURN(userdata);
345     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetPositionInfoResult(res, device, info, userdata);
346   }
347
348   virtual void OnGetTransportInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_TransportInfo* info, void* userdata)
349   { CHECK_USERDATA_RETURN(userdata);
350     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetTransportInfoResult(res, device, info, userdata);
351   }
352
353   virtual bool OnMRAdded(PLT_DeviceDataReference& device )
354   {
355     CPlayerCoreFactory::Get().OnPlayerDiscovered((const char*)device->GetUUID()
356                                           ,(const char*)device->GetFriendlyName()
357                                           , EPC_UPNPPLAYER);
358     return true;
359   }
360
361   virtual void OnMRRemoved(PLT_DeviceDataReference& device )
362   {
363     CPlayerCoreFactory::Get().OnPlayerRemoved((const char*)device->GetUUID());
364   }
365 };
366
367 /*----------------------------------------------------------------------
368 |   CUPnP::CUPnP
369 +---------------------------------------------------------------------*/
370 CUPnP::CUPnP() :
371     m_MediaBrowser(NULL),
372     m_MediaController(NULL),
373     m_ServerHolder(new CDeviceHostReferenceHolder()),
374     m_RendererHolder(new CRendererReferenceHolder()),
375     m_CtrlPointHolder(new CCtrlPointReferenceHolder())
376 {
377     // initialize upnp context
378     m_UPnP = new PLT_UPnP();
379
380     // keep main IP around
381     if (g_application.getNetwork().GetFirstConnectedInterface()) {
382         m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
383     }
384     NPT_List<NPT_IpAddress> list;
385     if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list)) && list.GetItemCount()) {
386         m_IP = (*(list.GetFirstItem())).ToString();
387     }
388     else if(m_IP.empty())
389         m_IP = "localhost";
390
391     // start upnp monitoring
392     m_UPnP->Start();
393 }
394
395 /*----------------------------------------------------------------------
396 |   CUPnP::~CUPnP
397 +---------------------------------------------------------------------*/
398 CUPnP::~CUPnP()
399 {
400     m_UPnP->Stop();
401     StopClient();
402     StopServer();
403
404     delete m_UPnP;
405     delete m_ServerHolder;
406     delete m_RendererHolder;
407     delete m_CtrlPointHolder;
408 }
409
410 /*----------------------------------------------------------------------
411 |   CUPnP::GetInstance
412 +---------------------------------------------------------------------*/
413 CUPnP*
414 CUPnP::GetInstance()
415 {
416     if (!upnp) {
417         upnp = new CUPnP();
418     }
419
420     return upnp;
421 }
422
423 /*----------------------------------------------------------------------
424 |   CUPnP::ReleaseInstance
425 +---------------------------------------------------------------------*/
426 void
427 CUPnP::ReleaseInstance(bool bWait)
428 {
429     if (upnp) {
430         CUPnP* _upnp = upnp;
431         upnp = NULL;
432
433         if (bWait) {
434             delete _upnp;
435         } else {
436             // since it takes a while to clean up
437             // starts a detached thread to do this
438             CUPnPCleaner* cleaner = new CUPnPCleaner(_upnp);
439             cleaner->Start();
440         }
441     }
442 }
443
444 /*----------------------------------------------------------------------
445 |   CUPnP::StartServer
446 +---------------------------------------------------------------------*/
447 CUPnPServer* CUPnP::GetServer()
448 {
449   if(upnp)
450     return (CUPnPServer*)upnp->m_ServerHolder->m_Device.AsPointer();
451   return NULL;
452 }
453
454 /*----------------------------------------------------------------------
455 |   CUPnP::MarkWatched
456 +---------------------------------------------------------------------*/
457 bool
458 CUPnP::MarkWatched(const CFileItem& item, const bool watched)
459 {
460     if (upnp && upnp->m_MediaBrowser) {
461         // dynamic_cast is safe here, avoids polluting CUPnP.h header file
462         CMediaBrowser* browser = dynamic_cast<CMediaBrowser*>(upnp->m_MediaBrowser);
463         return browser->MarkWatched(item, watched);
464     }
465     return false;
466 }
467
468 /*----------------------------------------------------------------------
469 |   CUPnP::SaveFileState
470 +---------------------------------------------------------------------*/
471 bool
472 CUPnP::SaveFileState(const CFileItem& item, const CBookmark& bookmark, const bool updatePlayCount)
473 {
474     if (upnp && upnp->m_MediaBrowser) {
475         // dynamic_cast is safe here, avoids polluting CUPnP.h header file
476         CMediaBrowser* browser = dynamic_cast<CMediaBrowser*>(upnp->m_MediaBrowser);
477         return browser->SaveFileState(item, bookmark, updatePlayCount);
478     }
479     return false;
480 }
481
482 /*----------------------------------------------------------------------
483 |   CUPnP::StartClient
484 +---------------------------------------------------------------------*/
485 void
486 CUPnP::StartClient()
487 {
488     if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
489
490     // create controlpoint
491     m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint();
492
493     // start it
494     m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
495
496     // start browser
497     m_MediaBrowser = new CMediaBrowser(m_CtrlPointHolder->m_CtrlPoint);
498
499     // start controller
500     if (CSettings::Get().GetBool("services.upnpcontroller")) {
501         m_MediaController = new CMediaController(m_CtrlPointHolder->m_CtrlPoint);
502     }
503 }
504
505 /*----------------------------------------------------------------------
506 |   CUPnP::StopClient
507 +---------------------------------------------------------------------*/
508 void
509 CUPnP::StopClient()
510 {
511     if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
512
513     m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
514     m_CtrlPointHolder->m_CtrlPoint = NULL;
515
516     delete m_MediaBrowser;
517     m_MediaBrowser = NULL;
518     delete m_MediaController;
519     m_MediaController = NULL;
520 }
521
522 /*----------------------------------------------------------------------
523 |   CUPnP::CreateServer
524 +---------------------------------------------------------------------*/
525 CUPnPServer*
526 CUPnP::CreateServer(int port /* = 0 */)
527 {
528     CUPnPServer* device =
529         new CUPnPServer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
530                         CUPnPSettings::Get().GetServerUUID().length() ? CUPnPSettings::Get().GetServerUUID().c_str() : NULL,
531                         port);
532
533     // trying to set optional upnp values for XP UPnP UI Icons to detect us
534     // but it doesn't work anyways as it requires multicast for XP to detect us
535     device->m_PresentationURL =
536         NPT_HttpUrl(m_IP,
537                     CSettings::Get().GetInt("services.webserverport"),
538                     "/").ToString();
539
540     device->m_ModelName        = "XBMC Media Center";
541     device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
542     device->m_ModelDescription = "XBMC Media Center - Media Server";
543     device->m_ModelURL         = "http://xbmc.org/";
544     device->m_Manufacturer     = "Team XBMC";
545     device->m_ManufacturerURL  = "http://xbmc.org/";
546
547     device->SetDelegate(device);
548     return device;
549 }
550
551 /*----------------------------------------------------------------------
552 |   CUPnP::StartServer
553 +---------------------------------------------------------------------*/
554 bool
555 CUPnP::StartServer()
556 {
557     if (!m_ServerHolder->m_Device.IsNull()) return false;
558
559     // load upnpserver.xml
560     CStdString filename = URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml");
561     CUPnPSettings::Get().Load(filename);
562
563     // create the server with a XBox compatible friendlyname and UUID from upnpserver.xml if found
564     m_ServerHolder->m_Device = CreateServer(CUPnPSettings::Get().GetServerPort());
565
566     // start server
567     NPT_Result res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
568     if (NPT_FAILED(res)) {
569         // if the upnp device port was not 0, it could have failed because
570         // of port being in used, so restart with a random port
571         if (CUPnPSettings::Get().GetServerPort() > 0) m_ServerHolder->m_Device = CreateServer(0);
572
573         res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
574     }
575
576     // save port but don't overwrite saved settings if port was random
577     if (NPT_SUCCEEDED(res)) {
578         if (CUPnPSettings::Get().GetServerPort() == 0) {
579             CUPnPSettings::Get().SetServerPort(m_ServerHolder->m_Device->GetPort());
580         }
581         CUPnPServer::m_MaxReturnedItems = UPNP_DEFAULT_MAX_RETURNED_ITEMS;
582         if (CUPnPSettings::Get().GetMaximumReturnedItems() > 0) {
583             // must be > UPNP_DEFAULT_MIN_RETURNED_ITEMS
584             CUPnPServer::m_MaxReturnedItems = max(UPNP_DEFAULT_MIN_RETURNED_ITEMS, CUPnPSettings::Get().GetMaximumReturnedItems());
585         }
586         CUPnPSettings::Get().SetMaximumReturnedItems(CUPnPServer::m_MaxReturnedItems);
587     }
588
589     // save UUID
590     CUPnPSettings::Get().SetServerUUID(m_ServerHolder->m_Device->GetUUID().GetChars());
591     return CUPnPSettings::Get().Save(filename);
592 }
593
594 /*----------------------------------------------------------------------
595 |   CUPnP::StopServer
596 +---------------------------------------------------------------------*/
597 void
598 CUPnP::StopServer()
599 {
600     if (m_ServerHolder->m_Device.IsNull()) return;
601
602     m_UPnP->RemoveDevice(m_ServerHolder->m_Device);
603     m_ServerHolder->m_Device = NULL;
604 }
605
606 /*----------------------------------------------------------------------
607 |   CUPnP::CreateRenderer
608 +---------------------------------------------------------------------*/
609 CUPnPRenderer*
610 CUPnP::CreateRenderer(int port /* = 0 */)
611 {
612     CUPnPRenderer* device =
613         new CUPnPRenderer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
614                           false,
615                           (CUPnPSettings::Get().GetRendererUUID().length() ? CUPnPSettings::Get().GetRendererUUID().c_str() : NULL),
616                           port);
617
618     device->m_PresentationURL =
619         NPT_HttpUrl(m_IP,
620                     CSettings::Get().GetInt("services.webserverport"),
621                     "/").ToString();
622     device->m_ModelName        = "XBMC Media Center";
623     device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
624     device->m_ModelDescription = "XBMC Media Center - Media Renderer";
625     device->m_ModelURL         = "http://xbmc.org/";
626     device->m_Manufacturer     = "Team XBMC";
627     device->m_ManufacturerURL  = "http://xbmc.org/";
628
629     return device;
630 }
631
632 /*----------------------------------------------------------------------
633 |   CUPnP::StartRenderer
634 +---------------------------------------------------------------------*/
635 bool CUPnP::StartRenderer()
636 {
637     if (!m_RendererHolder->m_Device.IsNull()) return false;
638
639     CStdString filename = URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml");
640     CUPnPSettings::Get().Load(filename);
641
642     m_RendererHolder->m_Device = CreateRenderer(CUPnPSettings::Get().GetRendererPort());
643
644     NPT_Result res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
645
646     // failed most likely because port is in use, try again with random port now
647     if (NPT_FAILED(res) && CUPnPSettings::Get().GetRendererPort() != 0) {
648         m_RendererHolder->m_Device = CreateRenderer(0);
649
650         res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
651     }
652
653     // save port but don't overwrite saved settings if random
654     if (NPT_SUCCEEDED(res) && CUPnPSettings::Get().GetRendererPort() == 0) {
655         CUPnPSettings::Get().SetRendererPort(m_RendererHolder->m_Device->GetPort());
656     }
657
658     // save UUID
659     CUPnPSettings::Get().SetRendererUUID(m_RendererHolder->m_Device->GetUUID().GetChars());
660     return CUPnPSettings::Get().Save(filename);
661 }
662
663 /*----------------------------------------------------------------------
664 |   CUPnP::StopRenderer
665 +---------------------------------------------------------------------*/
666 void CUPnP::StopRenderer()
667 {
668     if (m_RendererHolder->m_Device.IsNull()) return;
669
670     m_UPnP->RemoveDevice(m_RendererHolder->m_Device);
671     m_RendererHolder->m_Device = NULL;
672 }
673
674 /*----------------------------------------------------------------------
675 |   CUPnP::UpdateState
676 +---------------------------------------------------------------------*/
677 void CUPnP::UpdateState()
678 {
679   if (!m_RendererHolder->m_Device.IsNull())
680       ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();
681 }
682
683 void CUPnP::RegisterUserdata(void* ptr)
684 {
685   NPT_AutoLock lock(g_UserDataLock);
686   g_UserData.Add(ptr);
687 }
688
689 void CUPnP::UnregisterUserdata(void* ptr)
690 {
691   NPT_AutoLock lock(g_UserDataLock);
692   g_UserData.Remove(ptr);
693 }
694
695 } /* namespace UPNP */