[vlc-devel] [PATCH 3/5] upnp: add renderer discoverer

Shaleen Jain shaleen at jain.sh
Mon Oct 15 16:15:33 CEST 2018


On Mon, 2018-10-15 at 15:55 +0200, Steve Lhomme wrote:
> On 15/10/2018 10:18, Shaleen Jain wrote:
> > ---
> >   modules/services_discovery/upnp.cpp | 342 ++++++++++++++++++++++++++++
> >   modules/services_discovery/upnp.hpp |  39 ++++
> >   2 files changed, 381 insertions(+)
> > 
> > diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp
> > index 2e9decee52..c1c2b947b2 100644
> > --- a/modules/services_discovery/upnp.cpp
> > +++ b/modules/services_discovery/upnp.cpp
> > @@ -8,6 +8,7 @@
> >    *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
> >    *          Hugo Beauzée-Luyssen <
hugo at beauzee.fr> > >
> >    *          Shaleen Jain <
shaleen at jain.sh> > >
> > + *          William Ung <
william1.ung at epitech.eu> > >
> 
> What part of this code did this author contribute to ? Is s·he OK with 
> the license of the code ?

Mainly the MediaRendererList and MediaRendererDesc. With a few changes and addition due to the
UpnpInstanceWrapper class.
Yes, the code was originally licensed under the current license.
> >    *   * This program is free software; you can redistribute it
> > and/or modify it   * under the terms of the GNU Lesser General
> > Public License as published by@@ -34,6 +35,7 @@  #include
> > <vlc_plugin.h>  #include <vlc_interrupt.h>  #include
> > <vlc_services_discovery.h>+#include
> > <vlc_renderer_discovery.h>    #include <assert.h>  #include
> > <limits.h>@@ -45,9 +47,11 @@   * Constants  */  const char*
> > MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-
> > org:device:MediaServer:1";+const char* MEDIA_RENDERER_DEVICE_TYPE =
> > "urn:schemas-upnp-org:device:MediaRenderer:1";  const char*
> > CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-
> > org:service:ContentDirectory:1";  const char*
> > SATIP_SERVER_DEVICE_TYPE = "urn:ses-
> > com:device:SatIPServer:1";  +#define UPNP_SEARCH_TIMEOUT_SECONDS
> > 15  #define SATIP_CHANNEL_LIST N_("SAT>IP channel list")  #define
> > SATIP_CHANNEL_LIST_URL N_("Custom SAT>IP channel list URL")  static
> > const char *const ppsz_satip_channel_lists[] = {@@ -69,6 +73,14 @@
> > struct
> > services_discovery_sys_t      vlc_thread_t         thread;  };  ++s
> > truct renderer_discovery_sys_t+{+    UpnpInstanceWrapper*
> > p_upnp;+    std::shared_ptr<RD::MediaRendererList>
> > p_renderer_list;+    vlc_thread_t thread;+};+  struct
> > access_sys_t  {      UpnpInstanceWrapper* p_upnp;@@ -91,7 +103,14
> > @@ namespace Access      static void CloseAccess( vlc_object_t*
> > );  }  +namespace RD+{+    static int OpenRD(
> > vlc_object_t*);+    static void CloseRD( vlc_object_t*
> > );+}+  VLC_SD_PROBE_HELPER( "upnp", N_("Universal Plug'n'Play"),
> > SD_CAT_LAN )+VLC_RD_PROBE_HELPER( "upnp_renderer", N_("UPnP
> > Renderer Discovery") )    /*   * Module descriptor@@ -117,6 +136,17
> > @@ vlc_module_begin()          set_capability( "access", 0
> > )        VLC_SD_PROBE_SUBMODULE++    add_submodule()+        set_de
> > scription( N_( "UPnP Renderer Discovery" ) )+        set_category(
> > CAT_SOUT )+        set_subcategory( SUBCAT_SOUT_RENDERER
> > )+        set_callbacks( RD::OpenRD, RD::CloseRD
> > )+        set_capability( "renderer_discovery", 0
> > )+        add_shortcut( "upnp_renderer"
> > )++    VLC_RD_PROBE_SUBMODULE+  vlc_module_end()    /*@@ -172,6
> > +202,32 @@ IXML_Document* parseBrowseResult( IXML_Document* p_doc
> > )      return (IXML_Document*)p_node;  }  +/**+ * Reads the base
> > URL from an XML device list+ *+ * \param services_discovery_t* p_sd
> > This SD instance+ * \param IXML_Document* p_desc an XML device list
> > document+ *+ * \return const char* The base URL+ */+static const
> > char *parseBaseUrl( IXML_Document *p_desc )+{+    const
> > char    *psz_base_url = nullptr;+    IXML_NodeList *p_url_list =
> > nullptr;++    if( ( p_url_list = ixmlDocument_getElementsByTagName(
> > p_desc, "URLBase" ) ) )+    {+        if ( IXML_Node* p_url_node =
> > ixmlNodeList_item( p_url_list, 0 )
> > )+        {+            IXML_Node* p_text_node =
> > ixmlNode_getFirstChild( p_url_node );+            if ( p_text_node
> > )+                psz_base_url = ixmlNode_getNodeValue( p_text_node
> > );+        }+        ixmlNodeList_free( p_url_list
> > );+    }+    return psz_base_url;+}+  namespace SD  {  @@ -1280,4
> > +1336,290 @@ static void CloseAccess( vlc_object_t* p_this
> > )      delete sys;  }  +} // namespace Access++namespace RD+{++/**+
> > * Crafts an MRL with the 'dlna' stream out+ * containing the host
> > and port.+ *+ * \param psz_location URL to the MediaRenderer device
> > description doc+ */+const char *getUrl(const char
> > *psz_location)+{+    char *psz_res;+    vlc_url_t
> > url;++    vlc_UrlParse(&url, psz_location);+    if
> > (asprintf(&psz_res, "dlna://%s:%d", url.psz_host, url.i_port) <
> > 0)+    {+        vlc_UrlClean(&url);+        return
> > NULL;+    }+    vlc_UrlClean(&url);+    return
> > psz_res;+}++MediaRendererDesc::MediaRendererDesc( const
> > std::string& udn,+                                      const
> > std::string& fName,+                                      const
> > std::string& base,+                                      const
> > std::string& loc )+    : UDN( udn )+    , friendlyName( fName
> > )+    , base_url( base )+    , location( loc )+    , inputItem(
> > NULL )+{+}++MediaRendererDesc::~MediaRendererDesc()+{+    if
> > (inputItem)+        vlc_renderer_item_release(inputItem);+}++MediaR
> > endererList::MediaRendererList(vlc_renderer_discovery_t
> > *p_rd)+    : m_rd( p_rd
> > )+{+}++MediaRendererList::~MediaRendererList()+{+    vlc_delete_all
> > (m_list);+}++bool MediaRendererList::addRenderer(MediaRendererDesc
> > *desc)+{+    const char* psz_url = getUrl(desc->location.c_str());
> 
> getUrl creates a string that needs to be free'd.
> > ++    char *extra_sout;++    if (asprintf(&extra_sout,
> > "base_url=%s,url=%s", desc-
> > >base_url.c_str(),+                        desc->location.c_str())
> > < 0)+        return false;+    desc->inputItem =
> > vlc_renderer_item_new("stream_out_dlna",+                          
> >                   desc-
> > >friendlyName.c_str(),+                                            
> > psz_url,
> 
> It will assert if you pass a NULL psz_url.
> > +                                            extra_sout,+          
> >                                   NULL, "",
> > 3);+    free(extra_sout);+    if ( !desc->inputItem
> > )+        return false;+    msg_Dbg( m_rd, "Adding renderer '%s'
> > with uuid %s",+                        desc-
> > >friendlyName.c_str(),+                        desc->UDN.c_str()
> > );+    vlc_rd_add_item(m_rd, desc-
> > >inputItem);+    m_list.push_back(desc);+    return
> > true;+}++MediaRendererDesc* MediaRendererList::getRenderer( const
> > std::string& udn
> > )+{+    std::vector<MediaRendererDesc*>::const_iterator it =
> > m_list.begin();+    std::vector<MediaRendererDesc*>::const_iterator
> > ite = m_list.end();++    for ( ; it != ite; ++it
> > )+    {+        if( udn == (*it)->UDN )+            return
> > *it;+    }+    return NULL;+}++void
> > MediaRendererList::removeRenderer( const std::string& udn
> > )+{+    MediaRendererDesc* p_renderer = getRenderer( udn );+    if
> > ( !p_renderer )+        return;++    assert( p_renderer->inputItem
> > );++    std::vector<MediaRendererDesc*>::iterator it
> > =+                        std::find(
> > m_list.begin(),+                                   m_list.end(),+  
> >                                  p_renderer );+    if( it !=
> > m_list.end() )+    {+        msg_Dbg( m_rd, "Removing renderer '%s'
> > with uuid %s",+                            p_renderer-
> > >friendlyName.c_str(),+                            p_renderer-
> > >UDN.c_str() );+        m_list.erase( it );+    }+    delete
> > p_renderer;+}++void MediaRendererList::parseNewRenderer(
> > IXML_Document* doc,+                                          const
> > std::string&
> > location)+{+    assert(!location.empty());+    msg_Dbg( m_rd , "Got
> > device desc doc:\n%s", ixmlPrintDocument( doc ));++    const char*
> > psz_base_url = nullptr;+    IXML_NodeList* p_device_nodes =
> > nullptr;++    /* Fallback to the Device description URL
> > basename+    * if no base URL is advertised */+    psz_base_url =
> > parseBaseUrl( doc );+    if( !psz_base_url && !location.empty()
> > )+    {+        psz_base_url =
> > location.c_str();+    }++    p_device_nodes =
> > ixmlDocument_getElementsByTagName( doc, "device" );+    if (
> > !p_device_nodes )+        return;++    for ( unsigned int i = 0; i
> > < ixmlNodeList_length( p_device_nodes ); i++
> > )+    {+        IXML_Element* p_device_element = ( IXML_Element* )
> > ixmlNodeList_item( p_device_nodes, i );+        const char*
> > psz_device_name = nullptr;+        const char* psz_udn =
> > nullptr;++        if( !p_device_element
> > )+            continue;++        psz_device_name =
> > xml_getChildElementValue( p_device_element,
> > "friendlyName");+        if (psz_device_name ==
> > nullptr)+            msg_Dbg( m_rd, "No friendlyName!"
> > );++        psz_udn = xml_getChildElementValue( p_device_element,
> > "UDN");+        if (psz_udn ==
> > nullptr)+        {+            msg_Err( m_rd, "No UDN"
> > );+            continue;+        }++        /* Check if renderer is
> > already added */+        if (getRenderer( psz_udn
> > ))+        {+            msg_Warn( m_rd, "Renderer with UDN '%s'
> > already exists.", psz_udn
> > );+            continue;+        }++        MediaRendererDesc
> > *p_renderer = new
> > MediaRendererDesc(psz_udn,+                                        
> >         psz_device_name,+                                          
> >       psz_base_url,+                                               
> >  location);+        if (!addRenderer( p_renderer
> > ))+            delete p_renderer;+    }+    ixmlNodeList_free(
> > p_device_nodes );+}++int MediaRendererList::onEvent( Upnp_EventType
> > event_type,+                                UpnpEventPtr
> > Event,+                                void *p_user_data )+{+    if
> > (p_user_data != MEDIA_RENDERER_DEVICE_TYPE)+        return
> > 0;++    switch (event_type)+    {+        case
> > UPNP_DISCOVERY_SEARCH_RESULT:+        {+            struct
> > Upnp_Discovery *p_discovery = (struct
> > Upnp_Discovery*)Event;+            IXML_Document *p_doc =
> > NULL;+            int i_res;++            i_res =
> > UpnpDownloadXmlDoc( UpnpDiscovery_get_Location_cstr( p_discovery ),
> > &p_doc);+            if (i_res !=
> > UPNP_E_SUCCESS)+            {+                fprintf(stderr,
> > "%s\n", UpnpDiscovery_get_Location_cstr( p_discovery ));
> 
> No fprintf.
> > +                return
> > i_res;+            }+            parseNewRenderer(p_doc,
> > UpnpDiscovery_get_Location_cstr( p_discovery )
> > );+            ixmlDocument_free(p_doc);+        }+        break;++
> >         case
> > UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:+        {+            struct
> > Upnp_Discovery* p_discovery = ( struct Upnp_Discovery*
> > )Event;++            removeRenderer( p_discovery->DeviceId
> > );+        }+        break;++        case
> > UPNP_DISCOVERY_SEARCH_TIMEOUT:+        {+                msg_Warn(
> > m_rd, "search timeout"
> > );+        }+        break;++        default:+        break;+    }+
> >     return UPNP_E_SUCCESS;+}++void *SearchThread(void
> > *data)+{+    vlc_renderer_discovery_t *p_rd =
> > (vlc_renderer_discovery_t*)data;+    renderer_discovery_sys_t
> > *p_sys = (renderer_discovery_sys_t*)p_rd->p_sys;+    int
> > i_res;++    i_res = UpnpSearchAsync(p_sys->p_upnp->handle(),
> > UPNP_SEARCH_TIMEOUT_SECONDS,+            MEDIA_RENDERER_DEVICE_TYPE
> > , MEDIA_RENDERER_DEVICE_TYPE);+    if( i_res != UPNP_E_SUCCESS
> > )+    {+        msg_Err( p_rd, "Error sending search request: %s",
> > UpnpGetErrorMessage( i_res ) );+        return
> > NULL;+    }+    return data;+}++static int OpenRD( vlc_object_t
> > *p_this )+{+    vlc_renderer_discovery_t *p_rd = (
> > vlc_renderer_discovery_t* )p_this;+    renderer_discovery_sys_t
> > *p_sys  = new(std::nothrow) renderer_discovery_sys_t;
> 
> you may use vlc_obj_malloc() so you don't have to free it manually.
> > ++    if ( !p_sys )+        return VLC_ENOMEM;+    p_rd->p_sys = (
> > vlc_renderer_discovery_sys* ) p_sys;+    p_sys->p_upnp =
> > UpnpInstanceWrapper::get( p_this );++    if ( !p_sys->p_upnp
> > )+    {+        delete p_sys;+        return
> > VLC_EGENERIC;+    }++    try+    {+        p_sys->p_renderer_list =
> > std::make_shared<RD::MediaRendererList>( p_rd );+    }+    catch (
> > const std::bad_alloc& )+    {+        msg_Err( p_rd, "Failed to
> > create a MediaRendererList");+        p_sys->p_upnp-
> > >release();+        free(p_sys);
> 
> delete instead of free ?
> > +        return VLC_EGENERIC;+    }+    p_sys->p_upnp->addListener( 
> > p_sys->p_renderer_list );++    if( vlc_clone( &p_sys->thread,
> > SearchThread,
> > (void*)p_rd,+                    VLC_THREAD_PRIORITY_LOW )
> > )+        {
> 
> odd identation
> > +            msg_Err( p_rd, "Can't run the lookup thread"
> > );+            p_sys->p_upnp->removeListener( p_sys-
> > >p_renderer_list );+            p_sys->p_upnp-
> > >release();+            delete p_sys;+            return
> > VLC_EGENERIC;+        }+    return VLC_SUCCESS;+}++static void
> > CloseRD( vlc_object_t *p_this )+{+    vlc_renderer_discovery_t
> > *p_rd = ( vlc_renderer_discovery_t*
> > )p_this;+    renderer_discovery_sys_t *p_sys =
> > (renderer_discovery_sys_t*)p_rd->p_sys;++    vlc_join(p_sys-
> > >thread, NULL);+    p_sys->p_upnp->removeListener( p_sys-
> > >p_renderer_list );+    p_sys->p_upnp->release();+    delete
> > p_sys;  }++} // namespace RDdiff --git
> > a/modules/services_discovery/upnp.hpp
> > b/modules/services_discovery/upnp.hppindex 8b6f947b9e..5a0de1f284
> > 100644--- a/modules/services_discovery/upnp.hpp+++
> > b/modules/services_discovery/upnp.hpp@@ -128,3 +128,42 @@
> > private:  };    }++namespace RD+{++struct
> > MediaRendererDesc+{+    MediaRendererDesc( const std::string& udn,
> > const std::string& fName,+                    const std::string&
> > base, const std::string& loc
> > );+    ~MediaRendererDesc();+    std::string UDN;+    std::string
> > friendlyName;+    std::string base_url;               // base url
> > of the renderer+    std::string location;               // device
> > description url+    vlc_renderer_item_t *inputItem;+};++class
> > MediaRendererList : public
> > UpnpInstanceWrapper::Listener+{+public:+    MediaRendererList(
> > vlc_renderer_discovery_t *p_rd
> > );+    ~MediaRendererList();++    bool
> > addRenderer(MediaRendererDesc *desc );+    void
> > removeRenderer(const std::string &udn );+    MediaRendererDesc*
> > getRenderer( const std::string& udn );+    int onEvent(
> > Upnp_EventType event_type,+                 UpnpEventPtr
> > p_event,+                 void* p_user_data )
> > override;++private:+    void parseNewRenderer( IXML_Document* doc,
> > const std::string& location
> > );++private:+    vlc_renderer_discovery_t* const
> > m_rd;+    std::vector<MediaRendererDesc*> m_list;++};++}--
> > 2.19.1_______________________________________________vlc-devel
> > mailing listTo unsubscribe or modify your subscription options:
> > https://mailman.videolan.org/listinfo/vlc-devel
> 
> _______________________________________________vlc-devel mailing
> listTo unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
-- 
Regards,
Shaleen Jain
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20181015/94d7d58a/attachment.html>


More information about the vlc-devel mailing list