FFmpeg 是如何實現多態的?

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

前言

眾所周知,FFmpeg 在解碼的時候,無論輸入文件是 MP4 文件還是 FLV 文件,或者其它文件格式,都能正確解封裝、解碼,而代碼不需要針對不同的格式做出任何改變,這是面向對象中很常見的多態特性,但 FFmpeg 是用 C 語言編寫的,那么它是如何使用 C 語言實現了多態特性的呢?

要解決這個問題,首先需要從函數 av_register_all 說起。

av_register_all

av_register_all 是幾乎所有 FFmpeg 程序中第一個被調用的函數,用于注冊在編譯 FFmpeg 時設置了 --enable 選項的封裝器、解封裝器、編碼HX器、解碼HX器等。源碼如下:

#define REGISTER_MUXER(X, x)                                            \{                                                                   \extern AVOutputFormat ff_##x##_muxer;                           \if (CONFIG_##X##_MUXER)                                         \av_register_output_format(&ff_##x##_muxer);                 \}#define REGISTER_DEMUXER(X, x)                                          \{                                                                   \extern AVInputFormat ff_##x##_demuxer;                          \if (CONFIG_##X##_DEMUXER)                                       \av_register_input_format(&ff_##x##_demuxer);                \}#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)static void register_all(void)
{// 注冊編解碼HX器avcodec_register_all();// 注冊封裝器、解封裝器/* (de)muxers */REGISTER_MUXER   (A64,              a64);REGISTER_DEMUXER (AA,               aa);REGISTER_DEMUXER (AAC,              aac);REGISTER_MUXDEMUX(AC3,              ac3);REGISTER_MUXDEMUX(FLV,              flv);REGISTER_MUXDEMUX(GIF,              gif);.../* image demuxers */REGISTER_DEMUXER (IMAGE_BMP_PIPE,        image_bmp_pipe);REGISTER_DEMUXER (IMAGE_JPEG_PIPE,       image_jpeg_pipe);REGISTER_DEMUXER (IMAGE_SVG_PIPE,        image_svg_pipe);REGISTER_DEMUXER (IMAGE_WEBP_PIPE,       image_webp_pipe);REGISTER_DEMUXER (IMAGE_PNG_PIPE,        image_png_pipe);.../* external libraries */REGISTER_MUXER   (CHROMAPRINT,      chromaprint);...
}void av_register_all(void)
{static AVOnce control = AV_ONCE_INIT;ff_thread_once(&control, register_all);
}

define 里的 ## 用于拼接兩個字符串,比如 REGISTER_DEMUXER(AAC, aac) ,它等效于:

extern AVInputFormat ff_aac_demuxer;
if(CONFIG_AAC_DEMUXER) av_register_input_format(&ff_aac_demuxer);

可以看出,編譯 ffmpeg 時類似于 "--enable-muxer=xxx" 這樣的選項在此時發揮了作用,它決定是否注冊某個格式對應的(解)封裝器,以便之后處理該格式的時候找到這個(解)封裝器。

av_register_input_format

av_register_input_format、av_register_output_format 源碼如下:

/** head of registered input format linked list */
static AVInputFormat *first_iformat = NULL;
/** head of registered output format linked list */
static AVOutputFormat *first_oformat = NULL;static AVInputFormat **last_iformat = &first_iformat;
static AVOutputFormat **last_oformat = &first_oformat;void av_register_input_format(AVInputFormat *format)
{AVInputFormat **p = last_iformat;// Note, format could be added after the first 2 checks but that implies that *p is no longer NULLwhile(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))p = &(*p)->next;if (!format->next)last_iformat = &format->next;
}void av_register_output_format(AVOutputFormat *format)
{AVOutputFormat **p = last_oformat;// Note, format could be added after the first 2 checks but that implies that *p is no longer NULLwhile(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))p = &(*p)->next;if (!format->next)last_oformat = &format->next;
}

從代碼中可以看到,這兩個注冊方法會把指定的 AVInputFormat、AVOutputFormat 加到鏈表的尾部。

AVInputFormat

接著看 AVInputFormat 的定義:

typedef struct AVInputFormat {/*** A comma separated list of short names for the format. New names* may be appended with a minor bump.*/const char *name;/*** Descriptive name for the format, meant to be more human-readable* than name. You should use the NULL_IF_CONFIG_SMALL() macro* to define it.*/const char *long_name;/*** Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,* AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,* AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.*/int flags;/*** If extensions are defined, then no probe is done. You should* usually not use extension format guessing because it is not* reliable enough*/const char *extensions;.../*** Tell if a given file has a chance of being parsed as this format.* The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes* big so you do not have to check for that unless you need more.*/int (*read_probe)(AVProbeData *);/*** Read the format header and initialize the AVFormatContext* structure. Return 0 if OK. 'avformat_new_stream' should be* called to create new streams.*/int (*read_header)(struct AVFormatContext *);/*** Read one packet and put it in 'pkt'. pts and flags are also* set. 'avformat_new_stream' can be called only if the flag* AVFMTCTX_NOHEADER is used and only in the calling thread (not in a* background thread).* @return 0 on success, < 0 on error.*         When returning an error, pkt must not have been allocated*         or must be freed before returning*/int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);...
} AVInputFormat;

