vlc | branch: master | Hugo Beauzée-Luyssen <h...@beauzee.fr> | Fri Feb 17 15:31:38 2017 +0100| [bf234d5733cc6a171cfebaa3edba88fdd853b21e] | committer: Hugo Beauzée-Luyssen
chromecast: Move ChromecastCommunication in its own file > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=bf234d5733cc6a171cfebaa3edba88fdd853b21e --- modules/stream_out/Makefile.am | 2 +- modules/stream_out/chromecast/chromecast.h | 14 + .../chromecast/chromecast_communication.cpp | 463 +++++++++++++++++++++ modules/stream_out/chromecast/chromecast_ctrl.cpp | 440 -------------------- 4 files changed, 478 insertions(+), 441 deletions(-) diff --git a/modules/stream_out/Makefile.am b/modules/stream_out/Makefile.am index a598bcc..f30069e 100644 --- a/modules/stream_out/Makefile.am +++ b/modules/stream_out/Makefile.am @@ -87,7 +87,7 @@ libdemux_chromecast_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -Istream_out/chromecast libstream_out_chromecast_plugin_la_SOURCES = stream_out/chromecast/cast.cpp stream_out/chromecast/chromecast.h \ stream_out/chromecast/cast_channel.proto \ stream_out/chromecast/chromecast_common.h stream_out/chromecast/chromecast_ctrl.cpp \ - misc/webservices/json.h misc/webservices/json.c + misc/webservices/json.h misc/webservices/json.c stream_out/chromecast/chromecast_communication.cpp nodist_libstream_out_chromecast_plugin_la_SOURCES = stream_out/chromecast/cast_channel.pb.cc libstream_out_chromecast_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -Istream_out/chromecast $(CHROMECAST_CFLAGS) libstream_out_chromecast_plugin_la_LIBADD = $(CHROMECAST_LIBS) $(SOCKET_LIBS) diff --git a/modules/stream_out/chromecast/chromecast.h b/modules/stream_out/chromecast/chromecast.h index cf1763c..27406bd 100644 --- a/modules/stream_out/chromecast/chromecast.h +++ b/modules/stream_out/chromecast/chromecast.h @@ -46,10 +46,24 @@ static const std::string DEFAULT_CHOMECAST_RECEIVER = "receiver-0"; /* see https://developers.google.com/cast/docs/reference/messages */ static const std::string NAMESPACE_MEDIA = "urn:x-cast:com.google.cast.media"; +static const std::string NAMESPACE_DEVICEAUTH = "urn:x-cast:com.google.cast.tp.deviceauth"; +static const std::string NAMESPACE_CONNECTION = "urn:x-cast:com.google.cast.tp.connection"; +static const std::string NAMESPACE_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat"; +static const std::string NAMESPACE_RECEIVER = "urn:x-cast:com.google.cast.receiver"; + #define CHROMECAST_CONTROL_PORT 8009 #define HTTP_PORT 8010 +/* deadline regarding pings sent from receiver */ +#define PING_WAIT_TIME 6000 +#define PING_WAIT_RETRIES 0 + +#define PACKET_MAX_LEN 10 * 1024 + +// Media player Chromecast app id +#define APP_ID "CC1AD845" // Default media player aka DEFAULT_MEDIA_RECEIVER_APPLICATION_ID + // Status enum connection_status { diff --git a/modules/stream_out/chromecast/chromecast_communication.cpp b/modules/stream_out/chromecast/chromecast_communication.cpp new file mode 100644 index 0000000..8c2e93b --- /dev/null +++ b/modules/stream_out/chromecast/chromecast_communication.cpp @@ -0,0 +1,463 @@ +/***************************************************************************** + * chromecast_communication.cpp: Handle chromecast protocol messages + ***************************************************************************** + * Copyright © 2014-2017 VideoLAN + * + * Authors: Adrien Maglo <mags...@videolan.org> + * Jean-Baptiste Kempf <j...@videolan.org> + * Steve Lhomme <rob...@videolabs.io> + * Hugo Beauzée-Luyssen <h...@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 +# include <poll.h> +#endif + +/* deadline regarding pong we expect after pinging the receiver */ +#define PONG_WAIT_TIME 500 +#define PONG_WAIT_RETRIES 2 + +ChromecastCommunication::ChromecastCommunication( vlc_object_t* p_module ) + : p_module( p_module ) + , i_sock_fd( -1 ) + , p_creds( NULL ) + , p_tls( NULL ) + , i_receiver_requestId( 0 ) + , i_requestId( 0 ) +{ +} + +bool ChromecastCommunication::connect( const char* targetIP, unsigned int devicePort ) +{ + if (devicePort == 0) + devicePort = CHROMECAST_CONTROL_PORT; + i_sock_fd = net_ConnectTCP( p_module, targetIP, devicePort); + if (i_sock_fd < 0) + return false; + + char psz_localIP[NI_MAXNUMERICHOST]; + if ( net_GetSockAddress( i_sock_fd, psz_localIP, NULL ) ) + { + msg_Err( p_module, "Cannot get local IP address" ); + return false; + } + serverIp = psz_localIP; + + p_creds = vlc_tls_ClientCreate( p_module->obj.parent ); + if (p_creds == NULL) + { + msg_Err( p_module, "Failed to create TLS client" ); + net_Close(i_sock_fd); + return false; + } + + p_tls = vlc_tls_ClientSessionCreateFD( p_creds, i_sock_fd, targetIP, "tcps", NULL, NULL ); + + if (p_tls == NULL) + { + msg_Err( p_module, "Failed to create client session" ); + net_Close(i_sock_fd); + vlc_tls_Delete(p_creds); + return false; + } + return true; +} + +ChromecastCommunication::~ChromecastCommunication() +{ + disconnect(); +} + +void ChromecastCommunication::disconnect() +{ + if ( p_tls != NULL ) + { + vlc_tls_Close(p_tls); + vlc_tls_Delete(p_creds); + p_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 + */ +void 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); + + sendMessage(msg); +} + +/** + * @brief Receive a data packet from the Chromecast + * @param p_module the module to log with + * @param b_msgReceived returns true if a message has been entirely received else false + * @param i_payloadSize returns the payload size of the message received + * @return the number of bytes received of -1 on error + */ +int ChromecastCommunication::recvPacket(bool *b_msgReceived, + uint32_t &i_payloadSize, + unsigned *pi_received, uint8_t *p_data, bool *pb_pingTimeout, + int *pi_wait_delay, int *pi_wait_retries) +{ + struct pollfd ufd[1]; + ufd[0].fd = i_sock_fd; + ufd[0].events = POLLIN; + + /* 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. */ + ssize_t val = vlc_poll_i11e(ufd, 1, *pi_wait_delay); + if ( val == -1 && errno != EINTR ) + return -1; + + if (val == 0) + { + if (*pb_pingTimeout) + { + if (!*pi_wait_retries) + { + msg_Err( p_module, "No PONG answer received from the Chromecast"); + return 0; // Connection died + } + (*pi_wait_retries)--; + } + else + { + /* now expect a pong */ + *pi_wait_delay = PONG_WAIT_TIME; + *pi_wait_retries = PONG_WAIT_RETRIES; + msg_Warn( p_module, "No PING received from the Chromecast, sending a PING"); + } + *pb_pingTimeout = true; + } + else + { + *pb_pingTimeout = false; + /* reset to default ping waiting */ + *pi_wait_delay = PING_WAIT_TIME; + *pi_wait_retries = PING_WAIT_RETRIES; + } + + int i_ret = 0; + if ( ufd[0].revents & POLLIN ) + { + /* we have received stuff */ + + /* Packet structure: + * +------------------------------------+------------------------------+ + * | Payload size (uint32_t big endian) | Payload data | + * +------------------------------------+------------------------------+ */ + while (*pi_received < PACKET_HEADER_LEN) + { + // We receive the header. + i_ret = tls_Recv(p_tls, p_data + *pi_received, PACKET_HEADER_LEN - *pi_received); + if (i_ret <= 0) + return i_ret; + *pi_received += i_ret; + } + + // We receive the payload. + + // Get the size of the payload + i_payloadSize = U32_AT( p_data ); + const uint32_t i_maxPayloadSize = PACKET_MAX_LEN - PACKET_HEADER_LEN; + + if (i_payloadSize > i_maxPayloadSize) + { + // Error case: the packet sent by the Chromecast is too long: we drop it. + msg_Err( p_module, "Packet too long: droping its data"); + + uint32_t i_size = i_payloadSize - (*pi_received - PACKET_HEADER_LEN); + if (i_size > i_maxPayloadSize) + i_size = i_maxPayloadSize; + + i_ret = tls_Recv(p_tls, p_data + PACKET_HEADER_LEN, i_size); + if (i_ret <= 0) + return i_ret; + *pi_received += i_ret; + + if (*pi_received < i_payloadSize + PACKET_HEADER_LEN) + return i_ret; + + *pi_received = 0; + return -1; + } + + // Normal case + i_ret = tls_Recv(p_tls, p_data + *pi_received, + i_payloadSize - (*pi_received - PACKET_HEADER_LEN)); + if (i_ret <= 0) + return i_ret; + *pi_received += i_ret; + + if (*pi_received < i_payloadSize + PACKET_HEADER_LEN) + return i_ret; + + assert(*pi_received == i_payloadSize + PACKET_HEADER_LEN); + *pi_received = 0; + *b_msgReceived = true; + } + + if ( val == -1 && errno == EINTR ) + /* we have stuff to send */ + i_ret = 1; + + return i_ret; +} + + +/***************************************************************************** + * Message preparation + *****************************************************************************/ +void ChromecastCommunication::msgAuth() +{ + castchannel::DeviceAuthMessage authMessage; + authMessage.mutable_challenge(); + + buildMessage(NAMESPACE_DEVICEAUTH, authMessage.SerializeAsString(), + DEFAULT_CHOMECAST_RECEIVER, castchannel::CastMessage_PayloadType_BINARY); +} + + +void ChromecastCommunication::msgPing() +{ + std::string s("{\"type\":\"PING\"}"); + buildMessage( NAMESPACE_HEARTBEAT, s, DEFAULT_CHOMECAST_RECEIVER ); +} + + +void ChromecastCommunication::msgPong() +{ + std::string s("{\"type\":\"PONG\"}"); + buildMessage( NAMESPACE_HEARTBEAT, s, DEFAULT_CHOMECAST_RECEIVER ); +} + +void ChromecastCommunication::msgConnect( const std::string& destinationId ) +{ + std::string s("{\"type\":\"CONNECT\"}"); + buildMessage( NAMESPACE_CONNECTION, s, destinationId ); +} + +void ChromecastCommunication::msgReceiverClose( const std::string& destinationId ) +{ + std::string s("{\"type\":\"CLOSE\"}"); + buildMessage( NAMESPACE_CONNECTION, s, destinationId ); +} + +void ChromecastCommunication::msgReceiverGetStatus() +{ + std::stringstream ss; + ss << "{\"type\":\"GET_STATUS\"," + << "\"requestId\":" << i_receiver_requestId++ << "}"; + + buildMessage( NAMESPACE_RECEIVER, ss.str(), DEFAULT_CHOMECAST_RECEIVER ); +} + +void ChromecastCommunication::msgReceiverLaunchApp() +{ + std::stringstream ss; + ss << "{\"type\":\"LAUNCH\"," + << "\"appId\":\"" << APP_ID << "\"," + << "\"requestId\":" << i_receiver_requestId++ << "}"; + + buildMessage( NAMESPACE_RECEIVER, ss.str(), DEFAULT_CHOMECAST_RECEIVER ); +} + +void ChromecastCommunication::msgPlayerGetStatus( const std::string& destinationId ) +{ + std::stringstream ss; + ss << "{\"type\":\"GET_STATUS\"," + << "\"requestId\":" << i_requestId++ + << "}"; + + pushMediaPlayerMessage( destinationId, ss ); +} + +std::string ChromecastCommunication::GetMedia( unsigned int i_port, + const std::string& title, const std::string& artwork, + const std::string& mime ) +{ + std::stringstream ss; + + if ( title.size() ) + { + ss << "\"metadata\":{" + << " \"metadataType\":0" + << ",\"title\":\"" << title << "\""; + + if ( artwork.size() && !strncmp(artwork.c_str(), "http", 4)) + ss << ",\"images\":[\"" << artwork << "\"]"; + + ss << "},"; + } + + std::stringstream chromecast_url; + chromecast_url << "http://" << serverIp << ":" << i_port << "/stream"; + + msg_Dbg( p_module, "s_chromecast_url: %s", chromecast_url.str().c_str()); + + ss << "\"contentId\":\"" << chromecast_url.str() << "\"" + << ",\"streamType\":\"LIVE\"" + << ",\"contentType\":\"" << mime << "\""; + + return ss.str(); +} + +void ChromecastCommunication::msgPlayerLoad( const std::string& destinationId, unsigned int i_port, + const std::string& title, const std::string& artwork, + const std::string& mime ) +{ + std::stringstream ss; + ss << "{\"type\":\"LOAD\"," + << "\"media\":{" << GetMedia( i_port, title, artwork, mime ) << "}," + << "\"autoplay\":\"false\"," + << "\"requestId\":" << i_requestId++ + << "}"; + + pushMediaPlayerMessage( destinationId, ss ); +} + +void ChromecastCommunication::msgPlayerPlay( const std::string& destinationId, const std::string& mediaSessionId ) +{ + assert(!mediaSessionId.empty()); + + std::stringstream ss; + ss << "{\"type\":\"PLAY\"," + << "\"mediaSessionId\":" << mediaSessionId << "," + << "\"requestId\":" << i_requestId++ + << "}"; + + pushMediaPlayerMessage( destinationId, ss ); +} + +void ChromecastCommunication::msgPlayerStop( const std::string& destinationId, const std::string& mediaSessionId ) +{ + assert(!mediaSessionId.empty()); + + std::stringstream ss; + ss << "{\"type\":\"STOP\"," + << "\"mediaSessionId\":" << mediaSessionId << "," + << "\"requestId\":" << i_requestId++ + << "}"; + + pushMediaPlayerMessage( destinationId, ss ); +} + +void ChromecastCommunication::msgPlayerPause( const std::string& destinationId, const std::string& mediaSessionId ) +{ + assert(!mediaSessionId.empty()); + + std::stringstream ss; + ss << "{\"type\":\"PAUSE\"," + << "\"mediaSessionId\":" << mediaSessionId << "," + << "\"requestId\":" << i_requestId++ + << "}"; + + pushMediaPlayerMessage( destinationId, ss ); +} + +void ChromecastCommunication::msgPlayerSetVolume( const std::string& destinationId, const std::string& mediaSessionId, float f_volume, bool b_mute ) +{ + assert(!mediaSessionId.empty()); + + if ( f_volume < 0.0 || f_volume > 1.0) + return; + + std::stringstream ss; + ss << "{\"type\":\"SET_VOLUME\"," + << "\"volume\":{\"level\":" << f_volume << ",\"muted\":" << ( b_mute ? "true" : "false" ) << "}," + << "\"mediaSessionId\":" << mediaSessionId << "," + << "\"requestId\":" << i_requestId++ + << "}"; + + pushMediaPlayerMessage( destinationId, ss ); +} + +void ChromecastCommunication::msgPlayerSeek( const std::string& destinationId, const std::string& mediaSessionId, const std::string& currentTime ) +{ + assert(!mediaSessionId.empty()); + + std::stringstream ss; + ss << "{\"type\":\"SEEK\"," + << "\"currentTime\":" << currentTime << "," + << "\"mediaSessionId\":" << mediaSessionId << "," + << "\"requestId\":" << i_requestId++ + << "}"; + + pushMediaPlayerMessage( destinationId, ss ); +} + +/** + * @brief Send a message to the Chromecast + * @param msg the CastMessage to send + * @return vlc error code + */ +int ChromecastCommunication::sendMessage( const castchannel::CastMessage &msg ) +{ + int i_size = msg.ByteSize(); + 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( p_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); + + int i_ret = tls_Send(p_tls, p_data, PACKET_HEADER_LEN + i_size); + delete[] p_data; + if (i_ret == PACKET_HEADER_LEN + i_size) + return VLC_SUCCESS; + + msg_Warn( p_module, "failed to send message %s (%s)", msg.payload_utf8().c_str(), strerror( errno ) ); + + return VLC_EGENERIC; +} + +void ChromecastCommunication::pushMediaPlayerMessage( const std::string& destinationId, const std::stringstream & payload ) +{ + assert(!destinationId.empty()); + buildMessage( NAMESPACE_MEDIA, payload.str(), destinationId ); +} diff --git a/modules/stream_out/chromecast/chromecast_ctrl.cpp b/modules/stream_out/chromecast/chromecast_ctrl.cpp index 70d069f..4b17c58 100644 --- a/modules/stream_out/chromecast/chromecast_ctrl.cpp +++ b/modules/stream_out/chromecast/chromecast_ctrl.cpp @@ -34,121 +34,15 @@ #include <cassert> #include <cerrno> -#ifdef HAVE_POLL -# include <poll.h> -#endif #include "../../misc/webservices/json.h" -#define PACKET_MAX_LEN 10 * 1024 - -// Media player Chromecast app id -#define APP_ID "CC1AD845" // Default media player aka DEFAULT_MEDIA_RECEIVER_APPLICATION_ID - /* deadline regarding pings sent from receiver */ #define PING_WAIT_TIME 6000 #define PING_WAIT_RETRIES 0 -/* deadline regarding pong we expect after pinging the receiver */ -#define PONG_WAIT_TIME 500 -#define PONG_WAIT_RETRIES 2 static const mtime_t SEEK_FORWARD_OFFSET = 1000000; -static const std::string NAMESPACE_DEVICEAUTH = "urn:x-cast:com.google.cast.tp.deviceauth"; -static const std::string NAMESPACE_CONNECTION = "urn:x-cast:com.google.cast.tp.connection"; -static const std::string NAMESPACE_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat"; -static const std::string NAMESPACE_RECEIVER = "urn:x-cast:com.google.cast.receiver"; - -ChromecastCommunication::ChromecastCommunication( vlc_object_t* p_module ) - : p_module( p_module ) - , i_sock_fd( -1 ) - , p_creds( NULL ) - , p_tls( NULL ) - , i_receiver_requestId( 0 ) - , i_requestId( 0 ) -{ -} - -bool ChromecastCommunication::connect( const char* targetIP, unsigned int devicePort ) -{ - if (devicePort == 0) - devicePort = CHROMECAST_CONTROL_PORT; - i_sock_fd = net_ConnectTCP( p_module, targetIP, devicePort); - if (i_sock_fd < 0) - return false; - - char psz_localIP[NI_MAXNUMERICHOST]; - if ( net_GetSockAddress( i_sock_fd, psz_localIP, NULL ) ) - { - msg_Err( p_module, "Cannot get local IP address" ); - return false; - } - serverIp = psz_localIP; - - p_creds = vlc_tls_ClientCreate( p_module->obj.parent ); - if (p_creds == NULL) - { - msg_Err( p_module, "Failed to create TLS client" ); - net_Close(i_sock_fd); - return false; - } - - p_tls = vlc_tls_ClientSessionCreateFD( p_creds, i_sock_fd, targetIP, "tcps", NULL, NULL ); - - if (p_tls == NULL) - { - msg_Err( p_module, "Failed to create client session" ); - net_Close(i_sock_fd); - vlc_tls_Delete(p_creds); - return false; - } - return true; -} - -ChromecastCommunication::~ChromecastCommunication() -{ - disconnect(); -} - -void ChromecastCommunication::disconnect() -{ - if ( p_tls != NULL ) - { - vlc_tls_Close(p_tls); - vlc_tls_Delete(p_creds); - p_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 - */ -void 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); - - sendMessage(msg); -} - /***************************************************************************** * intf_sys_t: class definition *****************************************************************************/ @@ -283,126 +177,6 @@ void intf_sys_t::disconnectChromecast() receiverState = RECEIVER_IDLE; } - -/** - * @brief Receive a data packet from the Chromecast - * @param p_module the module to log with - * @param b_msgReceived returns true if a message has been entirely received else false - * @param i_payloadSize returns the payload size of the message received - * @return the number of bytes received of -1 on error - */ -int ChromecastCommunication::recvPacket(bool *b_msgReceived, - uint32_t &i_payloadSize, - unsigned *pi_received, uint8_t *p_data, bool *pb_pingTimeout, - int *pi_wait_delay, int *pi_wait_retries) -{ - struct pollfd ufd[1]; - ufd[0].fd = i_sock_fd; - ufd[0].events = POLLIN; - - /* 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. */ - ssize_t val = vlc_poll_i11e(ufd, 1, *pi_wait_delay); - if ( val == -1 && errno != EINTR ) - return -1; - - if (val == 0) - { - if (*pb_pingTimeout) - { - if (!*pi_wait_retries) - { - msg_Err( p_module, "No PONG answer received from the Chromecast"); - return 0; // Connection died - } - (*pi_wait_retries)--; - } - else - { - /* now expect a pong */ - *pi_wait_delay = PONG_WAIT_TIME; - *pi_wait_retries = PONG_WAIT_RETRIES; - msg_Warn( p_module, "No PING received from the Chromecast, sending a PING"); - } - *pb_pingTimeout = true; - } - else - { - *pb_pingTimeout = false; - /* reset to default ping waiting */ - *pi_wait_delay = PING_WAIT_TIME; - *pi_wait_retries = PING_WAIT_RETRIES; - } - - int i_ret = 0; - if ( ufd[0].revents & POLLIN ) - { - /* we have received stuff */ - - /* Packet structure: - * +------------------------------------+------------------------------+ - * | Payload size (uint32_t big endian) | Payload data | - * +------------------------------------+------------------------------+ */ - while (*pi_received < PACKET_HEADER_LEN) - { - // We receive the header. - i_ret = tls_Recv(p_tls, p_data + *pi_received, PACKET_HEADER_LEN - *pi_received); - if (i_ret <= 0) - return i_ret; - *pi_received += i_ret; - } - - // We receive the payload. - - // Get the size of the payload - i_payloadSize = U32_AT( p_data ); - const uint32_t i_maxPayloadSize = PACKET_MAX_LEN - PACKET_HEADER_LEN; - - if (i_payloadSize > i_maxPayloadSize) - { - // Error case: the packet sent by the Chromecast is too long: we drop it. - msg_Err( p_module, "Packet too long: droping its data"); - - uint32_t i_size = i_payloadSize - (*pi_received - PACKET_HEADER_LEN); - if (i_size > i_maxPayloadSize) - i_size = i_maxPayloadSize; - - i_ret = tls_Recv(p_tls, p_data + PACKET_HEADER_LEN, i_size); - if (i_ret <= 0) - return i_ret; - *pi_received += i_ret; - - if (*pi_received < i_payloadSize + PACKET_HEADER_LEN) - return i_ret; - - *pi_received = 0; - return -1; - } - - // Normal case - i_ret = tls_Recv(p_tls, p_data + *pi_received, - i_payloadSize - (*pi_received - PACKET_HEADER_LEN)); - if (i_ret <= 0) - return i_ret; - *pi_received += i_ret; - - if (*pi_received < i_payloadSize + PACKET_HEADER_LEN) - return i_ret; - - assert(*pi_received == i_payloadSize + PACKET_HEADER_LEN); - *pi_received = 0; - *b_msgReceived = true; - } - - if ( val == -1 && errno == EINTR ) - /* we have stuff to send */ - i_ret = 1; - - return i_ret; -} - /** * @brief Process a message received from the Chromecast * @param msg the CastMessage to process @@ -707,222 +481,8 @@ void intf_sys_t::processMessage(const castchannel::CastMessage &msg) } } -/***************************************************************************** - * Message preparation - *****************************************************************************/ -void ChromecastCommunication::msgAuth() -{ - castchannel::DeviceAuthMessage authMessage; - authMessage.mutable_challenge(); - - buildMessage(NAMESPACE_DEVICEAUTH, authMessage.SerializeAsString(), - DEFAULT_CHOMECAST_RECEIVER, castchannel::CastMessage_PayloadType_BINARY); -} - - -void ChromecastCommunication::msgPing() -{ - std::string s("{\"type\":\"PING\"}"); - buildMessage( NAMESPACE_HEARTBEAT, s, DEFAULT_CHOMECAST_RECEIVER ); -} -void ChromecastCommunication::msgPong() -{ - std::string s("{\"type\":\"PONG\"}"); - buildMessage( NAMESPACE_HEARTBEAT, s, DEFAULT_CHOMECAST_RECEIVER ); -} - -void ChromecastCommunication::msgConnect( const std::string& destinationId ) -{ - std::string s("{\"type\":\"CONNECT\"}"); - buildMessage( NAMESPACE_CONNECTION, s, destinationId ); -} - -void ChromecastCommunication::msgReceiverClose( const std::string& destinationId ) -{ - std::string s("{\"type\":\"CLOSE\"}"); - buildMessage( NAMESPACE_CONNECTION, s, destinationId ); -} - -void ChromecastCommunication::msgReceiverGetStatus() -{ - std::stringstream ss; - ss << "{\"type\":\"GET_STATUS\"," - << "\"requestId\":" << i_receiver_requestId++ << "}"; - - buildMessage( NAMESPACE_RECEIVER, ss.str(), DEFAULT_CHOMECAST_RECEIVER ); -} - -void ChromecastCommunication::msgReceiverLaunchApp() -{ - std::stringstream ss; - ss << "{\"type\":\"LAUNCH\"," - << "\"appId\":\"" << APP_ID << "\"," - << "\"requestId\":" << i_receiver_requestId++ << "}"; - - buildMessage( NAMESPACE_RECEIVER, ss.str(), DEFAULT_CHOMECAST_RECEIVER ); -} - -void ChromecastCommunication::msgPlayerGetStatus( const std::string& destinationId ) -{ - std::stringstream ss; - ss << "{\"type\":\"GET_STATUS\"," - << "\"requestId\":" << i_requestId++ - << "}"; - - pushMediaPlayerMessage( destinationId, ss ); -} - -std::string ChromecastCommunication::GetMedia( unsigned int i_port, - const std::string& title, const std::string& artwork, - const std::string& mime ) -{ - std::stringstream ss; - - if ( title.size() ) - { - ss << "\"metadata\":{" - << " \"metadataType\":0" - << ",\"title\":\"" << title << "\""; - - if ( artwork.size() && !strncmp(artwork.c_str(), "http", 4)) - ss << ",\"images\":[\"" << artwork << "\"]"; - - ss << "},"; - } - - std::stringstream chromecast_url; - chromecast_url << "http://" << serverIp << ":" << i_port << "/stream"; - - msg_Dbg( p_module, "s_chromecast_url: %s", chromecast_url.str().c_str()); - - ss << "\"contentId\":\"" << chromecast_url.str() << "\"" - << ",\"streamType\":\"LIVE\"" - << ",\"contentType\":\"" << mime << "\""; - - return ss.str(); -} - -void ChromecastCommunication::msgPlayerLoad( const std::string& destinationId, unsigned int i_port, - const std::string& title, const std::string& artwork, - const std::string& mime ) -{ - std::stringstream ss; - ss << "{\"type\":\"LOAD\"," - << "\"media\":{" << GetMedia( i_port, title, artwork, mime ) << "}," - << "\"autoplay\":\"false\"," - << "\"requestId\":" << i_requestId++ - << "}"; - - pushMediaPlayerMessage( destinationId, ss ); -} - -void ChromecastCommunication::msgPlayerPlay( const std::string& destinationId, const std::string& mediaSessionId ) -{ - assert(!mediaSessionId.empty()); - - std::stringstream ss; - ss << "{\"type\":\"PLAY\"," - << "\"mediaSessionId\":" << mediaSessionId << "," - << "\"requestId\":" << i_requestId++ - << "}"; - - pushMediaPlayerMessage( destinationId, ss ); -} - -void ChromecastCommunication::msgPlayerStop( const std::string& destinationId, const std::string& mediaSessionId ) -{ - assert(!mediaSessionId.empty()); - - std::stringstream ss; - ss << "{\"type\":\"STOP\"," - << "\"mediaSessionId\":" << mediaSessionId << "," - << "\"requestId\":" << i_requestId++ - << "}"; - - pushMediaPlayerMessage( destinationId, ss ); -} - -void ChromecastCommunication::msgPlayerPause( const std::string& destinationId, const std::string& mediaSessionId ) -{ - assert(!mediaSessionId.empty()); - - std::stringstream ss; - ss << "{\"type\":\"PAUSE\"," - << "\"mediaSessionId\":" << mediaSessionId << "," - << "\"requestId\":" << i_requestId++ - << "}"; - - pushMediaPlayerMessage( destinationId, ss ); -} - -void ChromecastCommunication::msgPlayerSetVolume( const std::string& destinationId, const std::string& mediaSessionId, float f_volume, bool b_mute ) -{ - assert(!mediaSessionId.empty()); - - if ( f_volume < 0.0 || f_volume > 1.0) - return; - - std::stringstream ss; - ss << "{\"type\":\"SET_VOLUME\"," - << "\"volume\":{\"level\":" << f_volume << ",\"muted\":" << ( b_mute ? "true" : "false" ) << "}," - << "\"mediaSessionId\":" << mediaSessionId << "," - << "\"requestId\":" << i_requestId++ - << "}"; - - pushMediaPlayerMessage( destinationId, ss ); -} - -void ChromecastCommunication::msgPlayerSeek( const std::string& destinationId, const std::string& mediaSessionId, const std::string& currentTime ) -{ - assert(!mediaSessionId.empty()); - - std::stringstream ss; - ss << "{\"type\":\"SEEK\"," - << "\"currentTime\":" << currentTime << "," - << "\"mediaSessionId\":" << mediaSessionId << "," - << "\"requestId\":" << i_requestId++ - << "}"; - - pushMediaPlayerMessage( destinationId, ss ); -} - -/** - * @brief Send a message to the Chromecast - * @param msg the CastMessage to send - * @return vlc error code - */ -int ChromecastCommunication::sendMessage( const castchannel::CastMessage &msg ) -{ - int i_size = msg.ByteSize(); - 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( p_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); - - int i_ret = tls_Send(p_tls, p_data, PACKET_HEADER_LEN + i_size); - delete[] p_data; - if (i_ret == PACKET_HEADER_LEN + i_size) - return VLC_SUCCESS; - - msg_Warn( p_module, "failed to send message %s (%s)", msg.payload_utf8().c_str(), strerror( errno ) ); - - return VLC_EGENERIC; -} - -void ChromecastCommunication::pushMediaPlayerMessage( const std::string& destinationId, const std::stringstream & payload ) -{ - assert(!destinationId.empty()); - buildMessage( NAMESPACE_MEDIA, payload.str(), destinationId ); -} - /***************************************************************************** * Chromecast thread *****************************************************************************/ _______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits