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

Quelle  chromecast_communication.cpp   Sprache: C

 

/*****************************************************************************
 * chromecast_communication.cpp: Handle chromecast protocol messages
 *****************************************************************************
 * Copyright © 2014-2017 VideoLAN
 *
 * Authors: Adrien Maglo <magsoft@videolan.org>
 *          Jean-Baptiste Kempf <jb@videolan.org>
 *          Steve Lhomme <robux4@videolabs.io>
 *          Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * 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 "chromecast.h"
#ifdef HAVE_POLL_H
# include <poll.h>
#endif

#include <iomanip>

#include <vlc_input_item.h>
#include <vlc_url.h>

ChromecastCommunication::ChromecastCommunication( vlc_object_t* p_module,
    std::string serverPath, unsigned int serverPort, const char* targetIP, unsigned int devicePort )
    : m_module( p_module )
    , m_creds( NULL )
    , m_tls( NULL )
    , m_receiver_requestId( 1 )
    , m_requestId( 1 )
    , m_serverPath( serverPath )
    , m_serverPort( serverPort )
{
    if (devicePort == 0)
        devicePort = CHROMECAST_CONTROL_PORT;

    m_creds = vlc_tls_ClientCreate( vlc_object_parent(m_module) );
    if (m_creds == NULL)
        throw std::runtime_error( "Failed to create TLS client" );

    m_tls = vlc_tls_SocketOpenTLS( m_creds, targetIP, devicePort, "tcps",
                                   NULL, NULL );
    if (m_tls == NULL)
    {
        vlc_tls_ClientDelete(m_creds);
        throw std::runtime_error( "Failed to create client session" );
    }

    char psz_localIP[NI_MAXNUMERICHOST];
    if (net_GetSockAddress( vlc_tls_GetFD(m_tls), psz_localIP, NULL ))
        throw std::runtime_error( "Cannot get local IP address" );

    m_serverIp = psz_localIP;
}

ChromecastCommunication::~ChromecastCommunication()
{
    disconnect();
}

void ChromecastCommunication::disconnect()
{
    if ( m_tls != NULL )
    {
        vlc_tls_Close(m_tls);
        vlc_tls_ClientDelete(m_creds);
        m_tls = NULL;
    }
}

/**
 * @brief Build a CastMessage to send to the Chromecast
 * @param namespace_ the message namespace
 * @param payloadType the payload type (CastMessage_PayloadType_STRING or
 * CastMessage_PayloadType_BINARY
 * @param payload the payload
 * @param destinationId the destination idenifier
 * @return the generated CastMessage
 */

int ChromecastCommunication::buildMessage(const std::string & namespace_,
                              const std::string & payload,
                              const std::string & destinationId,
                              castchannel::CastMessage_PayloadType payloadType)
{
    castchannel::CastMessage msg;

    msg.set_protocol_version(castchannel::CastMessage_ProtocolVersion_CASTV2_1_0);
    msg.set_namespace_(namespace_);
    msg.set_payload_type(payloadType);
    msg.set_source_id("sender-vlc");
    msg.set_destination_id(destinationId);
    if (payloadType == castchannel::CastMessage_PayloadType_STRING)
        msg.set_payload_utf8(payload);
    else // CastMessage_PayloadType_BINARY
        msg.set_payload_binary(payload);

    return sendMessage(msg);
}

/**
 * @brief Receive a data packet from the Chromecast
 * @param p_data the buffer in which to store the data
 * @param i_size the size of the buffer
 * @param i_timeout maximum time to wait for a packet, in millisecond
 * @param pb_timeout Output parameter that will contain true if no packet was received due to a timeout
 * @return the number of bytes received of -1 on error
 */

ssize_t ChromecastCommunication::receive( uint8_t *p_data, size_t i_size, int i_timeout, bool *pb_timeout )
{
    ssize_t i_received = 0;

    struct iovec iov;
    iov.iov_base = p_data;
    iov.iov_len = i_size;

    /* The Chromecast normally sends a PING command every 5 seconds or so.
     * If we do not receive one after 6 seconds, we send a PING.
     * If after this PING, we do not receive a PONG, then we consider the
     * connection as dead. */

    do
    {
        ssize_t i_ret = m_tls->ops->readv( m_tls, &iov, 1 );
        if ( i_ret < 0 )
        {
#ifdef _WIN32
            if ( WSAGetLastError() != WSAEWOULDBLOCK )
#else
            if ( errno != EAGAIN )
#endif
            {
                return -1;
            }

            struct pollfd ufd[1];
            ufd[0].events = POLLIN;
            ufd[0].fd = vlc_tls_GetPollFD( m_tls, &ufd[0].events );

            ssize_t val = vlc_poll_i11e(ufd, 1, i_timeout);
            if ( val < 0 )
                return -1;
            else if ( val == 0 )
            {
                *pb_timeout = true;
                return i_received;
            }
            assert( ufd[0].revents & POLLIN );
            continue;
        }
        else if ( i_ret == 0 )
            return -1;
        assert( i_size >= (size_t)i_ret );
        i_size -= i_ret;
        i_received += i_ret;
        iov.iov_base = (uint8_t*)iov.iov_base + i_ret;
        iov.iov_len = i_size;
    } while ( i_size > 0 );
    return i_received;
}


/*****************************************************************************
 * Message preparation
 *****************************************************************************/

unsigned ChromecastCommunication::getNextReceiverRequestId()
{
    unsigned id = m_receiver_requestId++;
    return likely(id != 0) ? id : m_receiver_requestId++;
}

unsigned ChromecastCommunication::getNextRequestId()
{
    unsigned id = m_requestId++;
    return likely(id != 0) ? id : m_requestId++;
}

unsigned ChromecastCommunication::msgAuth()
{
    castchannel::DeviceAuthMessage authMessage;
    authMessage.mutable_challenge();

    return buildMessage(NAMESPACE_DEVICEAUTH, authMessage.SerializeAsString(),
                        DEFAULT_CHOMECAST_RECEIVER, castchannel::CastMessage_PayloadType_BINARY)
           == VLC_SUCCESS ? 1 : kInvalidId;
}


unsigned ChromecastCommunication::msgPing()
{
    std::string s("{\"type\":\"PING\"}");
    return buildMessage( NAMESPACE_HEARTBEAT, s, DEFAULT_CHOMECAST_RECEIVER )
           == VLC_SUCCESS ? 1 : kInvalidId;
}


unsigned ChromecastCommunication::msgPong()
{
    std::string s("{\"type\":\"PONG\"}");
    return buildMessage( NAMESPACE_HEARTBEAT, s, DEFAULT_CHOMECAST_RECEIVER )
           == VLC_SUCCESS ? 1 : kInvalidId;
}

unsigned ChromecastCommunication::msgConnect( const std::string& destinationId )
{
    std::string s("{\"type\":\"CONNECT\"}");
    return buildMessage( NAMESPACE_CONNECTION, s, destinationId )
           == VLC_SUCCESS ? 1 : kInvalidId;
}

unsigned ChromecastCommunication::msgReceiverClose( const std::string& destinationId )
{
    std::string s("{\"type\":\"CLOSE\"}");
    return buildMessage( NAMESPACE_CONNECTION, s, destinationId )
           == VLC_SUCCESS ? 1 : kInvalidId;
}

unsigned ChromecastCommunication::msgReceiverGetStatus()
{
    unsigned id = getNextReceiverRequestId();
    std::stringstream ss;
    ss << "{\"type\":\"GET_STATUS\","
       <<  "\"requestId\":" << id << "}";

    return buildMessage( NAMESPACE_RECEIVER, ss.str(), DEFAULT_CHOMECAST_RECEIVER )
           == VLC_SUCCESS ? id : kInvalidId;
}

unsigned ChromecastCommunication::msgReceiverLaunchApp()
{
    unsigned id = getNextReceiverRequestId();
    std::stringstream ss;
    ss << "{\"type\":\"LAUNCH\","
       <<  "\"appId\":\"" << APP_ID << "\","
       <<  "\"requestId\":" << id << "}";

    return buildMessage( NAMESPACE_RECEIVER, ss.str(), DEFAULT_CHOMECAST_RECEIVER )
           == VLC_SUCCESS ? id : kInvalidId;
}

unsigned ChromecastCommunication::msgPlayerGetStatus( const std::string& destinationId )
{
    unsigned id = getNextRequestId();
    std::stringstream ss;
    ss << "{\"type\":\"GET_STATUS\","
       <<  "\"requestId\":" << id
       << "}";

    return pushMediaPlayerMessage( destinationId, ss ) == VLC_SUCCESS ? id : kInvalidId;
}

