<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>