音訊流的 濾鏡是透過
configure_audio_filters()
函式來建立的,因為
ffplay
為了程式碼的通用性,即便命令列引數不使用濾鏡,
AVFrame
也會過一遍 空濾鏡做下樣子。
configure_audio_filters()
函式的流程圖如下:
configure_audio_filters()
函式的定義如下:
static int configure_audio_filters(VideoState *is, const char *afilters, int force_output_format){。。。。}
下面講解一下這個函式的引數。
VideoState *is
,是 ffplay 播放器的全域性管理器。
char *afilters
,是濾鏡字串,例如 下面的命令:
ffplay -af “atempo=2。0” -i juren-5s。mp4
“atempo=2。0”
這個字串就會賦值給
afilters
。
int force_output_format
,代表是否強制把
buffersink
出口濾鏡的音訊幀取樣等資訊 設定為 跟
is->audio_tgt
一樣。
之前說過
is->audio_tgt
是音響硬體裝置開啟的資訊。
is->audio_tgt
是
最終
要傳遞給 SDL 的音訊格式。所有的取樣率,聲道數等等最後都要轉成
is->audio_tgt
。
下面來分析一下
configure_audio_filters()
函數里面的重點程式碼,如下:
這個函式一開始就定義了 一些只有 2 個元素的陣列,這其實是 ffmpeg 專案傳遞引數的方式,傳遞一個數組進去函式,主要有兩種方式。
1,
傳遞陣列的大小。就是有多少個元素。
2,
傳遞陣列的結尾,只要讀到結尾元素 (-1),就算結束了。
ffmpeg 大部分函式採用的是第二種方式。
然後他會調
avfilter_graph_free()
釋放
濾鏡容器
(FilterGraph),有些同學可能會疑惑,
is->agraph
一開始不是
NULL
嗎? 為什麼需要釋放?
is->agraph
一開始確實是 NULL,但是
configure_audio_filters()
這個函式可能會呼叫
第二次
,第二次的時候
is->agraph
就不是 NULL了。
configure_audio_filters()
第一次呼叫
是在
stream_component_open()
裡面,如下:
第二次呼叫
是在
audio_thread()
裡面,如下:
【騰訊文件】FFmpegWebRTCRTMPRTSPHLSRTP播放器-音影片流媒體高階開發-資料領取FFmpegWebRTCRTMPRTSPHLSRTP鎾斁鍣�-闊寵棰戞祦濯掍綋楂樼駭寮€鍙�-璧勬枡棰嗗彇
第二次呼叫
configure_audio_filters()
是因為實際解碼出來的 AVFrame 的取樣率,聲道等,跟容器裡面記錄的不一致,之前
is->audio_filter_src
是直接從容器,封裝層取的資料。封裝層記錄的音訊取樣率等,
可能是錯的
,需要以實際解碼出來的
AVFrame
為準。
而且,注意,第二次的時候,
force_output_format
引數會置為 1,這樣會強制
buffersink
出口濾鏡的取樣資訊等 設定為
is->audio_tgt
一樣。
其實
configure_audio_filters()
必然會調第二次的
,因為
is->auddec。pkt_serial != last_serial
這個條件肯定是真。
接著就是設定 濾鏡使用的執行緒數量,0 為自動選擇執行緒數量,如下:
is->agraph->nb_threads = filter_nbthreads;
第三個重點是,設定重取樣選項(aresample_swr_opts),如下:
什麼樣的命令列引數才是重取樣選項的,在
libswresample/options。c
裡面可以找到,如下:
舉個例子,如下:
ffpaly -ich 1 -i juren-5s。mp4
ich 1
就會被解析複製進去
ffplay。c
裡面的
swr_opts
變數裡面。
這裡還用到了一個新的函式
av_opt_set()
,這個函式其實不只可以設定
濾鏡的屬性欄位
,還可以設定大多數
資料結構
的屬性欄位,例如解碼器,封裝器 等等,只要內部有
AVClass
的資料結構,都能用
av_opt_set()
來設定屬性,詳情請閱讀《opt。h檔案函式分析》
接下來的重點是設定入口跟出口濾鏡,如下:
出口濾鏡
還設定了
sample_fmts
為
AV_SAMPLE_FMT_S16
,這是
ffpaly
播放器自己的特性,就是說無論MP4檔案裡面的音訊格式是怎樣的,他都會轉成
AV_SAMPLE_FMT_S16
格式丟給 SDL 播放,而且它在用 SDL_OpenAudioDevice 開啟音訊裝置的時候,就是用的 S16 格式,
這是寫死的
。
force_output_format
的邏輯主要是 強制
buffersink
出口濾鏡的取樣資訊等 設定為跟
is->audio_tgt
一樣。
audio_tgt
是 SDL 接受音訊幀的最終格式。
第一次呼叫
configure_audio_filters()
函式,
force_output_format
為 0,不會跑進去這塊邏輯。
最後就是調
configure_filtergraph()
函式來
連結入口跟出口濾鏡
,同時建立濾鏡容器(FilterGraph),如下:
上圖最重要的是,入口濾鏡 跟 出口濾鏡 被賦值到全域性管理器
is
了。後面只要把解碼器輸出的 AVFrame 往入口濾鏡丟,然後往出口濾鏡讀就行了。