日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

VLC架构及流程分析

發(fā)布時間:2023/11/27 生活经验 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 VLC架构及流程分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

0x00 前置信息

VLC是一個非常龐大的工程,我從它的架構(gòu)及流程入手進行分析,涉及到一些很細(xì)的概念先擱置一邊,日后詳細(xì)分析。

0x01 源碼結(jié)構(gòu)(Android Java相關(guān)的暫未分析)

# build-android-arm-linux-androideabi/:第三方庫。
# modules/:模塊代碼。
# modules/demux:      解復(fù)用模塊代碼。
# modules/codec:       解碼模塊相關(guān)代碼。
# modules/access:    訪問模塊相關(guān)代碼。
# 其他:未詳細(xì)分析。
# src/: VLC架構(gòu)核心代碼。
# src/config/:           從命令行和配置文件加載配置,提供功能模塊的讀取和寫入配置。
# src/control/:            提供動作控制功能,如播放/暫停,音量管理,全屏,日志等。
# src/extras/:             平臺特殊性相關(guān)代碼。
# src/modules/:          模塊管理。
# src/network/:           提供網(wǎng)絡(luò)接口。
# src/posix/:               多線程相關(guān)。
# src/osd/:                 顯示屏幕上的操作。
# src/interface/ :         提供代碼中可以調(diào)用的接口中,如按鍵后硬件作出反應(yīng)。
# src/playlist/:            管理播放功能,如停止,播放,下一首,隨機播放等。
# src/text/:                 字符集。
# src/input/:               輸入流相關(guān)代碼。
# src/video_output/ :   初始化視頻播放器,把從解碼器獲得的數(shù)據(jù)處理后播放。
# src/audio_output/ :   初始化音頻混合器,把從解碼器獲得的數(shù)據(jù)處理后播放。
# src/stream_output/:  輸出音頻流和視頻流到網(wǎng)絡(luò)。
# src/test/:                  libvlc測試模塊。
# src/misc/:                libvlc使用的其他部分功能,如線程系統(tǒng),消息隊列,CPU的檢測,對象查找系統(tǒng),或平臺的特定代碼。
# 其他:未詳細(xì)分析。

0x02 基礎(chǔ)概念

對于一個視頻的播放,播放器的執(zhí)行步驟大致如下:

  1. 讀取原始數(shù)據(jù)
  2. 解復(fù)用
  3. 解碼
  4. 顯示

VLC在包含以上概念的基礎(chǔ)上,又抽象出幾個其他概念,先列出VLC中抽象出來的重要概念:

  1. playlist: playlist表示播放列表,VLC在啟動后,即創(chuàng)建一個playlist thread,用戶輸入后,動態(tài)創(chuàng)建input。
  2. input: input表示輸入,當(dāng)用戶通過界面輸入一個文件或者流地址時,input thread 被動態(tài)創(chuàng)建,該線程的生命周期直到本次播放結(jié)束。
  3. access: access表示訪問,是VLC抽象的一個層,該層向下直接使用文件或網(wǎng)絡(luò)IO接口,向上為stream層服務(wù),提供IO接口。
  4. stream: stream表示流,是VLC抽象的一個層,該層向下直接使用access層提供的IO接口,向上為demux層服務(wù),提供IO接口。
  5. demux: demux表示解復(fù)用,是視頻技術(shù)中的概念,該層向下直接使用stream層提供的IO接口,數(shù)據(jù)出來后送es_out。
  6. es_out: es_out表示輸出,是VLC抽象的一個層,該層獲取demux后的數(shù)據(jù),送decode解碼。
  7. decode: decode表示解碼,是視頻技術(shù)中的概念,獲取es_out出來的數(shù)據(jù)(通過一個fifo交互),解碼后送output。
  8. output: output表示輸出,獲取從decode出來的數(shù)據(jù),送readerer。
  9. readerer: readerer表示顯示,獲取從output出來的數(shù)據(jù)(通過一個fifo交互),然后顯示。

下圖顯示了這些抽象的概念的關(guān)系,其中藍(lán)色表示VLC抽象的概念。

0x04 架構(gòu)綜述

VLC的整體框架是設(shè)計成一套module的管理機制,將功能分類并抽象成modules。

VLC main: player的main。初始化libVLC 并加載用戶界面。
libVLCcore:libvlc的核心,抽象出了一個libvlc_instance_t 對象,提供modules的裝載/卸載機制。
modules: modules提供具體的功能,比如上面的access,demux,decode就是以一個模塊的形式存在。
External libraries:外部開源庫。

模塊的加載方式:
首先模塊先將自身注冊到VLC中,代碼片段如:

vlc_module_begin()
...
vlc_module_end()

然后在需要加載模塊的時候,調(diào)用module_need接口,去找到合適的模塊。找到合適的模塊后,會執(zhí)行注冊中設(shè)置的回調(diào)方法,諸如Open*名字的方法。
同樣自己可以實現(xiàn)模塊,只需要按照VLC模塊的標(biāo)準(zhǔn)即可。VLC中很多模塊就是通過外部的開源庫實現(xiàn)的。

vlc中模塊大致分類:

0x05 流程分析

首先,給出流程圖,參照該圖,再繼續(xù)下面的流程分析,綠色線表示打開VLC后的執(zhí)行操作;黑色線表示用戶輸入一個視頻后的執(zhí)行操作;藍(lán)色線從紅色圈開始,表示開始播放輸入流后的數(shù)據(jù)流向。

(1) main函數(shù)(vlc/bin/vlc.c)
  1. 參數(shù)信號處理相關(guān),不詳分析。
  2. 調(diào)用libvlc_new()初始化一個libvlc_instance_t實例。(libvlc_instance_t is opaque. It represents a libvlc instance)
    2.1 調(diào)用libvlc_InternalCreate創(chuàng)建一個libvlc_int_t。(This structure is a LibVLC instance, for use by libvlc core and plugins.)
    2.2 調(diào)用libvlc_InternalInit初始化libvlc_int_t實例。
    2.3 初始化libvlc_instance_t其他成員。
  3. 調(diào)用libvlc_set_exit_handler設(shè)置VLC退出時的回調(diào)函數(shù)。
  4. 調(diào)用libvlc_add_intf添加模塊。
    4.1 獲取playlist,如果為空,則調(diào)用playlist_Create創(chuàng)建一個playlist結(jié)構(gòu),并調(diào)用playlist_Activate創(chuàng)建新的playlist線程Thread(src/playlist/thread.c)。
    4.2 調(diào)用intf_Create創(chuàng)建一個默認(rèn)的interface。
    4.2.1 調(diào)用vlc_custom_create創(chuàng)建一個vlc object(intf_thread_t)。
    4.2.2 注冊一個添加interface的回調(diào)方法。
    4.2.3 調(diào)用module_need加載一個interface模塊。
  5. 調(diào)用libvlc_playlist_play,如果播放列表不為空,并且被設(shè)置為自動播放,則播放播放列表內(nèi)容。
  6. 信號處理相關(guān),不詳分析。
(2) 創(chuàng)建一個輸入
  1. 初始化成功后,程序運行在playlist的線程Thread(src/playlist/thread.c)中,循環(huán)接收界面輸入的請求。
  2. 當(dāng)輸入一個新的文件或者流地址,在PlaylistVAControl獲得信號,并發(fā)送該信號。
  3. Thread接收到播放請求后,在LoopRequest中調(diào)用PlayItem方法。
    3.1 調(diào)用input_Create創(chuàng)建一個input結(jié)構(gòu),并初始化各種成員,其中包括調(diào)用input_EsOutNew創(chuàng)建p_es_out_display(es_out)。
    3.2 調(diào)用input_Start創(chuàng)建一個input線程Run(src/input/input.c)。
(3) 初始化輸入

