Commit d0fb8bbe authored by Steve Lhomme's avatar Steve Lhomme Committed by Thomas Guillem

chromecast: only create the sub-sout when we know the ES in use

we then decide whether we want to remux or transcode
use the Matroska container as it allows better streaming and more codec than
the Chromecast supports (VP8 / Vorbis)
Signed-off-by: Thomas Guillem's avatarThomas Guillem <thomas@gllm.fr>
parent ada686e8
......@@ -38,10 +38,15 @@
struct sout_stream_sys_t
{
sout_stream_sys_t(intf_sys_t *intf, sout_stream_t *sout, bool has_video)
: p_out(sout)
sout_stream_sys_t(intf_sys_t * const intf, bool has_video, int port,
const char *psz_default_muxer, const char *psz_default_mime)
: p_out(NULL)
, default_muxer(psz_default_muxer)
, default_mime(psz_default_mime)
, p_intf(intf)
, b_has_video(has_video)
, i_port(port)
, es_changed( true )
{
assert(p_intf != NULL);
}
......@@ -51,17 +56,34 @@ struct sout_stream_sys_t
sout_StreamChainDelete(p_out, p_out);
}
sout_stream_t * const p_out;
bool canDecodeVideo( const es_format_t *p_es ) const;
bool canDecodeAudio( const es_format_t *p_es ) const;
sout_stream_t *p_out;
std::string sout;
const std::string default_muxer;
const std::string default_mime;
intf_sys_t * const p_intf;
const bool b_has_video;
const int i_port;
sout_stream_id_sys_t *GetSubId( sout_stream_t*, sout_stream_id_sys_t* );
bool es_changed;
std::vector<sout_stream_id_sys_t*> streams;
private:
int UpdateOutput( sout_stream_t * );
};
#define SOUT_CFG_PREFIX "sout-chromecast-"
static const vlc_fourcc_t DEFAULT_TRANSCODE_AUDIO = VLC_CODEC_MP3;
static const vlc_fourcc_t DEFAULT_TRANSCODE_VIDEO = VLC_CODEC_H264;
static const char DEFAULT_MUXER[] = "avformat{mux=matroska}";
/*****************************************************************************
* Local prototypes
*****************************************************************************/
......@@ -98,8 +120,8 @@ vlc_module_begin ()
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)
add_string(SOUT_CFG_PREFIX "mux", DEFAULT_MUXER, MUX_TEXT, MUX_LONGTEXT, false)
add_string(SOUT_CFG_PREFIX "mime", "video/x-matroska", MIME_TEXT, MIME_LONGTEXT, false)
vlc_module_end ()
......@@ -130,6 +152,7 @@ static sout_stream_id_sys_t *Add(sout_stream_t *p_stream, const es_format_t *p_f
p_sys_id->p_sub_id = NULL;
p_sys->streams.push_back( p_sys_id );
p_sys->es_changed = true;
}
return p_sys_id;
}
......@@ -149,9 +172,153 @@ static void Del(sout_stream_t *p_stream, sout_stream_id_sys_t *id)
es_format_Clean( &p_sys->streams[i]->fmt );
free( p_sys->streams[i] );
p_sys->streams.erase( p_sys->streams.begin() + i );
p_sys->es_changed = true;
break;
}
}
if ( p_sys->streams.empty() )
{
sout_StreamChainDelete( p_sys->p_out, p_sys->p_out );
p_sys->p_out = NULL;
p_sys->sout = "";
}
}
bool sout_stream_sys_t::canDecodeVideo( const es_format_t *p_es ) const
{
if (p_es->i_codec == VLC_CODEC_H264 || p_es->i_codec == VLC_CODEC_VP8)
return true;
return false;
}
bool sout_stream_sys_t::canDecodeAudio( const es_format_t *p_es ) const
{
if (p_es->i_codec == VLC_CODEC_VORBIS ||
p_es->i_codec == VLC_CODEC_MP4A ||
p_es->i_codec == VLC_FOURCC('h', 'a', 'a', 'c') ||
p_es->i_codec == VLC_FOURCC('l', 'a', 'a', 'c') ||
p_es->i_codec == VLC_FOURCC('s', 'a', 'a', 'c') ||
p_es->i_codec == VLC_CODEC_MPGA ||
p_es->i_codec == VLC_CODEC_MP3 ||
p_es->i_codec == VLC_CODEC_A52 ||
p_es->i_codec == VLC_CODEC_EAC3)
return true;
return false;
}
int sout_stream_sys_t::UpdateOutput( sout_stream_t *p_stream )
{
assert( p_stream->p_sys == this );
if ( es_changed )
{
es_changed = false;
bool canRemux = true;
vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0;
for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it)
{
const es_format_t *p_es = &(*it)->fmt;
if (p_es->i_cat == AUDIO_ES)
{
if (!canDecodeAudio( p_es ))
{
msg_Dbg( p_stream, "can't remux audio track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec );
canRemux = false;
}
else if (i_codec_audio == 0)
i_codec_audio = p_es->i_codec;
}
else if (b_has_video && p_es->i_cat == VIDEO_ES)
{
if (!canDecodeVideo( p_es ))
{
msg_Dbg( p_stream, "can't remux video track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec );
canRemux = false;
}
else if (i_codec_video == 0)
i_codec_video = p_es->i_codec;
}
}
std::stringstream ssout;
if ( !canRemux )
{
if ( i_codec_audio == 0 )
i_codec_audio = DEFAULT_TRANSCODE_AUDIO;
/* avcodec AAC encoder is experimental */
if ( i_codec_audio == VLC_CODEC_MP4A ||
i_codec_audio == VLC_FOURCC('h', 'a', 'a', 'c') ||
i_codec_audio == VLC_FOURCC('l', 'a', 'a', 'c') ||
i_codec_audio == VLC_FOURCC('s', 'a', 'a', 'c'))
i_codec_audio = DEFAULT_TRANSCODE_AUDIO;
if ( i_codec_video == 0 )
i_codec_video = DEFAULT_TRANSCODE_VIDEO;
/* TODO: provide audio samplerate and channels */
ssout << "transcode{acodec=";
char s_fourcc[5];
vlc_fourcc_to_char( i_codec_audio, s_fourcc );
s_fourcc[4] = '\0';
ssout << s_fourcc;
if ( b_has_video )
{
/* TODO: provide maxwidth,maxheight */
ssout << ",vcodec=";
vlc_fourcc_to_char( i_codec_video, s_fourcc );
s_fourcc[4] = '\0';
ssout << s_fourcc;
}
ssout << "}:";
}
std::string mime;
if ( !b_has_video && default_muxer == DEFAULT_MUXER )
mime = "audio/x-matroska";
else if ( i_codec_audio == VLC_CODEC_VORBIS &&
i_codec_video == VLC_CODEC_VP8 &&
default_muxer == DEFAULT_MUXER )
mime = "video/webm";
else
mime = default_mime;
ssout << "http{dst=:" << i_port << "/stream"
<< ",mux=" << default_muxer
<< ",access=http{mime=" << mime << "}}";
if ( sout != ssout.str() )
{
if ( unlikely( p_out != NULL ) )
{
sout_StreamChainDelete( p_out, p_out );
sout = "";
}
p_out = sout_StreamChainNew( p_stream->p_sout, ssout.str().c_str(), NULL, NULL);
if (p_out == NULL) {
msg_Dbg(p_stream, "could not create sout chain:%s", ssout.str().c_str());
return VLC_EGENERIC;
}
sout = ssout.str();
}
/* check the streams we can actually add */
for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it)
{
sout_stream_id_sys_t *p_sys_id = *it;
p_sys_id->p_sub_id = sout_StreamIdAdd( p_out, &p_sys_id->fmt );
if ( p_sys_id->p_sub_id == NULL )
{
msg_Err( p_stream, "can't handle a stream" );
streams.erase( it, it );
}
}
}
return VLC_SUCCESS;
}
sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
......@@ -161,6 +328,9 @@ sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
assert( p_stream->p_sys == this );
if ( UpdateOutput( p_stream ) != VLC_SUCCESS )
return NULL;
for (i = 0; i < streams.size(); ++i)
{
if ( id == (sout_stream_id_sys_t*) streams[i] )
......@@ -216,9 +386,11 @@ static int Open(vlc_object_t *p_this)
char *psz_var_mime = NULL;
sout_stream_t *p_sout = NULL;
bool b_has_video = true;
int i_local_server_port;
std::stringstream ss;
config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);
i_local_server_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port");
psz_mux = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "mux");
if (psz_mux == NULL)
......@@ -229,7 +401,8 @@ static int Open(vlc_object_t *p_this)
if (psz_var_mime == NULL)
goto error;
ss << "http{dst=:" << var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port") << "/stream"
/* check if we can open the proper sout */
ss << "http{dst=:" << i_local_server_port << "/stream"
<< ",mux=" << psz_mux
<< ",access=http{mime=" << psz_var_mime << "}}";
......@@ -238,10 +411,12 @@ static int Open(vlc_object_t *p_this)
msg_Dbg(p_stream, "could not create sout chain:%s", ss.str().c_str());
goto error;
}
sout_StreamChainDelete( p_sout, p_sout );
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);
p_sys = new(std::nothrow) sout_stream_sys_t( p_intf, b_has_video, i_local_server_port,
psz_mux, psz_var_mime );
if (unlikely(p_sys == NULL))
goto error;
......@@ -258,7 +433,6 @@ static int Open(vlc_object_t *p_this)
return VLC_SUCCESS;
error:
sout_StreamChainDelete(p_sout, p_sout);
free(psz_mux);
free(psz_var_mime);
delete p_sys;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment