Merge pull request #2531 from Montellese/settings_cleanup_4
[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 *
6 * http://www.plutinosoft.com/blog/category/platinum/
7 *
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.
12 *
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.
17 *
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.
21 */
22
23 #include "threads/SystemClock.h"
24 #include "UPnP.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"
34 #include "Platinum.h"
35 #include "URL.h"
36 #include "profiles/ProfilesManager.h"
37 #include "settings/GUISettings.h"
38 #include "GUIUserMessages.h"
39 #include "FileItem.h"
40 #include "guilib/GUIWindowManager.h"
41 #include "GUIInfoManager.h"
42 #include "utils/TimeUtils.h"
43 #include "guilib/Key.h"
44 #include "Util.h"
45
46 using namespace std;
47 using namespace UPNP;
48
49 NPT_SET_LOCAL_LOGGER("xbmc.upnp")
50
51 #define UPNP_DEFAULT_MAX_RETURNED_ITEMS 200
52 #define UPNP_DEFAULT_MIN_RETURNED_ITEMS 30
53
54 /*
55 # Play speed
56 #    1 normal
57 #    0 invalid
58 DLNA_ORG_PS = 'DLNA.ORG_PS'
59 DLNA_ORG_PS_VAL = '1'
60
61 # Convertion Indicator
62 #    1 transcoded
63 #    0 not transcoded
64 DLNA_ORG_CI = 'DLNA.ORG_CI'
65 DLNA_ORG_CI_VAL = '0'
66
67 # Operations
68 #    00 not time seek range, not range
69 #    01 range supported
70 #    10 time seek range supported
71 #    11 both supported
72 DLNA_ORG_OP = 'DLNA.ORG_OP'
73 DLNA_ORG_OP_VAL = '01'
74
75 # Flags
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'
90 */
91
92 /*----------------------------------------------------------------------
93 |   NPT_Console::Output
94 +---------------------------------------------------------------------*/
95 void
96 NPT_Console::Output(const char* message)
97 {
98     CLog::Log(LOGDEBUG, "%s", message);
99 }
100
101 namespace UPNP
102 {
103
104 /*----------------------------------------------------------------------
105 |   static
106 +---------------------------------------------------------------------*/
107 CUPnP* CUPnP::upnp = NULL;
108
109 /*----------------------------------------------------------------------
110 |   CDeviceHostReferenceHolder class
111 +---------------------------------------------------------------------*/
112 class CDeviceHostReferenceHolder
113 {
114 public:
115     PLT_DeviceHostReference m_Device;
116 };
117
118 /*----------------------------------------------------------------------
119 |   CCtrlPointReferenceHolder class
120 +---------------------------------------------------------------------*/
121 class CCtrlPointReferenceHolder
122 {
123 public:
124     PLT_CtrlPointReference m_CtrlPoint;
125 };
126
127 /*----------------------------------------------------------------------
128 |   CUPnPCleaner class
129 +---------------------------------------------------------------------*/
130 class CUPnPCleaner : public NPT_Thread
131 {
132 public:
133     CUPnPCleaner(CUPnP* upnp) : NPT_Thread(true), m_UPnP(upnp) {}
134     void Run() {
135         delete m_UPnP;
136     }
137
138     CUPnP* m_UPnP;
139 };
140
141 /*----------------------------------------------------------------------
142 |   CMediaBrowser class
143 +---------------------------------------------------------------------*/
144 class CMediaBrowser : public PLT_SyncMediaBrowser,
145                       public PLT_MediaContainerChangesListener
146 {
147 public:
148     CMediaBrowser(PLT_CtrlPointReference& ctrlPoint)
149         : PLT_SyncMediaBrowser(ctrlPoint, true)
150     {
151         SetContainerListener(this);
152     }
153
154     // PLT_MediaBrowser methods
155     virtual bool OnMSAdded(PLT_DeviceDataReference& device)
156     {
157         CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
158         message.SetStringParam("upnp://");
159         g_windowManager.SendThreadMessage(message);
160
161         return PLT_SyncMediaBrowser::OnMSAdded(device);
162     }
163     virtual void OnMSRemoved(PLT_DeviceDataReference& device)
164     {
165         PLT_SyncMediaBrowser::OnMSRemoved(device);
166
167         CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
168         message.SetStringParam("upnp://");
169         g_windowManager.SendThreadMessage(message);
170
171         PLT_SyncMediaBrowser::OnMSRemoved(device);
172     }
173
174     // PLT_MediaContainerChangesListener methods
175     virtual void OnContainerChanged(PLT_DeviceDataReference& device,
176                                     const char*              item_id,
177                                     const char*              update_id)
178     {
179         NPT_String path = "upnp://"+device->GetUUID()+"/";
180         if (!NPT_StringsEqual(item_id, "0")) {
181             CStdString id = item_id;
182             CURL::Encode(id);
183             URIUtils::AddSlashAtEnd(id);
184             path += id.c_str();
185         }
186
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);
191     }
192 };
193
194
195 /*----------------------------------------------------------------------
196 |   CMediaController class
197 +---------------------------------------------------------------------*/
198 class CMediaController
199   : public PLT_MediaControllerDelegate
200   , public PLT_MediaController
201 {
202 public:
203   CMediaController(PLT_CtrlPointReference& ctrl_point)
204     : PLT_MediaController(ctrl_point)
205   {
206     PLT_MediaController::SetDelegate(this);
207   }
208
209   ~CMediaController()
210   {
211   }
212
213   virtual void OnStopResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
214   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnStopResult(res, device, userdata); }
215
216   virtual void OnSetPlayModeResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
217   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetPlayModeResult(res, device, userdata); }
218
219   virtual void OnSetAVTransportURIResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
220   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSetAVTransportURIResult(res, device, userdata); }
221
222   virtual void OnSeekResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
223   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnSeekResult(res, device, userdata); }
224
225   virtual void OnPreviousResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
226   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPreviousResult(res, device, userdata); }
227
228   virtual void OnPlayResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
229   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPlayResult(res, device, userdata); }
230
231   virtual void OnPauseResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
232   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnPauseResult(res, device, userdata); }
233
234   virtual void OnNextResult(NPT_Result res, PLT_DeviceDataReference& device, void* userdata)
235   { static_cast<PLT_MediaControllerDelegate*>(userdata)->OnNextResult(res, device, userdata); }
236
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); }
239
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); }
242
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); }
245
246
247   virtual bool OnMRAdded(PLT_DeviceDataReference& device )
248   {
249     CPlayerCoreFactory::Get().OnPlayerDiscovered((const char*)device->GetUUID()
250                                           ,(const char*)device->GetFriendlyName()
251                                           , EPC_UPNPPLAYER);
252     return true;
253   }
254
255   virtual void OnMRRemoved(PLT_DeviceDataReference& device )
256   {
257     CPlayerCoreFactory::Get().OnPlayerRemoved((const char*)device->GetUUID());
258   }
259 };
260
261 /*----------------------------------------------------------------------
262 |   CUPnP::CUPnP
263 +---------------------------------------------------------------------*/
264 CUPnP::CUPnP() :
265     m_MediaBrowser(NULL),
266     m_MediaController(NULL),
267     m_ServerHolder(new CDeviceHostReferenceHolder()),
268     m_RendererHolder(new CRendererReferenceHolder()),
269     m_CtrlPointHolder(new CCtrlPointReferenceHolder())
270 {
271     // initialize upnp context
272     m_UPnP = new PLT_UPnP();
273
274     // keep main IP around
275     if (g_application.getNetwork().GetFirstConnectedInterface()) {
276         m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
277     }
278     NPT_List<NPT_IpAddress> list;
279     if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list)) && list.GetItemCount()) {
280         m_IP = (*(list.GetFirstItem())).ToString();
281     }
282     else if(m_IP.IsEmpty())
283         m_IP = "localhost";
284
285     // start upnp monitoring
286     m_UPnP->Start();
287 }
288
289 /*----------------------------------------------------------------------
290 |   CUPnP::~CUPnP
291 +---------------------------------------------------------------------*/
292 CUPnP::~CUPnP()
293 {
294     m_UPnP->Stop();
295     StopClient();
296     StopServer();
297
298     delete m_UPnP;
299     delete m_ServerHolder;
300     delete m_RendererHolder;
301     delete m_CtrlPointHolder;
302 }
303
304 /*----------------------------------------------------------------------
305 |   CUPnP::GetInstance
306 +---------------------------------------------------------------------*/
307 CUPnP*
308 CUPnP::GetInstance()
309 {
310     if (!upnp) {
311         upnp = new CUPnP();
312     }
313
314     return upnp;
315 }
316
317 /*----------------------------------------------------------------------
318 |   CUPnP::ReleaseInstance
319 +---------------------------------------------------------------------*/
320 void
321 CUPnP::ReleaseInstance(bool bWait)
322 {
323     if (upnp) {
324         CUPnP* _upnp = upnp;
325         upnp = NULL;
326
327         if (bWait) {
328             delete _upnp;
329         } else {
330             // since it takes a while to clean up
331             // starts a detached thread to do this
332             CUPnPCleaner* cleaner = new CUPnPCleaner(_upnp);
333             cleaner->Start();
334         }
335     }
336 }
337
338 /*----------------------------------------------------------------------
339 |   CUPnP::StartServer
340 +---------------------------------------------------------------------*/
341 CUPnPServer* CUPnP::GetServer()
342 {
343   if(upnp)
344     return (CUPnPServer*)upnp->m_ServerHolder->m_Device.AsPointer();
345   return NULL;
346 }
347
348 /*----------------------------------------------------------------------
349 |   CUPnP::StartClient
350 +---------------------------------------------------------------------*/
351 void
352 CUPnP::StartClient()
353 {
354     if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
355
356     // create controlpoint
357     m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint();
358
359     // start it
360     m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
361
362     // start browser
363     m_MediaBrowser = new CMediaBrowser(m_CtrlPointHolder->m_CtrlPoint);
364
365     // start controller
366     if (g_guiSettings.GetBool("services.upnpcontroller")) {
367         m_MediaController = new CMediaController(m_CtrlPointHolder->m_CtrlPoint);
368     }
369 }
370
371 /*----------------------------------------------------------------------
372 |   CUPnP::StopClient
373 +---------------------------------------------------------------------*/
374 void
375 CUPnP::StopClient()
376 {
377     if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
378
379     m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
380     m_CtrlPointHolder->m_CtrlPoint = NULL;
381
382     delete m_MediaBrowser;
383     m_MediaBrowser = NULL;
384     delete m_MediaController;
385     m_MediaController = NULL;
386 }
387
388 /*----------------------------------------------------------------------
389 |   CUPnP::CreateServer
390 +---------------------------------------------------------------------*/
391 CUPnPServer*
392 CUPnP::CreateServer(int port /* = 0 */)
393 {
394     CUPnPServer* device =
395         new CUPnPServer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
396                         CUPnPSettings::Get().GetServerUUID().length() ? CUPnPSettings::Get().GetServerUUID().c_str() : NULL,
397                         port);
398
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 =
402         NPT_HttpUrl(m_IP,
403                     atoi(g_guiSettings.GetString("services.webserverport")),
404                     "/").ToString();
405
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/";
412
413     device->SetDelegate(device);
414     return device;
415 }
416
417 /*----------------------------------------------------------------------
418 |   CUPnP::StartServer
419 +---------------------------------------------------------------------*/
420 void
421 CUPnP::StartServer()
422 {
423     if (!m_ServerHolder->m_Device.IsNull()) return;
424
425     // load upnpserver.xml so that g_settings.m_vecUPnPMusiCMediaSources, etc.. are loaded
426     CStdString filename;
427     URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml", filename);
428     CUPnPSettings::Get().Load(filename);
429
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());
432
433     // start server
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);
439
440         res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
441     }
442
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());
447         }
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());
452         }
453         CUPnPSettings::Get().SetMaximumReturnedItems(CUPnPServer::m_MaxReturnedItems);
454     }
455
456     // save UUID
457     CUPnPSettings::Get().SetServerUUID(m_ServerHolder->m_Device->GetUUID().GetChars());
458     CUPnPSettings::Get().Save(filename);
459 }
460
461 /*----------------------------------------------------------------------
462 |   CUPnP::StopServer
463 +---------------------------------------------------------------------*/
464 void
465 CUPnP::StopServer()
466 {
467     if (m_ServerHolder->m_Device.IsNull()) return;
468
469     m_UPnP->RemoveDevice(m_ServerHolder->m_Device);
470     m_ServerHolder->m_Device = NULL;
471 }
472
473 /*----------------------------------------------------------------------
474 |   CUPnP::CreateRenderer
475 +---------------------------------------------------------------------*/
476 CUPnPRenderer*
477 CUPnP::CreateRenderer(int port /* = 0 */)
478 {
479     CUPnPRenderer* device =
480         new CUPnPRenderer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
481                           false,
482                           (CUPnPSettings::Get().GetRendererUUID().length() ? CUPnPSettings::Get().GetRendererUUID().c_str() : NULL),
483                           port);
484
485     device->m_PresentationURL =
486         NPT_HttpUrl(m_IP,
487                     atoi(g_guiSettings.GetString("services.webserverport")),
488                     "/").ToString();
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/";
495
496     return device;
497 }
498
499 /*----------------------------------------------------------------------
500 |   CUPnP::StartRenderer
501 +---------------------------------------------------------------------*/
502 void CUPnP::StartRenderer()
503 {
504     if (!m_RendererHolder->m_Device.IsNull()) return;
505
506     CStdString filename;
507     URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(), "upnpserver.xml", filename);
508     CUPnPSettings::Get().Load(filename);
509
510     m_RendererHolder->m_Device = CreateRenderer(CUPnPSettings::Get().GetRendererPort());
511
512     NPT_Result res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
513
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);
517
518         res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
519     }
520
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());
524     }
525
526     // save UUID
527     CUPnPSettings::Get().SetRendererUUID(m_RendererHolder->m_Device->GetUUID().GetChars());
528     CUPnPSettings::Get().Save(filename);
529 }
530
531 /*----------------------------------------------------------------------
532 |   CUPnP::StopRenderer
533 +---------------------------------------------------------------------*/
534 void CUPnP::StopRenderer()
535 {
536     if (m_RendererHolder->m_Device.IsNull()) return;
537
538     m_UPnP->RemoveDevice(m_RendererHolder->m_Device);
539     m_RendererHolder->m_Device = NULL;
540 }
541
542 /*----------------------------------------------------------------------
543 |   CUPnP::UpdateState
544 +---------------------------------------------------------------------*/
545 void CUPnP::UpdateState()
546 {
547   if (!m_RendererHolder->m_Device.IsNull())
548       ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();
549 }
550
551 } /* namespace UPNP */