Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/stream_out/dlna/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 29 kB image not shown  

Quelle  dlna.cpp   Sprache: C

 

/*****************************************************************************
 * dlna.cpp : DLNA/UPNP (renderer) sout module
 *****************************************************************************
 * Copyright © 2018 VLC authors and VideoLAN
 *
 * Authors: Shaleen Jain <shaleen@jain.sh>
 *          William Ung <william1.ung@epitech.eu>
 *
 * 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_configuration.h>
#include <vlc_cxx_helpers.hpp>
#include <vlc_dialog.h>
#include <vlc_rand.h>
#include <vlc_sout.h>
#include <vlc_block.h>

static const char* AV_TRANSPORT_SERVICE_TYPE = "urn:schemas-upnp-org:service:AVTransport:1";
static const char* CONNECTION_MANAGER_SERVICE_TYPE = "urn:schemas-upnp-org:service:ConnectionManager:1";

static const char *const ppsz_sout_options[] = {
    "ip", "port", "http-port", "video", "base_url", "url", nullptr
};

namespace DLNA
{

struct sout_stream_id_sys_t
{
    es_format_t           fmt;
    const char            *es_id;
    sout_stream_id_sys_t  *p_sub_id;
};

struct sout_stream_sys_t
{
    sout_stream_sys_t(int http_port, bool supports_video)
        : p_out( nullptr )
        , es_changed( true )
        , b_supports_video( supports_video )
        , perf_warning_shown( false )
        , venc_opt_idx ( -1 )
        , http_port( http_port )
    {
    }

    std::shared_ptr<MediaRenderer> renderer;
    UpnpInstanceWrapper *p_upnp;

    ProtocolPtr canDecodeAudio( vlc_fourcc_t i_codec ) const;
    ProtocolPtr canDecodeVideo( vlc_fourcc_t audio_codec,
                         vlc_fourcc_t video_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;
    bool                                es_changed;
    bool                                b_supports_video;
    bool                                perf_warning_shown;
    int                                 venc_opt_idx;
    int                                 http_port;
    std::vector<sout_stream_id_sys_t*>  streams;
    std::vector<sout_stream_id_sys_t*>  out_streams;
    std::vector<protocol_info_t>        device_protocols;

private:
    std::string GetAcodecOption( sout_stream_t *, vlc_fourcc_t *, const audio_format_t *, int );
    int UpdateOutput( sout_stream_t *p_stream );

};

static char *getServerIPAddress() {
    const char *ip = UpnpGetServerIpAddress();
    if (ip == nullptr) {
        ip = UpnpGetServerIp6Address();
    }
    return ip != nullptr ? strdup(ip) : nullptr;
}

std::string dlna_write_protocol_info (const protocol_info_t info)
{
    const dlna_org_flags_t flags = DLNA_ORG_FLAG_STREAMING_TRANSFER_MODE |
                                   DLNA_ORG_FLAG_BACKGROUND_TRANSFERT_MODE |
                                   DLNA_ORG_FLAG_CONNECTION_STALL |
                                   DLNA_ORG_FLAG_DLNA_V15;
  return dlna_write_protocol_info(info, flags, DLNA_ORG_OPERATION_RANGE);
}

std::vector<std::string> split(const std::string &s, char delim) {
    std::stringstream ss(s);
    std::string item;
    std::vector<std::string> elems;
    while (std::getline(ss, item, delim)) {
        elems.push_back(std::move(item));
    }
    return elems;
}

ProtocolPtr sout_stream_sys_t::canDecodeAudio(vlc_fourcc_t audio_codec) const
{
    for (protocol_info_t protocol : device_protocols) {
        if (protocol.profile.media == DLNA_CLASS_AUDIO
                && protocol.profile.audio_codec == audio_codec)
        {
            return std::unique_ptr<protocol_info_t>(new protocol_info_t(protocol));
        }
    }
    return nullptr;
}

ProtocolPtr sout_stream_sys_t::canDecodeVideo(vlc_fourcc_t audio_codec,
                vlc_fourcc_t video_codec) const
{
    for (protocol_info_t protocol : device_protocols) {
        if (protocol.profile.media == DLNA_CLASS_AV
                && protocol.profile.audio_codec == audio_codec
                && protocol.profile.video_codec == video_codec)
        {
            return std::unique_ptr<protocol_info_t>(new protocol_info_t(protocol));
        }
    }
    return nullptr;
}

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(VLC_OBJECT(p_stream), sout.c_str(), nullptr);
    if (p_out == nullptr) {
        msg_Err(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, p_sys_id->es_id ) );
        if ( p_sys_id->p_sub_id == nullptr )
        {
            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)
{
    msg_Dbg( p_stream, "Destroying dlna sout chain");

    for ( size_t i = 0; i < out_streams.size(); i++ )
    {
        sout_StreamIdDel( p_out, out_streams[i]->p_sub_id );
        out_streams[i]->p_sub_id = nullptr;
    }
    out_streams.clear();
    sout_StreamChainDelete( p_out, nullptr );
    p_out = nullptr;
}

sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
                                                   sout_stream_id_sys_t *id,
                                                   bool update)
{
    assert( p_stream->p_sys == this );

    if ( update && UpdateOutput( p_stream ) != VLC_SUCCESS )
        return nullptr;

    for (size_t 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 nullptr;
}

std::string
sout_stream_sys_t::GetAcodecOption( sout_stream_t *p_stream, vlc_fourcc_t *p_codec_audio,
                                    const audio_format_t *p_aud, int i_quality )
{
    VLC_UNUSED(p_aud);
    VLC_UNUSED(i_quality);
    std::stringstream ssout;

    msg_Dbg( p_stream, "Converting audio to %.4s", (const char*)p_codec_audio );

    ssout << "acodec=";
    char fourcc[5];
    vlc_fourcc_to_char( *p_codec_audio, fourcc );
    fourcc[4] = '\0';
    ssout << fourcc << ',';

    ssout << "aenc=avcodec{codec=aac},";
    return ssout.str();
}

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;
    // To keep track of which stream needs transcoding if at all.
    vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0;
    const es_format_t *p_original_audio = nullptr;
    const es_format_t *p_original_video = nullptr;
    std::vector<sout_stream_id_sys_t*> new_streams;

    for (sout_stream_id_sys_t *stream : streams)
    {
        const es_format_t *p_es = &stream->fmt;
        if (p_es->i_cat == AUDIO_ES)
        {
            p_original_audio = p_es;
            new_streams.push_back(stream);
        }
        else if (b_supports_video && p_es->i_cat == VIDEO_ES)
        {
            p_original_video = p_es;
            new_streams.push_back(stream);
        }
    }

    if (new_streams.empty())
        return VLC_SUCCESS;

    ProtocolPtr stream_protocol;
    // check if we have an audio only stream
    if (!p_original_video && p_original_audio)
    {
        if( !(stream_protocol = canDecodeAudio(p_original_audio->i_codec)) )
        {
            msg_Dbg( p_stream, "can't remux audio track %d codec %4.4s",
                p_original_audio->i_id, (const char*)&p_original_audio->i_codec );
            stream_protocol = make_protocol(default_audio_protocol);
            canRemux = false;
        }
        else
            i_codec_audio = p_original_audio->i_codec;
    }
    // video only stream
    else if (p_original_video && !p_original_audio)
    {
        if( !(stream_protocol = canDecodeVideo(VLC_CODEC_NONE,
                        p_original_video->i_codec)) )
        {
            msg_Dbg(p_stream, "can't remux video track %d codec: %4.4s",
                p_original_video->i_id, (const char*)&p_original_video->i_codec);
            stream_protocol = make_protocol(default_video_protocol);
            canRemux = false;
        }
        else
            i_codec_video = p_original_video->i_codec;
    }
    else
    {
        if( !(stream_protocol = canDecodeVideo( p_original_audio->i_codec,
                        p_original_video->i_codec)) )
        {
            msg_Dbg(p_stream, "can't remux video track %d with audio: %4.4s and video: %4.4s",
                p_original_video->i_id, (const char*)&p_original_audio->i_codec,
                    (const char*)&p_original_video->i_codec);
            stream_protocol = make_protocol(default_video_protocol);
            canRemux = false;

            // check which codec needs transcoding
            if (stream_protocol->profile.audio_codec == p_original_audio->i_codec)
                i_codec_audio = p_original_audio->i_codec;
            if (stream_protocol->profile.video_codec == p_original_video->i_codec)
                i_codec_video = p_original_video->i_codec;
        }
        else
        {
            i_codec_audio = p_original_audio->i_codec;
            i_codec_video = p_original_video->i_codec;
        }
    }

    msg_Dbg( p_stream, "using DLNA profile %s:%s",
                stream_protocol->profile.mime.c_str(),
                stream_protocol->profile.name.c_str() );

    std::ostringstream ssout;
    if ( !canRemux )
    {
        if ( !perf_warning_shown && i_codec_video == 0 && p_original_video
          && var_InheritInteger( p_stream, RENDERER_CFG_PREFIX "show-perf-warning" ) )
        {
            int res = vlc_dialog_wait_question( p_stream,
                          VLC_DIALOG_QUESTION_WARNING,
                         _("Cancel"), _("OK"), _("OK, don't warn me again"),
                         _("Performance warning"),
                         _("Casting this video requires conversion. "
                           "This conversion can use all the available power and "
                           "could quickly drain your battery." ) );
            perf_warning_shown = true;
            if ( res <= 0 )
                 return false;
            if ( res == 2 )
                config_PutInt(RENDERER_CFG_PREFIX "show-perf-warning", 0 );
        }

        const int i_quality = var_InheritInteger( p_stream, SOUT_CFG_PREFIX "conversion-quality" );

        /* TODO: provide audio samplerate and channels */
        ssout << "transcode{";
        if ( i_codec_audio == 0 && p_original_audio )
        {
            i_codec_audio = stream_protocol->profile.audio_codec;
            ssout << GetAcodecOption( p_stream, &i_codec_audio,
                                      &p_original_audio->audio, i_quality );
        }
        if ( i_codec_video == 0 && p_original_video )
        {
            i_codec_video = stream_protocol->profile.video_codec;
            try {
                ssout << vlc_sout_renderer_GetVcodecOption( p_stream,
                                        { i_codec_video },
                                        &i_codec_video,
                                        &p_original_video->video, i_quality );
            } catch(const std::exception& e) {
                return VLC_EGENERIC ;
            }
        }
        ssout << "}:";
    }

    std::ostringstream ss;
    ss << "/dlna"
       << "/" << vlc_tick_now()
       << "/" << static_cast<uint64_t>( vlc_mrand48() )
       << "/stream";
    std::string root_url = ss.str();

    ssout << "http{dst=:" << http_port << root_url
          << ",mux=" << stream_protocol->profile.mux
          << ",access=http{mime=" << stream_protocol->profile.mime << "}}";

    auto ip = vlc::wrap_cptr<char>(getServerIPAddress());
    if (ip == nullptr)
    {
        msg_Err(p_stream, "could not get the local ip address");
        return VLC_EGENERIC;
    }

    char *uri;
    if (asprintf(&uri, "http://%s:%d%s", ip.get(), http_port, root_url.c_str()) < 0) {
        return VLC_ENOMEM;
    }

    if ( !startSoutChain( p_stream, new_streams, ssout.str() ) ) {
        free(uri);
        return VLC_EGENERIC;
    }

    renderer->PrepareForConnection(dlna_write_protocol_info(*stream_protocol).c_str());

    msg_Dbg(p_stream, "AVTransportURI: %s", uri);
    renderer->Stop();
    renderer->SetAVTransportURI(uri, *stream_protocol);
    renderer->Play("1");

    free(uri);
    return VLC_SUCCESS;
}

