1 /*****************************************************************************
2 * cast.cpp: Chromecast module for vlc
3 *****************************************************************************
4 * Copyright © 2014 VideoLAN
6 * Authors: Adrien Maglo <magsoft@videolan.org>
7 * Jean-Baptiste Kempf <jb@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
36 #ifndef __STDC_CONSTANT_MACROS
37 # define __STDC_CONSTANT_MACROS
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
45 #include <vlc_threads.h>
46 #include <vlc_atomic.h>
53 #include <google/protobuf/io/zero_copy_stream_impl.h>
54 #include <google/protobuf/io/coded_stream.h>
55 #include "cast_channel.pb.h"
57 #include "../../misc/webservices/json.h"
62 CHROMECAST_DISCONNECTED
,
63 CHROMECAST_TLS_CONNECTED
,
64 CHROMECAST_AUTHENTICATED
,
65 CHROMECAST_APP_STARTED
,
66 CHROMECAST_MEDIA_LOAD_SENT
,
67 CHROMECAST_CONNECTION_DEAD
,
70 #define PACKET_MAX_LEN 10 * 1024
71 #define PACKET_HEADER_LEN 4
73 struct sout_stream_sys_t
76 : p_tls(NULL
), i_requestId(0),
77 i_status(CHROMECAST_DISCONNECTED
), p_out(NULL
)
79 atomic_init(&ab_error
, false);
85 vlc_tls_creds_t
*p_creds
;
88 vlc_thread_t chromecastThread
;
91 std::string appTransportId
;
93 std::queue
<castchannel::CastMessage
> messagesToSend
;
98 vlc_cond_t loadCommandCond
;
100 sout_stream_t
*p_out
;
103 // Media player Chromecast app id
104 #define APP_ID "CC1AD845" // Default media player
106 #define CHROMECAST_CONTROL_PORT 8009
107 #define HTTP_PORT 8010
109 #define SOUT_CFG_PREFIX "sout-chromecast-"
111 /* deadline regarding pings sent from receiver */
112 #define PING_WAIT_TIME 6000
113 #define PING_WAIT_RETRIES 0
114 /* deadline regarding pong we expect after pinging the receiver */
115 #define PONG_WAIT_TIME 500
116 #define PONG_WAIT_RETRIES 2
118 /*****************************************************************************
120 *****************************************************************************/
121 static int Open(vlc_object_t
*);
122 static void Close(vlc_object_t
*);
123 static void Clean(sout_stream_t
*p_stream
);
124 static int connectChromecast(sout_stream_t
*p_stream
, char *psz_ipChromecast
);
125 static void disconnectChromecast(sout_stream_t
*p_stream
);
126 static int sendMessages(sout_stream_t
*p_stream
);
128 static void msgAuth(sout_stream_t
*p_stream
);
129 static void msgPing(sout_stream_t
*p_stream
);
130 static void msgPong(sout_stream_t
*p_stream
);
131 static void msgConnect(sout_stream_t
*p_stream
, std::string destinationId
);
132 static void msgClose(sout_stream_t
*p_stream
, std::string destinationId
);
133 static void msgLaunch(sout_stream_t
*p_stream
);
134 static void msgLoad(sout_stream_t
*p_stream
);
135 static void msgStatus(sout_stream_t
*p_stream
);
137 static void *chromecastThread(void *data
);
139 static const char *const ppsz_sout_options
[] = {
140 "ip", "http-port", "mux", "mime", NULL
143 /*****************************************************************************
145 *****************************************************************************/
147 #define IP_TEXT N_("Chromecast IP address")
148 #define IP_LONGTEXT N_("This sets the IP adress of the Chromecast receiver.")
149 #define HTTP_PORT_TEXT N_("HTTP port")
150 #define HTTP_PORT_LONGTEXT N_("This sets the HTTP port of the server " \
151 "used to stream the media to the Chromecast.")
152 #define MUX_TEXT N_("Muxer")
153 #define MUX_LONGTEXT N_("This sets the muxer used to stream to the Chromecast.")
154 #define MIME_TEXT N_("MIME content type")
155 #define MIME_LONGTEXT N_("This sets the media MIME content type sent to the Chromecast.")
159 set_shortname(N_("Chromecast"))
160 set_description(N_("Chromecast stream output"))
161 set_capability("sout stream", 0)
162 add_shortcut("chromecast")
163 set_category(CAT_SOUT
)
164 set_subcategory(SUBCAT_SOUT_STREAM
)
165 set_callbacks(Open
, Close
)
167 add_string(SOUT_CFG_PREFIX
"ip", "", IP_TEXT
, IP_LONGTEXT
, false)
168 add_integer(SOUT_CFG_PREFIX
"http-port", HTTP_PORT
, HTTP_PORT_TEXT
, HTTP_PORT_LONGTEXT
, false)
169 add_string(SOUT_CFG_PREFIX
"mux", "mp4stream", MUX_TEXT
, MUX_LONGTEXT
, false)
170 add_string(SOUT_CFG_PREFIX
"mime", "video/mp4", MIME_TEXT
, MIME_LONGTEXT
, false)
175 /*****************************************************************************
177 *****************************************************************************/
178 static sout_stream_id_sys_t
*Add(sout_stream_t
*p_stream
, es_format_t
*p_fmt
)
180 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
181 return p_sys
->p_out
->pf_add(p_sys
->p_out
, p_fmt
);
185 static int Del(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
)
187 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
188 return p_sys
->p_out
->pf_del(p_sys
->p_out
, id
);
192 static int Send(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
,
195 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
196 if (atomic_load(&p_sys
->ab_error
))
199 return p_sys
->p_out
->pf_send(p_sys
->p_out
, id
, p_buffer
);
203 /*****************************************************************************
204 * Open: connect to the Chromecast and initialize the sout
205 *****************************************************************************/
206 static int Open(vlc_object_t
*p_this
)
208 sout_stream_t
*p_stream
= (sout_stream_t
*)p_this
;
209 sout_stream_sys_t
*p_sys
;
210 p_sys
= new(std::nothrow
) sout_stream_sys_t
;
213 p_stream
->p_sys
= p_sys
;
215 config_ChainParse(p_stream
, SOUT_CFG_PREFIX
, ppsz_sout_options
, p_stream
->p_cfg
);
217 char *psz_ipChromecast
= var_GetNonEmptyString(p_stream
, SOUT_CFG_PREFIX
"ip");
218 if (psz_ipChromecast
== NULL
)
220 msg_Err(p_stream
, "No Chromecast receiver IP provided");
225 p_sys
->i_sock_fd
= connectChromecast(p_stream
, psz_ipChromecast
);
226 free(psz_ipChromecast
);
227 if (p_sys
->i_sock_fd
< 0)
229 msg_Err(p_stream
, "Could not connect the Chromecast");
233 p_sys
->i_status
= CHROMECAST_TLS_CONNECTED
;
235 char psz_localIP
[NI_MAXNUMERICHOST
];
236 if (net_GetSockAddress(p_sys
->i_sock_fd
, psz_localIP
, NULL
))
238 msg_Err(p_this
, "Cannot get local IP address");
242 p_sys
->serverIP
= psz_localIP
;
244 char *psz_mux
= var_GetNonEmptyString(p_stream
, SOUT_CFG_PREFIX
"mux");
250 char *psz_chain
= NULL
;
251 int i_bytes
= asprintf(&psz_chain
, "http{dst=:%u/stream,mux=%s}",
252 (unsigned)var_InheritInteger(p_stream
, SOUT_CFG_PREFIX
"http-port"),
261 p_sys
->p_out
= sout_StreamChainNew(p_stream
->p_sout
, psz_chain
, NULL
, NULL
);
263 if (p_sys
->p_out
== NULL
)
269 vlc_mutex_init(&p_sys
->lock
);
270 vlc_cond_init(&p_sys
->loadCommandCond
);
272 // Start the Chromecast event thread.
273 if (vlc_clone(&p_sys
->chromecastThread
, chromecastThread
, p_stream
,
274 VLC_THREAD_PRIORITY_LOW
))
276 msg_Err(p_stream
, "Could not start the Chromecast talking thread");
282 * We want to be sure that the Chromecast receives the first data packet sent by
283 * the HTTP server. */
285 // Lock the sout thread until we have sent the media loading command to the Chromecast.
287 const mtime_t deadline
= mdate() + 6 * CLOCK_FREQ
;
288 vlc_mutex_lock(&p_sys
->lock
);
289 while (p_sys
->i_status
!= CHROMECAST_MEDIA_LOAD_SENT
)
291 i_ret
= vlc_cond_timedwait(&p_sys
->loadCommandCond
, &p_sys
->lock
, deadline
);
292 if (i_ret
== ETIMEDOUT
)
294 msg_Err(p_stream
, "Timeout reached before sending the media loading command");
295 vlc_mutex_unlock(&p_sys
->lock
);
296 vlc_cancel(p_sys
->chromecastThread
);
301 vlc_mutex_unlock(&p_sys
->lock
);
303 /* Even uglier: sleep more to let to the Chromecast initiate the connection
304 * to the http server. */
305 msleep(2 * CLOCK_FREQ
);
307 // Set the sout callbacks.
308 p_stream
->pf_add
= Add
;
309 p_stream
->pf_del
= Del
;
310 p_stream
->pf_send
= Send
;
316 /*****************************************************************************
317 * Close: destroy interface
318 *****************************************************************************/
319 static void Close(vlc_object_t
*p_this
)
321 sout_stream_t
*p_stream
= (sout_stream_t
*)p_this
;
322 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
324 vlc_cancel(p_sys
->chromecastThread
);
325 vlc_join(p_sys
->chromecastThread
, NULL
);
327 switch (p_sys
->i_status
)
329 case CHROMECAST_MEDIA_LOAD_SENT
:
330 case CHROMECAST_APP_STARTED
:
331 // Generate the close messages.
332 msgClose(p_stream
, p_sys
->appTransportId
);
334 case CHROMECAST_AUTHENTICATED
:
335 msgClose(p_stream
, "receiver-0");
336 // Send the just added close messages.
337 sendMessages(p_stream
);
348 * @brief Clean and release the variables in a sout_stream_sys_t structure
350 static void Clean(sout_stream_t
*p_stream
)
352 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
356 vlc_mutex_destroy(&p_sys
->lock
);
357 vlc_cond_destroy(&p_sys
->loadCommandCond
);
358 sout_StreamChainDelete(p_sys
->p_out
, p_sys
->p_out
);
361 disconnectChromecast(p_stream
);
368 * @brief Connect to the Chromecast
369 * @param p_stream the sout_stream_t structure
370 * @return the opened socket file descriptor or -1 on error
372 static int connectChromecast(sout_stream_t
*p_stream
, char *psz_ipChromecast
)
374 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
375 int fd
= net_ConnectTCP(p_stream
, psz_ipChromecast
, CHROMECAST_CONTROL_PORT
);
379 p_sys
->p_creds
= vlc_tls_ClientCreate(VLC_OBJECT(p_stream
));
380 if (p_sys
->p_creds
== NULL
)
383 p_sys
->p_tls
= vlc_tls_ClientSessionCreate(p_sys
->p_creds
, fd
, psz_ipChromecast
,
386 if (p_sys
->p_tls
== NULL
)
388 vlc_tls_Delete(p_sys
->p_creds
);
397 * @brief Disconnect from the Chromecast
399 static void disconnectChromecast(sout_stream_t
*p_stream
)
401 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
405 vlc_tls_SessionDelete(p_sys
->p_tls
);
406 vlc_tls_Delete(p_sys
->p_creds
);
408 p_sys
->i_status
= CHROMECAST_DISCONNECTED
;
414 * @brief Send a message to the Chromecast
415 * @param p_stream the sout_stream_t structure
416 * @param msg the CastMessage to send
417 * @return the number of bytes sent or -1 on error
419 static int sendMessage(sout_stream_t
*p_stream
, castchannel::CastMessage
&msg
)
421 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
423 uint32_t i_size
= msg
.ByteSize();
424 uint32_t i_sizeNetwork
= hton32(i_size
);
426 char *p_data
= new(std::nothrow
) char[PACKET_HEADER_LEN
+ i_size
];
430 memcpy(p_data
, &i_sizeNetwork
, PACKET_HEADER_LEN
);
431 msg
.SerializeWithCachedSizesToArray((uint8_t *)(p_data
+ PACKET_HEADER_LEN
));
433 int i_ret
= tls_Send(p_sys
->p_tls
, p_data
, PACKET_HEADER_LEN
+ i_size
);
441 * @brief Send all the messages in the pending queue to the Chromecast
442 * @param p_stream the sout_stream_t structure
443 * @param msg the CastMessage to send
444 * @return the number of bytes sent or -1 on error
446 static int sendMessages(sout_stream_t
*p_stream
)
448 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
451 while (!p_sys
->messagesToSend
.empty())
453 unsigned i_retSend
= sendMessage(p_stream
, p_sys
->messagesToSend
.front());
457 p_sys
->messagesToSend
.pop();
468 * @brief Receive a data packet from the Chromecast
469 * @param p_stream the sout_stream_t structure
470 * @param b_msgReceived returns true if a message has been entirely received else false
471 * @param i_payloadSize returns the payload size of the message received
472 * @return the number of bytes received of -1 on error
474 // Use here only C linkage and POD types as this function is a cancelation point.
475 extern "C" int recvPacket(sout_stream_t
*p_stream
, bool &b_msgReceived
,
476 uint32_t &i_payloadSize
, int i_sock_fd
, vlc_tls_t
*p_tls
,
477 unsigned *pi_received
, char *p_data
, bool *pb_pingTimeout
,
478 int *pi_wait_delay
, int *pi_wait_retries
)
480 struct pollfd ufd
[1];
481 ufd
[0].fd
= i_sock_fd
;
482 ufd
[0].events
= POLLIN
;
484 /* The Chromecast normally sends a PING command every 5 seconds or so.
485 * If we do not receive one after 6 seconds, we send a PING.
486 * If after this PING, we do not receive a PONG, then we consider the
487 * connection as dead. */
488 if (poll(ufd
, 1, *pi_wait_delay
) == 0)
492 if (!*pi_wait_retries
)
494 msg_Err(p_stream
, "No PONG answer received from the Chromecast");
495 return 0; // Connection died
497 (*pi_wait_retries
)--;
501 /* now expect a pong */
502 *pi_wait_delay
= PONG_WAIT_TIME
;
503 *pi_wait_retries
= PONG_WAIT_RETRIES
;
504 msg_Warn(p_stream
, "No PING received from the Chromecast, sending a PING");
506 *pb_pingTimeout
= true;
510 *pb_pingTimeout
= false;
511 /* reset to default ping waiting */
512 *pi_wait_delay
= PING_WAIT_TIME
;
513 *pi_wait_retries
= PING_WAIT_RETRIES
;
519 * +------------------------------------+------------------------------+
520 * | Payload size (uint32_t big endian) | Payload data |
521 * +------------------------------------+------------------------------+ */
522 if (*pi_received
< PACKET_HEADER_LEN
)
524 // We receive the header.
525 i_ret
= tls_Recv(p_tls
, p_data
, PACKET_HEADER_LEN
- *pi_received
);
528 *pi_received
+= i_ret
;
532 // We receive the payload.
534 // Get the size of the payload
535 memcpy(&i_payloadSize
, p_data
, PACKET_HEADER_LEN
);
536 i_payloadSize
= hton32(i_payloadSize
);
537 const uint32_t i_maxPayloadSize
= PACKET_MAX_LEN
- PACKET_HEADER_LEN
;
539 if (i_payloadSize
> i_maxPayloadSize
)
541 // Error case: the packet sent by the Chromecast is too long: we drop it.
542 msg_Err(p_stream
, "Packet too long: droping its data");
544 uint32_t i_size
= i_payloadSize
- (*pi_received
- PACKET_HEADER_LEN
);
545 if (i_size
> i_maxPayloadSize
)
546 i_size
= i_maxPayloadSize
;
548 i_ret
= tls_Recv(p_tls
, p_data
+ PACKET_HEADER_LEN
, i_size
);
551 *pi_received
+= i_ret
;
553 if (*pi_received
< i_payloadSize
+ PACKET_HEADER_LEN
)
561 i_ret
= tls_Recv(p_tls
, p_data
+ *pi_received
,
562 i_payloadSize
- (*pi_received
- PACKET_HEADER_LEN
));
565 *pi_received
+= i_ret
;
567 if (*pi_received
< i_payloadSize
+ PACKET_HEADER_LEN
)
570 assert(*pi_received
== i_payloadSize
+ PACKET_HEADER_LEN
);
572 b_msgReceived
= true;
581 * @brief Process a message received from the Chromecast
582 * @param p_stream the sout_stream_t structure
583 * @param msg the CastMessage to process
584 * @return 0 if the message has been successfuly processed else -1
586 static int processMessage(sout_stream_t
*p_stream
, const castchannel::CastMessage
&msg
)
589 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
590 std::string namespace_
= msg
.namespace_();
592 if (namespace_
== "urn:x-cast:com.google.cast.tp.deviceauth")
594 castchannel::DeviceAuthMessage authMessage
;
595 authMessage
.ParseFromString(msg
.payload_binary());
597 if (authMessage
.has_error())
599 msg_Err(p_stream
, "Authentification error: %d", authMessage
.error().error_type());
602 else if (!authMessage
.has_response())
604 msg_Err(p_stream
, "Authentification message has no response field");
609 vlc_mutex_locker
locker(&p_sys
->lock
);
610 p_sys
->i_status
= CHROMECAST_AUTHENTICATED
;
611 msgConnect(p_stream
, "receiver-0");
615 else if (namespace_
== "urn:x-cast:com.google.cast.tp.heartbeat")
617 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
618 std::string
type((*p_data
)["type"]);
622 msg_Dbg(p_stream
, "PING received from the Chromecast");
625 else if (type
== "PONG")
627 msg_Dbg(p_stream
, "PONG received from the Chromecast");
631 msg_Err(p_stream
, "Heartbeat command not supported");
635 json_value_free(p_data
);
637 else if (namespace_
== "urn:x-cast:com.google.cast.receiver")
639 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
640 std::string
type((*p_data
)["type"]);
642 if (type
== "RECEIVER_STATUS")
644 json_value applications
= (*p_data
)["status"]["applications"];
645 const json_value
*p_app
= NULL
;
646 for (unsigned i
= 0; i
< applications
.u
.array
.length
; ++i
)
648 std::string
appId(applications
[i
]["appId"]);
651 p_app
= &applications
[i
];
652 vlc_mutex_lock(&p_sys
->lock
);
653 if (p_sys
->appTransportId
.empty())
654 p_sys
->appTransportId
= std::string(applications
[i
]["transportId"]);
655 vlc_mutex_unlock(&p_sys
->lock
);
660 vlc_mutex_lock(&p_sys
->lock
);
663 if (!p_sys
->appTransportId
.empty()
664 && p_sys
->i_status
== CHROMECAST_AUTHENTICATED
)
666 p_sys
->i_status
= CHROMECAST_APP_STARTED
;
667 msgConnect(p_stream
, p_sys
->appTransportId
);
669 p_sys
->i_status
= CHROMECAST_MEDIA_LOAD_SENT
;
670 vlc_cond_signal(&p_sys
->loadCommandCond
);
675 switch(p_sys
->i_status
)
677 /* If the app is no longer present */
678 case CHROMECAST_APP_STARTED
:
679 case CHROMECAST_MEDIA_LOAD_SENT
:
680 msg_Warn(p_stream
, "app is no longer present. closing");
681 msgClose(p_stream
, p_sys
->appTransportId
);
682 vlc_mutex_lock(&p_sys
->lock
);
683 p_sys
->i_status
= CHROMECAST_CONNECTION_DEAD
;
684 vlc_mutex_unlock(&p_sys
->lock
);
691 vlc_mutex_unlock(&p_sys
->lock
);
695 msg_Err(p_stream
, "Receiver command not supported: %s",
696 msg
.payload_utf8().c_str());
700 json_value_free(p_data
);
702 else if (namespace_
== "urn:x-cast:com.google.cast.media")
704 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
705 std::string
type((*p_data
)["type"]);
707 if (type
== "MEDIA_STATUS")
709 json_value status
= (*p_data
)["status"];
710 msg_Dbg(p_stream
, "Player state: %s",
711 status
[0]["playerState"].operator const char *());
713 else if (type
== "LOAD_FAILED")
715 msg_Err(p_stream
, "Media load failed");
716 atomic_store(&p_sys
->ab_error
, true);
717 msgClose(p_stream
, p_sys
->appTransportId
);
718 vlc_mutex_lock(&p_sys
->lock
);
719 p_sys
->i_status
= CHROMECAST_CONNECTION_DEAD
;
720 vlc_mutex_unlock(&p_sys
->lock
);
724 msg_Err(p_stream
, "Media command not supported: %s",
725 msg
.payload_utf8().c_str());
729 json_value_free(p_data
);
731 else if (namespace_
== "urn:x-cast:com.google.cast.tp.connection")
733 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
734 std::string
type((*p_data
)["type"]);
735 json_value_free(p_data
);
739 msg_Warn(p_stream
, "received close message");
740 vlc_mutex_lock(&p_sys
->lock
);
741 p_sys
->i_status
= CHROMECAST_CONNECTION_DEAD
;
742 vlc_mutex_unlock(&p_sys
->lock
);
747 msg_Err(p_stream
, "Unknown namespace: %s", msg
.namespace_().c_str());
756 * @brief Build a CastMessage to send to the Chromecast
757 * @param namespace_ the message namespace
758 * @param payloadType the payload type (CastMessage_PayloadType_STRING or
759 * CastMessage_PayloadType_BINARY
760 * @param payload the payload
761 * @param destinationId the destination idenifier
762 * @return the generated CastMessage
764 static castchannel::CastMessage
buildMessage(std::string namespace_
,
765 castchannel::CastMessage_PayloadType payloadType
,
766 std::string payload
, std::string destinationId
= "receiver-0")
768 castchannel::CastMessage msg
;
770 msg
.set_protocol_version(castchannel::CastMessage_ProtocolVersion_CASTV2_1_0
);
771 msg
.set_namespace_(namespace_
);
772 msg
.set_payload_type(payloadType
);
773 msg
.set_source_id("sender-vlc");
774 msg
.set_destination_id(destinationId
);
775 if (payloadType
== castchannel::CastMessage_PayloadType_STRING
)
776 msg
.set_payload_utf8(payload
);
777 else // CastMessage_PayloadType_BINARY
778 msg
.set_payload_binary(payload
);
784 /*****************************************************************************
785 * Message preparation
786 *****************************************************************************/
787 static void msgAuth(sout_stream_t
*p_stream
)
789 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
791 castchannel::DeviceAuthMessage authMessage
;
792 authMessage
.mutable_challenge();
793 std::string authMessageString
;
794 authMessage
.SerializeToString(&authMessageString
);
796 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.tp.deviceauth",
797 castchannel::CastMessage_PayloadType_BINARY
, authMessageString
);
799 p_sys
->messagesToSend
.push(msg
);
803 static void msgPing(sout_stream_t
*p_stream
)
805 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
807 std::string
s("{\"type\":\"PING\"}");
808 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.tp.heartbeat",
809 castchannel::CastMessage_PayloadType_STRING
, s
);
811 p_sys
->messagesToSend
.push(msg
);
815 static void msgPong(sout_stream_t
*p_stream
)
817 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
819 std::string
s("{\"type\":\"PONG\"}");
820 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.tp.heartbeat",
821 castchannel::CastMessage_PayloadType_STRING
, s
);
823 p_sys
->messagesToSend
.push(msg
);
827 static void msgConnect(sout_stream_t
*p_stream
, std::string destinationId
)
829 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
831 std::string
s("{\"type\":\"CONNECT\"}");
832 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.tp.connection",
833 castchannel::CastMessage_PayloadType_STRING
, s
, destinationId
);
835 p_sys
->messagesToSend
.push(msg
);
839 static void msgClose(sout_stream_t
*p_stream
, std::string destinationId
)
841 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
843 std::string
s("{\"type\":\"CLOSE\"}");
844 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.tp.connection",
845 castchannel::CastMessage_PayloadType_STRING
, s
, destinationId
);
847 p_sys
->messagesToSend
.push(msg
);
850 static void msgStatus(sout_stream_t
*p_stream
)
852 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
854 std::stringstream ss
;
855 ss
<< "{\"type\":\"GET_STATUS\"}";
857 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.receiver",
858 castchannel::CastMessage_PayloadType_STRING
, ss
.str());
860 p_sys
->messagesToSend
.push(msg
);
863 static void msgLaunch(sout_stream_t
*p_stream
)
865 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
867 std::stringstream ss
;
868 ss
<< "{\"type\":\"LAUNCH\","
869 << "\"appId\":\"" << APP_ID
<< "\","
870 << "\"requestId\":" << p_stream
->p_sys
->i_requestId
++ << "}";
872 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.receiver",
873 castchannel::CastMessage_PayloadType_STRING
, ss
.str());
875 p_sys
->messagesToSend
.push(msg
);
879 static void msgLoad(sout_stream_t
*p_stream
)
881 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
883 char *psz_mime
= var_GetNonEmptyString(p_stream
, SOUT_CFG_PREFIX
"mime");
884 if (psz_mime
== NULL
)
887 std::stringstream ss
;
888 ss
<< "{\"type\":\"LOAD\","
889 << "\"media\":{\"contentId\":\"http://" << p_sys
->serverIP
<< ":"
890 << var_InheritInteger(p_stream
, SOUT_CFG_PREFIX
"http-port")
892 << "\"streamType\":\"LIVE\","
893 << "\"contentType\":\"" << std::string(psz_mime
) << "\"},"
894 << "\"requestId\":" << p_stream
->p_sys
->i_requestId
++ << "}";
898 castchannel::CastMessage msg
= buildMessage("urn:x-cast:com.google.cast.media",
899 castchannel::CastMessage_PayloadType_STRING
, ss
.str(), p_sys
->appTransportId
);
901 p_sys
->messagesToSend
.push(msg
);
905 /*****************************************************************************
907 *****************************************************************************/
908 static void* chromecastThread(void* p_data
)
910 int canc
= vlc_savecancel();
911 // Not cancellation-safe part.
912 sout_stream_t
* p_stream
= (sout_stream_t
*)p_data
;
913 sout_stream_sys_t
* p_sys
= p_stream
->p_sys
;
915 unsigned i_received
= 0;
916 char p_packet
[PACKET_MAX_LEN
];
917 bool b_pingTimeout
= false;
919 int i_waitdelay
= PING_WAIT_TIME
;
920 int i_retries
= PING_WAIT_RETRIES
;
923 sendMessages(p_stream
);
924 vlc_restorecancel(canc
);
928 bool b_msgReceived
= false;
929 uint32_t i_payloadSize
= 0;
930 int i_ret
= recvPacket(p_stream
, b_msgReceived
, i_payloadSize
, p_sys
->i_sock_fd
,
931 p_sys
->p_tls
, &i_received
, p_packet
, &b_pingTimeout
,
932 &i_waitdelay
, &i_retries
);
934 canc
= vlc_savecancel();
935 // Not cancellation-safe part.
937 if ((i_ret
< 0 && errno
!= EAGAIN
) || i_ret
== 0)
939 msg_Err(p_stream
, "The connection to the Chromecast died.");
940 vlc_mutex_locker
locker(&p_sys
->lock
);
941 p_sys
->i_status
= CHROMECAST_CONNECTION_DEAD
;
942 atomic_store(&p_sys
->ab_error
, true);
954 castchannel::CastMessage msg
;
955 msg
.ParseFromArray(p_packet
+ PACKET_HEADER_LEN
, i_payloadSize
);
956 processMessage(p_stream
, msg
);
959 // Send the answer messages if there is any.
960 if (!p_sys
->messagesToSend
.empty())
962 i_ret
= sendMessages(p_stream
);
963 if ((i_ret
< 0 && errno
!= EAGAIN
) || i_ret
== 0)
965 msg_Err(p_stream
, "The connection to the Chromecast died.");
966 vlc_mutex_locker
locker(&p_sys
->lock
);
967 p_sys
->i_status
= CHROMECAST_CONNECTION_DEAD
;
971 vlc_mutex_lock(&p_sys
->lock
);
972 if ( p_sys
->i_status
== CHROMECAST_CONNECTION_DEAD
)
974 atomic_store(&p_sys
->ab_error
, true);
975 vlc_mutex_unlock(&p_sys
->lock
);
978 vlc_mutex_unlock(&p_sys
->lock
);
980 vlc_restorecancel(canc
);