調(diào)用Run(src/input/input.c)中的Init方法,開始初始化。

  1. 調(diào)用input_EsOutTimeshiftNew新建一個50M的Timeshift(暫停緩存),包括創(chuàng)建并初始化p_es_out(es_out),與后續(xù)步驟9相關(guān)。
  2. 設(shè)置input的狀態(tài)為OPENING_S。
  3. 調(diào)用InputSourceInit。
    3.1 調(diào)用input_SplitMRL分解輸入uri。
    3.2 以stream形參為NULL調(diào)用demux_New加載"access_demux"模塊。
    3.3 如果沒有合適的"access_demux"模塊,則調(diào)用access_New創(chuàng)建一個實際的access。
    3.3.1 調(diào)用vlc_custom_create創(chuàng)建access_t結(jié)構(gòu)體。
    3.3.2 調(diào)用module_need加載合適的access模塊。
    3.3.3 調(diào)用access模塊的Open*方法,以avio模塊為例。
    3.3.3.1 調(diào)用vlc_init_avformat初始化VLC即avformat環(huán)境。
    3.3.3.2 調(diào)用avio_open2打開該uri。
    3.3.3.3 設(shè)置access的IO方法指針。
    3.4 調(diào)用stream_AccessNew創(chuàng)建一個stream。
    3.4.1 根據(jù)模式(stream/block)設(shè)置steam層的IO方法指針。stream層的IO方法實際指向access層對應(yīng)的IO方法指針。
    3.4.2 為stream層的緩沖申請并初始化內(nèi)存。
    3.4.3 調(diào)用AStreamPrebufferStream執(zhí)行一次讀操作。
    3.5 調(diào)用stream_FilterChainNew,Add stream filters(源碼描述)。
    3.6 調(diào)用demux_New創(chuàng)建一個demux。
    3.6.1 調(diào)用vlc_custom_create創(chuàng)建demux_t結(jié)構(gòu)體。
    3.6.2 調(diào)用module_need加載合適的demux模塊。
    3.6.3 調(diào)用demux模塊的Open*方法,以avformat/demux模塊為例。
    3.6.3.1 調(diào)用stream_Peek從stream層獲取數(shù)據(jù),用于分析輸入的文件格式。
    3.6.3.2 調(diào)用av_probe_input_format分析輸入的文件格式。
    3.6.3.3 設(shè)置demux_sys_t結(jié)構(gòu)體部分變量的值。
    3.6.3.4 調(diào)用avformat_alloc_context分配AVFormatContext結(jié)構(gòu)體。
    3.6.3.5 調(diào)用avio_alloc_context設(shè)置AVFormatContext結(jié)構(gòu)體的AVIOContext類型成員pb,并設(shè)置read和seek方法指針。
    3.6.3.6 調(diào)用avformat_open_input打開一個輸入,這里的input與VLC中的input不是一個概念,關(guān)于avformat_open_input的分析詳見我的另一篇文章《avformat_open_input詳細(xì)分析》鏈接地址。
    3.6.3.7 調(diào)用avformat_find_stream_info分析流信息,該方法通過讀取數(shù)據(jù)初始化流以及流解碼信息。
    3.6.3.8 根據(jù)分析的流信息,設(shè)置fmt變量,并調(diào)用es_out_Add。
    3.6.3.9 實際調(diào)用EsOutAdd(src/input/es_out.c),添加一個es_out,有幾個流就做幾次es_out_Add操作,比如該輸入中有一個視頻流和一個音頻流,則作兩次es_out_Add操作。
    3.6.3.10 nb_chapters相關(guān)未詳細(xì)分析。
    3.7 設(shè)置record相關(guān)。
    3.8 調(diào)用demux_Control設(shè)置demux pts delay。
    3.9 調(diào)用demux_Control設(shè)置fps。
  4. 調(diào)用demux_Control獲取輸入的長度。
  5. 調(diào)用StartTitle顯示標(biāo)題。
  6. 調(diào)用LoadSubtitles加載字幕。
  7. 調(diào)用LoadSlaves,含義不詳。
  8. 調(diào)用InitPrograms,設(shè)置es_out和decoder相關(guān)。
    8.1 調(diào)用UpdatePtsDelay計算正確的pts_delay值。
    8.2 sout相關(guān)可選,暫不分析。
    8.3 調(diào)用es_out_SetMode,設(shè)置es_out的mode為ES_OUT_MODE_AUTO。
    8.4 以DEMUX_SET_GROUP指令調(diào)用demux_Control,DEMUX_SET_GROUP/SET_ES only a hint for demuxer (mainly DVB) to allow not reading everything。
  9. 續(xù)8.3,實際調(diào)用EsOutControlLocked進入case ES_OUT_SET_MODE分支。
    9.1 設(shè)置es_out_sys_t 的b_active和i_mode。
    9.2 調(diào)用EsOutSelect方法,根據(jù)指定模塊選擇一個es_out。
    9.3 在EsOutSelect方法中進入ES_OUT_MODE_AUTO分支,進一步調(diào)用EsSelect方法,再進一步調(diào)用EsCreateDecoder方法創(chuàng)建decoder。
    9.3.1 調(diào)用input_DecoderNew創(chuàng)建一個新的decoder。
    9.3.2 如果需要緩存,調(diào)用input_DecoderStartWait發(fā)送信號,開始線程等待。
    9.3.3 調(diào)用EsOutDecoderChangeDelay設(shè)置decode delay。
  10. 續(xù)9.3.1進入decoder_New方法。
    10.1 調(diào)用CreateDecoder創(chuàng)建decoder配置結(jié)構(gòu)體。
    10.1.1 調(diào)用vlc_custom_create創(chuàng)建一個vlc object(decoder_t)。
    10.1.2 新建decode fifo。
    10.1.3 調(diào)用module_need加載適配的解碼模塊。
    10.1.3.1 調(diào)用decode模塊的OpenDecoder方法,以codec/avcodec模塊為例。
    10.1.3.2 調(diào)用GetFfmpegCodec方法 determine codec type(源碼描述)。
    10.1.3.3 調(diào)用vlc_init_avcodec方法初始化解碼環(huán)境。
    10.1.3.4 調(diào)用avcodec_find_decoder設(shè)置AVCodec。
    10.1.3.5 調(diào)用avcodec_alloc_context3分配一個AVCodecContext。
    10.1.3.6 調(diào)用Init*Dec系列初始化解碼環(huán)境。
    10.1.4 初始化decoder_t結(jié)構(gòu)體其他成員。
    10.2 調(diào)用vlc_clone創(chuàng)建解碼線程DecoderThread。
  11. 續(xù)10.1.3.5,以InitVideoDec為例。
    12.1 為decoder_sys_t結(jié)構(gòu)分配內(nèi)存。
    12.2 設(shè)置相關(guān)回調(diào)方法。
    12.3 設(shè)置解碼線程類型。
    12.4 調(diào)用ffmpeg_InitCodec初始化extradata相關(guān)數(shù)據(jù)。
    12.5 調(diào)用OpenVideoCodec方法,設(shè)置解碼的長寬及采用率,進一步調(diào)用avcodec_open2打開codec。
  12. 根據(jù)需要,設(shè)置線程優(yōu)先級。
  13. 設(shè)置meta相關(guān)。
  14. 初始化完成,設(shè)置該input的狀態(tài)為PLAYING_S。
