DVDCodecs: Amlogic: Handle conditions in which amcodec should be opened during Open()
[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(CURL::Encode(item_id));
187             URIUtils::AddSlashAtEnd(id);
188             path += id.c_str();
189         }
190
191         CLog::Log(LOGDEBUG, "UPNP: notfified container update %s", (const char*)path);
192         CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
193         message.SetStringParam(path.GetChars());
194         g_windowManager.SendThreadMessage(message);
195     }
196
197     bool MarkWatched(const CFileItem& item, const bool watched)
198     {
199         if (watched) {
200             CFileItem temp(item);
201             temp.SetProperty("original_listitem_url", item.GetPath());
202             return SaveFileState(temp, CBookmark(), watched);
203         }
204         else {
205             CLog::Log(LOGDEBUG, "UPNP: Marking video item %s as watched", item.GetPath().c_str());
206             return InvokeUpdateObject(item.GetPath().c_str(), "<upnp:playCount>1</upnp:playCount>", "<upnp:playCount>0</upnp:playCount>");
207         }
208     }
209
210     bool SaveFileState(const CFileItem& item, const CBookmark& bookmark, const bool updatePlayCount)
211     {
212         string path = item.GetProperty("original_listitem_url").asString();
213         if (!item.HasVideoInfoTag() || path.empty())  {
214           return false;
215         }
216
217         NPT_String curr_value;
218         NPT_String new_value;
219
220         if (item.GetVideoInfoTag()->m_resumePoint.timeInSeconds != bookmark.timeInSeconds) {
221             CLog::Log(LOGDEBUG, "UPNP: Updating resume point for item %s", path.c_str());
222             long time = (long)bookmark.timeInSeconds;
223             if (time < 0) time = 0;
224             curr_value.Append(NPT_String::Format("<upnp:lastPlaybackPosition>%ld</upnp:lastPlaybackPosition>",
225                                                  (long)item.GetVideoInfoTag()->m_resumePoint.timeInSeconds));
226             new_value.Append(NPT_String::Format("<upnp:lastPlaybackPosition>%ld</upnp:lastPlaybackPosition>", time));
227         }
228         if (updatePlayCount) {
229             CLog::Log(LOGDEBUG, "UPNP: Marking video item %s as watched", path.c_str());
230             if (!curr_value.IsEmpty()) curr_value.Append(",");
231             if (!new_value.IsEmpty()) new_value.Append(",");
232             curr_value.Append("<upnp:playCount>0</upnp:playCount>");
233             new_value.Append("<upnp:playCount>1</upnp:playCount>");
234         }
235
236         return InvokeUpdateObject(path.c_str(), (const char*)curr_value, (const char*)new_value);
237     }
238
239     bool InvokeUpdateObject(const char* id, const char* curr_value, const char* new_value)
240     {
241         CURL url(id);
242         PLT_DeviceDataReference device;
243         PLT_Service* cds;
244         PLT_ActionReference action;
245
246         CLog::Log(LOGDEBUG, "UPNP: attempting to invoke UpdateObject for %s", id);
247
248         // check this server supports UpdateObject action
249         NPT_CHECK_LABEL(FindServer(url.GetHostName().c_str(), device),failed);
250         NPT_CHECK_LABEL(device->FindServiceById("urn:upnp-org:serviceId:ContentDirectory", cds),failed);
251
252         NPT_CHECK_SEVERE(m_CtrlPoint->CreateAction(
253             device,
254             "urn:schemas-upnp-org:service:ContentDirectory:1",
255             "UpdateObject",
256             action));
257
258         NPT_CHECK_LABEL(action->SetArgumentValue("ObjectID", url.GetFileName().c_str()), failed);
259         NPT_CHECK_LABEL(action->SetArgumentValue("CurrentTagValue", curr_value), failed);
260         NPT_CHECK_LABEL(action->SetArgumentValue("NewTagValue", new_value), failed);
261
262         NPT_CHECK_LABEL(m_CtrlPoint->InvokeAction(action, NULL),failed);
263
264         CLog::Log(LOGDEBUG, "UPNP: invoked UpdateObject successfully");
265         return true;
266
267     failed:
268         CLog::Log(LOGINFO, "UPNP: invoking UpdateObject failed");
269         return false;
270     }
271 };
272
273
274 /*----------------------------------------------------------------------
275 |   CMediaController class
276 +---------------------------------------------------------------------*/
277 class CMediaController
278   : public PLT_MediaControllerDelegate
279   , public PLT_MediaController
280 {
281 public:
282   CMediaController(PLT_CtrlPointReference& ctrl_point)
283     : PLT_MediaController(ctrl_point)
284   {
285     PLT_MediaController::SetDelegate(this);
286   }
287
288   ~CMediaController()
289   {
290   }
291
292 #define CHECK_USERDATA_RETURN(userdata) do {     \
293   if (!g_UserData.Contains(userdata))            \
294       return;                                    \
295   } while(0)
296
297   virtual void OnStopResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
298   { CHECK_USERDATA_RETURN(userdata);
299     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnStopResult(res, device, userdata);
300   }
301
302   virtual void OnSetPlayModeResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
303   { CHECK_USERDATA_RETURN(userdata);
304     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetPlayModeResult(res, device, userdata);
305   }
306
307   virtual void OnSetAVTransportURIResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
308   { CHECK_USERDATA_RETURN(userdata);
309     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetAVTransportURIResult(res, device, userdata);
310   }
311
312   virtual void OnSeekResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
313   { CHECK_USERDATA_RETURN(userdata);
314     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSeekResult(res, device, userdata);
315   }
316
317   virtual void OnPreviousResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
318   { CHECK_USERDATA_RETURN(userdata);
319     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPreviousResult(res, device, userdata);
320   }
321
322   virtual void OnPlayResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
323   { CHECK_USERDATA_RETURN(userdata);
324     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPlayResult(res, device, userdata);
325   }
326
327   virtual void OnPauseResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
328   { CHECK_USERDATA_RETURN(userdata);
329     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPauseResult(res, device, userdata);
330   }
331
332   virtual void OnNextResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
333   { CHECK_USERDATA_RETURN(userdata);
334     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnNextResult(res, device, userdata);
335   }
336
337   virtual void OnGetMediaInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_MediaInfo* info, void* userdata)
338   { CHECK_USERDATA_RETURN(userdata);
339     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetMediaInfoResult(res, device, info, userdata);
340   }
341
342   virtual void OnGetPositionInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_PositionInfo* info, void* userdata)
343   { CHECK_USERDATA_RETURN(userdata);
344     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetPositionInfoResult(res, device, info, userdata);
345   }
346
347   virtual void OnGetTransportInfoResult(NPT_Result res, PLT_DeviceDataReference& device, PLT_TransportInfo* info, void* userdata)
348   { CHECK_USERDATA_RETURN(userdata);
349     static_cast<PLT_MediaControllerDelegate*>(userdata)->OnGetTransportInfoResult(res, device, info, userdata);
350   }
351
352   virtual bool OnMRAdded(PLT_DeviceDataReference& device )
353   {
354     CPlayerCoreFactory::Get().OnPlayerDiscovered((const char*)device->GetUUID()
355                                           ,(const char*)device->GetFriendlyName()
356                                           , EPC_UPNPPLAYER);
357     return true;
358   }
359
360   virtual void OnMRRemoved(PLT_DeviceDataReference& device )
361   {
362     CPlayerCoreFactory::Get().OnPlayerRemoved((const char*)device->GetUUID());
363   }
364 };
365
366 /*----------------------------------------------------------------------
367 |   CUPnP::CUPnP
368 +---------------------------------------------------------------------*/
369 CUPnP::CUPnP() :
370     m_MediaBrowser(NULL),
371     m_MediaController(NULL),
372     m_ServerHolder(new CDeviceHostReferenceHolder()),
373     m_RendererHolder(new CRendererReferenceHolder()),
374     m_CtrlPointHolder(new CCtrlPointReferenceHolder())
375 {
376     // initialize upnp context
377     m_UPnP = new PLT_UPnP();
378
379     // keep main IP around
380     if (g_application.getNetwork().GetFirstConnectedInterface()) {
381         m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
382     }
383     NPT_List<NPT_IpAddress> list;
384     if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list)) && list.GetItemCount()) {
385         m_IP = (*(list.GetFirstItem())).ToString();
386     }
387     else if(m_IP.empty())
388         m_IP = "localhost";
389
390     // start upnp monitoring
391     m_UPnP->Start();
392 }
393
394 /*----------------------------------------------------------------------
395 |   CUPnP::~CUPnP
396 +---------------------------------------------------------------------*/
397 CUPnP::~CUPnP()
398 {
399     m_UPnP->Stop();
400     StopClient();
401     StopServer();
402
403     delete m_UPnP;
404     delete m_ServerHolder;
405     delete m_RendererHolder;
406     delete m_CtrlPointHolder;
407 }
408
409 /*----------------------------------------------------------------------
410 |   CUPnP::GetInstance
411 +---------------------------------------------------------------------*/
412 CUPnP*
413 CUPnP::GetInstance()
414 {
415     if (!upnp) {
416         upnp = new CUPnP();
417     }
418
419     return upnp;
420 }
421
422 /*----------------------------------------------------------------------
423 |   CUPnP::ReleaseInstance
424 +---------------------------------------------------------------------*/
425 void
426 CUPnP::ReleaseInstance(bool bWait)
427 {
428     if (upnp) {
429         CUPnP* _upnp = upnp;
430         upnp = NULL;
431
432         if (bWait) {
433             delete _upnp;
434         } else {
435             // since it takes a while to clean up
436             // starts a detached thread to do this
437             CUPnPCleaner* cleaner = new CUPnPCleaner(_upnp);
438             cleaner->Start();
439         }
440     }
441 }
442
443 /*----------------------------------------------------------------------
444 |   CUPnP::StartServer
445 +---------------------------------------------------------------------*/
446 CUPnPServer* CUPnP::GetServer()
447 {
448   if(upnp)
449     return (CUPnPServer*)upnp->m_ServerHolder->m_Device.AsPointer();
450   return NULL;
451 }
452
453 /*----------------------------------------------------------------------
454 |   CUPnP::MarkWatched
455 +---------------------------------------------------------------------*/
456 bool
457 CUPnP::MarkWatched(const CFileItem& item, const bool watched)
458 {
459     if (upnp && upnp->m_MediaBrowser) {
460         // dynamic_cast is safe here, avoids polluting CUPnP.h header file
461         CMediaBrowser* browser = dynamic_cast<CMediaBrowser*>(upnp->m_MediaBrowser);
462         return browser->MarkWatched(item, watched);
463     }
464     return false;
465 }
466
467 /*----------------------------------------------------------------------
468 |   CUPnP::SaveFileState
469 +---------------------------------------------------------------------*/
470 bool
471 CUPnP::SaveFileState(const CFileItem& item, const CBookmark& bookmark, const bool updatePlayCount)
472 {
473     if (upnp && upnp->m_MediaBrowser) {
474         // dynamic_cast is safe here, avoids polluting CUPnP.h header file
475         CMediaBrowser* browser = dynamic_cast<CMediaBrowser*>(upnp->m_MediaBrowser);
476         return browser->SaveFileState(item, bookmark, updatePlayCount);
477     }
478     return false;
479 }
480
481 /*----------------------------------------------------------------------
482 |   CUPnP::StartClient
483 +---------------------------------------------------------------------*/
484 void
485 CUPnP::StartClient()
486 {
487     if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
488
489     // create controlpoint
490     m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint();
491
492     // start it
493     m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
494
495     // start browser
496     m_MediaBrowser = new CMediaBrowser(m_CtrlPointHolder->m_CtrlPoint);
497
498     // start controller
499     if (CSettings::Get().GetBool("services.upnpcontroller")) {
500         m_MediaController = new CMediaController(m_CtrlPointHolder->m_CtrlPoint);
501     }
502 }
503
504 /*----------------------------------------------------------------------
505 |   CUPnP::StopClient
506 +---------------------------------------------------------------------*/
507 void
508 CUPnP::StopClient()
509 {
510     if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
511
512     m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
513     m_CtrlPointHolder->m_CtrlPoint = NULL;
514
515     delete m_MediaBrowser;
516     m_MediaBrowser = NULL;
517     delete m_MediaController;
518     m_MediaController = NULL;
519 }
520
521 /*----------------------------------------------------------------------
522 |   CUPnP::CreateServer
523 +---------------------------------------------------------------------*/
524 CUPnPServer*
525 CUPnP::CreateServer(int port /* = 0 */)
526 {
527     CUPnPServer* device =
528         new CUPnPServer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
529                         CUPnPSettings::Get().GetServerUUID().length() ? CUPnPSettings::Get().GetServerUUID().c_str() : NULL,
530                         port);
531
532     // trying to set optional upnp values for XP UPnP UI Icons to detect us
533     // but it doesn't work anyways as it requires multicast for XP to detect us
534     device->m_PresentationURL =
535         NPT_HttpUrl(m_IP,
536                     CSettings::Get().GetInt("services.webserverport"),
537                     "/").ToString();
538
539     device->m_ModelName        = "XBMC Media Center";
540     device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
541     device->m_ModelDescription = "XBMC Media Center - Media Server";
542     device->m_ModelURL         = "http://xbmc.org/";
543     device->m_Manufacturer     = "Team XBMC";
544     device->m_ManufacturerURL  = "http://xbmc.org/";
545
546     device->SetDelegate(device);
547     return device;
548 }
549
550 /*----------------------------------------------------------------------
551 |   CUPnP::StartServer
552 +---------------------------------------------------------------------*/
553 bool
554 CUPnP::StartServer()
555 {
556     if (!m_ServerHolder->m_Device.IsNull()) return false;
557
558     // load upnpserver.xml
559     CStdString filename = URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml");
560     CUPnPSettings::Get().Load(filename);
561
562     // create the server with a XBox compatible friendlyname and UUID from upnpserver.xml if found
563     m_ServerHolder->m_Device = CreateServer(CUPnPSettings::Get().GetServerPort());
564
565     // start server
566     NPT_Result res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
567     if (NPT_FAILED(res)) {
568         // if the upnp device port was not 0, it could have failed because
569         // of port being in used, so restart with a random port
570         if (CUPnPSettings::Get().GetServerPort() > 0) m_ServerHolder->m_Device = CreateServer(0);
571
572         res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
573     }
574
575     // save port but don't overwrite saved settings if port was random
576     if (NPT_SUCCEEDED(res)) {
577         if (CUPnPSettings::Get().GetServerPort() == 0) {
578             CUPnPSettings::Get().SetServerPort(m_ServerHolder->m_Device->GetPort());
579         }
580         CUPnPServer::m_MaxReturnedItems = UPNP_DEFAULT_MAX_RETURNED_ITEMS;
581         if (CUPnPSettings::Get().GetMaximumReturnedItems() > 0) {
582             // must be > UPNP_DEFAULT_MIN_RETURNED_ITEMS
583             CUPnPServer::m_MaxReturnedItems = max(UPNP_DEFAULT_MIN_RETURNED_ITEMS, CUPnPSettings::Get().GetMaximumReturnedItems());
584         }
585         CUPnPSettings::Get().SetMaximumReturnedItems(CUPnPServer::m_MaxReturnedItems);
586     }
587
588     // save UUID
589     CUPnPSettings::Get().SetServerUUID(m_ServerHolder->m_Device->GetUUID().GetChars());
590     return CUPnPSettings::Get().Save(filename);
591 }
592
593 /*----------------------------------------------------------------------
594 |   CUPnP::StopServer
595 +---------------------------------------------------------------------*/
596 void
597 CUPnP::StopServer()
598 {
599     if (m_ServerHolder->m_Device.IsNull()) return;
600
601     m_UPnP->RemoveDevice(m_ServerHolder->m_Device);
602     m_ServerHolder->m_Device = NULL;
603 }
604
605 /*----------------------------------------------------------------------
606 |   CUPnP::CreateRenderer
607 +---------------------------------------------------------------------*/
608 CUPnPRenderer*
609 CUPnP::CreateRenderer(int port /* = 0 */)
610 {
611     CUPnPRenderer* device =
612         new CUPnPRenderer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
613                           false,
614                           (CUPnPSettings::Get().GetRendererUUID().length() ? CUPnPSettings::Get().GetRendererUUID().c_str() : NULL),
615                           port);
616
617     device->m_PresentationURL =
618         NPT_HttpUrl(m_IP,
619                     CSettings::Get().GetInt("services.webserverport"),
620                     "/").ToString();
621     device->m_ModelName        = "XBMC Media Center";
622     device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
623     device->m_ModelDescription = "XBMC Media Center - Media Renderer";
624     device->m_ModelURL         = "http://xbmc.org/";
625     device->m_Manufacturer     = "Team XBMC";
626     device->m_ManufacturerURL  = "http://xbmc.org/";
627
628     return device;
629 }
630
631 /*----------------------------------------------------------------------
632 |   CUPnP::StartRenderer
633 +---------------------------------------------------------------------*/
634 bool CUPnP::StartRenderer()
635 {
636     if (!m_RendererHolder->m_Device.IsNull()) return false;
637
638     CStdString filename = URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml");
639     CUPnPSettings::Get().Load(filename);
640
641     m_RendererHolder->m_Device = CreateRenderer(CUPnPSettings::Get().GetRendererPort());
642
643     NPT_Result res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
644
645     // failed most likely because port is in use, try again with random port now
646     if (NPT_FAILED(res) && CUPnPSettings::Get().GetRendererPort() != 0) {
647         m_RendererHolder->m_Device = CreateRenderer(0);
648
649         res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
650     }
651
652     // save port but don't overwrite saved settings if random
653     if (NPT_SUCCEEDED(res) && CUPnPSettings::Get().GetRendererPort() == 0) {
654         CUPnPSettings::Get().SetRendererPort(m_RendererHolder->m_Device->GetPort());
655     }
656
657     // save UUID
658     CUPnPSettings::Get().SetRendererUUID(m_RendererHolder->m_Device->GetUUID().GetChars());
659     return CUPnPSettings::Get().Save(filename);
660 }
661
662 /*----------------------------------------------------------------------
663 |   CUPnP::StopRenderer
664 +---------------------------------------------------------------------*/
665 void CUPnP::StopRenderer()
666 {
667     if (m_RendererHolder->m_Device.IsNull()) return;
668
669     m_UPnP->RemoveDevice(m_RendererHolder->m_Device);
670     m_RendererHolder->m_Device = NULL;
671 }
672
673 /*----------------------------------------------------------------------
674 |   CUPnP::UpdateState
675 +---------------------------------------------------------------------*/
676 void CUPnP::UpdateState()
677 {
678   if (!m_RendererHolder->m_Device.IsNull())
679       ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();
680 }
681
682 void CUPnP::RegisterUserdata(void* ptr)
683 {
684   NPT_AutoLock lock(g_UserDataLock);
685   g_UserData.Add(ptr);
686 }
687
688 void CUPnP::UnregisterUserdata(void* ptr)
689 {
690   NPT_AutoLock lock(g_UserDataLock);
691   g_UserData.Remove(ptr);
692 }
693
694 } /* namespace UPNP */