1. 初始化流程
在这里插入图片描述2. 创建input thread,解析输入url
在这里插入图片描述值得注意的是,在input_EsOutNew方法中设置了pf_control指向EsOutControl,pf_add指向EsOutAdd,这两个指针很重要,后面还会用到
es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
{
es_out_t *out = malloc( sizeof( *out ) );
if( !out )
return NULL;
es_out_sys_t *p_sys = calloc( 1, sizeof( *p_sys ) );
if( !p_sys )
{
free( out );
return NULL;
}
out->pf_add = EsOutAdd;
out->pf_send = EsOutSend;
out->pf_del = EsOutDel;
out->pf_control = EsOutControl;
out->pf_destroy = EsOutDelete;
out->p_sys = p_sys;
3. 创建access和demux模块,读入输入并做parse
在这里插入图片描述在这部分有几点值得注意:
a.在第一次demux_NewAdvanced方法中尝试创建access_demux类型的module,如果没有符合的,再分别创建access类型module和demux类型module;选择module的过程就是从module list中先按照类型取出所有可选module,再按照module score排序,再比对前缀、后缀名判断是否可用。access_demux类型的module很少, 像bluray这种属于,所以一般这一步都找不到符合的,以file类型为例,就要分别创建access和demux。
在module_load方法中,会去调用module的pf_activate指针所指向的方法,如下
static int module_load (vlc_object_t *obj, module_t *m,
vlc_activate_t init, va_list args)
{
int ret = VLC_SUCCESS;
if (module_Map(obj, m->plugin))
return VLC_EGENERIC;
if (m->pf_activate != NULL)
{
va_list ap;
va_copy (ap, args);
ret = init (m->pf_activate, ap); //vlc_activate_t的定义为typedef int (*vlc_activate_t)(void *func, va_list args);
va_end (ap);
}
if (ret != VLC_SUCCESS)
vlc_objres_clear(obj);
return ret;
}
以文件类型为例,对应的module是modules/access/file.c,module定义在modules/access/fs.c,如下
vlc_module_begin ()
set_description( N_("File input") )
set_shortname( N_("File") )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_ACCESS )
add_obsolete_string( "file-cat" )
set_capability( "access", 50 )
add_shortcut( "file", "fd", "stream" )
set_callbacks( FileOpen, FileClose ) //在这里设置pf_activate指向modules/access/file.c中的FileOpen方法
...
vlc_module_end ()
demux module也类似,以avformat module为例,会去调用avformat_OpenDemux方法
b.在AStreamPrebufferStream会做一次pre buffer操作,读取1024byte长度的数据
for (;;)
{
stream_track_t *tk = &sys->tk[sys->i_tk];
mtime_t now = mdate();
int i_read;
int i_buffered = tk->i_end - tk->i_start;
if (vlc_killed() || i_buffered >= STREAM_CACHE_PREBUFFER_SIZE)
{
int64_t i_byterate;
/* Update stat */
sys->stat.i_bytes = i_buffered;
sys->stat.i_read_time = now - start;
i_byterate = (CLOCK_FREQ * sys->stat.i_bytes) /
(sys->stat.i_read_time+1);
msg_Dbg(s, "pre-buffering done %"PRId64" bytes in %"PRId64"s - "
"%"PRId64" KiB/s", sys->stat.i_bytes,
sys->stat.i_read_time / CLOCK_FREQ, i_byterate / 1024);
break;
}
i_read = STREAM_CACHE_TRACK_SIZE - i_buffered;
i_read = __MIN((int)sys->i_read_size, i_read);
i_read = vlc_stream_Read(s->p_source, &tk->p_buffer[i_buffered],
i_read);
if (i_read < 0)
continue;
else if (i_read == 0)
break; /* EOF */
if (first)
{
msg_Dbg(s, "received first data after %"PRId64" ms",
(mdate() - start) / 1000);
first = false;
}
tk->i_end += i_read;
sys->stat.i_read_count++;
}
c.在avformat/demux.c中调用ffmpeg的方法获取到流信息后,针对视频流,音频流,字幕流分别调用es_format_init方法来初始化,设置分辨率,帧率,声道数等信息。然后调用es_out_Add方法添加一个es_out,有几个流就做几次es_out_Add操作,比如该输入中有一个视频流和一个音频流,则作两次es_out_Add操作。而es_out_Add方法就是我们在第二小节所提到过的。
4. 创建decoder,开始解码
在这里插入图片描述在这部分有几点值得注意:
a.在UpdatePtsDelay方法中,可以设置音频的字幕的delay值,如下
/* Take care of audio/spu delay */
const mtime_t i_audio_delay = var_GetInteger( p_input, "audio-delay" );
const mtime_t i_spu_delay = var_GetInteger( p_input, "spu-delay" );
const mtime_t i_extra_delay = __MIN( i_audio_delay, i_spu_delay );
if( i_extra_delay < 0 )
i_pts_delay -= i_extra_delay;
/* Update cr_average depending on the caching */
const int i_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;
/* */
es_out_SetDelay( input_priv(p_input)->p_es_out_display, AUDIO_ES, i_audio_delay );
es_out_SetDelay( input_priv(p_input)->p_es_out_display, SPU_ES, i_spu_delay );
es_out_SetJitter( input_priv(p_input)->p_es_out, i_pts_delay, 0, i_cr_average );
b.在es_out_vaControl中,调用es_out_t的pf_control指针指向的方法,这块在第二小节中已经做过解释
c.在input/decoder.c中,创建了decoder fifo,在vlc中,除了decoder fifo外,还有用于显示输出的picture fifo,将在下面进行介绍
5. 播放输出
image image关注公众号,掌握更多多媒体领域知识与资讯,开启精彩程序人生
image
网友评论