(4) 播放輸入

MainLoop(src/input/input.c)

  1. 調(diào)用MainLoopDemux訪問demuxer去demux數(shù)據(jù)。
  2. 進一步調(diào)用在加載demux模塊時設(shè)置的demux方法,同樣以avformat/demux模塊為例,實際調(diào)用Demux方法(module/demux/avformat/demux.c)。
    2.1 調(diào)用av_read_frame讀取一幀數(shù)據(jù)。
    2.2 判斷讀取無誤時,則為block_t結(jié)構(gòu)分配內(nèi)存,并將這一幀從AVPacket中拷貝至block_t結(jié)構(gòu)中。
    2.3 如果該幀是I幀,則設(shè)置I幀標(biāo)致位。
    2.4 時間戳處理相關(guān),未深入分析。
    2.5 根據(jù)需要調(diào)用es_out_Control設(shè)置PCR,未深入分析。
    2.6 調(diào)用es_out_Send將這一幀數(shù)據(jù)發(fā)送給es_out。
    2.7 調(diào)用av_free_packet釋放這一幀數(shù)據(jù)。
  3. 調(diào)用es_out_Send后,實際調(diào)用EsOutSend(src/input/es_out.c)方法。
    3.1 調(diào)用stats_Update更新相關(guān)狀態(tài),具體未詳分析。
    3.2 設(shè)置預(yù)讀相關(guān),如果需要預(yù)讀,并且到的數(shù)據(jù)的pts小于預(yù)讀需要的時間,則設(shè)置BLOCK_FLAG_PREROLL標(biāo)志位。
    3.3 檢查sout mode,具體有sync 和async mode,異同未詳細(xì)分析。
    3.4 如果設(shè)置record,將數(shù)據(jù)dup后送decoder。
    3.5 調(diào)用input_DecoderDecode將block_t的數(shù)據(jù)送至decode fifo中。
    3.5.1 判斷控制速度線程等待相關(guān)信息,具體未詳細(xì)分析。
    3.5.2 如果decode fifo超過最大長度,則清空重置decode fifo。
    3.5.3 調(diào)用block_FifoPut將該block_t的數(shù)據(jù)壓入decode fifo,并通知讀取線程。
    3.6 格式變化判斷處理相關(guān),未詳細(xì)分析。
    3.7 字幕處理相關(guān),未詳細(xì)分析。
  4. 續(xù)3.5進入decode read thread,即DecoderThread(src/input/decoder.c)。
    4.1 調(diào)用block_FifoGet方法,從decode fifo中獲取數(shù)據(jù)。
    4.2 基于某些條件,發(fā)送停止等待消息給其他線程,未詳細(xì)分析。
    4.3 調(diào)用DecoderProcess方法開始decode a block。
    4.4 判斷輸入流的格式,調(diào)用不同的方法,這里以視頻流為例,調(diào)用DecoderProcessVideo方法。
    4.5 packetizer相關(guān)為深入分析,在DecoderProcessVideo方法中進一步調(diào)用DecoderDecodeVideo方法。
  5. 續(xù)4.5調(diào)用pf_decode_video,這里以avcodec模塊的decoder為例,即DecodeVideo(modules/codec/avcodec/video.c)方法,在該方法中,開始真正的解碼。
    5.1 如果在Demux中獲取的流信息中包含新的extradata,并且原來的extradata數(shù)據(jù)為空,則調(diào)用ffmpeg_InitCodec初始化 codec,如果b_delayed_open為true,則調(diào)用OpenVideoCodec重新打開codec。
    5.2 調(diào)用av_init_packet初始化解碼數(shù)據(jù)包。
    5.3 調(diào)用avcodec_decode_video2解碼數(shù)據(jù)。
    5.4 調(diào)用av_free_packet釋放內(nèi)存。
    5.5 計算pts值,返回解碼后的數(shù)據(jù)。
    5.6 如果opaque為空,則調(diào)用ffmpeg_NewPictBuf方法創(chuàng)建一個新的picture buffer。具體調(diào)用回調(diào)指針pf_vout_buffer_new指向的vout_new_buffer,進一步調(diào)用 input_resource_RequestVout最終調(diào)用VoutCreate。
    5.6.1 調(diào)用vlc_custom_create創(chuàng)建一個vlc object(vout_thread_t)。
    5.6.2 調(diào)用spu_Create初始化sub picture unit。
    5.6.3 調(diào)用vlc_clone創(chuàng)建一個output線程Thread(src/video_output/video_output.c)。
    5.6.4 output線程循環(huán)調(diào)用vout_control_Pop,首次進入ThreadControl方法中,執(zhí)行ThreadStart方向,創(chuàng)建picture fifo(p->decoder_fifo)。
  6. pf_decode_video返回后,解碼后的數(shù)據(jù)保存在p_pic中,進一步調(diào)用DecoderPlayVideo方法,在該方法中調(diào)用vout_PutPicture將解碼后的數(shù)據(jù)壓入picture fifo中。
  7. 當(dāng)picture fifo中有數(shù)據(jù)后,vout線程調(diào)用ThreadDisplayPicture中的ThreadDisplayPreparePicture方法。
    7.1 調(diào)用picture_fifo_Pop從picture fifo中獲取解碼后的數(shù)據(jù)。
    7.2 如果延遲太大,并且設(shè)置延遲丟幀,則丟掉該幀數(shù)據(jù)。
  8. 調(diào)用ThreadDisplayRenderPicture顯示圖像。
0x06 總結(jié)

對VLC的流程分析,主要通過跟蹤數(shù)據(jù)流向的方式展開。對于最后顯示部分的分析還不足,另外很多細(xì)節(jié)尚未深入。

參考:

    1. http://blog.csdn.net/tx3344/article/details/8517062
    2. http://my.oschina.net/xiaot99/blog/197555

?

來源:http://blog.csdn.net/hpb21/article/details/43271095

轉(zhuǎn)載于:https://www.cnblogs.com/sunminmin/p/4502602.html

總結(jié)

以上是生活随笔為你收集整理的VLC架构及流程分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。