FFmpeg5.0源碼閱讀——格式檢測

??摘要:在拿到一個新的格式后,FFmpeg總是能夠足夠正確的判斷格式的內容并進行相應的處理。本文在描述FFmpeg如何進行格式檢測來確認正在處理的媒體格式類型,并進行相應的處理。
??關鍵字:FFmpeg,format,probe

??在調用FFmpeg的APIavformat_open_input之后就能在對應的AVFormatContext看到具體的媒體信息。為了獲取媒體信息,首先需要打開流文件,然后讀取HEAD檢測媒體格式。

//打開文件
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)return ret;if (s->iformat)return 0;
//探測文件格式
return av_probe_input_buffer2(s->pb, &s->iformat, filename,s, 0, s->format_probesize);

1 打開文件

流協議
??為了打開文件首先需要確認輸入的流協議。由于FFMpeg支持很多種協議,比如文件、RTMP、HTTP等,為了正確的讀取數據首先需要確認輸入的流是哪一種協議。對于輸入文件,FFMpeg中確認具體是那種流協議,需要使用哪種protcol打開文件讀取數據。
??FFmpeg中很多場景對于支持格式的檢測都是對當前支持的格式列表的遍歷來選擇最匹配的那一個作為最終匹配的結果。協議選擇也是一樣。如果外界調用API時指定了打開的協議的類型則不會進行檢測,沒有的花會按照下面的調用鏈進行匹配。

init_input->io_open_default->ffio_open_whitelist->ffurl_open_whitelist->ffurl_alloc->url_find_protocol

??Protcol的核心代碼如下,通過遍歷內部維護的全局靜態url表格匹配每一個url的name,最終選擇能夠匹配到的URLProtocol作為最終的流協議。一旦確認了流協議后續就是調用對應的open和read函數指針打開和讀文件。

protocols = ffurl_get_protocols(NULL, NULL);
if (!protocols)return NULL;
for (i = 0; protocols[i]; i++) {const URLProtocol *up = protocols[i];if (!strcmp(proto_str, up->name)) {av_freep(&protocols);return up;}if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&!strcmp(proto_nested, up->name)) {av_freep(&protocols);return up;}
}

2 檢測媒體格式

確認iformat
??媒體格式探測其實就是確認使用哪種AVInputFormat解析(該成員是AVFormatContext->iformat字段)。如上面的代碼所示這里的流協議檢測是調用av_probe_input_format2來實現的,而該函數也只是轉調了av_probe_input_format3而已。

??av_probe_input_format3檢測文件格式的方式就是遍歷FFmpeg內部的iformat靜態表格,選擇匹配度個最高的那個作為最終的格式。核心代碼如下。

while ((fmt1 = av_demuxer_iterate(&i))) {if (fmt1->flags & AVFMT_EXPERIMENTAL)continue;if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))continue;score = 0;if (ffifmt(fmt1)->read_probe) {score = ffifmt(fmt1)->read_probe(&lpd);if (score)av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {switch (nodat) {case NO_ID3:score = FFMAX(score, 1);break;case ID3_GREATER_PROBE:case ID3_ALMOST_GREATER_PROBE:score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);break;case ID3_GREATER_MAX_PROBE:score = FFMAX(score, AVPROBE_SCORE_EXTENSION);break;}}} else if (fmt1->extensions) {if (av_match_ext(lpd.filename, fmt1->extensions))score = AVPROBE_SCORE_EXTENSION;}if (av_match_name(lpd.mime_type, fmt1->mime_type)) {if (AVPROBE_SCORE_MIME > score) {av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME);score = AVPROBE_SCORE_MIME;}}if (score > score_max) {score_max = score;fmt       = fmt1;} else if (score == score_max)fmt = NULL;
}

??從上面的流程中可以看到FFmpeg媒體信息探測會給每個格式一個分數,將根據輸入流和對應的格式確認當前的分數,分數越高則匹配度越高,最后選擇分數最高的格式作為選中的格式。而分數計算主要參考read_probe返回的分數值、后綴名和ID3元數據。
??從代碼中也能夠看到read_probe給的分數相比比較優先,而輸入路徑的后綴名會作為兜底策略,最低50分。這樣是為什么對于一個不是媒體文件的格式個更改后綴名后卻有可能檢測成功的原因。那為什么需要保留后綴名檢測呢?原因是并不是所有格式都能夠通過文件內容判斷其格式,比如相機的raw格式往往更改后綴名后就無法正常解析,這是因為raw格式只包含數據不包含任何其他多余的信息。

read_probe
??FFmpeg內部read_probe其實就是就是調用對應AVFormatInputread_probe函數,每個格式都有自己的實現。一般情況下分為兩種,一種是文件開頭有tag直接表明當前文件類型的,比如GIF等;另一種是需要讀取部分文件內容來確認格式信息的,比如mov、mp4格式。

??第一種比較簡單直接讀取文件內容然后匹配tag即可,比如gif的格式檢測:

static int gif_probe(const AVProbeData *p){/* check magick */if (memcmp(p->buf, gif87a_sig, 6) && memcmp(p->buf, gif89a_sig, 6))return 0;/* width or height contains zero? */if (!AV_RL16(&p->buf[6]) || !AV_RL16(&p->buf[8]))return 0;return AVPROBE_SCORE_MAX;
}

??另一種需要度卻文件內容,比如mp4格式檢測需要讀取其中的box來確認部分文件信息。

static int mov_probe(const AVProbeData *p)
{int64_t offset;uint32_t tag;int score = 0;int moov_offset = -1;/* check file header */offset = 0;for (;;) {int64_t size;int minsize = 8;/* ignore invalid offset */if ((offset + 8ULL) > (unsigned int)p->buf_size)break;size = AV_RB32(p->buf + offset);if (size == 1 && offset + 16 <= (unsigned int)p->buf_size) {size = AV_RB64(p->buf+offset + 8);minsize = 16;} else if (size == 0) {size = p->buf_size - offset;}if (size < minsize) {offset += 4;continue;}tag = AV_RL32(p->buf + offset + 4);switch(tag) {/* check for obvious tags */case MKTAG('m','o','o','v'):moov_offset = offset + 4;case MKTAG('m','d','a','t'):case MKTAG('p','n','o','t'): /* detect movs with preview pics like ew.mov and april.mov */case MKTAG('u','d','t','a'): /* Packet Video PVAuthor adds this and a lot of more junk */case MKTAG('f','t','y','p'):if (tag == MKTAG('f','t','y','p') &&(   AV_RL32(p->buf + offset + 8) == MKTAG('j','p','2',' ')|| AV_RL32(p->buf + offset + 8) == MKTAG('j','p','x',' ')|| AV_RL32(p->buf + offset + 8) == MKTAG('j','x','l',' '))) {score = FFMAX(score, 5);} else {score = AVPROBE_SCORE_MAX;}break;/* those are more common words, so rate then a bit less */case MKTAG('e','d','i','w'): /* xdcam files have reverted first tags */case MKTAG('w','i','d','e'):case MKTAG('f','r','e','e'):case MKTAG('j','u','n','k'):case MKTAG('p','i','c','t'):score  = FFMAX(score, AVPROBE_SCORE_MAX - 5);break;case MKTAG(0x82,0x82,0x7f,0x7d):score  = FFMAX(score, AVPROBE_SCORE_EXTENSION - 5);break;case MKTAG('s','k','i','p'):case MKTAG('u','u','i','d'):case MKTAG('p','r','f','l'):/* if we only find those cause probedata is too small at least rate them */score  = FFMAX(score, AVPROBE_SCORE_EXTENSION);break;}if (size > INT64_MAX - offset)break;offset += size;}if (score > AVPROBE_SCORE_MAX - 50 && moov_offset != -1) {/* moov atom in the header - we should make sure that this is not a* MOV-packed MPEG-PS */offset = moov_offset;while (offset < (p->buf_size - 16)) { /* Sufficient space *//* We found an actual hdlr atom */if (AV_RL32(p->buf + offset     ) == MKTAG('h','d','l','r') &&AV_RL32(p->buf + offset +  8) == MKTAG('m','h','l','r') &&AV_RL32(p->buf + offset + 12) == MKTAG('M','P','E','G')) {av_log(NULL, AV_LOG_WARNING, "Found media data tag MPEG indicating this is a MOV-packed MPEG-PS.\n");/* We found a media handler reference atom describing an* MPEG-PS-in-MOV, return a* low score to force expanding the probe window until* mpegps_probe finds what it needs */return 5;} else {/* Keep looking */offset += 2;}}}return score;
}

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

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

