Commit b613bad6 authored by Steve Lhomme's avatar Steve Lhomme Committed by Jean-Baptiste Kempf

chromecast: move the packet receiver in the communication handler

Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent ad959d90
......@@ -44,8 +44,6 @@
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#define PACKET_MAX_LEN 10 * 1024
struct sout_stream_sys_t
{
sout_stream_sys_t(intf_sys_t *intf)
......@@ -59,7 +57,6 @@ struct sout_stream_sys_t
delete p_intf;
}
int i_sock_fd;
vlc_tls_creds_t *p_creds;
vlc_thread_t chromecastThread;
......@@ -76,13 +73,6 @@ struct sout_stream_sys_t
#define SOUT_CFG_PREFIX "sout-chromecast-"
/* 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
/*****************************************************************************
* Local prototypes
*****************************************************************************/
......@@ -199,9 +189,9 @@ static int Open(vlc_object_t *p_this)
return VLC_EGENERIC;
}
p_sys->i_sock_fd = connectChromecast(p_stream, psz_ipChromecast);
p_sys->p_intf->i_sock_fd = connectChromecast(p_stream, psz_ipChromecast);
free(psz_ipChromecast);
if (p_sys->i_sock_fd < 0)
if (p_sys->p_intf->i_sock_fd < 0)
{
msg_Err(p_stream, "Could not connect the Chromecast");
Clean(p_stream);
......@@ -210,7 +200,7 @@ static int Open(vlc_object_t *p_this)
p_sys->p_intf->setConnectionStatus(CHROMECAST_TLS_CONNECTED);
char psz_localIP[NI_MAXNUMERICHOST];
if (net_GetSockAddress(p_sys->i_sock_fd, psz_localIP, NULL))
if (net_GetSockAddress(p_sys->p_intf->i_sock_fd, psz_localIP, NULL))
{
msg_Err(p_this, "Cannot get local IP address");
Clean(p_stream);
......@@ -388,118 +378,6 @@ static void disconnectChromecast(sout_stream_t *p_stream)
/**
* @brief Receive a data packet from the Chromecast
* @param p_stream the sout_stream_t structure
* @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
*/
// Use here only C linkage and POD types as this function is a cancelation point.
extern "C" int recvPacket(sout_stream_t *p_stream, bool &b_msgReceived,
uint32_t &i_payloadSize, int i_sock_fd, vlc_tls_t *p_tls,
unsigned *pi_received, char *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. */
if (poll(ufd, 1, *pi_wait_delay) == 0)
{
if (*pb_pingTimeout)
{
if (!*pi_wait_retries)
{
msg_Err(p_stream, "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_stream, "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;
/* Packet structure:
* +------------------------------------+------------------------------+
* | Payload size (uint32_t big endian) | Payload data |
* +------------------------------------+------------------------------+ */
if (*pi_received < PACKET_HEADER_LEN)
{
// We receive the header.
i_ret = tls_Recv(p_tls, p_data, PACKET_HEADER_LEN - *pi_received);
if (i_ret <= 0)
return i_ret;
*pi_received += i_ret;
}
else
{
// We receive the payload.
// Get the size of the payload
memcpy(&i_payloadSize, p_data, PACKET_HEADER_LEN);
i_payloadSize = hton32(i_payloadSize);
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_stream, "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;
return i_ret;
}
return i_ret;
}
/*****************************************************************************
* Chromecast thread
......@@ -511,53 +389,12 @@ static void* chromecastThread(void* p_data)
sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_data);
sout_stream_sys_t* p_sys = p_stream->p_sys;
unsigned i_received = 0;
char p_packet[PACKET_MAX_LEN];
bool b_pingTimeout = false;
int i_waitdelay = PING_WAIT_TIME;
int i_retries = PING_WAIT_RETRIES;
p_sys->p_intf->msgAuth();
p_sys->p_intf->sendMessages();
vlc_restorecancel(canc);
while (1)
{
bool b_msgReceived = false;
uint32_t i_payloadSize = 0;
int i_ret = recvPacket(p_stream, b_msgReceived, i_payloadSize, p_sys->i_sock_fd,
p_sys->p_intf->p_tls, &i_received, p_packet, &b_pingTimeout,
&i_waitdelay, &i_retries);
canc = vlc_savecancel();
// Not cancellation-safe part.
#if defined(_WIN32)
if ((i_ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) || (i_ret == 0))
#else
if ((i_ret < 0 && errno != EAGAIN) || i_ret == 0)
#endif
{
msg_Err(p_stream, "The connection to the Chromecast died.");
vlc_mutex_locker locker(&p_sys->p_intf->lock);
p_sys->p_intf->setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
break;
}
if (b_pingTimeout)
{
p_sys->p_intf->msgPing();
p_sys->p_intf->msgReceiverGetStatus();
}
if (b_msgReceived)
{
castchannel::CastMessage msg;
msg.ParseFromArray(p_packet + PACKET_HEADER_LEN, i_payloadSize);
p_sys->p_intf->processMessage(msg);
}
p_sys->p_intf->handleMessages();
vlc_mutex_lock(&p_sys->p_intf->lock);
......@@ -567,8 +404,6 @@ static void* chromecastThread(void* p_data)
break;
}
vlc_mutex_unlock(&p_sys->p_intf->lock);
vlc_restorecancel(canc);
}
return NULL;
......
......@@ -66,6 +66,7 @@ struct intf_sys_t
std::string serverIP;
std::string appTransportId;
int i_sock_fd;
vlc_tls_t *p_tls;
vlc_mutex_t lock;
......
......@@ -35,12 +35,24 @@
#include <vlc_sout.h>
#include <sstream>
#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
#define SOUT_CFG_PREFIX "sout-chromecast-"
static const std::string NAMESPACE_DEVICEAUTH = "urn:x-cast:com.google.cast.tp.deviceauth";
......@@ -94,6 +106,118 @@ intf_sys_t::~intf_sys_t()
vlc_mutex_destroy(&lock);
}
/**
* @brief Receive a data packet from the Chromecast
* @param p_stream the sout_stream_t structure
* @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
*/
// Use here only C linkage and POD types as this function is a cancelation point.
extern "C" int recvPacket(sout_stream_t *p_stream, bool &b_msgReceived,
uint32_t &i_payloadSize, int i_sock_fd, vlc_tls_t *p_tls,
unsigned *pi_received, char *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. */
if (poll(ufd, 1, *pi_wait_delay) == 0)
{
if (*pb_pingTimeout)
{
if (!*pi_wait_retries)
{
msg_Err(p_stream, "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_stream, "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;
/* Packet structure:
* +------------------------------------+------------------------------+
* | Payload size (uint32_t big endian) | Payload data |
* +------------------------------------+------------------------------+ */
if (*pi_received < PACKET_HEADER_LEN)
{
// We receive the header.
i_ret = tls_Recv(p_tls, p_data, PACKET_HEADER_LEN - *pi_received);
if (i_ret <= 0)
return i_ret;
*pi_received += i_ret;
}
else
{
// We receive the payload.
// Get the size of the payload
memcpy(&i_payloadSize, p_data, PACKET_HEADER_LEN);
i_payloadSize = hton32(i_payloadSize);
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_stream, "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;
return i_ret;
}
return i_ret;
}
/**
* @brief Process a message received from the Chromecast
* @param msg the CastMessage to process
......@@ -384,7 +508,48 @@ int intf_sys_t::sendMessages()
void intf_sys_t::handleMessages()
{
int i_ret;
unsigned i_received = 0;
char p_packet[PACKET_MAX_LEN];
bool b_pingTimeout = false;
int i_waitdelay = PING_WAIT_TIME;
int i_retries = PING_WAIT_RETRIES;
bool b_msgReceived = false;
uint32_t i_payloadSize = 0;
int i_ret = recvPacket(p_stream, b_msgReceived, i_payloadSize, i_sock_fd,
p_tls, &i_received, p_packet, &b_pingTimeout,
&i_waitdelay, &i_retries);
int canc = vlc_savecancel();
// Not cancellation-safe part.
#if defined(_WIN32)
if ((i_ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) || (i_ret == 0))
#else
if ((i_ret < 0 && errno != EAGAIN) || i_ret == 0)
#endif
{
msg_Err(p_stream, "The connection to the Chromecast died.");
vlc_mutex_locker locker(&lock);
setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
vlc_restorecancel(canc);
return;
}
if (b_pingTimeout)
{
msgPing();
msgReceiverGetStatus();
}
if (b_msgReceived)
{
castchannel::CastMessage msg;
msg.ParseFromArray(p_packet + PACKET_HEADER_LEN, i_payloadSize);
processMessage(msg);
}
// Send the answer messages if there is any.
if (!messagesToSend.empty())
{
......@@ -400,4 +565,6 @@ void intf_sys_t::handleMessages()
setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
}
}
vlc_restorecancel(canc);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment