[vlc-devel] [PATCH v4 2/6] upnp: Add basic support for registering MediaRenderer service

Johan Gunnarsson johan.gunnarsson at gmail.com
Mon Aug 19 00:32:27 CEST 2019


This introduces a UPnP device handle that is independent of the UPnP client
handle. With this, modules can enable the SOAP server, web server and register
the media renderer services only when they are in use.
---
 modules/services_discovery/upnp-wrapper.cpp | 109 +++++++++++++++++++-
 modules/services_discovery/upnp-wrapper.hpp |   7 ++
 2 files changed, 115 insertions(+), 1 deletion(-)

diff --git a/modules/services_discovery/upnp-wrapper.cpp b/modules/services_discovery/upnp-wrapper.cpp
index 8564d8c768..b4a15d4639 100644
--- a/modules/services_discovery/upnp-wrapper.cpp
+++ b/modules/services_discovery/upnp-wrapper.cpp
@@ -36,19 +36,65 @@
 #include "upnp-wrapper.hpp"
 #include <vlc_cxx_helpers.hpp>
 
+static const char *mediarenderer_desc =
+    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+    "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
+      "<specVersion>"
+        "<major>1</major>"
+        "<minor>0</minor>"
+      "</specVersion>"
+      "<device>"
+        "<deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType>"
+        "<friendlyName>VLC media player</friendlyName>" /* TODO: include hostname */
+        "<manufacturer>VideoLAN</manufacturer>"
+        "<modelName>" PACKAGE_NAME "</modelName>"
+        "<modelNumber>" PACKAGE_VERSION "</modelNumber>"
+        "<modelURL>https://www.videolan.org/vlc/</modelURL>"
+        "<UDN>" UPNP_UDN "</UDN>" /* TODO: generate at each startup */
+        "<serviceList>"
+          "<service>"
+            "<serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType>"
+            "<serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId>"
+            "<SCPDURL>/RenderingControlSCPD.xml</SCPDURL>"
+            "<controlURL>/upnp/control/RenderingControl</controlURL>"
+            "<eventSubURL>/upnp/event/RenderingControl</eventSubURL>"
+          "</service>"
+          "<service>"
+            "<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>"
+            "<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>"
+            "<SCPDURL>/ConnectionManagerSCPD.xml</SCPDURL>"
+            "<controlURL>/upnp/control/ConnectionManager</controlURL>"
+            "<eventSubURL>/upnp/event/ConnectionManager</eventSubURL>"
+          "</service>"
+          "<service>"
+            "<serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>"
+            "<serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>"
+            "<SCPDURL>/AVTransportSCPD.xml</SCPDURL>"
+            "<controlURL>/upnp/control/AVTransport</controlURL>"
+            "<eventSubURL>/upnp/event/AVTransport</eventSubURL>"
+          "</service>"
+        "</serviceList>"
+      "</device>"
+    "</root>";
+
 UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
 UpnpInstanceWrapper::Listeners UpnpInstanceWrapper::s_listeners;
 vlc_mutex_t UpnpInstanceWrapper::s_lock = VLC_STATIC_MUTEX;
 
 UpnpInstanceWrapper::UpnpInstanceWrapper()
     : m_client_handle( -1 )
+    , m_device_handle( -1 )
     , m_refcount( 0 )
+    , m_mediarenderer_refcount( 0 )
 {
 }
 
 UpnpInstanceWrapper::~UpnpInstanceWrapper()
 {
-    UpnpUnRegisterClient( m_client_handle );
+    if( m_client_handle != -1 )
+        UpnpUnRegisterClient( m_client_handle );
+    if( m_device_handle != -1 )
+        UpnpUnRegisterRootDevice( m_device_handle );
     UpnpFinish();
 }
 
@@ -104,6 +150,15 @@ UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj)
             delete instance;
             return NULL;
         }
+
+        char *root = config_GetSysPath( VLC_PKG_DATA_DIR, "upnp" );
+        if( (i_res = UpnpSetWebServerRootDir( root )) != UPNP_E_SUCCESS)
+        {
+            msg_Warn( p_obj, "UpnpSetWebServerRootDir failed: %s",
+                      UpnpGetErrorMessage( i_res ));
+        }
+        free( root );
+
         s_instance = instance;
     }
     s_instance->m_refcount++;
@@ -127,6 +182,11 @@ UpnpClient_Handle UpnpInstanceWrapper::client_handle() const
     return m_client_handle;
 }
 
+UpnpDevice_Handle UpnpInstanceWrapper::device_handle() const
+{
+    return m_device_handle;
+}
+
 int UpnpInstanceWrapper::Callback(Upnp_EventType event_type, UpnpEventPtr p_event, void *p_user_data)
 {
     vlc::threads::mutex_locker lock( &s_lock );
@@ -152,3 +212,50 @@ void UpnpInstanceWrapper::removeListener(ListenerPtr listener)
     if ( iter != s_listeners.end() )
         s_listeners.erase( iter );
 }
+
+void UpnpInstanceWrapper::startMediaRenderer( vlc_object_t *p_obj )
+{
+    vlc::threads::mutex_locker lock( &s_lock );
+    if( m_mediarenderer_refcount == 0 )
+    {
+        int i_res;
+        if( (i_res = UpnpEnableWebserver( TRUE )) != UPNP_E_SUCCESS)
+        {
+            msg_Err( p_obj, "Failed to enable webserver: %s", UpnpGetErrorMessage( i_res ) );
+            return;
+        }
+        i_res = UpnpRegisterRootDevice2( UPNPREG_BUF_DESC,
+                                         mediarenderer_desc,
+                                         strlen(mediarenderer_desc),
+                                         1,
+                                         Callback,
+                                         nullptr,
+                                         &m_device_handle );
+        if( i_res != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Device registration failed: %s", UpnpGetErrorMessage( i_res ) );
+        }
+    }
+    m_mediarenderer_refcount++;
+}
+
+void UpnpInstanceWrapper::stopMediaRenderer( vlc_object_t *p_obj )
+{
+    vlc::threads::mutex_locker lock( &s_lock );
+    m_mediarenderer_refcount--;
+    if( m_mediarenderer_refcount == 0 )
+    {
+        int i_res;
+        i_res = UpnpUnRegisterRootDevice( m_device_handle );
+        if( i_res != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Device unregistration failed: %s", UpnpGetErrorMessage( i_res ) );
+        }
+        m_device_handle = -1;
+        i_res = UpnpEnableWebserver( FALSE );
+        if( i_res != UPNP_E_SUCCESS )
+        {
+            msg_Warn( p_obj, "Failed to disable webserver: %s", UpnpGetErrorMessage( i_res ) );
+        }
+    }
+}
diff --git a/modules/services_discovery/upnp-wrapper.hpp b/modules/services_discovery/upnp-wrapper.hpp
index c59592b0b3..c9f3e152d4 100644
--- a/modules/services_discovery/upnp-wrapper.hpp
+++ b/modules/services_discovery/upnp-wrapper.hpp
@@ -41,6 +41,8 @@
 #include <upnp.h>
 #include <upnptools.h>
 
+#define UPNP_UDN "uuid:034fc8dc-ec22-44e5-a79b-38c935f11663"
+
 #if UPNP_VERSION < 10800
 typedef void* UpnpEventPtr;
 #else
@@ -71,7 +73,9 @@ private:
     static UpnpInstanceWrapper* s_instance;
     static vlc_mutex_t s_lock;
     UpnpClient_Handle m_client_handle;
+    UpnpDevice_Handle m_device_handle;
     int m_refcount;
+    int m_mediarenderer_refcount;
     typedef std::shared_ptr<Listener> ListenerPtr;
     typedef std::vector<ListenerPtr> Listeners;
     static Listeners s_listeners;
@@ -81,8 +85,11 @@ public:
     static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
     void release();
     UpnpClient_Handle client_handle() const;
+    UpnpDevice_Handle device_handle() const;
     void addListener(ListenerPtr listener);
     void removeListener(ListenerPtr listener);
+    void startMediaRenderer( vlc_object_t *p_obj );
+    void stopMediaRenderer( vlc_object_t *p_obj );
 
 private:
     static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
-- 
2.17.1



More information about the vlc-devel mailing list