相關文章

變量的定義和使用

1.定義 變量&#xff0c;就是用來表示數據的名字 Python 中定義變量非常簡單&#xff0c;只需將數據通過等號()賦值給一個符合命名規范的標識符即可 name"Camille" name 123 變量的使用 變量的使用是指在程序中引用一個已經定義的變量。 例如&#xff0c;如果…

LeetCode 196, 73, 105

目錄 196. 刪除重復的電子郵箱題目鏈接表要求知識點思路代碼 73. 矩陣置零題目鏈接標簽簡單版思路代碼 優化版思路代碼 105. 從前序與中序遍歷序列構造二叉樹題目鏈接標簽思路代碼 196. 刪除重復的電子郵箱 題目鏈接 196. 刪除重復的電子郵箱 表 表Person的字段為id和email…

昇思MindSpore學習總結七——模型訓練

1、模型訓練 模型訓練一般分為四個步驟&#xff1a; 構建數據集。定義神經網絡模型。定義超參、損失函數及優化器。輸入數據集進行訓練與評估。 現在我們有了數據集和模型后&#xff0c;可以進行模型的訓練與評估。 2、構建數據集 首先從數據集 Dataset加載代碼&#xff0…

檢測站機動車授權簽字人試題附答案

16、___的輪胎胎冠上花紋深度不得小于3.2mm。( ) A、乘用車 B、摩托車 C、貨車的轉向輪&#xff08;正確答案&#xff09; D、掛車 17、最大設計時速≥100km/h的機動車其轉向盤自由轉動量不大于__。( ) A、30 度 B、20 度&#xff08;正確答案&#xff09; C、45 度 D、40度…

在windows上安裝objection

安裝命令pip install objection -i https://mirrors.aliyun.com/pypi/simple hook指定進程 objection -g 測試 explore 進程名不定是包名&#xff0c;也可能是app名字&#xff0c;如“測試”就是app的名字 若出現如下錯誤&#xff0c;說明python 缺少setuptools 直接安裝setu…

擲骰子游戲 、 求絕對值,平方根,對數,正弦值 題目

題目 JAVA33 擲骰子游戲分析&#xff1a;代碼&#xff1a; JAVA34 求絕對值&#xff0c;平方根&#xff0c;對數&#xff0c;正弦值分析&#xff1a;代碼&#xff1a; JAVA33 擲骰子游戲 描述開發一個擲骰子游戲&#xff0c;即每次運行程序時&#xff0c;產生一個[1,6]之間的隨…

秋招突擊——設計模式補充——單例模式、依賴倒轉原則、工廠方法模式

文章目錄 引言正文依賴倒轉原則工廠方法模式工廠模式的實現簡單工廠和工廠方法的對比 抽線工廠模式最基本的數據訪問程序使用工廠模式實現數據庫的訪問使用抽象工廠模式的數據訪問程序抽象工廠模式的優點和缺點使用反射抽象工廠的數據訪問程序使用反射配置文件實現數據訪問程序…

檢索增強生成RAG系列6--RAG提升之查詢結構化(Query Construction)

系列5中講到會講解3個方面RAG的提升&#xff0c;它們可能與RAG的準確率有關系&#xff0c;但是更多的它們是有其它用途。本期來講解第二部分&#xff1a;查詢結構化&#xff08;Query Construction&#xff09;。在系列3文檔處理中&#xff0c;我們著重講解了文檔解析&#xff…

C++ dll導出類的方法

要在C動態庫中導出類&#xff0c;可以使用以下步驟&#xff1a; 定義一個類并實現其成員函數。在類的聲明前加上__declspec(dllexport)標記&#xff08;Windows平臺&#xff09;或__attribute__((visibility("default")))標記&#xff08;Linux平臺&#xff09;&…

C語言學習筆記--第一個程序

第一個C語言程序 #include<stdio.h> //引用輸入輸出頭文件&#xff0c;每一次都需要引用這個文件 //.h是頭文件 // .c是源文件 // .cpp是C源文件&#xff0c;兼容C //C的第一個程序 // 行注釋&#xff08;只能注釋這一行&#xff09; /*塊注釋 */ int main() {printf(&…

