深入理解VLC之代码流程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nonmarking/article/details/85170651

本文以vlc-android项目为例,介绍vlc player的代码流程。对vlc不太熟悉的朋友,可以在我的公众号中阅读《深入理解VLC之纵观全局》,从而对vlc有一个整体上的认识。
在这里插入图片描述

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. 播放输出

在这里插入图片描述

在这里插入图片描述

文章帮到你了?可以扫描如下二维码进行打赏,打赏多少您随意~
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/nonmarking/article/details/85170651