[vlc-devel] [PATCH 5/5] chromecast: use libmicrodns to get the device IP/port based on its name
Denis Charmet
typx at dinauz.org
Thu Sep 24 15:21:05 CEST 2015
Hi,
Le jeudi 24 septembre 2015 à 02:42:53, Steve Lhomme a écrit :
> no name/port provided looks for the first Chromecast to reply to the mDNS request
> ---
> modules/stream_out/chromecast/cast.cpp | 198 ++++++++++++++++++++++++---------
> 1 file changed, 145 insertions(+), 53 deletions(-)
>
> diff --git a/modules/stream_out/chromecast/cast.cpp b/modules/stream_out/chromecast/cast.cpp
> index b8f2cb9..deecd41 100644
> --- a/modules/stream_out/chromecast/cast.cpp
> +++ b/modules/stream_out/chromecast/cast.cpp
> @@ -5,6 +5,7 @@
> *
> * Authors: Adrien Maglo <magsoft at videolan.org>
> * Jean-Baptiste Kempf <jb at videolan.org>
> + * Steve Lhomme <robux4 at videolabs.io>
> *
> * 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
> @@ -51,6 +52,12 @@
>
> #include "../../misc/webservices/json.h"
>
> +#ifdef HAVE_MICRODNS
> +# include <microdns/microdns.h>
> +
> +static const std::string MDNS_CHROMECAST = "._googlecast._tcp.local";
> +#endif
> +
> // Status
> enum
> {
> @@ -68,11 +75,17 @@ enum
> struct sout_stream_sys_t
> {
> sout_stream_sys_t()
> - : p_tls(NULL), i_requestId(0),
> + : devicePort(8009)
> + ,p_tls(NULL), i_requestId(0),
> i_status(CHROMECAST_DISCONNECTED), p_out(NULL)
> +#ifdef HAVE_MICRODNS
> + ,microdns_ctx(NULL)
> +#endif
> {
> }
>
> + uint16_t devicePort;
> + std::string deviceIP;
> std::string serverIP;
>
> int i_sock_fd;
> @@ -88,15 +101,18 @@ struct sout_stream_sys_t
>
> int i_status;
> vlc_mutex_t lock;
> - vlc_cond_t loadCommandCond;
>
> sout_stream_t *p_out;
> +#ifdef HAVE_MICRODNS
> + struct mdns_ctx *microdns_ctx;
> + mtime_t i_timeout;
> + std::string nameChromecast; /* name we're looking for */
> +#endif
> };
>
> // Media player Chromecast app id
> #define APP_ID "CC1AD845" // Default media player
>
> -#define CHROMECAST_CONTROL_PORT 8009
> #define HTTP_PORT 8010
>
> #define SOUT_CFG_PREFIX "sout-chromecast-"
> @@ -114,7 +130,7 @@ struct sout_stream_sys_t
> static int Open(vlc_object_t *);
> static void Close(vlc_object_t *);
> static void Clean(sout_stream_t *p_stream);
> -static int connectChromecast(sout_stream_t *p_stream, char *psz_ipChromecast);
> +static int connectChromecast(sout_stream_t *p_stream);
> static void disconnectChromecast(sout_stream_t *p_stream);
> static int sendMessages(sout_stream_t *p_stream);
>
> @@ -130,7 +146,7 @@ static void msgStatus(sout_stream_t *p_stream);
> static void *chromecastThread(void *data);
>
> static const char *const ppsz_sout_options[] = {
> - "ip", "http-port", "mux", "mime", NULL
> + "ip", "target", "http-port", "mux", "mime", NULL
> };
>
> /*****************************************************************************
> @@ -158,6 +174,7 @@ vlc_module_begin ()
> set_callbacks(Open, Close)
>
> add_string(SOUT_CFG_PREFIX "ip", "", IP_TEXT, IP_LONGTEXT, false)
> + add_string(SOUT_CFG_PREFIX "target", "", IP_TEXT, IP_LONGTEXT, false)
> add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)
> add_string(SOUT_CFG_PREFIX "mux", "mp4stream", MUX_TEXT, MUX_LONGTEXT, false)
> add_string(SOUT_CFG_PREFIX "mime", "video/mp4", MIME_TEXT, MIME_LONGTEXT, false)
> @@ -187,6 +204,8 @@ static int Send(sout_stream_t *p_stream, sout_stream_id_sys_t *id,
> block_t *p_buffer)
> {
> sout_stream_sys_t *p_sys = p_stream->p_sys;
> + if (p_sys->i_status == CHROMECAST_CONNECTION_DEAD)
> + return VLC_EGENERIC;
>
> return p_sys->p_out->pf_send(p_sys->p_out, id, p_buffer);
> }
> @@ -207,31 +226,29 @@ static int Open(vlc_object_t *p_this)
> config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);
>
> char *psz_ipChromecast = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "ip");
> - if (psz_ipChromecast == NULL)
> + if (psz_ipChromecast != NULL)
> {
> - msg_Err(p_stream, "No Chromecast receiver IP provided");
> - Clean(p_stream);
> - return VLC_EGENERIC;
> + p_sys->deviceIP = psz_ipChromecast;
> + free(psz_ipChromecast);
> }
> -
> - p_sys->i_sock_fd = connectChromecast(p_stream, psz_ipChromecast);
> - free(psz_ipChromecast);
> - if (p_sys->i_sock_fd < 0)
> + else
> +#ifdef HAVE_MICRODNS
> + if (mdns_init(&p_sys->microdns_ctx, MDNS_ADDR_IPV4, MDNS_PORT) >= 0)
> {
> - msg_Err(p_stream, "Could not connect the Chromecast");
> - Clean(p_stream);
> - return VLC_EGENERIC;
> + char *psz_nameChromecast = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "target");
> + if (psz_nameChromecast != NULL)
> + {
> + p_sys->nameChromecast = psz_nameChromecast;
> + free(psz_nameChromecast);
> + }
> }
> - p_sys->i_status = CHROMECAST_TLS_CONNECTED;
> -
> - char psz_localIP[NI_MAXNUMERICHOST];
> - if (net_GetSockAddress(p_sys->i_sock_fd, psz_localIP, NULL))
> + if (p_sys->microdns_ctx == NULL)
> +#endif /* HAVE_MICRODNS */
> {
> - msg_Err(p_this, "Cannot get local IP address");
> + msg_Err(p_stream, "No Chromecast receiver IP/Name provided");
> Clean(p_stream);
> return VLC_EGENERIC;
> }
> - p_sys->serverIP = psz_localIP;
>
> char *psz_mux = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "mux");
> if (psz_mux == NULL)
> @@ -259,7 +276,6 @@ static int Open(vlc_object_t *p_this)
> }
>
> vlc_mutex_init(&p_sys->lock);
> - vlc_cond_init(&p_sys->loadCommandCond);
>
> // Start the Chromecast event thread.
> if (vlc_clone(&p_sys->chromecastThread, chromecastThread, p_stream,
> @@ -270,31 +286,7 @@ static int Open(vlc_object_t *p_this)
> return VLC_EGENERIC;
> }
>
> - /* Ugly part:
> - * We want to be sure that the Chromecast receives the first data packet sent by
> - * the HTTP server. */
> -
> - // Lock the sout thread until we have sent the media loading command to the Chromecast.
> - int i_ret = 0;
> - const mtime_t deadline = mdate() + 6 * CLOCK_FREQ;
> - vlc_mutex_lock(&p_sys->lock);
> - while (p_sys->i_status != CHROMECAST_MEDIA_LOAD_SENT)
> - {
> - i_ret = vlc_cond_timedwait(&p_sys->loadCommandCond, &p_sys->lock, deadline);
> - if (i_ret == ETIMEDOUT)
> - {
> - msg_Err(p_stream, "Timeout reached before sending the media loading command");
> - vlc_mutex_unlock(&p_sys->lock);
> - vlc_cancel(p_sys->chromecastThread);
> - Clean(p_stream);
> - return VLC_EGENERIC;
> - }
> - }
> - vlc_mutex_unlock(&p_sys->lock);
> -
> - /* Even uglier: sleep more to let to the Chromecast initiate the connection
> - * to the http server. */
> - msleep(2 * CLOCK_FREQ);
> + // TODO p_stream->pace_nocontrol = true;
>
> // Set the sout callbacks.
> p_stream->pf_add = Add;
> @@ -346,12 +338,15 @@ static void Clean(sout_stream_t *p_stream)
> if (p_sys->p_out)
> {
> vlc_mutex_destroy(&p_sys->lock);
> - vlc_cond_destroy(&p_sys->loadCommandCond);
> sout_StreamChainDelete(p_sys->p_out, p_sys->p_out);
> }
>
> disconnectChromecast(p_stream);
>
> +#ifdef HAVE_MICRODNS
> + mdns_cleanup(p_sys->microdns_ctx);
> +#endif
> +
> delete p_sys;
> }
>
> @@ -361,10 +356,10 @@ static void Clean(sout_stream_t *p_stream)
> * @param p_stream the sout_stream_t structure
> * @return the opened socket file descriptor or -1 on error
> */
> -static int connectChromecast(sout_stream_t *p_stream, char *psz_ipChromecast)
> +static int connectChromecast(sout_stream_t *p_stream)
> {
> sout_stream_sys_t *p_sys = p_stream->p_sys;
> - int fd = net_ConnectTCP(p_stream, psz_ipChromecast, CHROMECAST_CONTROL_PORT);
> + int fd = net_ConnectTCP(p_stream, p_sys->deviceIP.c_str(), p_sys->devicePort);
> if (fd < 0)
> return -1;
>
> @@ -375,7 +370,7 @@ static int connectChromecast(sout_stream_t *p_stream, char *psz_ipChromecast)
> return -1;
> }
>
> - p_sys->p_tls = vlc_tls_ClientSessionCreate(p_sys->p_creds, fd, psz_ipChromecast,
> + p_sys->p_tls = vlc_tls_ClientSessionCreate(p_sys->p_creds, fd, p_sys->deviceIP.c_str(),
> "tcps", NULL, NULL);
>
> if (p_sys->p_tls == NULL)
> @@ -662,7 +657,6 @@ static int processMessage(sout_stream_t *p_stream, const castchannel::CastMessag
> msgConnect(p_stream, p_sys->appTransportId);
> msgLoad(p_stream);
> p_sys->i_status = CHROMECAST_MEDIA_LOAD_SENT;
> - vlc_cond_signal(&p_sys->loadCommandCond);
> }
> }
> else
> @@ -900,6 +894,54 @@ static void msgLoad(sout_stream_t *p_stream)
> }
>
>
> +#ifdef HAVE_MICRODNS
> +static bool mdnsShouldStop(void *p_callback_cookie)
> +{
> + sout_stream_t* p_stream = (sout_stream_t*)p_callback_cookie;
> + sout_stream_sys_t* p_sys = p_stream->p_sys;
> +
> + return !p_sys->deviceIP.empty() || mdate() > p_sys->i_timeout;
> +}
> +
> +static void mdnsCallback(void *p_callback_cookie, int i_status, const struct rr_entry *p_entry)
> +{
> + sout_stream_t* p_stream = (sout_stream_t*)p_callback_cookie;
> + sout_stream_sys_t* p_sys = p_stream->p_sys;
> +
> + if (i_status < 0)
> + {
> + char err_str[128];
> + if (mdns_strerror(i_status, err_str, sizeof(err_str)) == 0)
> + msg_Dbg(p_stream, "mDNS lookup error: %s", err_str);
> + }
> + else if (p_entry != NULL && p_entry->next != NULL)
> + {
> + std::string deviceName = p_entry->next->name;
> + if (deviceName.length() >= MDNS_CHROMECAST.length())
> + {
> + std::string serviceName = deviceName.substr(deviceName.length() - MDNS_CHROMECAST.length());
> + deviceName = deviceName.substr(0, deviceName.length() - MDNS_CHROMECAST.length());
> + if (!serviceName.compare(MDNS_CHROMECAST) &&
> + (p_sys->nameChromecast.empty() ||
> + !strncasecmp(deviceName.c_str(), p_sys->nameChromecast.c_str(), p_sys->nameChromecast.length())))
> + {
> + while (p_entry != NULL)
> + {
> + if (p_entry->type == RR_A)
> + p_sys->deviceIP = p_entry->data.A.addr_str;
> + else if (p_entry->type == RR_AAAA)
> + p_sys->deviceIP = p_entry->data.AAAA.addr_str;
> + else if (p_entry->type == RR_SRV)
> + p_sys->devicePort = p_entry->data.SRV.port;
> + p_entry = p_entry->next;
> + }
> + msg_Dbg(p_stream, "Found %s:%d for target %s", p_sys->deviceIP.c_str(), p_sys->devicePort, deviceName.c_str());
> + }
> + }
> + }
> +}
> +#endif /* HAVE_MICRODNS */
> +
> /*****************************************************************************
> * Chromecast thread
> *****************************************************************************/
> @@ -917,6 +959,48 @@ static void* chromecastThread(void* p_data)
> int i_waitdelay = PING_WAIT_TIME;
> int i_retries = PING_WAIT_RETRIES;
>
> + mtime_t deadline;
> +
> +#ifdef HAVE_MICRODNS
> + if (p_sys->microdns_ctx != NULL)
> + {
> + int err;
> + p_sys->i_timeout = mdate() + 5 * CLOCK_FREQ;
> + if ((err = mdns_listen(p_sys->microdns_ctx, MDNS_CHROMECAST.c_str()+1, 2, &mdnsShouldStop, &mdnsCallback, p_stream)) < 0)
> + {
> + char err_str[128];
> + if (mdns_strerror(err, err_str, sizeof(err_str)) == 0)
> + msg_Err(p_stream, "Failed to look for the target Name: %s", err_str);
> + p_sys->i_status = CHROMECAST_CONNECTION_DEAD;
Shouldn't you call vlc_restorecancel(canc) before the return?
> + return NULL;
> + }
> + }
> +#endif /* HAVE_MICRODNS */
> +
> + p_sys->i_sock_fd = connectChromecast(p_stream);
> + if (p_sys->i_sock_fd < 0)
> + {
> + msg_Err(p_stream, "Could not connect the Chromecast");
> + p_sys->i_status = CHROMECAST_CONNECTION_DEAD;
same
> + return NULL;
> + }
> +
> + char psz_localIP[NI_MAXNUMERICHOST];
> + if (net_GetSockAddress(p_sys->i_sock_fd, psz_localIP, NULL))
> + {
> + msg_Err(p_stream, "Cannot get local IP address");
idem
> + return NULL;
> + }
> + p_sys->serverIP = psz_localIP;
> +
> + p_sys->i_status = CHROMECAST_TLS_CONNECTED;
> +
> + /* Ugly part:
> + * We want to be sure that the Chromecast receives the first data packet sent by
> + * the HTTP server. */
> +
> + deadline = mdate() + 6 * CLOCK_FREQ;
> +
> msgAuth(p_stream);
> sendMessages(p_stream);
> vlc_restorecancel(canc);
> @@ -973,6 +1057,14 @@ static void* chromecastThread(void* p_data)
> }
> }
>
> + if (p_sys->i_status == CHROMECAST_MEDIA_LOAD_SENT)
> + deadline = 0;
> + else if (mdate() > deadline)
> + {
> + msg_Err(p_stream, "Timeout reached before sending the media loading command");
> + p_sys->i_status = CHROMECAST_CONNECTION_DEAD;
> + }
> +
> vlc_mutex_lock(&p_sys->lock);
> if ( p_sys->i_status == CHROMECAST_CONNECTION_DEAD )
> {
> --
> 2.5.1
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
Regards,
--
Denis Charmet - TypX
Le mauvais esprit est un art de vivre
More information about the vlc-devel
mailing list