<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr"><head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta http-equiv="Content-Style-Type" content="text/css">
  <meta name="generator" content="pandoc">
  <title></title>
  <style type="text/css">
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
  </style>
</head>
<body style="text-align:left; direction:ltr;"><div><div style="white-space: normal;">Hi Filip,</div><div style="white-space: normal;"><br></div><div style="white-space: normal;">Thanks for the review.</div><div style="white-space: normal;">On Thu, 2018-09-20 at 16:02 +0200, Filip Roséen wrote:</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><p>Hi Shaleen,</p><p>For what it is worth, do note that you are allowed to implement this using <code>c++11</code> (which would come in handy to shorten some of the paths in your implementation).</p></blockquote><div style="white-space: normal;">C++ has a lot of boilerplate but I'll try and use it more where I can (maybe you have a specific block in mind?).</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><p>I do agree with j-b though related to querying what formats the upnp device actually supports, nonetheless: good job, and keep it up.</p></blockquote><div style="white-space: normal;">Thanks, Yes I'll post a new series with the format quering.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><p>On 2018-09-04 16:44, Shaleen Jain wrote:</p><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> ---</code></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><br></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><br></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/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><br></pre><pre>  libupnp_plugin_la_SOURCES = services_discovery/upnp.cpp 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/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><br></pre><pre>      VLC_RD_PROBE_SUBMODULE</pre><pre><br></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, IP_ADDR_LONGTEXT, false)</pre><pre> +        add_integer(SOUT_CFG_PREFIX "port", NULL, PORT_TEXT, PORT_LONGTEXT, false)</pre><pre> +        add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)</pre><pre> +        add_bool(SOUT_CFG_PREFIX "video", true, HAS_VIDEO_TEXT, HAS_VIDEO_LONGTEXT, false)</pre><pre> +        add_string(SOUT_CFG_PREFIX "mux", DEFAULT_MUXER, MUX_TEXT, MUX_LONGTEXT, false)</pre><pre> +        add_string(SOUT_CFG_PREFIX "mime", "video/x-matroska", MIME_TEXT, MIME_LONGTEXT, false)</pre><pre> +        add_string(SOUT_CFG_PREFIX "base_url", NULL, BASE_URL_TEXT, BASE_URL_LONGTEXT, false)</pre><pre> +        add_string(SOUT_CFG_PREFIX "url", NULL, URL_TEXT, URL_LONGTEXT, false)</pre><pre>  vlc_module_end()</pre><pre><br></pre><pre>  /*</pre><pre> diff --git a/modules/services_discovery/upnp.hpp b/modules/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><br></pre><pre>  #include "upnp-wrapper.hpp"</pre><pre> +#include "../stream_out/dlna.hpp"</pre><pre><br></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> + * dlna.cpp : DLNA/UPNP (renderer) sout module</pre><pre> + *****************************************************************************</pre><pre> + * Copyright (C) 2004-2018 VLC authors and VideoLAN</pre><pre> + *</pre><pre> + * Authors: William Ung <william1.ung@epitech.eu></pre><pre> + *          Shaleen Jain <shaleen@jain.sh></pre><pre> + *</pre><pre> + * This program is free software; you can redistribute it and/or modify it</pre><pre> + * under the terms of the GNU Lesser General Public License as published by</pre><pre> + * 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 License</pre><pre> + * along with this program; if not, write to the Free Software Foundation,</pre><pre> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.</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 = "urn:schemas-upnp-org:service:AVTransport:1";</pre><pre> +const char* CONNECTION_MANAGER_SERVICE_TYPE = "urn:schemas-upnp-org:service:ConnectionManager:1";</pre></blockquote><p>The above should either be declared in an anonymous namespace, or explicitly declared <code>static</code>.</p></blockquote><div style="white-space: normal;">Ok</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +</code></pre><pre> +static const char *const ppsz_sout_options[] = {</pre><pre> +    "ip", "port", "http-port", "mux", "mime", "video", "base_url", "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*> &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></blockquote><p>Might be easier read if written as a switch, and avoids you having to prefix each line with <code>i_codec ==</code>.</p></blockquote><div style="white-space: normal;">Ok</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +}</code></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 std::vector<sout_stream_id_sys_t*> &new_streams,</pre><pre> +                                       const std::string &sout)</pre><pre> +{</pre><pre> +    stopSoutChain( p_stream );</pre></blockquote><p>I can understand the rationale for unconditionally stopping any previous instance on start, but it is not immediately obvious when one sees an invocation of <code>startSoutChain</code>, and as such I would personally prefer if the calleer is responsible for stopping any previous stream prior to starting new ones (if that is intended).</p></blockquote><div style="white-space: normal;">This is mainly because to enforce only ever having a single output stream running considering every chain could have a transcoding step. The soutChain start and stop are similar designs to that of chromecast.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +</code></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, NULL);</pre><pre> +    if (p_out == NULL) {</pre><pre> +        msg_Dbg(p_stream, "could not create sout chain:%s", sout.c_str());</pre></blockquote><p>Not being able to create a sout-chain must be worthy of something higher than <code>msg_Dbg</code>, especially that no other diagnostic is issued on the path that leads to the above.</p></blockquote><div style="white-space: normal;">Yes, changed to msg_Err()</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +        out_streams.clear();</code></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 = 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></blockquote><p><code>VLC_UNUSED</code></p><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +</code></pre><pre> +    if ( unlikely( p_out != NULL ) )</pre></blockquote><p>I would consider it a usage-error if <code>stopSoutChain</code> is invoked with <code>NULL</code>, so is it not safe to leave out the above check - and, if it is even possible to end up with such case, handle it ouside of this function?</p></blockquote><div style="white-space: normal;">p_out here is the output stream chain which can be null if the stream chain is not yet started. What is passed here is the input stream. Again similar design of chromecast.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +    {</code></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></blockquote><p>Maybe I am missing something, but I don’t see any case where <code>out_streams[i]->p_sub_id</code> would be <code>NULL</code>, but maybe I read the implementation a little bit too fast.</p></blockquote><div style="white-space: normal;">Similar to what is done by chromecast.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +                sout_StreamIdDel( p_out, out_streams[i]->p_sub_id );</code></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 *p_stream,</pre><pre> +                                                   sout_stream_id_sys_t *id,</pre><pre> +                                                   bool update)</pre><pre> +{</pre><pre> +    size_t i;</pre></blockquote><p>no need to declare <code>i</code> in this scope, do it as part of the for-loop where it is actually relevant.</p></blockquote><div style="white-space: normal;">Ok</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +</code></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 = 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 %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 %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></blockquote><p>Use <code>std::ostringstream</code> as you are only writing to it.</p></blockquote><div style="white-space: normal;">Ok</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +    if ( !canRemux )</code></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></blockquote><p>This suffers from the same problem as a previous bug in <code>stream_out/chromecast</code>, see the below linked ticket.</p><ul><li>https://trac.videolan.org/vlc/ticket/20380</li><li>https://trac.videolan.org/vlc/ticket/20890</li><li><a href="http://git.videolan.org/?p=vlc.git;a=commit;h=710a2ef49669806e208d6845a494a01ed90b18db">http://git.videolan.org/?p=vlc.git;a=commit;h=710a2ef49669806e208d6845a494a01ed90b18db</a></li></ul></blockquote><div style="white-space: normal;">The DLNA/UPNP spec specifies separate actions for syncronised playback to multiple devices which I plan to add once the single playback is merged and see how that could work with VLM. As for the webinterface, it works for me locally while streaming similtaneously but to be safe I have changed the http-port for dlna to 7070 to not be the same as the web interface default port. </div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><ul></ul><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +</code></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, "http://%s:%d/stream.mp4", 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 *service)</pre><pre> +{</pre><pre> +    IXML_Document *p_description_doc = NULL;</pre></blockquote><p>This resource is never released, shouldn’t it be to prevent leaks?</p></blockquote><div style="white-space: normal;">Yes, fixed.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +</code></pre><pre> +    UpnpDownloadXmlDoc(device_url.c_str(), &p_description_doc);</pre></blockquote><p>Does the specification of <code>UpnpDownloadXmlDoc</code> really guarantee that <code>p_description_doc</code> will be left untouched in case of error? If not the error-check below is somewhat useless.</p></blockquote><div style="white-space: normal;">Fixed, thanks.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +    if (!p_description_doc)</code></pre><pre> +        return NULL;</pre><pre> +</pre><pre> +    IXML_NodeList* p_device_list = 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); ++i)</pre><pre> +    {</pre><pre> +        IXML_Element* p_device_element = ( IXML_Element* ) ixmlNodeList_item( p_device_list, i );</pre><pre> +        if( !p_device_element )</pre><pre> +            continue;</pre><pre> +</pre><pre> +        IXML_NodeList* p_service_list = ixmlElement_getElementsByTagName( p_device_element, "service" );</pre><pre> +        if ( !p_service_list )</pre><pre> +            continue;</pre><pre> +        for ( unsigned int j = 0; j < ixmlNodeList_length( p_service_list ); j++ )</pre><pre> +        {</pre><pre> +            IXML_Element* p_service_element = (IXML_Element*)ixmlNodeList_item( p_service_list, j );</pre><pre> +</pre><pre> +            const char* psz_service_type = 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 = xml_getChildElementValue( p_service_element,</pre><pre> +                                                                    service );</pre><pre> +            if ( !psz_control_url )</pre><pre> +                continue;</pre><pre> +</pre><pre> +            char* psz_url = ( char* ) malloc( base_url.length() + strlen( psz_control_url ) + 1 );</pre><pre> +            if ( psz_url )</pre><pre> +            {</pre><pre> +                if ( UpnpResolveURL( base_url.c_str(), psz_control_url, 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 char *service_type,</pre><pre> +                    std::list<std::pair<const char*, const char*>> 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></blockquote><p>This applies to the entire implementation, but if there is no need to separate declaration from initialization, please do both at the same time. It generally makes the implementation easier to read.</p></blockquote><div style="white-space: normal;">Ok</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +</code></pre><pre> +    /* Add argument to action */</pre><pre> +    std::list<std::pair<const char*, const char*>>::iterator arg;</pre></blockquote><p>If you are worried about the loop head getting too long and hard to read, please introduce a <code>typedef</code> (or equivalent) instead of declararing variables in a scope larger than necessary.</p></blockquote><div style="white-space: normal;">Done.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +    for (arg=arguments.begin(); arg != arguments.end(); ++arg) {</code></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, 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, 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, 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, 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", "")); // NOT_IMPLEMENTED</pre><pre> +</pre><pre> +    p_response = SendAction("SetAVTransportURI", 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 *>( 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 *)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 *>( p_stream->p_sys );</pre><pre> +    sout_stream_id_sys_t *id_sys = 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 *>( p_stream->p_sys );</pre><pre> +    sout_stream_id_sys_t *id_sys = 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 *>( 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 *>( p_stream->p_sys );</pre><pre> +    sout_stream_id_sys_t *id = static_cast<sout_stream_id_sys_t *>( _id );</pre><pre> +</pre><pre> +    for (std::vector<sout_stream_id_sys_t*>::iterator it = p_sys->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 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, p_stream->p_cfg);</pre><pre> +</pre><pre> +    http_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-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 "mux");</pre><pre> +    default_mime = var_GetNonEmptyString(p_stream,  SOUT_CFG_PREFIX "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: %s", ex.what() );</pre><pre> +        return VLC_EGENERIC;</pre><pre> +    }</pre><pre> +</pre><pre> +    base_url = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "base_url");</pre><pre> +    device_url = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "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 = 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 = reinterpret_cast<sout_stream_t*>( p_this );</pre><pre> +    sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t *>( 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> + * dlna.hpp : DLNA/UPNP (renderer) sout module header</pre><pre> + *****************************************************************************</pre><pre> + * Copyright (C) 2004-2018 VLC authors and VideoLAN</pre><pre> + *</pre><pre> + * Authors: William Ung <william1.ung@epitech.eu></pre><pre> + *          Shaleen Jain <shaleen@jain.sh></pre><pre> + *</pre><pre> + * This program is free software; you can redistribute it and/or modify it</pre><pre> + * under the terms of the GNU Lesser General Public License as published by</pre><pre> + * 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 License</pre><pre> + * along with this program; if not, write to the Free Software Foundation,</pre><pre> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.</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 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 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 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 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 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[] = "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></blockquote><p>The above assignment does nothing.</p></blockquote><div style="white-space: normal;">Removed.</div><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><blockquote type="cite" style="margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex"><pre><code> +    }</code></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 *service_type,</pre><pre> +                    std::list<std::pair<const char*, const char*>> 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></blockquote><p>Best Regards,<br>Filip</p><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></blockquote><div style="white-space: normal;"><pre>-- <br></pre><div style="width: 71ch;">Regards,</div><div style="width: 71ch;">Shaleen Jain</div></div><div style="white-space: normal;"><span><pre>-- <br></pre><div style="width: 71ch;">Regards,</div><div style="width: 71ch;">Shaleen Jain</div></span></div></div></body></html>