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