日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

FFmpeg?Principle學習new_video_stream添加視頻輸出流_Android

作者:Loken1 ? 更新時間: 2022-12-03 編程語言

new_video_stream()?函數流程

new_video_stream()?函數的流程相對來說比較簡單,主要的邏輯如下:

1, 調?new_output_stream()?函數來創建?OutputStream?輸出流,以及?AVCodecContext?編碼器上下文。

new_output_stream()?是一個公共函數,創建 音頻流,數據流,字幕流都用了它。

new_output_stream()?會把命令行的一些公共參數賦值給?OutputStream?跟?AVCodecContext

這些公共參數是指音頻,視頻,字幕都可能會有的參數。因為?new_output_stream()?是一個公共函數。

2, 調?MATCH_PER_STREAM_OPT()?宏函數,把?OptionsContext?里面視頻相關的參數,賦值給 給?OutputStream?跟?AVCodecContext

流程圖

可以看到,實際上就兩步,new_video_stream()?肯定會創建視頻的輸出流,還有視頻的編碼器實例。

公共參數,就在?new_output_stream()?函數 里面賦值了。

視頻相關的參數,就在?new_video_stream()?函數再賦值。

new_video_stream()?跟?new_output_stream()?函數都調用了多次?MATCH_PER_STREAM_OPT()?宏函數來提取?OptionsContext?的內容,

MATCH_PER_STREAM_OPT()?其實是?MATCH_PER_TYPE_OPT()?的兄弟函數

#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\
{\
    int i;\
    for (i = 0; i < o->nb_ ## name; i++) {\
        char *spec = o->name[i].specifier;\
        if (!strcmp(spec, mediatype))\
            outvar = o->name[i].u.type;\
    }\
}
#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
{\
    int i, ret, matches = 0;\
    SpecifierOpt *so;\
    for (i = 0; i < o->nb_ ## name; i++) {\
        char *spec = o->name[i].specifier;\
        if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\
            outvar = o->name[i].u.type;\
            so = &o->name[i];\
            matches++;\
        } else if (ret < 0)\
            exit_program(1);\
    }\
    if (matches > 1)\
       WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\
}

這兩個函數,只有最后一個參數,第四個參數是不一樣的。

mediatype 通常是 a 或者 v,也就是根據 a 還是 v 字符來提取?OptionsContext?里面音頻或者視頻的選項。

st 是 AVStream,所以如果?AVStream?是音頻,就提取?OptionsContext?里面的音頻選項,如果是視頻就提取視頻。

這兩個函數的宏實現看起來有點復雜,但他們的區別就是這么一點區別。

至此,new_video_stream()?函數分析完畢。new_audio_stream()?跟?new_video_stream()?類似,里面都調了?new_output_stream()?。

new_audio_stream()?主要是提取OptionsContext?里面音頻選項,對?OutputStream?輸出流,以及?AVCodecContext?編碼器 進行賦值操作。

補充一點:雖然?new_video_stream()?里創建了 編碼器實例,但是還沒真正打開編碼器的。打開編碼器,需要等到解碼出第一幀 AVFrame。才會打開編碼器。

原因解析

因為 ffmpeg.exe 的邏輯,是只有在解碼出第一幀AVFrame的時候,才去用?avfilter_graph_config()?打開 FilterGragh ,這樣才能從出口濾鏡讀取到 輸出的寬高是多少。

ffmpeg.exe?比較謹慎,他可能不太相信容器層記錄的寬度,也有可能有些容器根本沒記錄寬高,所以他必須等到解碼出 AVFrame,才能確定輸入的寬高,確定了輸入的寬高,才能創建 buffer入口濾鏡,創建了入口濾鏡,才能打開?FilterGragh?。

TODO:這個邏輯非常重要,在本章結尾的時候再重復講一次。

濾鏡出口里面獲取寬高

最后是在?init_output_stream_encode()?里面,從濾鏡出口里面獲取的寬高,如下:

enc_ctx->width  = av_buffersink_get_w(ost->filter->filter);
enc_ctx->height = av_buffersink_get_h(ost->filter->filter);
enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio

最后,推薦一下 clion 的 Call Hierarchy 功能,可以看到函數的調用流程,如下:

大部分的 集成開發環境都有這個功能,你只需用 “工具名稱” + Call Hierarchy 關鍵詞,即可搜索到相關教程。

原文鏈接:https://juejin.cn/post/7159423852345294861

欄目分類
最近更新