char *MediaRenderer::getServiceURL(const char* type, const char *service)
{
    IXML_Document *p_description_doc = nullptr;
    if (UpnpDownloadXmlDoc(device_url.c_str(), &p_description_doc) != UPNP_E_SUCCESS)
        return nullptr;

    IXML_NodeList* p_device_list = ixmlDocument_getElementsByTagName( p_description_doc, "device");
    free(p_description_doc);
    if ( !p_device_list )
        return nullptr;

    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 = NULL;
            if ( UpnpResolveURL2( base_url.c_str(), psz_control_url, &psz_url ) == UPNP_E_SUCCESS )
                return psz_url;
            return nullptr;
        }
    }
    return nullptr;
}

/**
 * Send an action to the control url of the service specified.
 *
 * \return the response as a IXML document or nullptr for failure
 **/

IXML_Document *MediaRenderer::SendAction(const char* action_name,const char *service_type,
                    std::list<std::pair<const char*, const char*>> arguments)
{
    /* Create action */
    IXML_Document *action = UpnpMakeAction(action_name, service_type, 0, nullptr);

    /* Add argument to action */
    for (std::pair<const char*, const char*> arg : arguments) {
      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 */
    IXML_Document *response = nullptr;
    int ret = UpnpSendAction(handle, control_url, service_type,
                                    nullptr, action, &response);

    /* Free action */
    ixmlDocument_free(action);
    free(control_url);

    if (ret != UPNP_E_SUCCESS) {
        msg_Err(parent, "Unable to send action: %s (%d: %s) response: %s",
                action_name, ret, UpnpGetErrorMessage(ret), ixmlPrintDocument(response));
        if (response) ixmlDocument_free(response);
        return nullptr;
    }

    return response;
}

int MediaRenderer::Play(const char *speed)
{
    std::list<std::pair<const char*, const char*>> arg_list = {
        {"InstanceID", AVTransportID.c_str()},
        {"Speed", speed},
    };

    IXML_Document *p_response = SendAction("Play", AV_TRANSPORT_SERVICE_TYPE, arg_list);
    if(!p_response)
    {
        return VLC_EGENERIC;
    }
    ixmlDocument_free(p_response);
    return VLC_SUCCESS;
}

int MediaRenderer::Stop()
{
    std::list<std::pair<const char*, const char*>> arg_list = {
        {"InstanceID", AVTransportID.c_str()},
    };

    IXML_Document *p_response = SendAction("Stop", AV_TRANSPORT_SERVICE_TYPE, arg_list);
    if(!p_response)
    {
        return VLC_EGENERIC;
    }
    ixmlDocument_free(p_response);
    return VLC_SUCCESS;
}

std::vector<protocol_info_t> MediaRenderer::GetProtocolInfo()
{
    std::string protocol_csv;
    std::vector<protocol_info_t> supported_protocols;
    std::list<std::pair<const char*, const char*>> arg_list;

    IXML_Document *response = SendAction("GetProtocolInfo",
                                CONNECTION_MANAGER_SERVICE_TYPE, arg_list);
    if(!response)
    {
        return supported_protocols;
    }

    // Get the CSV list of protocols/profiles supported by the device
    IXML_NodeList *protocol_list = ixmlDocument_getElementsByTagName( response , "Sink" );
    if( protocol_list )
    {
        IXML_Node* protocol_node = ixmlNodeList_item( protocol_list, 0 );
        if ( protocol_node )
        {
            IXML_Node* p_text_node = ixmlNode_getFirstChild( protocol_node );
            if ( p_text_node )
            {
                protocol_csv.assign(ixmlNode_getNodeValue( p_text_node ));
            }
        }
        ixmlNodeList_free( protocol_list);
    }
    ixmlDocument_free(response);

    msg_Dbg(parent, "Device supports protocols: %s", protocol_csv.c_str());
    // parse the CSV list
    // format: <transportProtocol>:<network>:<mime>:<additionalInfo>
    std::vector<std::string> protocols = split(protocol_csv, ',');
    for (std::string protocol : protocols ) {
        std::vector<std::string> protocol_info = split(protocol, ':');

        // We only support http transport for now.
        if (protocol_info.size() == 4 && protocol_info.at(0) == "http-get")
        {
            protocol_info_t proto;

            // Get the DLNA profile name
            std::string profile_name;
            std::string tag = "DLNA.ORG_PN=";
            std::size_t index = protocol_info.at(3).find(tag);

            if (protocol_info.at(3) == "*")
            {
               profile_name = "*";
            }
            else if (index != std::string::npos)
            {
                std::size_t end = protocol_info.at(3).find(';', index + 1);
                int start = index + tag.length() - 1;
                int length = end - start;
                profile_name = protocol_info.at(3).substr(start, length);
            }

            // Match our supported profiles to device profiles
            for (dlna_profile_t profile : dlna_profile_list) {
                if (protocol_info.at(2) == profile.mime
                        && (profile_name == profile.name || profile_name == "*"))
                {
                    proto.profile = std::move(profile);
                    supported_protocols.push_back(proto);
                    // we do not break here to account for wildcards
                    // as protocolInfo's fourth field aka <additionalInfo>
                }
            }
        }
    }

    msg_Dbg( parent , "Got %zu supported profiles", supported_protocols.size() );
    return supported_protocols;
}

int MediaRenderer::PrepareForConnection(const char* protocol_str)
{
    std::list<std::pair<const char*, const char*>> arg_list = {
        { "PeerConnectionID", "-1" },
        { "PeerConnectionManager", "" },
        { "Direction", "Input" },
        { "RemoteProtocolInfo", protocol_str },
    };

    IXML_Document *response = SendAction("PrepareForConnection",
                                    CONNECTION_MANAGER_SERVICE_TYPE, arg_list);
    if(!response)
    {
        return VLC_EGENERIC;
    }

    msg_Dbg(parent, "PrepareForConnection response: %s",
                                    ixmlPrintDocument(response));

    IXML_NodeList *node_list =
            ixmlDocument_getElementsByTagName(response , "ConnectionID");
    if (node_list)
    {
        IXML_Node* node = ixmlNodeList_item(node_list, 0);
        if (node)
        {
            IXML_Node* p_text_node = ixmlNode_getFirstChild(node);
            if (p_text_node)
            {
                ConnectionID.assign(ixmlNode_getNodeValue(p_text_node));
            }
        }
        ixmlNodeList_free(node_list);
    }

    node_list =
            ixmlDocument_getElementsByTagName(response , "AVTransportID");
    if (node_list)
    {
        IXML_Node* node = ixmlNodeList_item(node_list, 0);
        if (node)
        {
            IXML_Node* p_text_node = ixmlNode_getFirstChild(node);
            if (p_text_node)
            {
                AVTransportID.assign(ixmlNode_getNodeValue(p_text_node));
            }
        }
        ixmlNodeList_free(node_list);
    }

    node_list =
            ixmlDocument_getElementsByTagName(response , "RcsID");
    if (node_list)
    {
        IXML_Node* node = ixmlNodeList_item(node_list, 0);
        if (node)
        {
            IXML_Node* p_text_node = ixmlNode_getFirstChild(node);
            if (p_text_node)
            {
                RcsID.assign(ixmlNode_getNodeValue(p_text_node));
            }
        }
        ixmlNodeList_free(node_list);
    }

    ixmlDocument_free(response);
    return VLC_SUCCESS;
}

int MediaRenderer::ConnectionComplete()
{
    std::list<std::pair<const char*, const char*>> arg_list = {
        {"ConnectionID", ConnectionID.c_str()},
    };

    IXML_Document *p_response = SendAction("ConnectionComplete",
                                    CONNECTION_MANAGER_SERVICE_TYPE, arg_list);
    if(!p_response)
    {
        return VLC_EGENERIC;
    }

    ixmlDocument_free(p_response);
    return VLC_SUCCESS;
}

int MediaRenderer::SetAVTransportURI(const char* uri, const protocol_info_t& proto)
{
    static const char didl[] =
        "<DIDL-Lite "
        "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
        "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" "
        "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" "
        "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
        "<item id=\"f-0\" parentID=\"0\" restricted=\"0\">"
        "<dc:title>%s</dc:title>"
        "<upnp:class>%s</upnp:class>"
        "<res protocolInfo=\"%s\">%s</res>"
        "</item>"
        "</DIDL-Lite>";

    bool audio = proto.profile.media == DLNA_CLASS_AUDIO;
    std::string dlna_protocol = dlna_write_protocol_info(proto);

    char *meta_data;
    if (asprintf(&meta_data, didl,
                audio ? "Audio" : "Video",
                audio ? "object.item.audioItem" : "object.item.videoItem",
                dlna_protocol.c_str(),
                uri) < 0) {
        return VLC_ENOMEM;
    }

    msg_Dbg(parent, "didl: %s", meta_data);
    std::list<std::pair<const char*, const char*>> arg_list = {
        {"InstanceID", AVTransportID.c_str()},
        {"CurrentURI", uri},
        {"CurrentURIMetaData", meta_data},
    };

    IXML_Document *p_response = SendAction("SetAVTransportURI",
                                    AV_TRANSPORT_SERVICE_TYPE, arg_list);

    free(meta_data);
    if(!p_response)
    {
        return VLC_EGENERIC;
    }
    ixmlDocument_free(p_response);
    return VLC_SUCCESS;
}

static void *
Add(sout_stream_t *p_stream, const es_format_t *p_fmt, const char *es_id)
{
    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 nullptr;
    }

    sout_stream_id_sys_t *p_sys_id = (sout_stream_id_sys_t *)malloc(sizeof(sout_stream_id_sys_t));
    if(p_sys_id != nullptr)
    {
        es_format_Copy(&p_sys_id->fmt, p_fmt);
        p_sys_id->es_id = es_id;
        p_sys_id->p_sub_id = nullptr;
        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 == nullptr )
    {
        block_Release( p_buffer );
        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 == nullptr )
        return;

    sout_StreamFlush( p_sys->p_out, id_sys );
    p_sys->stopSoutChain( p_stream );
    p_sys->es_changed = true;
}

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 (auto it = p_sys->streams.begin(); it != p_sys->streams.end(); ++it)
    {
        sout_stream_id_sys_t *p_sys_id = *it;
        if ( p_sys_id == id )
        {
            if ( p_sys_id->p_sub_id != nullptr )
            {
                sout_StreamIdDel( p_sys->p_out, p_sys_id->p_sub_id );
                for (auto out_it = p_sys->out_streams.begin();
                     out_it != p_sys->out_streams.end(); ++out_it)
                {
                    if (*out_it == id)
                    {
                        p_sys->out_streams.erase(out_it);
                        break;
                    }
                }
            }

            es_format_Clean( &p_sys_id->fmt );
            free( p_sys_id );
            p_sys->streams.erase( it );
            break;
        }
    }

    if (p_sys->out_streams.empty())
    {
        p_sys->stopSoutChain(p_stream);
        p_sys->renderer->Stop();
    }
}

