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