static std::string escape_json(const std::string &s)
{
    /* Control characters ('\x00' to '\x1f'), '"' and '\"  must be escaped */
    std::ostringstream o;
    for (std::string::const_iterator c = s.begin(); c != s.end(); c++)
    {
        if (*c == '"' || *c == '\\' || ('\x00' <= *c && *c <= '\x1f'))
            o << "\\u"
              << std::hex << std::setw(4) << std::setfill('0') << (int)*c;
        else
            o << *c;
    }
    return o.str();
}

static std::string meta_get_escaped(const vlc_meta_t *p_meta, vlc_meta_type_t type)
{
    const char *psz = vlc_meta_Get(p_meta, type);
    if (!psz)
        return std::string();
    return escape_json(std::string(psz));
}

std::string ChromecastCommunication::getServerBaseURL() const
{

    vlc_url_t url_comps{};
    url_comps.psz_protocol = (char*)"http";
    url_comps.psz_host = (char*)m_serverIp.c_str();
    url_comps.i_port = m_serverPort;

    char *url = vlc_uri_compose(&url_comps);
    if (!url)
        throw std::bad_alloc();
    std::string url_str = url;
    std::free(url);

    return url_str;
}

std::string ChromecastCommunication::GetMedia( const std::string& mime,
                                               const vlc_meta_t *p_meta,
                                               vlc_tick_t input_length )
{
    std::stringstream ss;

    bool b_music = strncmp(mime.c_str(), "audio", strlen("audio")) == 0;

    std::string title;
    std::string artwork;
    std::string artist;
    std::string album;
    std::string albumartist;
    std::string tracknumber;
    std::string discnumber;

    if( p_meta )
    {
        title = meta_get_escaped( p_meta, vlc_meta_Title );
        artwork = meta_get_escaped( p_meta, vlc_meta_ArtworkURL );

        if( b_music && !title.empty() )
        {
            artist = meta_get_escaped( p_meta, vlc_meta_Artist );
            album = meta_get_escaped( p_meta, vlc_meta_Album );
            albumartist = meta_get_escaped( p_meta, vlc_meta_AlbumArtist );
            tracknumber = meta_get_escaped( p_meta, vlc_meta_TrackNumber );
            discnumber = meta_get_escaped( p_meta, vlc_meta_DiscNumber );
        }
        if( title.empty() )
        {
            title = meta_get_escaped( p_meta, vlc_meta_NowPlaying );
            if( title.empty() )
                title = meta_get_escaped( p_meta, vlc_meta_ESNowPlaying );
        }

        if ( !title.empty() )
        {
            ss << "\"metadata\":{"
               << " \"metadataType\":" << ( b_music ? "3" : "0" )
               << ",\"title\":\"" << title << "\"";
            if( b_music )
            {
                if( !artist.empty() )
                    ss << ",\"artist\":\"" << artist << "\"";
                if( !album.empty() )
                    ss << ",\"album\":\"" << album << "\"";
                if( !albumartist.empty() )
                    ss << ",\"albumArtist\":\"" << albumartist << "\"";
                if( !tracknumber.empty() )
                    ss << ",\"trackNumber\":\"" << tracknumber << "\"";
                if( !discnumber.empty() )
                    ss << ",\"discNumber\":\"" << discnumber << "\"";
            }

            if ( !artwork.empty() && !strncmp( artwork.c_str(), "http", 4 ) )
                ss << ",\"images\":[{\"url\":\"" << artwork << "\"}]";

            ss << "},";
        }
    }

    const std::string chromecast_url = getServerBaseURL() + m_serverPath;

    msg_Dbg( m_module, "s_chromecast_url: %s", chromecast_url.c_str());

    const bool is_buffered = input_length > VLC_TICK_0;
    const char *stream_type;
    if (is_buffered)
        stream_type = "BUFFERED";
    else if (input_length == INPUT_DURATION_INDEFINITE)
        stream_type = "LIVE";
    else
        stream_type = "NONE";

    ss << "\"contentId\":\"" << chromecast_url << "\""
       << ",\"streamType\":\"" << stream_type << "\""
       << ",\"contentType\":\"" << mime << "\"";
       
    if( is_buffered )
    {
        std::stringstream duration;
        duration.setf( std::ios_base::fixed, std::ios_base::floatfield );
        duration << std::setprecision(6) << secf_from_vlc_tick(input_length);
        ss << ",\"duration\":" << duration.str();
    }

    return ss.str();
}