static void CloseSout( sout_stream_t *p_stream )
{
    sout_stream_sys_t *p_sys = static_cast<sout_stream_sys_t *>( p_stream->p_sys );

    p_sys->renderer->ConnectionComplete();
    p_sys->p_upnp->release();
    delete p_sys;
}

static const struct sout_stream_operations ops = [] {
    struct sout_stream_operations ops {};
    ops.add = Add;
    ops.del = Del;
    ops.send = Send;
    ops.flush = Flush;
    ops.close = CloseSout;
    return ops;
}();

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 = nullptr;

    config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);

    int http_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port");
    bool b_supports_video = var_GetBool(p_stream, SOUT_CFG_PREFIX "video");
    auto base_url = vlc::wrap_cptr(
        var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "base_url"));
    auto device_url = vlc::wrap_cptr(
        var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "url"));
    if ( device_url == nullptr)
    {
        msg_Err( p_stream, "missing Url" );
        goto error;
    }

    try {
        p_sys = new sout_stream_sys_t(http_port, b_supports_video);
    }
    catch ( const std::exception& ex ) {
        msg_Err( p_stream, "Failed to instantiate sout_stream_sys_t: %s", ex.what() );
        return VLC_EGENERIC;
    }

    p_sys->p_upnp = UpnpInstanceWrapper::get( p_this );
    if ( !p_sys->p_upnp )
        goto error;
    try {
        p_sys->renderer = std::make_shared<MediaRenderer>(p_stream,
                            p_sys->p_upnp, base_url.get(), device_url.get());
    }
    catch ( const std::bad_alloc& ) {
        msg_Err( p_stream, "Failed to create a MediaRenderer");
        p_sys->p_upnp->release();
        goto error;
    }

    p_sys->device_protocols = p_sys->renderer->GetProtocolInfo();

    p_stream->p_sys = p_sys;
    p_stream->ops = &ops;

    return VLC_SUCCESS;

error:
    delete p_sys;
    return VLC_EGENERIC;
}

}

Messung
CHZG
82969089

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.