可以看到,這個結構體除了 name 等變量外,還具備 read_probe、read_header 等函數指針。

以前面提到的 ff_aac_demuxer 為例,這里看一下它的實現:

AVInputFormat ff_aac_demuxer = {// 名稱.name         = "aac",.long_name    = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"),// 把函數指針指向能夠處理 aac 格式的函數實現.read_probe   = adts_aac_probe,.read_header  = adts_aac_read_header,.read_packet  = adts_aac_read_packet,.flags        = AVFMT_GENERIC_INDEX,.extensions   = "aac",.mime_type    = "audio/aac,audio/aacp,audio/x-aac",.raw_codec_id = AV_CODEC_ID_AAC,
};

總結

根據以上代碼的分析,此時我們就能得出問題的答案了:

FFmpeg 之所以能夠作為一個平臺,無論是封裝、解封裝,還是編碼、解碼,在處理對應格式的文件/數據時,都能找到對應的庫來實現,而不需要修改代碼,主要就是通過結構體 + 函數指針實現的。具體實現方式是:首先設計一個結構體,然后創建該結構體的多個對象,每個對象都有著自己的成員屬性及函數實現。這樣就使得 FFmpeg 具備了類似于面向對象編程中的多態的效果。

PS:avcodec_register_all 也是一樣的,有興趣的可以看看 AVCodec 的聲明以及 ff_libx264_encoder 等編解碼HX器的實現。



作者:zouzhiheng
鏈接:https://www.jianshu.com/p/c12e6888de10
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

轉載于:https://my.oschina.net/u/4000302/blog/3021134

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388075.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388075.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388075.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于easyui開發Web版Activiti流程定制器詳解(五)——Draw2d詳解(一)

&#xfeff;&#xfeff;背景&#xff1a; 小弟工作已有十年有余&#xff0c;期間接觸了不少工作流產品&#xff0c;個人比較喜歡的還是JBPM&#xff0c;因為出自名門Jboss所以備受推崇&#xff0c;但是現在JBPM版本已經與自己當年使用的版本&#xff08;3.X&#xff09;大相徑…

Asp.net MVC模型數據驗證擴展ValidationAttribute

在Asp.Mvc項目中有自帶的一套完整的數據驗證功能&#xff0c;客戶端可以用HtmlHelper工具類&#xff0c;服務端可以用ModelState進行驗證。而他們都需要System.ComponentModel.DataAnnotations類庫中的特性功能&#xff0c;通過在屬性上方添加特性就可以達到驗證前后端驗證數據…

seaborn 子圖_Seaborn FacetGrid:進一步完善子圖

seaborn 子圖Data visualizations are essential in data analysis. The famous saying “one picture is worth a thousand words” holds true in the scope of data visualizations as well. In this post, I will explain a well-structured, very informative collection …

基于easyui開發Web版Activiti流程定制器詳解(六)——Draw2d的擴展(一)

&#xfeff;&#xfeff;題外話&#xff1a; 最近在忙公司的云項目空閑時間不是很多&#xff0c;所以很久沒來更新&#xff0c;今天補上一篇&#xff01; 回顧&#xff1a; 前幾篇介紹了一下設計器的界面和Draw2d基礎知識&#xff0c;這篇講解一下本設計器如何擴展Draw2d。 進…

深度學習網絡總結

1.Siamese network Siamese [sai? mi:z] 孿生 左圖的孿生網絡是指兩個網絡通過共享權值實現對輸入的輸出&#xff0c;右圖的偽孿生網絡則不共享權值(pseudo-siamese network)。 孿生神經網絡是用來衡量兩個輸入的相似度&#xff0c;可以用來人臉驗證、語義相似度分析、QA匹配…

異常檢測時間序列_時間序列的無監督異常檢測

異常檢測時間序列To understand the normal behaviour of any flow on time axis and detect anomaly situations is one of the prominent fields in data driven studies. These studies are mostly conducted in unsupervised manner, since labelling the data in real lif…

python設計模式(七):組合模式

組合&#xff0c;將對象組合成樹狀結構&#xff0c;來表示業務邏輯上的[部分-整體]層次&#xff0c;這種組合使單個對象和組合對象的使用方法一樣。 如描述一家公司的層次結構&#xff0c;那么我們用辦公室來表示節點&#xff0c;則總經理辦公司是根節點&#xff0c;下面分別由…

Django框架是什麼?

Django在新一代的Web框架中非常出色,為什么這么說呢&#xff1f;為回答該問題,讓我們考慮一下不使用框架設計Python網頁應用程序的情形.貫穿整本書,我們多次展示不使用框架實現網站基本功能的方法,讓讀者認識到框架開發的方便,&#xff08;不使用框架,更多情況是沒有合適的框架…

