至简播放器ffplay工作原理
下載,編譯及運行
參考博文 http://blog.csdn.net/ericbar/article/details/79382783 即可完成ffplay的基本測試。
如果要進行GDB調試,需要先編譯一個帶GDB信息的執行文件,編譯前修改配置選項,
保留GDB信息,以GDB方式運行ffplay,注意播放片源等參數是在運行命令 r 之后,
gdb ffplay r 1080p.avimain函數
C語言的代碼入口在main()函數里,下面是main函數內部主要函數介紹:
init_dynload
動態加載的初始化,這是Windows平臺的dll庫相關處理;
av_log_set_flags
設置打印的標記,AV_LOG_SKIP_REPEATED表示對于重復打印的語句,不重復輸出;
avdevice_register_all
注冊音視頻相關的設備,主要是和操作系統底層相關的圖像,聲音設備注冊;
avfilter_register_all
注冊音視頻處理相關的資源,filter在FFmpeg里主要是做各種變換的概念;
av_register_all
注冊復用/解復用相關的資源;
avformat_network_init
初始化網絡資源;
init_opts
初始化選項,通過av_dict_set設置字典,字典就是key-value對鍵值的集合,這在JAVA等高級語言里是很常見的類,比如map,由于FFmpeg是C語言實現的,所以設立了一個字典的集合;
signal(SIGINT , sigterm_handler);
signal(SIGTERM, sigterm_handler);
注冊了2個信號,分別對應程序終止(interrupt)信號和程序結束(terminate)信號,用于異常處理;
show_banner
顯示FFmpeg和ffplay程序相關的庫版本等信息,類似如下:
parse_options
解析ffplay程序啟動的所有命令行參數,并存儲到對應的變量(options有默認值),如獲取要播放的媒體文件名,通過注冊的回調函數opt_input_file,將最終的媒體文件名存儲在變量input_filename中;
display_disable
video_disable
表示是否關閉視頻顯示;
audio_disable
表示是否關閉音頻的播放
以上兩個參數決定了SDL初始化時的flag標記;
SDL_Init
SDL初始化,完成顯示和音頻的初始化;
av_lockmgr_register
注冊解碼和解復用兩部分的互斥量,這是一個平臺適配接口,滿足互斥量的實現;
av_init_packet
初始化flush_pkt結構體,flush_pkt作為連續包隊列中的標記,用于判斷是否連續的包;
SDL_CreateWindow
SDL_CreateRenderer
創建視頻顯示的圖層,SDL_GetRendererInfo獲取相關畫布信息;
stream_open
打開媒體文件播放,所有核心的播放流程代碼都由此函數實現;
event_loop
ffplay播放程序的按鍵和事件處理,包括快進快退,暫停,退出等;
stream_open函數
VideoState
在函數入口,將全局信息存儲在此結構體中,類似于上下文context;
frame_queue_init
初始化了3個幀隊列,分別保存解碼后的視頻,音頻以及字幕;
packet_queue_init
初始化了3個包隊列,分別存儲解碼前的視頻包,音頻包以及字幕包;
init_clock
完成視頻,音頻以及外部時鐘的初始化;
startup_volume
起播時的音量值,需要映射到SDL的音量范圍;
av_sync_type
為音視頻同步的類型,一般以音頻時鐘為同步基準;
SDL_CreateThread
SDL創建線程的函數,核心工作在read_thread線程中完成;
read_thread函數
avformat_alloc_context
分配一個avformat的上下文,context中的interrupt_callback是注冊到底層的回調函數,主要用于中斷退出,比如退出網絡阻塞。退出的條件由decode_interrupt_cb函數實現。
avformat_open_input
打開播放文件,得到context相關信息;
avformat_find_stream_info
分析媒體流的相關信息;
start_time變量
起播時跳轉到的時間戳位置;
realtime變量
是否是實時播放的節目,比如rtsp,udp,rtp節目;
av_dump_format
顯示媒體格式相關的內容,主要由dump_metadata和dump_stream_format函數完成,類似如下格式打印;
av_find_best_stream
查找適合的流進行播放;
av_guess_sample_aspect_ratio
獲取待解碼視頻的寬高比,從而得到視頻窗口的初始大小;
stream_component_open
打開視頻,音頻和字幕流;
暫停狀態如果發生變化,執行暫停或者恢復播放;
if (is->seek_req)
如有seek請求,則執行seek,avformat_seek_file定位到目標位置,并清除解碼前的數據隊列,packet_queue_flush清除隊列,packet_queue_put存儲一個flush標記包flush_pkt;
stream_has_enough_packets
讀取隊列中是否還有足夠的數據包,如果已經足夠,本輪循環延遲10毫秒;
loop變量
表示循環播放的次數,播放到頭時,重新通過stream_seek函數定位到start_time的位置;
av_read_frame
讀取一個數據包(解碼前),這是數據讀取的核心函數,根據包的類型(視頻,音頻,字幕),通過packet_queue_put函數將讀取到的包,存儲到隊列里;
失敗時,關閉文件播放上下文,退出SDL事件管理,銷毀互斥量。
小結
上述過程,很好的解釋了怎樣打開一個媒體文件,然后怎樣進行數據包的讀取,那么解碼以及顯示的工作在哪里完成呢?其實是在stream_component_open函數中完成的,此函數根據不同的流類型,最后通過decoder_start各自啟動一個線程負責視頻,音頻及字幕的解碼。這部分我們放到后面討論。
主要函數流圖
下面是ffplay的主要函數調用關系圖
從上圖可以看出,整個程序有如下4個線程:
read_thread
讀數據包線程,主要是通過函數av_read_frame從媒體文件中讀取包;
audio_thread
video_thread
subtitle_thread
上述3個線程分別負責音頻,視頻,字幕的解碼;
event_loop
其實我們也可以理解為一個線程循環,這是主線程的while循環函數,除了顧名思義做事件相關的處理外,其實它還通過refresh_loop_wait_event函數中的video_refresh函數,完成視頻顯示和刷新。視頻顯示的時候,需要參考PTS時鐘信息進行同步。
而音頻的播放,由于音頻數據的讀取是由 SDL音頻管理器自動進行的,sdl_audio_callback函數由SDL主動回調并請求數據,我們只要在此回調函數里完成音頻數據的解碼,并注入SDL音頻buffer即可。
總結
通過前面的介紹,大概了解了ffplay播放的主線,介紹了播放器打開文件,讀取數據包,解碼和顯示的主要函數,但是由于每一塊功能的實現原理都很復雜,并有很多基本理論需要了解,所以我們將在接下來的文章中,逐一展開分析,最終達到完整的掌握FFmpeg基本原理和實現機制的目的。
總結
以上是生活随笔為你收集整理的至简播放器ffplay工作原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 起点小说网小说爬取
- 下一篇: 单片机知识点总结框图_89C51单片机的