能保存到相冊的風景視頻在哪下載?下載風景視頻網站分享

在當今以視覺為核心的時代&#xff0c;高清美麗的風景視頻不僅能夠豐富我們的日常生活&#xff0c;還能提供心靈上的慰藉。無論是為了制作視頻項目&#xff0c;還是僅僅想要珍藏一些精美的風景畫面&#xff0c;獲取高質量的風景視頻素材顯得尤為重要。許多人可能會問&#xff1…

PTrade量化軟件常見問題整理系列2

一、研究界面使用get_fundamentals函數報錯&#xff1a;error_info:獲取token失敗&#xff1f; 研究界面使用get_fundamentals函數報錯&#xff1a;error_info:獲取token失敗&#xff1f; 1、測試版本202202.01.052&#xff0c;升級202202.01.051版本后&#xff0c;為了解決不…

在虛擬仿真中學習人工智能,可以達到什么目標?

人工智能已經成為引領社會創新的關鍵力量&#xff0c;想要在這個充滿機遇的領域中脫穎而出&#xff0c;掌握扎實的專業技能和積累豐富的實踐經驗至關重要。然而&#xff0c;許多學習者在追求這一目標的過程中面臨著幾個主要問題&#xff1a;專業技術掌握有難度、實踐經驗積累存…

linux中awk,sed, grep使用

《linux私房菜》這本書中將sed和awk一同歸為行的修改這一點&#xff0c;雖然對&#xff0c;但不利于實際處理問題時的思考。因為這樣的話&#xff0c;當我們實際處理問題時&#xff0c;遇到比如說統計文本打印內容時&#xff0c;我們選擇sed還是awk進行處理呢&#xff1f; 也因…

?香橙派AIpro測評:usb魚眼攝像頭的Camera圖像獲取

一、前言 近期收到了一塊受到業界人士關注的開發板"香橙派AIpro",因為這塊板子具有極高的性價比&#xff0c;同時還可以兼容ubuntu、安卓等多種操作系統&#xff0c;今天博主便要在一塊832g的香橙派AI香橙派AIpro進行YoloV5s算法的部署并使用一個外接的魚眼USB攝像頭…

React 中如何使用 Monaco

Monaco 是微軟開源的一個編輯器&#xff0c;VSCode 也是基于 Monaco 進行開發的。如果在 React 中如何使用 Monaco&#xff0c;本文將介紹如何在 React 中引入 Monaco。 安裝 React 依賴 yarn add react-app-rewired --dev yarn add monaco-editor-webpack-plugin --dev yarn…

學習和發展人工智能:新興趨勢和成功秘訣

人工智能(AI)繼續吸引組織&#xff0c;因為它似乎無窮無盡地提高生產力和業務成果。在本博客中&#xff0c;了解學習和發展(L&D)部門如何利用人工智能改進流程&#xff0c;簡化工作流程&#xff1f; 學習與發展(L&D)部門領導開始探索如何提高和支持人工智能能力的勞動…

1-認識網絡爬蟲

1.什么是網絡爬蟲 ? 網絡爬蟲&#xff08;Web Crawler&#xff09;又稱網絡蜘蛛、網絡機器人&#xff0c;它是一種按照一定規則&#xff0c;自動瀏覽萬維網的程序或腳本。通俗地講&#xff0c;網絡爬蟲就是一個模擬真人瀏覽萬維網行為的程序&#xff0c;這個程序可以代替真人…

工業智能網關在現代工業生產中的重要性-天拓四方

工業智能網關是一款具備挖掘工業設備數據并接入到自主開發的云平臺的智能嵌入式網絡設備。它具備數據采集、協議解析、邊緣計算&#xff0c;以及4G/5G/WiFi數據傳輸等功能&#xff0c;并能接入工業云平臺。這種網關不僅支持采集PLC、傳感器、儀器儀表和各種控制器&#xff0c;還…

iss文件本機可以訪問,其他電腦無法訪問解決

1.搜索的時候有很多答案&#xff0c;總結就是2種 引用來自這位大佬的博客跳轉 2.我實際解決了的方法 將這里的ip地址修改為你局域網wifi的ip 如何看自己wifi的ip&#xff0c;大家自行百度&#xff01;