[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