[vlc-devel] [PATCH 5/5] chromecast: use libmicrodns to get the device IP/port based on its name
Steve Lhomme
robux4 at gmail.com
Thu Sep 24 17:38:29 CEST 2015
On Thu, Sep 24, 2015 at 3:21 PM, Denis Charmet <typx at dinauz.org> wrote:
> 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?
You're right. I reworked the patch a bit
https://patches.videolan.org/patch/10127/
I also made i_status atomic to avoid many locks just for this variable.
>> + 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
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list