存款驚人_如何使您的圖快速美麗驚人

存款驚人So, you just finished retrieving, processing, and analyzing your data. You grab your data and you decide to graph it so you can show others your findings. You click ‘graph’ and……因此&#xff0c;您剛剛完成了數據的檢索&#xff0c;處理和分析。 您獲…

pytest自動化6:pytest.mark.parametrize裝飾器--測試用例參數化

前言&#xff1a;pytest.mark.parametrize裝飾器可以實現測試用例參數化。 parametrizing 1. 下面是一個簡單是實例&#xff0c;檢查一定的輸入和期望輸出測試功能的典型例子 2. 標記單個測試實例為失敗&#xff0c;例如使用內置的mark.xfail&#xff0c;則跳過該用例不執行直…

基于easyui開發Web版Activiti流程定制器詳解(六)——Draw2d詳解(二)

&#xfeff;&#xfeff;上一篇我們介紹了Draw2d整體結構&#xff0c;展示了組件類關系圖&#xff0c;其中比較重要的類有Node、Canvas、Command、Port、Connection等&#xff0c;這篇將進一步介紹Draw2d如何使用以及如何擴展。 進入主題&#xff1a; 詳細介紹一下Draw2d中幾個…

c#中ReadLine,Read,ReadKey的區別

Console.Read()、Console.ReadLine() 相同點&#xff1a; 1.兩者都是用于輸入的函數。 不同點&#xff1a; 1. Read只能讀取一個字符&#xff0c;ReadLine可以讀取一個字符串 如 Read讀取A和AASDGU的返回值都是一樣的 都為A的ASCII值&#xff0c;對于后續的ASDGU不理會。 而Rea…

Ubuntu16.04 開啟多個終端,一個終端多個小窗口

Ubuntu16.04 開啟多個終端&#xff0c;一個終端多個小窗口 CtrlShift T,一個終端開啟多個小終端 CtrlAlt T 開啟多個終端 posted on 2019-03-15 11:26 _孤城 閱讀(...) 評論(...) 編輯 收藏 轉載于:https://www.cnblogs.com/liuweijie/p/10535904.html

敏捷 橄欖球運動_澳大利亞橄欖球迷的研究聲稱南非裁判的偏見被證明是錯誤的

敏捷 橄欖球運動In February 2020, an Australian rugby fan produced a study, claiming to show how South African rugby referees were exhibiting favorable bias towards South African home teams. The study did not consider how other countries’ referees treat So…

activiti 部署流程圖后中文亂碼

Activiti工作流引擎使用 1.簡單介工作流引擎與Activiti 對于工作流引擎的解釋請參考百度百科&#xff1a;工作流引擎 1.1 我與工作流引擎 在第一家公司工作的時候主要任務就是開發OA系統&#xff0c;當然基本都是有工作流的支持&#xff0c;不過當時使用的工作流引擎是公司一些…

Luogu 4755 Beautiful Pair

分治 主席樹。 設$solve(l, r)$表示當前處理到$[l, r]$區間的情況&#xff0c;我們可以找到$[l, r]$中最大的一個數的位置$mid$&#xff0c;然后掃一半區間計算一下這個區間的答案。 注意&#xff0c;這時候左半邊是$[l, mid]$&#xff0c;而右區間是$[mid, r]$&#xff0c;我…

網絡傳播動力學_通過簡單的規則傳播動力

網絡傳播動力學When a single drop of paint is dropped on a surface the amount of space that the drop will cover depends both on time and space. A short amount of time will no be enough for the drop to cover a greater area, and a small surface will bound the…

【左偏樹】【P3261】 [JLOI2015]城池攻占

Description 小銘銘最近獲得了一副新的桌游&#xff0c;游戲中需要用 m 個騎士攻占 n 個城池。這 n 個城池用 1 到 n 的整數表示。除 1 號城池外&#xff0c;城池 i 會受到另一座城池 fi 的管轄&#xff0c;其中 fi <i。也就是說&#xff0c;所有城池構成了一棵有根樹。這 m…

【原創】數據庫中為什么不推薦使用外鍵約束

引言 其實這個話題是老生常談&#xff0c;很多人在工作中確實也不會使用外鍵。包括在阿里的JAVA規范中也有下面這一條 【強制】不得使用外鍵與級聯&#xff0c;一切外鍵概念必須在應用層解決。 但是呢&#xff0c;詢問他們原因&#xff0c;大多是這么回答的 每次做DELETE 或者…

初識Activiti

http://wenku.baidu.com/view/bb7364ad4693daef5ff73d32.html 1. 初識Activiti 1.1. 工作流與工作流引擎 工作流&#xff08;workflow&#xff09;就是工作流程的計算模型&#xff0c;即將工作流程中的工作如何前后組織在一起的邏輯和規則在計算機中以恰當的模型進行表示并對其…