美文网首页多媒体科技
深入理解VLC之代码流程

深入理解VLC之代码流程

作者: zhanghuicuc | 来源:发表于2019-07-15 22:17 被阅读16次

    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

    相关文章

      网友评论

        本文标题:深入理解VLC之代码流程

        本文链接:https://www.haomeiwen.com/subject/ftzokctx.html