[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