unsigned ChromecastCommunication::msgPlayerLoad( const std::string& destinationId,
                                             const std::string& mime, const vlc_meta_t *p_meta, vlc_tick_t input_length )
{
    unsigned id = getNextRequestId();
    std::stringstream ss;
    ss << "{\"type\":\"LOAD\","
       <<  "\"media\":{" << GetMedia( mime, p_meta, input_length ) << "},"
       <<  "\"requestId\":" << id
       << "}";

    return pushMediaPlayerMessage( destinationId, ss ) == VLC_SUCCESS ? id : kInvalidId;
}

unsigned ChromecastCommunication::msgPlayerPlay( const std::string& destinationId, int64_t mediaSessionId )
{
    assert(mediaSessionId != 0);
    unsigned id = getNextRequestId();

    std::stringstream ss;
    ss << "{\"type\":\"PLAY\","
       <<  "\"mediaSessionId\":" << mediaSessionId << ","
       <<  "\"requestId\":" << id
       << "}";

    return pushMediaPlayerMessage( destinationId, ss ) == VLC_SUCCESS ? id : kInvalidId;
}

unsigned ChromecastCommunication::msgPlayerStop( const std::string& destinationId, int64_t mediaSessionId )
{
    assert(mediaSessionId != 0);
    unsigned id = getNextRequestId();

    std::stringstream ss;
    ss << "{\"type\":\"STOP\","
       <<  "\"mediaSessionId\":" << mediaSessionId << ","
       <<  "\"requestId\":" << id
       << "}";

    return pushMediaPlayerMessage( destinationId, ss ) == VLC_SUCCESS ? id : kInvalidId;
}

unsigned ChromecastCommunication::msgPlayerPause( const std::string& destinationId, int64_t mediaSessionId )
{
    assert(mediaSessionId != 0);
    unsigned id = getNextRequestId();

    std::stringstream ss;
    ss << "{\"type\":\"PAUSE\","
       <<  "\"mediaSessionId\":" << mediaSessionId << ","
       <<  "\"requestId\":" << id
       << "}";

    return pushMediaPlayerMessage( destinationId, ss ) == VLC_SUCCESS ? id : kInvalidId;
}

unsigned ChromecastCommunication::msgPlayerSetVolume( const std::string& destinationId, int64_t mediaSessionId, float f_volume, bool b_mute )
{
    assert(mediaSessionId != 0);
    unsigned id = getNextRequestId();

    if ( f_volume < 0.0 || f_volume > 1.0)
        return VLC_EGENERIC;

    std::stringstream ss;
    ss << "{\"type\":\"SET_VOLUME\","
       <<  "\"volume\":{\"level\":" << f_volume << ",\"muted\":" << ( b_mute ? "true" : "false" ) << "},"
       <<  "\"mediaSessionId\":" << mediaSessionId << ","
       <<  "\"requestId\":" << id
       << "}";

    return pushMediaPlayerMessage( destinationId, ss ) == VLC_SUCCESS ? id : kInvalidId;
}

/**
 * @brief Send a message to the Chromecast
 * @param msg the CastMessage to send
 * @return vlc error code
 */

int ChromecastCommunication::sendMessage( const castchannel::CastMessage &msg )
{
    size_t i_size = msg.ByteSizeLong();
    uint8_t *p_data = new(std::nothrow) uint8_t[PACKET_HEADER_LEN + i_size];
    if (p_data == NULL)
        return VLC_ENOMEM;

#ifndef NDEBUG
    msg_Dbg( m_module, "sendMessage: %s->%s %s", msg.namespace_().c_str(), msg.destination_id().c_str(), msg.payload_utf8().c_str());
#endif

    SetDWBE(p_data, i_size);
    msg.SerializeWithCachedSizesToArray(p_data + PACKET_HEADER_LEN);

    ssize_t i_ret = vlc_tls_Write(m_tls, p_data, PACKET_HEADER_LEN + i_size);
    delete[] p_data;
    if (i_ret > 0 && (size_t)i_ret == PACKET_HEADER_LEN + i_size)
        return VLC_SUCCESS;

    msg_Warn( m_module, "failed to send message %s (%s)", msg.payload_utf8().c_str(), strerror( errno ) );

    return VLC_EGENERIC;
}

int ChromecastCommunication::pushMediaPlayerMessage( const std::string& destinationId, const std::stringstream & payload )
{
    assert(!destinationId.empty());
    return buildMessage( NAMESPACE_MEDIA, payload.str(), destinationId );
}

Messung
CHZG
839710093

¤ Dauer der Verarbeitung: 0.3 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.