[vlc-devel] [PATCH 4/4] dlna: add a DLNA stream out

Jean-Baptiste Kempf jb at videolan.org
Wed Sep 5 10:32:46 CEST 2018


Hello,

On Wed, 5 Sep 2018, at 09:20, Shaleen Jain wrote:
> 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 patch> 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.
Sorry, but *this* is the difficult part of the UPnP renderer.
The rest is very similar to Chromecast.



>>  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/NEWS index 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_LIST index
>> 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 ES diff --git
>>     a/modules/services_discovery/Makefile.am b/modules/
>>     services_discovery/Makefile.am index 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
>> +       services_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.cpp index
>>         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_description(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::OpenSout, 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.hpp index 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.cpp new file
>>  mode 100644 index 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::MediaRenderer> 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_supports_video;
>> +    bool                                es_changed;
>> +    int                                 http_port;
>> +    std::vector<sout_stream_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_strea-
>>                                         m_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();
>> +        sout_StreamChainDelete( p_out, NULL );
>> +        p_out = NULL;
>> +    } +} + +sout_stream_id_sys_t *sout_stream_sys_t::GetSubId(
>>      sout_stream_t *p_stream,
>> +                                                   sout_stream_id_s-
>>                                                     ys_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_muxer,
>>                                          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(base_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.hpp new file mode 100644 index
>>      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 list To unsubscribe or modify your subscription
>> options: https://mailman.videolan.org/listinfo/vlc-devel
>>
>>
>>> 
> -- 
> Regards,
> Shaleen Jain
> 

--
Jean-Baptiste Kempf -  President
+33 672 704 734
 


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20180905/eb53dcbd/attachment-0001.html>


More information about the vlc-devel mailing list