<html dir="ltr"><head></head><body style="text-align:left; direction:ltr;"><div>On Tue, 2018-09-04 at 19:24 +0200, Jean-Baptiste Kempf wrote:</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre>Hello,</pre><pre><br></pre><pre>Can you explain how you query what codecs are supported by the devices?</pre></blockquote><div>Supported codecs are queried by the GetProcotolInfo UpnpAction which is added in a separate patch</div><div>that I planned on sending later since that is more of an RFC patch. But I will include and send a new series if you want.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><br></pre><pre>Because for example, I doubt many devices support Opus inside Mp4.</pre><pre><br></pre><pre>Best,</pre><pre><br></pre><pre>On Tue, 4 Sep 2018, at 13:14, Shaleen Jain wrote:</pre><pre></pre><pre>---</pre><pre> NEWS | 3 +</pre><pre> modules/MODULES_LIST | 1 +</pre><pre> modules/services_discovery/Makefile.am | 4 +-</pre><pre> modules/services_discovery/upnp.cpp | 17 +</pre><pre> modules/services_discovery/upnp.hpp | 1 +</pre><pre> modules/stream_out/dlna.cpp | 620 +++++++++++++++++++++++++</pre><pre> modules/stream_out/dlna.hpp | 94 ++++</pre><pre> 7 files changed, 739 insertions(+), 1 deletion(-)</pre><pre> create mode 100644 modules/stream_out/dlna.cpp</pre><pre> create mode 100644 modules/stream_out/dlna.hpp</pre><pre><br></pre><pre>diff --git a/NEWS b/NEWS</pre><pre>index 6bc94096de..c4dbef7e7d 100644</pre><pre>--- a/NEWS</pre><pre>+++ b/NEWS</pre><pre>@@ -37,6 +37,9 @@ Video output:</pre><pre> * Remove RealRTSP plugin</pre><pre> * Remove Real demuxer plugin</pre><pre> </pre><pre>+Stream Output:</pre><pre>+ * Add DLNA/UPNP stream output module</pre><pre>+</pre><pre> macOS:</pre><pre> * Remove Growl notification support</pre><pre> </pre><pre>diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST</pre><pre>index e1978cc6e4..04256242a1 100644</pre><pre>--- a/modules/MODULES_LIST</pre><pre>+++ b/modules/MODULES_LIST</pre><pre>@@ -375,6 +375,7 @@ $Id$</pre><pre> * stream_out_delay: introduce delay in an ES when streaming</pre><pre> * stream_out_description: helper module for RTSP vod</pre><pre> * stream_out_display: displays a stream output chain</pre><pre>+ * stream_out_dlna: DLNA streaming output module</pre><pre> * stream_out_dummy: dummy stream out chain module</pre><pre> * stream_out_duplicate: duplicates a stream output chain</pre><pre> * stream_out_es: stream out module outputing ES</pre><pre>diff --git a/modules/services_discovery/Makefile.am b/modules/</pre><pre>services_discovery/Makefile.am</pre><pre>index f63df23b32..3ea850c920 100644</pre><pre>--- a/modules/services_discovery/Makefile.am</pre><pre>+++ b/modules/services_discovery/Makefile.am</pre><pre>@@ -28,7 +28,9 @@ sd_LTLIBRARIES += $(LTLIBmtp)</pre><pre> </pre><pre> libupnp_plugin_la_SOURCES = services_discovery/upnp.cpp </pre><pre>services_discovery/upnp.hpp \</pre><pre> services_discovery/upnp-wrapper.hpp \</pre><pre>- services_discovery/upnp-wrapper.cpp</pre><pre>+ services_discovery/upnp-wrapper.cpp \</pre><pre>+ stream_out/dlna.hpp \</pre><pre>+ stream_out/dlna.cpp</pre><pre> libupnp_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(UPNP_CFLAGS)</pre><pre> libupnp_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(sddir)'</pre><pre> libupnp_plugin_la_LIBADD = $(UPNP_LIBS)</pre><pre>diff --git a/modules/services_discovery/upnp.cpp b/modules/</pre><pre>services_discovery/upnp.cpp</pre><pre>index 6642e2b980..a75ec34dbd 100644</pre><pre>--- a/modules/services_discovery/upnp.cpp</pre><pre>+++ b/modules/services_discovery/upnp.cpp</pre><pre>@@ -147,6 +147,23 @@ vlc_module_begin()</pre><pre> </pre><pre> VLC_RD_PROBE_SUBMODULE</pre><pre> </pre><pre>+ add_submodule()</pre><pre>+ set_shortname("dlna")</pre><pre>+ set_description(N_("UPnP/DLNA stream output"))</pre><pre>+ set_capability("sout stream", 0)</pre><pre>+ add_shortcut("dlna")</pre><pre>+ set_category(CAT_SOUT)</pre><pre>+ set_subcategory(SUBCAT_SOUT_STREAM)</pre><pre>+ set_callbacks(Sout::OpenSout, Sout::CloseSout)</pre><pre>+</pre><pre>+ add_string(SOUT_CFG_PREFIX "ip", NULL, IP_ADDR_TEXT, </pre><pre>IP_ADDR_LONGTEXT, false)</pre><pre>+ add_integer(SOUT_CFG_PREFIX "port", NULL, PORT_TEXT, </pre><pre>PORT_LONGTEXT, false)</pre><pre>+ add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, </pre><pre>HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)</pre><pre>+ add_bool(SOUT_CFG_PREFIX "video", true, HAS_VIDEO_TEXT, </pre><pre>HAS_VIDEO_LONGTEXT, false)</pre><pre>+ add_string(SOUT_CFG_PREFIX "mux", DEFAULT_MUXER, MUX_TEXT, </pre><pre>MUX_LONGTEXT, false)</pre><pre>+ add_string(SOUT_CFG_PREFIX "mime", "video/x-matroska", </pre><pre>MIME_TEXT, MIME_LONGTEXT, false)</pre><pre>+ add_string(SOUT_CFG_PREFIX "base_url", NULL, BASE_URL_TEXT, </pre><pre>BASE_URL_LONGTEXT, false)</pre><pre>+ add_string(SOUT_CFG_PREFIX "url", NULL, URL_TEXT, URL_LONGTEXT, </pre><pre>false)</pre><pre> vlc_module_end()</pre><pre> </pre><pre> /*</pre><pre>diff --git a/modules/services_discovery/upnp.hpp b/modules/</pre><pre>services_discovery/upnp.hpp</pre><pre>index 5a0de1f284..711ef200c5 100644</pre><pre>--- a/modules/services_discovery/upnp.hpp</pre><pre>+++ b/modules/services_discovery/upnp.hpp</pre><pre>@@ -33,6 +33,7 @@</pre><pre> #endif</pre><pre> </pre><pre> #include "upnp-wrapper.hpp"</pre><pre>+#include "../stream_out/dlna.hpp"</pre><pre> </pre><pre> #include <vlc_url.h></pre><pre> #include <vlc_interrupt.h></pre><pre>diff --git a/modules/stream_out/dlna.cpp b/modules/stream_out/dlna.cpp</pre><pre>new file mode 100644</pre><pre>index 0000000000..8fe1e27ccc</pre><pre>--- /dev/null</pre><pre>+++ b/modules/stream_out/dlna.cpp</pre><pre>@@ -0,0 +1,620 @@</pre><pre>+/</pre><pre>*****************************************************************************</pre><pre>+ * dlna.cpp : DLNA/UPNP (renderer) sout module</pre><pre>+ </pre><pre>*****************************************************************************</pre><pre>+ * Copyright (C) 2004-2018 VLC authors and VideoLAN</pre><pre>+ *</pre><pre>+ * Authors: William Ung <<a href="mailto:william1.ung@epitech.eu">william1.ung@epitech.eu</a>></pre><pre>+ * Shaleen Jain <<a href="mailto:shaleen@jain.sh">shaleen@jain.sh</a>></pre><pre>+ *</pre><pre>+ * This program is free software; you can redistribute it and/or modify </pre><pre>it</pre><pre>+ * under the terms of the GNU Lesser General Public License as </pre><pre>published by</pre><pre>+ * the Free Software Foundation; either version 2.1 of the License, or</pre><pre>+ * (at your option) any later version.</pre><pre>+ *</pre><pre>+ * This program is distributed in the hope that it will be useful,</pre><pre>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</pre><pre>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</pre><pre>+ * GNU Lesser General Public License for more details.</pre><pre>+ *</pre><pre>+ * You should have received a copy of the GNU Lesser General Public </pre><pre>License</pre><pre>+ * along with this program; if not, write to the Free Software </pre><pre>Foundation,</pre><pre>+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.</pre><pre>+ </pre><pre>*****************************************************************************/</pre><pre>+</pre><pre>+#ifdef HAVE_CONFIG_H</pre><pre>+# include "config.h"</pre><pre>+#endif</pre><pre>+</pre><pre>+#include "dlna.hpp"</pre><pre>+</pre><pre>+#include <vector></pre><pre>+#include <string></pre><pre>+#include <sstream></pre><pre>+</pre><pre>+#include <vlc_plugin.h></pre><pre>+#include <vlc_renderer_discovery.h></pre><pre>+#include <vlc_sout.h></pre><pre>+</pre><pre>+const char* AV_TRANSPORT_SERVICE_TYPE = </pre><pre>"urn:schemas-upnp-org:service:AVTransport:1";</pre><pre>+const char* CONNECTION_MANAGER_SERVICE_TYPE = </pre><pre>"urn:schemas-upnp-org:service:ConnectionManager:1";</pre><pre>+</pre><pre>+static const char *const ppsz_sout_options[] = {</pre><pre>+ "ip", "port", "http-port", "mux", "mime", "video", "base_url", </pre><pre>"url", NULL</pre><pre>+};</pre><pre>+</pre><pre>+struct sout_stream_id_sys_t</pre><pre>+{</pre><pre>+ es_format_t fmt;</pre><pre>+ sout_stream_id_sys_t *p_sub_id;</pre><pre>+};</pre><pre>+</pre><pre>+struct sout_stream_sys_t</pre><pre>+{</pre><pre>+ sout_stream_sys_t(int http_port, bool supports_video,</pre><pre>+ std::string default_mux, std::string default_mime)</pre><pre>+ : p_out(NULL)</pre><pre>+ , default_muxer(default_mux)</pre><pre>+ , default_mime(default_mime)</pre><pre>+ , b_supports_video(supports_video)</pre><pre>+ , es_changed(true)</pre><pre>+ , http_port(http_port)</pre><pre>+ {</pre><pre>+ }</pre><pre>+</pre><pre>+ std::shared_ptr<Sout::MediaRenderer> renderer;</pre><pre>+ UpnpInstanceWrapper *p_upnp;</pre><pre>+</pre><pre>+ bool canDecodeAudio( vlc_fourcc_t i_codec ) const;</pre><pre>+ bool canDecodeVideo( vlc_fourcc_t i_codec ) const;</pre><pre>+ bool startSoutChain( sout_stream_t* p_stream,</pre><pre>+ const std::vector<sout_stream_id_sys_t*> </pre><pre>&new_streams,</pre><pre>+ const std::string &sout );</pre><pre>+ void stopSoutChain( sout_stream_t* p_stream );</pre><pre>+ sout_stream_id_sys_t *GetSubId( sout_stream_t *p_stream,</pre><pre>+ sout_stream_id_sys_t *id,</pre><pre>+ bool update = true );</pre><pre>+</pre><pre>+ sout_stream_t *p_out;</pre><pre>+ std::string default_muxer;</pre><pre>+ std::string default_mime;</pre><pre>+ bool b_supports_video;</pre><pre>+ bool es_changed;</pre><pre>+ int http_port;</pre><pre>+ std::vector<sout_stream_id_sys_t*> streams;</pre><pre>+ std::vector<sout_stream_id_sys_t*> out_streams;</pre><pre>+</pre><pre>+ int UpdateOutput( sout_stream_t *p_stream );</pre><pre>+</pre><pre>+};</pre><pre>+</pre><pre>+bool sout_stream_sys_t::canDecodeAudio(vlc_fourcc_t i_codec) const</pre><pre>+{</pre><pre>+ return i_codec == VLC_CODEC_VORBIS ||</pre><pre>+ i_codec == VLC_CODEC_MP4A ||</pre><pre>+ i_codec == VLC_FOURCC('h', 'a', 'a', 'c') ||</pre><pre>+ i_codec == VLC_FOURCC('l', 'a', 'a', 'c') ||</pre><pre>+ i_codec == VLC_FOURCC('s', 'a', 'a', 'c') ||</pre><pre>+ i_codec == VLC_CODEC_OPUS ||</pre><pre>+ i_codec == VLC_CODEC_MP3 ||</pre><pre>+ i_codec == VLC_CODEC_A52 ||</pre><pre>+ i_codec == VLC_CODEC_EAC3;</pre><pre>+}</pre><pre>+</pre><pre>+bool sout_stream_sys_t::canDecodeVideo(vlc_fourcc_t i_codec) const</pre><pre>+{</pre><pre>+ return i_codec == VLC_CODEC_H264 || i_codec == VLC_CODEC_VP8;</pre><pre>+}</pre><pre>+</pre><pre>+bool sout_stream_sys_t::startSoutChain(sout_stream_t *p_stream,</pre><pre>+ const </pre><pre>std::vector<sout_stream_id_sys_t*> &new_streams,</pre><pre>+ const std::string &sout)</pre><pre>+{</pre><pre>+ stopSoutChain( p_stream );</pre><pre>+</pre><pre>+ msg_Dbg( p_stream, "Creating chain %s", sout.c_str() );</pre><pre>+ out_streams = new_streams;</pre><pre>+</pre><pre>+ p_out = sout_StreamChainNew( p_stream->p_sout, sout.c_str(), NULL, </pre><pre>NULL);</pre><pre>+ if (p_out == NULL) {</pre><pre>+ msg_Dbg(p_stream, "could not create sout chain:%s", </pre><pre>sout.c_str());</pre><pre>+ out_streams.clear();</pre><pre>+ return false;</pre><pre>+ }</pre><pre>+</pre><pre>+ /* check the streams we can actually add */</pre><pre>+ for (std::vector<sout_stream_id_sys_t*>::iterator it = </pre><pre>out_streams.begin();</pre><pre>+ it != out_streams.end(); )</pre><pre>+ {</pre><pre>+ sout_stream_id_sys_t *p_sys_id = *it;</pre><pre>+ p_sys_id->p_sub_id = static_cast<sout_stream_id_sys_t *>(</pre><pre>+ sout_StreamIdAdd( p_out, &p_sys_id->fmt ) );</pre><pre>+ if ( p_sys_id->p_sub_id == NULL )</pre><pre>+ {</pre><pre>+ msg_Err( p_stream, "can't handle %4.4s stream",</pre><pre>+ (char *)&p_sys_id->fmt.i_codec );</pre><pre>+ es_format_Clean( &p_sys_id->fmt );</pre><pre>+ it = out_streams.erase( it );</pre><pre>+ }</pre><pre>+ else</pre><pre>+ ++it;</pre><pre>+ }</pre><pre>+</pre><pre>+ if (out_streams.empty())</pre><pre>+ {</pre><pre>+ stopSoutChain( p_stream );</pre><pre>+ return false;</pre><pre>+ }</pre><pre>+</pre><pre>+ return true;</pre><pre>+}</pre><pre>+</pre><pre>+void sout_stream_sys_t::stopSoutChain(sout_stream_t *p_stream)</pre><pre>+{</pre><pre>+ (void) p_stream;</pre><pre>+</pre><pre>+ if ( unlikely( p_out != NULL ) )</pre><pre>+ {</pre><pre>+ for ( size_t i = 0; i < out_streams.size(); i++ )</pre><pre>+ {</pre><pre>+ if ( out_streams[i]->p_sub_id != NULL )</pre><pre>+ {</pre><pre>+ sout_StreamIdDel( p_out, out_streams[i]->p_sub_id );</pre><pre>+ out_streams[i]->p_sub_id = NULL;</pre><pre>+ }</pre><pre>+ }</pre><pre>+ out_streams.clear();</pre><pre>+ sout_StreamChainDelete( p_out, NULL );</pre><pre>+ p_out = NULL;</pre><pre>+ }</pre><pre>+}</pre><pre>+</pre><pre>+sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t </pre><pre>*p_stream,</pre><pre>+ sout_stream_id_sys_t </pre><pre>*id,</pre><pre>+ bool update)</pre><pre>+{</pre><pre>+ size_t i;</pre><pre>+</pre><pre>+ assert( p_stream->p_sys == this );</pre><pre>+</pre><pre>+ if ( update && UpdateOutput( p_stream ) != VLC_SUCCESS )</pre><pre>+ return NULL;</pre><pre>+</pre><pre>+ for (i = 0; i < out_streams.size(); ++i)</pre><pre>+ {</pre><pre>+ if ( id == (sout_stream_id_sys_t*) out_streams[i] )</pre><pre>+ return out_streams[i]->p_sub_id;</pre><pre>+ }</pre><pre>+</pre><pre>+ msg_Err( p_stream, "unknown stream ID" );</pre><pre>+ return NULL;</pre><pre>+}</pre><pre>+</pre><pre>+int sout_stream_sys_t::UpdateOutput( sout_stream_t *p_stream )</pre><pre>+{</pre><pre>+ assert( p_stream->p_sys == this );</pre><pre>+</pre><pre>+ if ( !es_changed )</pre><pre>+ return VLC_SUCCESS;</pre><pre>+</pre><pre>+ es_changed = false;</pre><pre>+</pre><pre>+ bool canRemux = true;</pre><pre>+ vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0;</pre><pre>+ std::vector<sout_stream_id_sys_t*> new_streams;</pre><pre>+</pre><pre>+ for (std::vector<sout_stream_id_sys_t*>::iterator it = </pre><pre>streams.begin();</pre><pre>+ it != streams.end(); ++it)</pre><pre>+ {</pre><pre>+ const es_format_t *p_es = &(*it)->fmt;</pre><pre>+ if (p_es->i_cat == AUDIO_ES)</pre><pre>+ {</pre><pre>+ if (!canDecodeAudio( p_es->i_codec ))</pre><pre>+ {</pre><pre>+ msg_Dbg( p_stream, "can't remux audio track %d codec </pre><pre>%4.4s",</pre><pre>+ p_es->i_id, (const char*)&p_es->i_codec );</pre><pre>+ canRemux = false;</pre><pre>+ }</pre><pre>+ else if (i_codec_audio == 0)</pre><pre>+ {</pre><pre>+ i_codec_audio = p_es->i_codec;</pre><pre>+ }</pre><pre>+ new_streams.push_back(*it);</pre><pre>+ }</pre><pre>+ else if (b_supports_video && p_es->i_cat == VIDEO_ES)</pre><pre>+ {</pre><pre>+ if (!canDecodeVideo( p_es->i_codec ))</pre><pre>+ {</pre><pre>+ msg_Dbg( p_stream, "can't remux video track %d codec </pre><pre>%4.4s",</pre><pre>+ p_es->i_id, (const char*)&p_es->i_codec );</pre><pre>+ canRemux = false;</pre><pre>+ }</pre><pre>+ else if (i_codec_video == 0)</pre><pre>+ {</pre><pre>+ i_codec_video = p_es->i_codec;</pre><pre>+ }</pre><pre>+ new_streams.push_back(*it);</pre><pre>+ }</pre><pre>+ }</pre><pre>+</pre><pre>+ if (new_streams.empty())</pre><pre>+ {</pre><pre>+ return VLC_SUCCESS;</pre><pre>+ }</pre><pre>+</pre><pre>+ std::stringstream ssout;</pre><pre>+ if ( !canRemux )</pre><pre>+ {</pre><pre>+ /* TODO: provide audio samplerate and channels */</pre><pre>+ ssout << "transcode{";</pre><pre>+ char s_fourcc[5];</pre><pre>+ if ( i_codec_audio == 0 )</pre><pre>+ {</pre><pre>+ i_codec_audio = DEFAULT_TRANSCODE_AUDIO;</pre><pre>+ msg_Dbg( p_stream, "Converting audio to %.4s",</pre><pre>+ (const char*)&i_codec_audio );</pre><pre>+ ssout << "acodec=";</pre><pre>+ vlc_fourcc_to_char( i_codec_audio, s_fourcc );</pre><pre>+ s_fourcc[4] = '\0';</pre><pre>+ ssout << s_fourcc << ',';</pre><pre>+ }</pre><pre>+ if ( b_supports_video && i_codec_video == 0 )</pre><pre>+ {</pre><pre>+ i_codec_video = DEFAULT_TRANSCODE_VIDEO;</pre><pre>+ msg_Dbg( p_stream, "Converting video to %.4s",</pre><pre>+ (const char*)&i_codec_video );</pre><pre>+ /* TODO: provide maxwidth,maxheight */</pre><pre>+ ssout << "vcodec=";</pre><pre>+ vlc_fourcc_to_char( i_codec_video, s_fourcc );</pre><pre>+ s_fourcc[4] = '\0';</pre><pre>+ ssout << s_fourcc;</pre><pre>+ }</pre><pre>+ ssout << "}:";</pre><pre>+ }</pre><pre>+</pre><pre>+ std::string mime;</pre><pre>+ if ( !b_supports_video && default_muxer == DEFAULT_MUXER )</pre><pre>+ mime = "audio/x-matroska";</pre><pre>+ else if ( i_codec_audio == VLC_CODEC_VORBIS &&</pre><pre>+ i_codec_video == VLC_CODEC_VP8 &&</pre><pre>+ default_muxer == DEFAULT_MUXER )</pre><pre>+ mime = "video/webm";</pre><pre>+ else</pre><pre>+ mime = default_mime;</pre><pre>+</pre><pre>+ ssout << "http{dst=:" << http_port << "/stream.mp4"</pre><pre>+ << ",mux=" << default_muxer</pre><pre>+ << ",access=http{mime=" << mime << "}}";</pre><pre>+</pre><pre>+ if ( !startSoutChain( p_stream, new_streams, ssout.str() ) )</pre><pre>+ return VLC_EGENERIC;</pre><pre>+</pre><pre>+ char *ip = getIpv4ForMulticast();</pre><pre>+ if (ip == NULL)</pre><pre>+ {</pre><pre>+ ip = UpnpGetServerIpAddress();</pre><pre>+ }</pre><pre>+ if (ip == NULL)</pre><pre>+ {</pre><pre>+ msg_Err(p_stream, "could not get the local ip address");</pre><pre>+ return VLC_EGENERIC;</pre><pre>+ }</pre><pre>+</pre><pre>+ char *uri;</pre><pre>+ if (asprintf(&uri, "<a href="http://%s:%d/stream.mp4">http://%s:%d/stream.mp4</a>", ip, http_port) < 0) {</pre><pre>+ return VLC_ENOMEM;</pre><pre>+ }</pre><pre>+</pre><pre>+ msg_Dbg(p_stream, "AVTransportURI: %s", uri);</pre><pre>+ renderer->SetAVTransportURI(uri);</pre><pre>+ renderer->Play("1");</pre><pre>+</pre><pre>+ return VLC_SUCCESS;</pre><pre>+}</pre><pre>+</pre><pre>+namespace Sout</pre><pre>+{</pre><pre>+</pre><pre>+char *MediaRenderer::getServiceURL(const char* type, const char </pre><pre>*service)</pre><pre>+{</pre><pre>+ IXML_Document *p_description_doc = NULL;</pre><pre>+</pre><pre>+ UpnpDownloadXmlDoc(device_url.c_str(), &p_description_doc);</pre><pre>+ if (!p_description_doc)</pre><pre>+ return NULL;</pre><pre>+</pre><pre>+ IXML_NodeList* p_device_list = </pre><pre>ixmlDocument_getElementsByTagName( p_description_doc, "device");</pre><pre>+ if ( !p_device_list)</pre><pre>+ return NULL;</pre><pre>+ for (unsigned int i = 0; i < ixmlNodeList_length(p_device_list); +</pre><pre>+i)</pre><pre>+ {</pre><pre>+ IXML_Element* p_device_element = ( IXML_Element* ) </pre><pre>ixmlNodeList_item( p_device_list, i );</pre><pre>+ if( !p_device_element )</pre><pre>+ continue;</pre><pre>+</pre><pre>+ IXML_NodeList* p_service_list = </pre><pre>ixmlElement_getElementsByTagName( p_device_element, "service" );</pre><pre>+ if ( !p_service_list )</pre><pre>+ continue;</pre><pre>+ for ( unsigned int j = 0; j < </pre><pre>ixmlNodeList_length( p_service_list ); j++ )</pre><pre>+ {</pre><pre>+ IXML_Element* p_service_element = </pre><pre>(IXML_Element*)ixmlNodeList_item( p_service_list, j );</pre><pre>+</pre><pre>+ const char* psz_service_type = </pre><pre>xml_getChildElementValue( p_service_element, "serviceType" );</pre><pre>+ if ( !psz_service_type || !strstr(psz_service_type, type))</pre><pre>+ continue;</pre><pre>+ const char* psz_control_url = </pre><pre>xml_getChildElementValue( p_service_element,</pre><pre>+ </pre><pre>service );</pre><pre>+ if ( !psz_control_url )</pre><pre>+ continue;</pre><pre>+</pre><pre>+ char* psz_url = ( char* ) malloc( base_url.length() + </pre><pre>strlen( psz_control_url ) + 1 );</pre><pre>+ if ( psz_url )</pre><pre>+ {</pre><pre>+ if ( UpnpResolveURL( base_url.c_str(), psz_control_url, </pre><pre>psz_url ) == UPNP_E_SUCCESS )</pre><pre>+ return psz_url;</pre><pre>+ }</pre><pre>+ free(psz_url);</pre><pre>+ return NULL;</pre><pre>+ }</pre><pre>+ }</pre><pre>+ return NULL;</pre><pre>+}</pre><pre>+</pre><pre>+/**</pre><pre>+ * Send an action to the control url of the service specified.</pre><pre>+ *</pre><pre>+ * \return the response as a IXML document or NULL for failure</pre><pre>+ **/</pre><pre>+IXML_Document *MediaRenderer::SendAction(const char* action_name,const </pre><pre>char *service_type,</pre><pre>+ std::list<std::pair<const char*, const char*>> </pre><pre>arguments)</pre><pre>+{</pre><pre>+</pre><pre>+ int ret;</pre><pre>+ IXML_Document *action = NULL, *response = NULL;</pre><pre>+</pre><pre>+ /* Create action */</pre><pre>+ action = UpnpMakeAction(action_name, service_type, 0, NULL);</pre><pre>+</pre><pre>+ /* Add argument to action */</pre><pre>+ std::list<std::pair<const char*, const char*>>::iterator arg;</pre><pre>+ for (arg=arguments.begin(); arg != arguments.end(); ++arg) {</pre><pre>+ const char *arg_name, *arg_val;</pre><pre>+ arg_name = arg->first;</pre><pre>+ arg_val = arg->second;</pre><pre>+ UpnpAddToAction(&action, action_name, service_type, arg_name, </pre><pre>arg_val);</pre><pre>+ }</pre><pre>+</pre><pre>+ /* Get the controlURL of the service */</pre><pre>+ char *control_url = getServiceURL(service_type, "controlURL");</pre><pre>+</pre><pre>+ /* Send action */</pre><pre>+ ret = UpnpSendAction(handle, control_url, service_type,</pre><pre>+ NULL, action, &response);</pre><pre>+</pre><pre>+ /* Free action */</pre><pre>+ if (action) ixmlDocument_free(action);</pre><pre>+ if (control_url) free(control_url);</pre><pre>+</pre><pre>+ if (ret != UPNP_E_SUCCESS) {</pre><pre>+ msg_Err(parent, "Unable to send action: %d (%s)\n", ret, </pre><pre>UpnpGetErrorMessage(ret));</pre><pre>+ if (response) ixmlDocument_free(response);</pre><pre>+ return NULL;</pre><pre>+ }</pre><pre>+</pre><pre>+ return response;</pre><pre>+}</pre><pre>+</pre><pre>+int MediaRenderer::Play(const char *speed)</pre><pre>+{</pre><pre>+ IXML_Document* p_response = NULL;</pre><pre>+ std::list<std::pair<const char*, const char*>> arg_list;</pre><pre>+ arg_list.push_back(std::make_pair("InstanceID", "0"));</pre><pre>+ arg_list.push_back(std::make_pair("Speed", speed));</pre><pre>+</pre><pre>+ p_response = SendAction("Play", AV_TRANSPORT_SERVICE_TYPE, </pre><pre>arg_list);</pre><pre>+ ixmlDocument_free(p_response);</pre><pre>+ return VLC_SUCCESS;</pre><pre>+}</pre><pre>+</pre><pre>+int MediaRenderer::Stop()</pre><pre>+{</pre><pre>+ IXML_Document* p_response = NULL;</pre><pre>+ std::list<std::pair<const char*, const char*>> arg_list;</pre><pre>+ arg_list.push_back(std::make_pair("InstanceID", "0"));</pre><pre>+</pre><pre>+ p_response = SendAction("Stop", AV_TRANSPORT_SERVICE_TYPE, </pre><pre>arg_list);</pre><pre>+ ixmlDocument_free(p_response);</pre><pre>+ return VLC_SUCCESS;</pre><pre>+}</pre><pre>+</pre><pre>+int MediaRenderer::SetAVTransportURI(const char* uri)</pre><pre>+{</pre><pre>+ IXML_Document* p_response = NULL;</pre><pre>+ std::list<std::pair<const char*, const char*>> arg_list;</pre><pre>+ arg_list.push_back(std::make_pair("InstanceID", "0"));</pre><pre>+ arg_list.push_back(std::make_pair("CurrentURI", uri));</pre><pre>+ arg_list.push_back(std::make_pair("CurrentURIMetaData", "")); // </pre><pre>NOT_IMPLEMENTED</pre><pre>+</pre><pre>+ p_response = SendAction("SetAVTransportURI", </pre><pre>AV_TRANSPORT_SERVICE_TYPE, arg_list);</pre><pre>+ ixmlDocument_free(p_response);</pre><pre>+ return VLC_SUCCESS;</pre><pre>+}</pre><pre>+</pre><pre>+static void *Add(sout_stream_t *p_stream, const es_format_t *p_fmt)</pre><pre>+{</pre><pre>+ sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t </pre><pre>*>( p_stream->p_sys );</pre><pre>+</pre><pre>+ if (!p_sys->b_supports_video)</pre><pre>+ {</pre><pre>+ if (p_fmt->i_cat != AUDIO_ES)</pre><pre>+ return NULL;</pre><pre>+ }</pre><pre>+</pre><pre>+ sout_stream_id_sys_t *p_sys_id = (sout_stream_id_sys_t </pre><pre>*)malloc(sizeof(sout_stream_id_sys_t));</pre><pre>+ if(p_sys_id != NULL)</pre><pre>+ {</pre><pre>+ es_format_Copy(&p_sys_id->fmt, p_fmt);</pre><pre>+ p_sys_id->p_sub_id = NULL;</pre><pre>+ p_sys->streams.push_back(p_sys_id);</pre><pre>+ p_sys->es_changed = true;</pre><pre>+ }</pre><pre>+ return p_sys_id;</pre><pre>+}</pre><pre>+</pre><pre>+static int Send(sout_stream_t *p_stream, void *id,</pre><pre>+ block_t *p_buffer)</pre><pre>+{</pre><pre>+ sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t </pre><pre>*>( p_stream->p_sys );</pre><pre>+ sout_stream_id_sys_t *id_sys = </pre><pre>static_cast<sout_stream_id_sys_t*>( id );</pre><pre>+</pre><pre>+ id_sys = p_sys->GetSubId( p_stream, id_sys );</pre><pre>+ if ( id_sys == NULL )</pre><pre>+ return VLC_EGENERIC;</pre><pre>+</pre><pre>+ return sout_StreamIdSend(p_sys->p_out, id_sys, p_buffer);</pre><pre>+}</pre><pre>+</pre><pre>+static void Flush( sout_stream_t *p_stream, void *id )</pre><pre>+{</pre><pre>+ sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t </pre><pre>*>( p_stream->p_sys );</pre><pre>+ sout_stream_id_sys_t *id_sys = </pre><pre>static_cast<sout_stream_id_sys_t*>( id );</pre><pre>+</pre><pre>+ id_sys = p_sys->GetSubId( p_stream, id_sys, false );</pre><pre>+ if ( id_sys == NULL )</pre><pre>+ return;</pre><pre>+</pre><pre>+ sout_StreamFlush( p_sys->p_out, id_sys );</pre><pre>+}</pre><pre>+</pre><pre>+static int Control(sout_stream_t *p_stream, int i_query, va_list args)</pre><pre>+{</pre><pre>+ sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t </pre><pre>*>( p_stream->p_sys );</pre><pre>+</pre><pre>+ if (i_query == SOUT_STREAM_EMPTY)</pre><pre>+ return VLC_SUCCESS;</pre><pre>+ if (!p_sys->p_out->pf_control)</pre><pre>+ return VLC_EGENERIC;</pre><pre>+ return p_sys->p_out->pf_control(p_sys->p_out, i_query, args);</pre><pre>+}</pre><pre>+</pre><pre>+static void Del(sout_stream_t *p_stream, void *_id)</pre><pre>+{</pre><pre>+ sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t </pre><pre>*>( p_stream->p_sys );</pre><pre>+ sout_stream_id_sys_t *id = static_cast<sout_stream_id_sys_t </pre><pre>*>( _id );</pre><pre>+</pre><pre>+ for (std::vector<sout_stream_id_sys_t*>::iterator it = p_sys-</pre><pre></pre><pre>streams.begin();</pre><pre>+ it != p_sys->streams.end(); )</pre><pre>+ {</pre><pre>+ sout_stream_id_sys_t *p_sys_id = *it;</pre><pre>+ if ( p_sys_id == id )</pre><pre>+ {</pre><pre>+ if ( p_sys_id->p_sub_id != NULL )</pre><pre>+ {</pre><pre>+ sout_StreamIdDel( p_sys->p_out, p_sys_id->p_sub_id );</pre><pre>+ for (std::vector<sout_stream_id_sys_t*>::iterator </pre><pre>out_it = p_sys->out_streams.begin();</pre><pre>+ out_it != p_sys->out_streams.end(); )</pre><pre>+ {</pre><pre>+ if (*out_it == id)</pre><pre>+ {</pre><pre>+ p_sys->out_streams.erase(out_it);</pre><pre>+ break;</pre><pre>+ }</pre><pre>+ out_it++;</pre><pre>+ }</pre><pre>+ }</pre><pre>+</pre><pre>+ es_format_Clean( &p_sys_id->fmt );</pre><pre>+ free( p_sys_id );</pre><pre>+ p_sys->streams.erase( it );</pre><pre>+ break;</pre><pre>+ }</pre><pre>+ it++;</pre><pre>+ }</pre><pre>+</pre><pre>+ if (p_sys->out_streams.empty())</pre><pre>+ {</pre><pre>+ p_sys->stopSoutChain(p_stream);</pre><pre>+ p_sys->renderer->Stop();</pre><pre>+ }</pre><pre>+}</pre><pre>+</pre><pre>+int OpenSout( vlc_object_t *p_this )</pre><pre>+{</pre><pre>+ sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);</pre><pre>+ sout_stream_sys_t *p_sys = NULL;</pre><pre>+ bool b_supports_video = true;</pre><pre>+ char *default_muxer = NULL;</pre><pre>+ char *default_mime = NULL;</pre><pre>+ char *base_url = NULL;</pre><pre>+ char *device_url = NULL;</pre><pre>+ int http_port;</pre><pre>+</pre><pre>+ config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, </pre><pre>p_stream->p_cfg);</pre><pre>+</pre><pre>+ http_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-</pre><pre>port");</pre><pre>+ b_supports_video = var_GetBool(p_stream, SOUT_CFG_PREFIX "video");</pre><pre>+ default_muxer = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX </pre><pre>"mux");</pre><pre>+ default_mime = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX </pre><pre>"mime");</pre><pre>+</pre><pre>+ try {</pre><pre>+ p_sys = new sout_stream_sys_t(http_port, b_supports_video,</pre><pre>+ default_muxer, default_mime);</pre><pre>+ } catch ( std::exception& ex ) {</pre><pre>+ msg_Err( p_stream, "Failed to instantiate sout_stream_sys_t: </pre><pre>%s", ex.what() );</pre><pre>+ return VLC_EGENERIC;</pre><pre>+ }</pre><pre>+</pre><pre>+ base_url = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX </pre><pre>"base_url");</pre><pre>+ device_url = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX </pre><pre>"url");</pre><pre>+ if ( device_url == NULL)</pre><pre>+ {</pre><pre>+ msg_Err( p_stream, "missing Url" );</pre><pre>+ goto error;</pre><pre>+ }</pre><pre>+</pre><pre>+ p_sys->p_upnp = UpnpInstanceWrapper::get( p_this );</pre><pre>+ if ( !p_sys->p_upnp )</pre><pre>+ goto error;</pre><pre>+ try {</pre><pre>+ p_sys->renderer = </pre><pre>std::make_shared<Sout::MediaRenderer>(p_stream,</pre><pre>+ p_sys->p_upnp, base_url, device_url);</pre><pre>+ }</pre><pre>+ catch (const std::bad_alloc&) {</pre><pre>+ msg_Err( p_stream, "Failed to create a MediaRenderer");</pre><pre>+ p_sys->p_upnp->release();</pre><pre>+ goto error;</pre><pre>+ }</pre><pre>+</pre><pre>+ p_stream->pf_add = Add;</pre><pre>+ p_stream->pf_del = Del;</pre><pre>+ p_stream->pf_send = Send;</pre><pre>+ p_stream->pf_flush = Flush;</pre><pre>+ p_stream->pf_control = Control;</pre><pre>+</pre><pre>+ p_stream->p_sys = p_sys;</pre><pre>+</pre><pre>+ free(default_mime);</pre><pre>+ free(default_muxer);</pre><pre>+ free(base_url);</pre><pre>+ free(device_url);</pre><pre>+</pre><pre>+ return VLC_SUCCESS;</pre><pre>+</pre><pre>+error:</pre><pre>+ free(default_mime);</pre><pre>+ free(default_muxer);</pre><pre>+ free(base_url);</pre><pre>+ free(device_url);</pre><pre>+ delete p_sys;</pre><pre>+ return VLC_EGENERIC;</pre><pre>+}</pre><pre>+</pre><pre>+void CloseSout( vlc_object_t *p_this)</pre><pre>+{</pre><pre>+ sout_stream_t *p_stream = </pre><pre>reinterpret_cast<sout_stream_t*>( p_this );</pre><pre>+ sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t </pre><pre>*>( p_stream->p_sys );</pre><pre>+</pre><pre>+ p_sys->p_upnp->release();</pre><pre>+ delete p_sys;</pre><pre>+}</pre><pre>+</pre><pre>+}</pre><pre>diff --git a/modules/stream_out/dlna.hpp b/modules/stream_out/dlna.hpp</pre><pre>new file mode 100644</pre><pre>index 0000000000..9f966b8237</pre><pre>--- /dev/null</pre><pre>+++ b/modules/stream_out/dlna.hpp</pre><pre>@@ -0,0 +1,94 @@</pre><pre>+/</pre><pre>*****************************************************************************</pre><pre>+ * dlna.hpp : DLNA/UPNP (renderer) sout module header</pre><pre>+ </pre><pre>*****************************************************************************</pre><pre>+ * Copyright (C) 2004-2018 VLC authors and VideoLAN</pre><pre>+ *</pre><pre>+ * Authors: William Ung <<a href="mailto:william1.ung@epitech.eu">william1.ung@epitech.eu</a>></pre><pre>+ * Shaleen Jain <<a href="mailto:shaleen@jain.sh">shaleen@jain.sh</a>></pre><pre>+ *</pre><pre>+ * This program is free software; you can redistribute it and/or modify </pre><pre>it</pre><pre>+ * under the terms of the GNU Lesser General Public License as </pre><pre>published by</pre><pre>+ * the Free Software Foundation; either version 2.1 of the License, or</pre><pre>+ * (at your option) any later version.</pre><pre>+ *</pre><pre>+ * This program is distributed in the hope that it will be useful,</pre><pre>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</pre><pre>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</pre><pre>+ * GNU Lesser General Public License for more details.</pre><pre>+ *</pre><pre>+ * You should have received a copy of the GNU Lesser General Public </pre><pre>License</pre><pre>+ * along with this program; if not, write to the Free Software </pre><pre>Foundation,</pre><pre>+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.</pre><pre>+ </pre><pre>*****************************************************************************/</pre><pre>+</pre><pre>+#include "../services_discovery/upnp-wrapper.hpp"</pre><pre>+</pre><pre>+#include <list></pre><pre>+</pre><pre>+#include <vlc_fourcc.h></pre><pre>+</pre><pre>+#define SOUT_CFG_PREFIX "sout-upnp-"</pre><pre>+</pre><pre>+#define HTTP_PORT 8080</pre><pre>+</pre><pre>+#define HTTP_PORT_TEXT N_("HTTP port")</pre><pre>+#define HTTP_PORT_LONGTEXT N_("This sets the HTTP port of the local </pre><pre>server used to stream the media to the UPnP Renderer.")</pre><pre>+#define HAS_VIDEO_TEXT N_("Video")</pre><pre>+#define HAS_VIDEO_LONGTEXT N_("The UPnP Renderer can receive video.")</pre><pre>+#define MUX_TEXT N_("Muxer")</pre><pre>+#define MUX_LONGTEXT N_("This sets the muxer used to stream to the UPnP </pre><pre>Renderer.")</pre><pre>+#define MIME_TEXT N_("MIME content type")</pre><pre>+#define MIME_LONGTEXT N_("This sets the media MIME content type sent to </pre><pre>the UPnP Renderer.")</pre><pre>+</pre><pre>+#define IP_ADDR_TEXT N_("IP Address")</pre><pre>+#define IP_ADDR_LONGTEXT N_("IP Address of the UPnP Renderer.")</pre><pre>+#define PORT_TEXT N_("UPnP Renderer port")</pre><pre>+#define PORT_LONGTEXT N_("The port used to talk to the UPnP Renderer.")</pre><pre>+#define BASE_URL_TEXT N_("base URL")</pre><pre>+#define BASE_URL_LONGTEXT N_("The base Url relative to which all other </pre><pre>UPnP operations must be called")</pre><pre>+#define URL_TEXT N_("description URL")</pre><pre>+#define URL_LONGTEXT N_("The Url used to get the xml descriptor of the </pre><pre>UPnP Renderer")</pre><pre>+</pre><pre>+static const vlc_fourcc_t DEFAULT_TRANSCODE_AUDIO = VLC_CODEC_MP3;</pre><pre>+static const vlc_fourcc_t DEFAULT_TRANSCODE_VIDEO = VLC_CODEC_H264;</pre><pre>+static const char DEFAULT_MUXER[] = </pre><pre>"avformat{mux=matroska,options={live=1}}}";</pre><pre>+</pre><pre>+namespace Sout</pre><pre>+{</pre><pre>+</pre><pre>+/* module callbacks */</pre><pre>+int OpenSout(vlc_object_t *);</pre><pre>+void CloseSout(vlc_object_t *);</pre><pre>+</pre><pre>+class MediaRenderer</pre><pre>+{</pre><pre>+public:</pre><pre>+ MediaRenderer(sout_stream_t *p_stream, UpnpInstanceWrapper *upnp,</pre><pre>+ std::string base_url, std::string device_url)</pre><pre>+ : parent(p_stream)</pre><pre>+ , base_url(base_url)</pre><pre>+ , device_url(device_url)</pre><pre>+ , handle(upnp->handle())</pre><pre>+ {</pre><pre>+ }</pre><pre>+</pre><pre>+ ~MediaRenderer()</pre><pre>+ {</pre><pre>+ parent = NULL;</pre><pre>+ }</pre><pre>+</pre><pre>+ sout_stream_t *parent;</pre><pre>+ std::string base_url;</pre><pre>+ std::string device_url;</pre><pre>+ UpnpClient_Handle handle;</pre><pre>+</pre><pre>+ char *getServiceURL(const char* type, const char* service);</pre><pre>+ IXML_Document *SendAction(const char* action_name, const char </pre><pre>*service_type,</pre><pre>+ std::list<std::pair<const char*, const char*>> </pre><pre>arguments);</pre><pre>+</pre><pre>+ int Play(const char *speed);</pre><pre>+ int Stop();</pre><pre>+ int SetAVTransportURI(const char* uri);</pre><pre>+};</pre><pre>+</pre><pre>+}</pre><pre>-- </pre><pre>2.18.0</pre><pre>_______________________________________________</pre><pre>vlc-devel mailing list</pre><pre>To unsubscribe or modify your subscription options:</pre><pre><a href="https://mailman.videolan.org/listinfo/vlc-devel">https://mailman.videolan.org/listinfo/vlc-devel</a></pre><pre><br></pre><pre><br></pre><pre><br></pre></blockquote><div><span><pre>-- <br></pre><div style="width: 71ch;">Regards,</div><div style="width: 71ch;">Shaleen Jain</div></span></div></body></html>