<html dir="ltr"><head></head><body style="text-align:left; direction:ltr;"><div>On Mon, 2018-10-15 at 15:55 +0200, Steve Lhomme wrote:</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>On 15/10/2018 10:18, Shaleen Jain wrote:</pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>---</pre><pre>  modules/services_discovery/upnp.cpp | 342 ++++++++++++++++++++++++++++</pre><pre>  modules/services_discovery/upnp.hpp |  39 ++++</pre><pre>  2 files changed, 381 insertions(+)</pre><pre><br></pre><pre>diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp</pre><pre>index 2e9decee52..c1c2b947b2 100644</pre><pre>--- a/modules/services_discovery/upnp.cpp</pre><pre>+++ b/modules/services_discovery/upnp.cpp</pre><pre>@@ -8,6 +8,7 @@</pre><pre>   *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com></pre><pre>   *          Hugo Beauzée-Luyssen <</pre><a href="mailto:hugo@beauzee.fr"><pre>hugo@beauzee.fr</pre></a><pre>></pre><pre>   *          Shaleen Jain <</pre><a href="mailto:shaleen@jain.sh"><pre>shaleen@jain.sh</pre></a><pre>></pre><pre>+ *          William Ung <</pre><a href="mailto:william1.ung@epitech.eu"><pre>william1.ung@epitech.eu</pre></a><pre>></pre></blockquote><pre><br></pre><pre>What part of this code did this author contribute to ? Is s·he OK with </pre><pre>the license of the code ?</pre></blockquote><pre><br></pre><pre><pre>Mainly the MediaRendererList and MediaRendererDesc. With a few changes and addition due to the</pre><pre>UpnpInstanceWrapper class.</pre><pre>Yes, the code was originally licensed under the current license.</pre></pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>   *</pre><pre>   * This program is free software; you can redistribute it and/or modify it</pre><pre>   * under the terms of the GNU Lesser General Public License as published by</pre><pre>@@ -34,6 +35,7 @@</pre><pre>  #include <vlc_plugin.h></pre><pre>  #include <vlc_interrupt.h></pre><pre>  #include <vlc_services_discovery.h></pre><pre>+#include <vlc_renderer_discovery.h></pre><pre>  </pre><pre>  #include <assert.h></pre><pre>  #include <limits.h></pre><pre>@@ -45,9 +47,11 @@</pre><pre>   * Constants</pre><pre>  */</pre><pre>  const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";</pre><pre>+const char* MEDIA_RENDERER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaRenderer:1";</pre><pre>  const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";</pre><pre>  const char* SATIP_SERVER_DEVICE_TYPE = "urn:ses-com:device:SatIPServer:1";</pre><pre>  </pre><pre>+#define UPNP_SEARCH_TIMEOUT_SECONDS 15</pre><pre>  #define SATIP_CHANNEL_LIST N_("SAT>IP channel list")</pre><pre>  #define SATIP_CHANNEL_LIST_URL N_("Custom SAT>IP channel list URL")</pre><pre>  static const char *const ppsz_satip_channel_lists[] = {</pre><pre>@@ -69,6 +73,14 @@ struct services_discovery_sys_t</pre><pre>      vlc_thread_t         thread;</pre><pre>  };</pre><pre>  </pre><pre>+</pre><pre>+struct renderer_discovery_sys_t</pre><pre>+{</pre><pre>+    UpnpInstanceWrapper* p_upnp;</pre><pre>+    std::shared_ptr<RD::MediaRendererList> p_renderer_list;</pre><pre>+    vlc_thread_t thread;</pre><pre>+};</pre><pre>+</pre><pre>  struct access_sys_t</pre><pre>  {</pre><pre>      UpnpInstanceWrapper* p_upnp;</pre><pre>@@ -91,7 +103,14 @@ namespace Access</pre><pre>      static void CloseAccess( vlc_object_t* );</pre><pre>  }</pre><pre>  </pre><pre>+namespace RD</pre><pre>+{</pre><pre>+    static int OpenRD( vlc_object_t*);</pre><pre>+    static void CloseRD( vlc_object_t* );</pre><pre>+}</pre><pre>+</pre><pre>  VLC_SD_PROBE_HELPER( "upnp", N_("Universal Plug'n'Play"), SD_CAT_LAN )</pre><pre>+VLC_RD_PROBE_HELPER( "upnp_renderer", N_("UPnP Renderer Discovery") )</pre><pre>  </pre><pre>  /*</pre><pre>   * Module descriptor</pre><pre>@@ -117,6 +136,17 @@ vlc_module_begin()</pre><pre>          set_capability( "access", 0 )</pre><pre>  </pre><pre>      VLC_SD_PROBE_SUBMODULE</pre><pre>+</pre><pre>+    add_submodule()</pre><pre>+        set_description( N_( "UPnP Renderer Discovery" ) )</pre><pre>+        set_category( CAT_SOUT )</pre><pre>+        set_subcategory( SUBCAT_SOUT_RENDERER )</pre><pre>+        set_callbacks( RD::OpenRD, RD::CloseRD )</pre><pre>+        set_capability( "renderer_discovery", 0 )</pre><pre>+        add_shortcut( "upnp_renderer" )</pre><pre>+</pre><pre>+    VLC_RD_PROBE_SUBMODULE</pre><pre>+</pre><pre>  vlc_module_end()</pre><pre>  </pre><pre>  /*</pre><pre>@@ -172,6 +202,32 @@ IXML_Document* parseBrowseResult( IXML_Document* p_doc )</pre><pre>      return (IXML_Document*)p_node;</pre><pre>  }</pre><pre>  </pre><pre>+/**</pre><pre>+ * Reads the base URL from an XML device list</pre><pre>+ *</pre><pre>+ * \param services_discovery_t* p_sd This SD instance</pre><pre>+ * \param IXML_Document* p_desc an XML device list document</pre><pre>+ *</pre><pre>+ * \return const char* The base URL</pre><pre>+ */</pre><pre>+static const char *parseBaseUrl( IXML_Document *p_desc )</pre><pre>+{</pre><pre>+    const char    *psz_base_url = nullptr;</pre><pre>+    IXML_NodeList *p_url_list = nullptr;</pre><pre>+</pre><pre>+    if( ( p_url_list = ixmlDocument_getElementsByTagName( p_desc, "URLBase" ) ) )</pre><pre>+    {</pre><pre>+        if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) )</pre><pre>+        {</pre><pre>+            IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node );</pre><pre>+            if ( p_text_node )</pre><pre>+                psz_base_url = ixmlNode_getNodeValue( p_text_node );</pre><pre>+        }</pre><pre>+        ixmlNodeList_free( p_url_list );</pre><pre>+    }</pre><pre>+    return psz_base_url;</pre><pre>+}</pre><pre>+</pre><pre>  namespace SD</pre><pre>  {</pre><pre>  </pre><pre>@@ -1280,4 +1336,290 @@ static void CloseAccess( vlc_object_t* p_this )</pre><pre>      delete sys;</pre><pre>  }</pre><pre>  </pre><pre>+} // namespace Access</pre><pre>+</pre><pre>+namespace RD</pre><pre>+{</pre><pre>+</pre><pre>+/**</pre><pre>+ * Crafts an MRL with the 'dlna' stream out</pre><pre>+ * containing the host and port.</pre><pre>+ *</pre><pre>+ * \param psz_location URL to the MediaRenderer device description doc</pre><pre>+ */</pre><pre>+const char *getUrl(const char *psz_location)</pre><pre>+{</pre><pre>+    char *psz_res;</pre><pre>+    vlc_url_t url;</pre><pre>+</pre><pre>+    vlc_UrlParse(&url, psz_location);</pre><pre>+    if (asprintf(&psz_res, "dlna://%s:%d", url.psz_host, url.i_port) < 0)</pre><pre>+    {</pre><pre>+        vlc_UrlClean(&url);</pre><pre>+        return NULL;</pre><pre>+    }</pre><pre>+    vlc_UrlClean(&url);</pre><pre>+    return psz_res;</pre><pre>+}</pre><pre>+</pre><pre>+MediaRendererDesc::MediaRendererDesc( const std::string& udn,</pre><pre>+                                      const std::string& fName,</pre><pre>+                                      const std::string& base,</pre><pre>+                                      const std::string& loc )</pre><pre>+    : UDN( udn )</pre><pre>+    , friendlyName( fName )</pre><pre>+    , base_url( base )</pre><pre>+    , location( loc )</pre><pre>+    , inputItem( NULL )</pre><pre>+{</pre><pre>+}</pre><pre>+</pre><pre>+MediaRendererDesc::~MediaRendererDesc()</pre><pre>+{</pre><pre>+    if (inputItem)</pre><pre>+        vlc_renderer_item_release(inputItem);</pre><pre>+}</pre><pre>+</pre><pre>+MediaRendererList::MediaRendererList(vlc_renderer_discovery_t *p_rd)</pre><pre>+    : m_rd( p_rd )</pre><pre>+{</pre><pre>+}</pre><pre>+</pre><pre>+MediaRendererList::~MediaRendererList()</pre><pre>+{</pre><pre>+    vlc_delete_all(m_list);</pre><pre>+}</pre><pre>+</pre><pre>+bool MediaRendererList::addRenderer(MediaRendererDesc *desc)</pre><pre>+{</pre><pre>+    const char* psz_url = getUrl(desc->location.c_str());</pre></blockquote><pre><br></pre><pre>getUrl creates a string that needs to be free'd.</pre><pre><br></pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>+</pre><pre>+    char *extra_sout;</pre><pre>+</pre><pre>+    if (asprintf(&extra_sout, "base_url=%s,url=%s", desc->base_url.c_str(),</pre><pre>+                        desc->location.c_str()) < 0)</pre><pre>+        return false;</pre><pre>+    desc->inputItem = vlc_renderer_item_new("stream_out_dlna",</pre><pre>+                                            desc->friendlyName.c_str(),</pre><pre>+                                            psz_url,</pre></blockquote><pre><br></pre><pre>It will assert if you pass a NULL psz_url.</pre><pre><br></pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>+                                            extra_sout,</pre><pre>+                                            NULL, "", 3);</pre><pre>+    free(extra_sout);</pre><pre>+    if ( !desc->inputItem )</pre><pre>+        return false;</pre><pre>+    msg_Dbg( m_rd, "Adding renderer '%s' with uuid %s",</pre><pre>+                        desc->friendlyName.c_str(),</pre><pre>+                        desc->UDN.c_str() );</pre><pre>+    vlc_rd_add_item(m_rd, desc->inputItem);</pre><pre>+    m_list.push_back(desc);</pre><pre>+    return true;</pre><pre>+}</pre><pre>+</pre><pre>+MediaRendererDesc* MediaRendererList::getRenderer( const std::string& udn )</pre><pre>+{</pre><pre>+    std::vector<MediaRendererDesc*>::const_iterator it = m_list.begin();</pre><pre>+    std::vector<MediaRendererDesc*>::const_iterator ite = m_list.end();</pre><pre>+</pre><pre>+    for ( ; it != ite; ++it )</pre><pre>+    {</pre><pre>+        if( udn == (*it)->UDN )</pre><pre>+            return *it;</pre><pre>+    }</pre><pre>+    return NULL;</pre><pre>+}</pre><pre>+</pre><pre>+void MediaRendererList::removeRenderer( const std::string& udn )</pre><pre>+{</pre><pre>+    MediaRendererDesc* p_renderer = getRenderer( udn );</pre><pre>+    if ( !p_renderer )</pre><pre>+        return;</pre><pre>+</pre><pre>+    assert( p_renderer->inputItem );</pre><pre>+</pre><pre>+    std::vector<MediaRendererDesc*>::iterator it =</pre><pre>+                        std::find( m_list.begin(),</pre><pre>+                                   m_list.end(),</pre><pre>+                                   p_renderer );</pre><pre>+    if( it != m_list.end() )</pre><pre>+    {</pre><pre>+        msg_Dbg( m_rd, "Removing renderer '%s' with uuid %s",</pre><pre>+                            p_renderer->friendlyName.c_str(),</pre><pre>+                            p_renderer->UDN.c_str() );</pre><pre>+        m_list.erase( it );</pre><pre>+    }</pre><pre>+    delete p_renderer;</pre><pre>+}</pre><pre>+</pre><pre>+void MediaRendererList::parseNewRenderer( IXML_Document* doc,</pre><pre>+                                          const std::string& location)</pre><pre>+{</pre><pre>+    assert(!location.empty());</pre><pre>+    msg_Dbg( m_rd , "Got device desc doc:\n%s", ixmlPrintDocument( doc ));</pre><pre>+</pre><pre>+    const char* psz_base_url = nullptr;</pre><pre>+    IXML_NodeList* p_device_nodes = nullptr;</pre><pre>+</pre><pre>+    /* Fallback to the Device description URL basename</pre><pre>+    * if no base URL is advertised */</pre><pre>+    psz_base_url = parseBaseUrl( doc );</pre><pre>+    if( !psz_base_url && !location.empty() )</pre><pre>+    {</pre><pre>+        psz_base_url = location.c_str();</pre><pre>+    }</pre><pre>+</pre><pre>+    p_device_nodes = ixmlDocument_getElementsByTagName( doc, "device" );</pre><pre>+    if ( !p_device_nodes )</pre><pre>+        return;</pre><pre>+</pre><pre>+    for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_nodes ); i++ )</pre><pre>+    {</pre><pre>+        IXML_Element* p_device_element = ( IXML_Element* ) ixmlNodeList_item( p_device_nodes, i );</pre><pre>+        const char* psz_device_name = nullptr;</pre><pre>+        const char* psz_udn = nullptr;</pre><pre>+</pre><pre>+        if( !p_device_element )</pre><pre>+            continue;</pre><pre>+</pre><pre>+        psz_device_name = xml_getChildElementValue( p_device_element, "friendlyName");</pre><pre>+        if (psz_device_name == nullptr)</pre><pre>+            msg_Dbg( m_rd, "No friendlyName!" );</pre><pre>+</pre><pre>+        psz_udn = xml_getChildElementValue( p_device_element, "UDN");</pre><pre>+        if (psz_udn == nullptr)</pre><pre>+        {</pre><pre>+            msg_Err( m_rd, "No UDN" );</pre><pre>+            continue;</pre><pre>+        }</pre><pre>+</pre><pre>+        /* Check if renderer is already added */</pre><pre>+        if (getRenderer( psz_udn ))</pre><pre>+        {</pre><pre>+            msg_Warn( m_rd, "Renderer with UDN '%s' already exists.", psz_udn );</pre><pre>+            continue;</pre><pre>+        }</pre><pre>+</pre><pre>+        MediaRendererDesc *p_renderer = new MediaRendererDesc(psz_udn,</pre><pre>+                                                psz_device_name,</pre><pre>+                                                psz_base_url,</pre><pre>+                                                location);</pre><pre>+        if (!addRenderer( p_renderer ))</pre><pre>+            delete p_renderer;</pre><pre>+    }</pre><pre>+    ixmlNodeList_free( p_device_nodes );</pre><pre>+}</pre><pre>+</pre><pre>+int MediaRendererList::onEvent( Upnp_EventType event_type,</pre><pre>+                                UpnpEventPtr Event,</pre><pre>+                                void *p_user_data )</pre><pre>+{</pre><pre>+    if (p_user_data != MEDIA_RENDERER_DEVICE_TYPE)</pre><pre>+        return 0;</pre><pre>+</pre><pre>+    switch (event_type)</pre><pre>+    {</pre><pre>+        case UPNP_DISCOVERY_SEARCH_RESULT:</pre><pre>+        {</pre><pre>+            struct Upnp_Discovery *p_discovery = (struct Upnp_Discovery*)Event;</pre><pre>+            IXML_Document *p_doc = NULL;</pre><pre>+            int i_res;</pre><pre>+</pre><pre>+            i_res = UpnpDownloadXmlDoc( UpnpDiscovery_get_Location_cstr( p_discovery ), &p_doc);</pre><pre>+            if (i_res != UPNP_E_SUCCESS)</pre><pre>+            {</pre><pre>+                fprintf(stderr, "%s\n", UpnpDiscovery_get_Location_cstr( p_discovery ));</pre></blockquote><pre><br></pre><pre>No fprintf.</pre><pre><br></pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>+                return i_res;</pre><pre>+            }</pre><pre>+            parseNewRenderer(p_doc, UpnpDiscovery_get_Location_cstr( p_discovery ) );</pre><pre>+            ixmlDocument_free(p_doc);</pre><pre>+        }</pre><pre>+        break;</pre><pre>+</pre><pre>+        case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:</pre><pre>+        {</pre><pre>+            struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )Event;</pre><pre>+</pre><pre>+            removeRenderer( p_discovery->DeviceId );</pre><pre>+        }</pre><pre>+        break;</pre><pre>+</pre><pre>+        case UPNP_DISCOVERY_SEARCH_TIMEOUT:</pre><pre>+        {</pre><pre>+                msg_Warn( m_rd, "search timeout" );</pre><pre>+        }</pre><pre>+        break;</pre><pre>+</pre><pre>+        default:</pre><pre>+        break;</pre><pre>+    }</pre><pre>+    return UPNP_E_SUCCESS;</pre><pre>+}</pre><pre>+</pre><pre>+void *SearchThread(void *data)</pre><pre>+{</pre><pre>+    vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t*)data;</pre><pre>+    renderer_discovery_sys_t *p_sys = (renderer_discovery_sys_t*)p_rd->p_sys;</pre><pre>+    int i_res;</pre><pre>+</pre><pre>+    i_res = UpnpSearchAsync(p_sys->p_upnp->handle(), UPNP_SEARCH_TIMEOUT_SECONDS,</pre><pre>+            MEDIA_RENDERER_DEVICE_TYPE, MEDIA_RENDERER_DEVICE_TYPE);</pre><pre>+    if( i_res != UPNP_E_SUCCESS )</pre><pre>+    {</pre><pre>+        msg_Err( p_rd, "Error sending search request: %s", UpnpGetErrorMessage( i_res ) );</pre><pre>+        return NULL;</pre><pre>+    }</pre><pre>+    return data;</pre><pre>+}</pre><pre>+</pre><pre>+static int OpenRD( vlc_object_t *p_this )</pre><pre>+{</pre><pre>+    vlc_renderer_discovery_t *p_rd = ( vlc_renderer_discovery_t* )p_this;</pre><pre>+    renderer_discovery_sys_t *p_sys  = new(std::nothrow) renderer_discovery_sys_t;</pre></blockquote><pre><br></pre><pre>you may use vlc_obj_malloc() so you don't have to free it manually.</pre><pre><br></pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>+</pre><pre>+    if ( !p_sys )</pre><pre>+        return VLC_ENOMEM;</pre><pre>+    p_rd->p_sys = ( vlc_renderer_discovery_sys* ) p_sys;</pre><pre>+    p_sys->p_upnp = UpnpInstanceWrapper::get( p_this );</pre><pre>+</pre><pre>+    if ( !p_sys->p_upnp )</pre><pre>+    {</pre><pre>+        delete p_sys;</pre><pre>+        return VLC_EGENERIC;</pre><pre>+    }</pre><pre>+</pre><pre>+    try</pre><pre>+    {</pre><pre>+        p_sys->p_renderer_list = std::make_shared<RD::MediaRendererList>( p_rd );</pre><pre>+    }</pre><pre>+    catch ( const std::bad_alloc& )</pre><pre>+    {</pre><pre>+        msg_Err( p_rd, "Failed to create a MediaRendererList");</pre><pre>+        p_sys->p_upnp->release();</pre><pre>+        free(p_sys);</pre></blockquote><pre><br></pre><pre>delete instead of free ?</pre><pre><br></pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>+        return VLC_EGENERIC;</pre><pre>+    }</pre><pre>+    p_sys->p_upnp->addListener( p_sys->p_renderer_list );</pre><pre>+</pre><pre>+    if( vlc_clone( &p_sys->thread, SearchThread, (void*)p_rd,</pre><pre>+                    VLC_THREAD_PRIORITY_LOW ) )</pre><pre>+        {</pre></blockquote><pre><br></pre><pre>odd identation</pre><pre><br></pre><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>+            msg_Err( p_rd, "Can't run the lookup thread" );</pre><pre>+            p_sys->p_upnp->removeListener( p_sys->p_renderer_list );</pre><pre>+            p_sys->p_upnp->release();</pre><pre>+            delete p_sys;</pre><pre>+            return VLC_EGENERIC;</pre><pre>+        }</pre><pre>+    return VLC_SUCCESS;</pre><pre>+}</pre><pre>+</pre><pre>+static void CloseRD( vlc_object_t *p_this )</pre><pre>+{</pre><pre>+    vlc_renderer_discovery_t *p_rd = ( vlc_renderer_discovery_t* )p_this;</pre><pre>+    renderer_discovery_sys_t *p_sys = (renderer_discovery_sys_t*)p_rd->p_sys;</pre><pre>+</pre><pre>+    vlc_join(p_sys->thread, NULL);</pre><pre>+    p_sys->p_upnp->removeListener( p_sys->p_renderer_list );</pre><pre>+    p_sys->p_upnp->release();</pre><pre>+    delete p_sys;</pre><pre>  }</pre><pre>+</pre><pre>+} // namespace RD</pre><pre>diff --git a/modules/services_discovery/upnp.hpp b/modules/services_discovery/upnp.hpp</pre><pre>index 8b6f947b9e..5a0de1f284 100644</pre><pre>--- a/modules/services_discovery/upnp.hpp</pre><pre>+++ b/modules/services_discovery/upnp.hpp</pre><pre>@@ -128,3 +128,42 @@ private:</pre><pre>  };</pre><pre>  </pre><pre>  }</pre><pre>+</pre><pre>+namespace RD</pre><pre>+{</pre><pre>+</pre><pre>+struct MediaRendererDesc</pre><pre>+{</pre><pre>+    MediaRendererDesc( const std::string& udn, const std::string& fName,</pre><pre>+                    const std::string& base, const std::string& loc );</pre><pre>+    ~MediaRendererDesc();</pre><pre>+    std::string UDN;</pre><pre>+    std::string friendlyName;</pre><pre>+    std::string base_url;               // base url of the renderer</pre><pre>+    std::string location;               // device description url</pre><pre>+    vlc_renderer_item_t *inputItem;</pre><pre>+};</pre><pre>+</pre><pre>+class MediaRendererList : public UpnpInstanceWrapper::Listener</pre><pre>+{</pre><pre>+public:</pre><pre>+    MediaRendererList( vlc_renderer_discovery_t *p_rd );</pre><pre>+    ~MediaRendererList();</pre><pre>+</pre><pre>+    bool addRenderer(MediaRendererDesc *desc );</pre><pre>+    void removeRenderer(const std::string &udn );</pre><pre>+    MediaRendererDesc* getRenderer( const std::string& udn );</pre><pre>+    int onEvent( Upnp_EventType event_type,</pre><pre>+                 UpnpEventPtr p_event,</pre><pre>+                 void* p_user_data ) override;</pre><pre>+</pre><pre>+private:</pre><pre>+    void parseNewRenderer( IXML_Document* doc, const std::string& location );</pre><pre>+</pre><pre>+private:</pre><pre>+    vlc_renderer_discovery_t* const m_rd;</pre><pre>+    std::vector<MediaRendererDesc*> m_list;</pre><pre>+</pre><pre>+};</pre><pre>+</pre><pre>+}</pre><pre>-- </pre><pre>2.19.1</pre><pre>_______________________________________________</pre><pre>vlc-devel mailing list</pre><pre>To unsubscribe or modify your subscription options:</pre><a href="https://mailman.videolan.org/listinfo/vlc-devel"><pre>https://mailman.videolan.org/listinfo/vlc-devel</pre></a><pre><br></pre></blockquote><pre><br></pre><pre>_______________________________________________</pre><pre>vlc-devel mailing list</pre><pre>To unsubscribe or modify your subscription options:</pre><a href="https://mailman.videolan.org/listinfo/vlc-devel"><pre>https://mailman.videolan.org/listinfo/vlc-devel</pre></a></blockquote><div><span><pre>-- <br></pre><div style="width: 71ch;">Regards,</div><div style="width: 71ch;">Shaleen Jain</div></span></div></body></html>