1. 程式人生 > >深入理解VLC之程式碼流程

深入理解VLC之程式碼流程

本文以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. 播放輸出

在這裡插入圖片描述

在這裡插入圖片描述

文章幫到你了?可以掃描如下二維碼進行打賞,打賞多少您隨意~
在這裡插入圖片描述