|
|
|
|
/*****************************************************************************
|
|
|
|
|
* cast.cpp: Chromecast sout module for vlc
|
|
|
|
|
*****************************************************************************
|
|
|
|
|
* Copyright © 2014-2015 VideoLAN
|
|
|
|
|
*
|
|
|
|
|
* Authors: Adrien Maglo <magsoft@videolan.org>
|
|
|
|
|
* Jean-Baptiste Kempf <jb@videolan.org>
|
|
|
|
|
* Steve Lhomme <robux4@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
|
|
|
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* Preamble
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "chromecast.h"
|
|
|
|
|
|
|
|
|
|
#include <vlc_sout.h>
|
|
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
|
|
struct sout_stream_sys_t
|
|
|
|
|
{
|
|
|
|
|
sout_stream_sys_t(intf_sys_t *intf, sout_stream_t *sout, bool has_video)
|
|
|
|
|
: p_out(sout)
|
|
|
|
|
, p_intf(intf)
|
|
|
|
|
, b_has_video(has_video)
|
|
|
|
|
{
|
|
|
|
|
assert(p_intf != NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~sout_stream_sys_t()
|
|
|
|
|
{
|
|
|
|
|
sout_StreamChainDelete(p_out, p_out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sout_stream_t * const p_out;
|
|
|
|
|
intf_sys_t * const p_intf;
|
|
|
|
|
const bool b_has_video;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define SOUT_CFG_PREFIX "sout-chromecast-"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* Local prototypes
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
static int Open(vlc_object_t *);
|
|
|
|
|
static void Close(vlc_object_t *);
|
|
|
|
|
|
|
|
|
|
static const char *const ppsz_sout_options[] = {
|
|
|
|
|
"http-port", "mux", "mime", "video", NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* Module descriptor
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define HTTP_PORT_TEXT N_("HTTP port")
|
|
|
|
|
#define HTTP_PORT_LONGTEXT N_("This sets the HTTP port of the server " \
|
|
|
|
|
"used to stream the media to the Chromecast.")
|
|
|
|
|
#define HAS_VIDEO_TEXT N_("Video")
|
|
|
|
|
#define HAS_VIDEO_LONGTEXT N_("The Chromecast receiver can receive video.")
|
|
|
|
|
#define MUX_TEXT N_("Muxer")
|
|
|
|
|
#define MUX_LONGTEXT N_("This sets the muxer used to stream to the Chromecast.")
|
|
|
|
|
#define MIME_TEXT N_("MIME content type")
|
|
|
|
|
#define MIME_LONGTEXT N_("This sets the media MIME content type sent to the Chromecast.")
|
|
|
|
|
|
|
|
|
|
vlc_module_begin ()
|
|
|
|
|
|
|
|
|
|
set_shortname(N_("Chromecast"))
|
|
|
|
|
set_description(N_("Chromecast stream output"))
|
|
|
|
|
set_capability("sout stream", 0)
|
|
|
|
|
add_shortcut("chromecast")
|
|
|
|
|
set_category(CAT_SOUT)
|
|
|
|
|
set_subcategory(SUBCAT_SOUT_STREAM)
|
|
|
|
|
set_callbacks(Open, Close)
|
|
|
|
|
|
|
|
|
|
add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)
|
|
|
|
|
add_bool(SOUT_CFG_PREFIX "video", true, HAS_VIDEO_TEXT, HAS_VIDEO_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)
|
|
|
|
|
|
|
|
|
|
vlc_module_end ()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* Sout callbacks
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
static sout_stream_id_sys_t *Add(sout_stream_t *p_stream, const es_format_t *p_fmt)
|
|
|
|
|
{
|
|
|
|
|
sout_stream_sys_t *p_sys = p_stream->p_sys;
|
|
|
|
|
|
|
|
|
|
if (!p_sys->b_has_video)
|
|
|
|
|
{
|
|
|
|
|
if (p_fmt->i_cat != AUDIO_ES)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return sout_StreamIdAdd(p_sys->p_out, p_fmt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void Del(sout_stream_t *p_stream, sout_stream_id_sys_t *id)
|
|
|
|
|
{
|
|
|
|
|
sout_stream_sys_t *p_sys = p_stream->p_sys;
|
|
|
|
|
|
|
|
|
|
sout_StreamIdDel(p_sys->p_out, id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
return sout_StreamIdSend(p_sys->p_out, id, p_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void Flush( sout_stream_t *p_stream, sout_stream_id_sys_t *id )
|
|
|
|
|
{
|
|
|
|
|
sout_stream_sys_t *p_sys = p_stream->p_sys;
|
|
|
|
|
|
|
|
|
|
sout_StreamFlush( p_sys->p_out, id );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int Control(sout_stream_t *p_stream, int i_query, va_list args)
|
|
|
|
|
{
|
|
|
|
|
sout_stream_sys_t *p_sys = p_stream->p_sys;
|
|
|
|
|
|
|
|
|
|
if ( !p_sys->p_out->pf_control )
|
|
|
|
|
return VLC_EGENERIC;
|
|
|
|
|
|
|
|
|
|
return p_sys->p_out->pf_control( p_sys->p_out, i_query, args );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* Open: connect to the Chromecast and initialize the sout
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
static int Open(vlc_object_t *p_this)
|
|
|
|
|
{
|
|
|
|
|
sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
|
|
|
|
|
sout_stream_sys_t *p_sys = NULL;
|
|
|
|
|
intf_sys_t *p_intf = NULL;
|
|
|
|
|
char *psz_mux = NULL;
|
|
|
|
|
char *psz_var_mime = NULL;
|
|
|
|
|
sout_stream_t *p_sout = NULL;
|
|
|
|
|
bool b_has_video = true;
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
|
|
|
|
|
config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);
|
|
|
|
|
|
|
|
|
|
psz_mux = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "mux");
|
|
|
|
|
if (psz_mux == NULL)
|
|
|
|
|
{
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
psz_var_mime = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "mime");
|
|
|
|
|
if (psz_var_mime == NULL)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
ss << "http{dst=:" << var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port") << "/stream"
|
|
|
|
|
<< ",mux=" << psz_mux
|
|
|
|
|
<< ",access=http{mime=" << psz_var_mime << "}}";
|
|
|
|
|
|
|
|
|
|
p_sout = sout_StreamChainNew( p_stream->p_sout, ss.str().c_str(), NULL, NULL);
|
|
|
|
|
if (p_sout == NULL) {
|
|
|
|
|
msg_Dbg(p_stream, "could not create sout chain:%s", ss.str().c_str());
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_has_video = var_GetBool(p_stream, SOUT_CFG_PREFIX "video");
|
|
|
|
|
|
|
|
|
|
p_sys = new(std::nothrow) sout_stream_sys_t(p_intf, p_sout, b_has_video);
|
|
|
|
|
if (unlikely(p_sys == NULL))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
// Set the sout callbacks.
|
|
|
|
|
p_stream->pf_add = Add;
|
|
|
|
|
p_stream->pf_del = Del;
|
|
|
|
|
p_stream->pf_send = Send;
|
|
|
|
|
p_stream->pf_flush = Flush;
|
|
|
|
|
p_stream->pf_control = Control;
|
|
|
|
|
|
|
|
|
|
p_stream->p_sys = p_sys;
|
|
|
|
|
free(psz_mux);
|
|
|
|
|
free(psz_var_mime);
|
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
sout_StreamChainDelete(p_sout, p_sout);
|
|
|
|
|
free(psz_mux);
|
|
|
|
|
free(psz_var_mime);
|
|
|
|
|
delete p_sys;
|
|
|
|
|
return VLC_EGENERIC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* Close: destroy interface
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
static void Close(vlc_object_t *p_this)
|
|
|
|
|
{
|
|
|
|
|
sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
|
|
|
|
|
|
|
|
|
|
delete p_stream->p_sys;
|
|
|
|
|
}
|
|
|
|
|
|