原文首發(fā)在LiveVideoStack映砖,請(qǐng)從原文轉(zhuǎn)載,本文不接受再次轉(zhuǎn)載灾挨。
從事音視頻技術(shù)開發(fā)對(duì)FFmpeg都不會(huì)感到陌生邑退,通過它可以完成音視頻采集、編解碼劳澄、轉(zhuǎn)碼地技、后處理以及流媒體服務(wù)等諸多的功能,可以說涵蓋了音視頻開發(fā)中絕大多數(shù)的領(lǐng)域秒拔。金山云多媒體SDK團(tuán)隊(duì)在移動(dòng)直播莫矗、短視頻等項(xiàng)目中遇到了許多問題,本文為《FFmpeg從入門到出家》系列的第一篇上半部分砂缩,由LiveVideoStack審校整理作谚,希望能讓大家對(duì)FFmpeg有更深入了解。
視頻流媒體過程中視頻數(shù)據(jù)的傳輸占據(jù)了絕大部分的帶寬庵芭,如何提升編碼效率食磕,使用更少的帶寬,提供更優(yōu)質(zhì)的畫面質(zhì)量喳挑,是音視頻開發(fā)人員一直努力的重點(diǎn)彬伦。HEVC(High Efficiency Video Coding滔悉,也叫H.265)編碼格式的推出,給這一方向帶來了突破點(diǎn)单绑,但由于其算法復(fù)雜度較高回官,前期未曾得到普遍應(yīng)用,而隨著移動(dòng)設(shè)備計(jì)算能力的提高和越來越多的設(shè)備開始支持HEVC的硬件編/解碼搂橙,直播平臺(tái)也開始逐漸引入HEVC視頻格式歉提。
HEVC屬視頻編碼層面標(biāo)準(zhǔn),它在直播過程中的應(yīng)用区转,還需要相應(yīng)的封裝格式和流媒體協(xié)議的支持苔巨。鑒于直播的大部分推拉流協(xié)議是基于RTMP的,本文主要介紹如何在RTMP協(xié)議中增加對(duì)HEVC視頻編碼格式的支持废离,其他協(xié)議或私有協(xié)議侄泽,可參考本文自行添加。此外蜻韭,除推流端和播放端要作出修改悼尾,用到的RTMP Server部分也要同步進(jìn)行相應(yīng)修改,才能夠保證HEVC在直播中的正常使用肖方。
1. 背景介紹
典型的直播框架通常包括三大部分闺魏,如下圖所示:
- 推流端:負(fù)責(zé)音視頻數(shù)據(jù)的采集、處理俯画、編碼及封裝后將數(shù)據(jù)推送至至源站析桥;
- 服務(wù)端:涵蓋源站和CDN,接收來自推流端的音視頻數(shù)據(jù)艰垂,然后將數(shù)據(jù)分發(fā)至各播放端烹骨;
- 播放端:從CDN拉取直播數(shù)據(jù),解復(fù)用材泄、解碼后渲染音視頻數(shù)據(jù)沮焕;
引入HEVC編碼,涉及到的變動(dòng)部分如上圖中紅色字體所標(biāo)注:
- 編碼模塊:需要支持HEVC格式的編解碼拉宗,該部分不屬于本文的介紹范疇峦树,我們有在其它文章中介紹如何在iOS11上進(jìn)行HEVC的硬編硬解,感興趣的朋友可自行查閱旦事;
- 封裝/傳輸模塊:RTMP魁巩、HTTP-FLV流媒體協(xié)議需要增加對(duì)HEVC視頻編碼格式的支持,該部分是本文介紹的重點(diǎn)姐浮。
相信廣大的音視頻開發(fā)者對(duì)于FFmpeg并不陌生谷遂,由于它在多媒體處理上提供的強(qiáng)大功能以及開源易于修改維護(hù)的特性,使得其被廣泛應(yīng)用于各音視頻相關(guān)軟件中卖鲤。但官方FFmpeg中沒有對(duì)RTMP/FLV中進(jìn)行HEVC的相關(guān)擴(kuò)展肾扰,這是應(yīng)為FLV與RTMP是Adobe發(fā)行的標(biāo)準(zhǔn)畴嘶,而Adobe暫停了對(duì)FLV與RTMP標(biāo)準(zhǔn)的更新,HEVC的相關(guān)擴(kuò)展屬于私有標(biāo)準(zhǔn)集晚,所以為了減少國際上不必要的不兼容性麻煩窗悯,官方FFmpeg并不會(huì)對(duì)FLV與RTMP中擴(kuò)展HEVC進(jìn)行支持。經(jīng)過CDN聯(lián)盟討論偷拔,我們制定了相關(guān)的協(xié)議擴(kuò)展規(guī)范蒋院,并在FFmpeg中完成了相關(guān)代碼實(shí)現(xiàn)。
本文后面介紹的就是如何在FFmpeg中莲绰,對(duì)RTMP進(jìn)行HEVC擴(kuò)展欺旧。如果您的開發(fā)工程中并沒有用到FFmpeg,可直接閱讀第四章節(jié)蛤签,也能夠很輕松的在您的代碼中增加這部分內(nèi)容辞友。
2. FFmpeg簡(jiǎn)析
FFmpeg從無到有,發(fā)展至今顷啼,功能日益強(qiáng)大踏枣,代碼也越來越多昌屉,很多初學(xué)者都被其眾多的源文件钙蒙、龐大的結(jié)構(gòu)體和復(fù)雜的算法打消了繼續(xù)學(xué)習(xí)的念頭。本章節(jié)將從總體對(duì)FFmpeg進(jìn)行簡(jiǎn)單的解析间驮,教您如何如何閱讀FFmpeg源碼躬厌。
2.1 總體說明
FFmpeg包含如下類庫:
libavformat - 用于各種音視頻封裝格式的生成和解析,包括獲取解碼所需信息竞帽、讀取音視頻數(shù)據(jù)等功能扛施。各種流媒體協(xié)議代碼(如rtmpproto.c等)以及音視頻格式的(解)復(fù)用代碼(如flvdec.c、flvenc.c等)都位于該目錄下屹篓。
libavcodec - 音視頻各種格式的編解碼疙渣。各種格式的編解碼代碼(如aacenc.c、aacdec.c等)都位于該目錄下堆巧。
libavutil - 包含一些公共的工具函數(shù)的使用庫妄荔,包括算數(shù)運(yùn)算,字符操作等谍肤。
libswscale - 提供原始視頻的比例縮放啦租、色彩映射轉(zhuǎn)換、圖像顏色空間或格式轉(zhuǎn)換的功能荒揣。
libswresample - 提供音頻重采樣篷角,采樣格式轉(zhuǎn)換和混合等功能。
libavfilter - 各種音視頻濾波器系任。
libpostproc - 用于后期效果處理恳蹲,如圖像的去塊效應(yīng)等虐块。
libavdevice - 用于硬件的音視頻采集、加速和顯示阱缓。
如果您之前沒有閱讀FFmpeg代碼的經(jīng)驗(yàn)非凌,建議優(yōu)先閱讀libavformat、libavcodec以及libavutil下面的代碼荆针,它們提供了音視頻開發(fā)的最基本功能敞嗡,應(yīng)用范圍也是最廣的。
2.2 常用結(jié)構(gòu)
FFmpeg里面最常用的數(shù)據(jù)結(jié)構(gòu)航背,按功能可大致分為以下幾類(以下代碼行數(shù)喉悴,以branch: origin/release/3.4為準(zhǔn)):
-
封裝格式
- AVFormatContext - 描述了媒體文件的構(gòu)成及基本信息,是統(tǒng)領(lǐng)全局的基本結(jié)構(gòu)體玖媚,貫穿程序始終箕肃,很多函數(shù)都要用它作為參數(shù);
- AVInputFormat - 解復(fù)用器對(duì)象今魔,每種作為輸入的封裝格式(例如FLV勺像、MP4、TS等)對(duì)應(yīng)一個(gè)該結(jié)構(gòu)體错森,如libavformat/flvdec.c的ff_flv_demuxer吟宦;
- AVOutputFormat - 復(fù)用器對(duì)象,每種作為輸出的封裝格式(例如FLV, MP4涩维、TS等)對(duì)應(yīng)一個(gè)該結(jié)構(gòu)體殃姓,如libavformat/flvenc.c的ff_flv_muxer;
- AVStream - 用于描述一個(gè)視頻/音頻流的相關(guān)數(shù)據(jù)信息瓦阐。
-
編解碼
- AVCodecContext - 描述編解碼器上下文的數(shù)據(jù)結(jié)構(gòu)蜗侈,包含了眾多編解碼器需要的參數(shù)信息;
- AVCodec - 編解碼器對(duì)象睡蟋,每種編解碼格式(例如H.264踏幻、AAC等)對(duì)應(yīng)一個(gè)該結(jié)構(gòu)體,如libavcodec/aacdec.c的ff_aac_decoder戳杀。每個(gè)AVCodecContext中含有一個(gè)AVCodec该面;
- AVCodecParameters - 編解碼參數(shù),每個(gè)AVStream中都含有一個(gè)AVCodecParameters豺瘤,用來存放當(dāng)前流的編解碼參數(shù)吆倦。
-
網(wǎng)絡(luò)協(xié)議
- AVIOContext - 管理輸入輸出數(shù)據(jù)的結(jié)構(gòu)體;
- URLProtocol - 描述了音視頻數(shù)據(jù)傳輸所使用的協(xié)議坐求,每種傳輸協(xié)議(例如HTTP蚕泽、RTMP)等,都會(huì)對(duì)應(yīng)一個(gè)URLProtocol結(jié)構(gòu),如libavformat/http.c中的ff_http_protocol须妻;
- URLContext - 封裝了協(xié)議對(duì)象及協(xié)議操作對(duì)象仔蝌。
-
數(shù)據(jù)存放
上述結(jié)構(gòu)體的關(guān)系圖如下所示(箭頭表示 派生出):
2.3 代碼結(jié)構(gòu)
下面這段代碼完成了讀取媒體文件中音視頻數(shù)據(jù)的基本功能,本節(jié)以此為例儡湾,分析FFmpeg內(nèi)部代碼的調(diào)用邏輯特恬。
char *url = "http://192.168.1.105/test.flv";
AVPacket pkt;
int ret = 0;
//注冊(cè)復(fù)用器、編碼器等
av_register_all();
avformat_network_init();
//打開文件
AVFormatContext *fmtCtx = avformat_alloc_context();
ret = avformat_open_input(&fmtCtx, url, NULL, NULL);
ret = avformat_find_stream_info(fmtCtx, NULL);
//讀取音視頻數(shù)據(jù)
while(ret >= 0)
{
ret = av_read_frame(s, &pkt);
}
2.3.1 注冊(cè)
av_register_all函數(shù)的作用是注冊(cè)一系列的(解)復(fù)用器徐钠、編/解碼器等癌刽。它在所有基于FFmpeg的應(yīng)用程序中幾乎都是第一個(gè)被調(diào)用的,只有調(diào)用了該函數(shù)尝丐,才能使用復(fù)用器显拜、編碼器等。
static void register_all(void)
{
avcodec_register_all();
/* (de)muxers */
……
REGISTER_MUXDEMUX(FLV, flv);
……
}
REGISTER_MUXDEMUX實(shí)際上調(diào)用的是av_register_input_format和av_register_output_format爹袁,通過這兩個(gè)方法远荠,將(解)復(fù)用器分別添加到了全局變量first_iformat與first_oformat鏈表的最后位置。
編/解碼其注冊(cè)過程相同呢簸,此處不再贅述矮台。
2.3.2 文件打開
FFmpeg讀取媒體數(shù)據(jù)的過程始于avformat_open_input乏屯,該方法中完成了媒體文件的打開和格式探測(cè)的功能根时。但FFmpeg是如何找到正確的流媒體協(xié)議和解復(fù)用器呢?可以看到avformat_open_input方法中調(diào)用了init_input函數(shù)辰晕,在這里面完成了查找流媒體協(xié)議和解復(fù)用器的工作蛤迎。
static int init_input(AVFormatContext *s, const char *filename,
AVDictionary **options)
{
int ret;
……
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);
}
-
s->io_open
實(shí)際上調(diào)用的就是io_open_default,它最終調(diào)用到url_find_protocol方法含友。
static const struct URLProtocol *url_find_protocol(const char *filename)
{
const URLProtocol **protocols;
……
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;
}
}
av_freep(&protocols);
return NULL;
}
ffurl_get_protocols可以得到當(dāng)前編譯的FFmpeg支持的所有流媒體協(xié)議替裆,通過url的scheme和protocol->name相比較,得到正確的protocol窘问。例如本例中URLProtocol最終指向了libavformat/http.c中的ff_http_protocol辆童。
- av_probe_input_buffer2最終調(diào)用到av_probe_input_format3, 該方法遍歷所有的解復(fù)用器,即first_iformat鏈表中的所有節(jié)點(diǎn)惠赫,調(diào)用它們的read_probe()函數(shù)計(jì)算匹配得分把鉴,函數(shù)最終返回計(jì)算找到的最匹配的解復(fù)用器。本例中AVInputFormat最終指向了libavformat/flvdec.c中的ff_flv_demuxer。
2.3.3 數(shù)據(jù)讀取
av_read_frame作用是讀取媒體數(shù)據(jù)中的每個(gè)音視頻幀庭砍,該方法中最關(guān)鍵的地方就是調(diào)用了AVInputFormat的read_packet()方法场晶。AVInputFormat的read_packet()是一個(gè)函數(shù)指針,指向當(dāng)前的AVInputFormat的讀取數(shù)據(jù)的函數(shù)怠缸。在本例中诗轻,AVInputFormat為ff_flv_demuxer,也就是說read_packet最終指向了flv_read_packet揭北。
3. FLV文件結(jié)構(gòu)解析
FLV(FLASH VIDEO)扳炬,是一種流行的網(wǎng)絡(luò)視頻格式,目前國內(nèi)外大部分視頻分享網(wǎng)站都是采用的這種格式搔体。其標(biāo)準(zhǔn)定義為《Adobe Flash Video File Format Specification》鞠柄。RTMP協(xié)議也是基于FLV視頻格式的。
FLV的文件格式在該規(guī)范中已闡述清楚嫉柴,本章節(jié)不再重復(fù)描述厌杜,而是結(jié)合下面的示例具體闡述如何分析FLV文件。
FLV文件的分析工具有很多计螺,這里給大家推薦FLV Parser這個(gè)小軟件夯尽,通過它可以很容易的看到文件的組成結(jié)構(gòu)。
3.1 文件結(jié)構(gòu)
從整個(gè)文件上看登馒,F(xiàn)LV是由Header和File Body組成匙握,如下圖所示:
FLV Header - 長(zhǎng)度為9, 其結(jié)構(gòu)的標(biāo)準(zhǔn)定義參見標(biāo)準(zhǔn)定義見E.2 The FLV header陈轿;
FLV File Body - 由一連串的PreviousTagSize + Tag構(gòu)成圈纺。previoutsTagSize是4個(gè)字節(jié)的數(shù)據(jù),表示前一個(gè)tag的size麦射。標(biāo)準(zhǔn)定義參見E.3 The FLV File Body蛾娶。
以FLV文件結(jié)構(gòu)示例1.jpg為例分析整體結(jié)構(gòu):
-
位置0x00000000 - 0x00000008, 共9個(gè)字節(jié), 為FLV Header潜秋,其中:
- 0x00000000 - 0x00000002 : 0x46 0x4C 0x56分別表示字符'F''L''V'蛔琅, 用來標(biāo)識(shí)這個(gè)文件是FLV格式的。在做格式探測(cè)的時(shí)候峻呛,如果發(fā)現(xiàn)前3個(gè)字節(jié)為“FLV”罗售,就認(rèn)為它是FLV文件;
- 0x00000003 : 0x01, 表示FLV版本號(hào)钩述;
- 0x00000004 : 0x05, 轉(zhuǎn)換為2進(jìn)制是0000 0101寨躁,其中第0位為1,表示存在video牙勘,第2位為1职恳,表示存在audio;
- 0x00000005 - 0x00000008 : 0x00 0x00 0x00 0x09税产,轉(zhuǎn)十進(jìn)制為9薇搁,表示FLV header的長(zhǎng)度宙地,當(dāng)FLV 版本號(hào)為1時(shí)罢猪,該值通常為9软驰。
-
位置0x00000009 - 晦鞋,為FLV File Body:
- 0x00000009 - 0x0000000C : 0x00 0x00 0x00 0x00 PreviousTagSize0得糜,轉(zhuǎn)十進(jìn)制為0载绿,該值永遠(yuǎn)為0床蜘;
- 0x0000000D - 0x00000209 : 0x12 ... 0x09辙培,共509個(gè)字節(jié),為Tag1的具體內(nèi)容邢锯;
- 0x0000020A - 0x0000020D : 0x00 0x00 0x01 0xFD扬蕊,轉(zhuǎn)十進(jìn)制為509,表示它前面的Tag丹擎,即Tag1的長(zhǎng)度為509尾抑;
- 0x0000020E - :按照Tag + PreviousTagSize的結(jié)構(gòu)依次遞推,此處不再舉例說明蒂培。
3.2 Tag定義
FLV File Body是由一系列的PreviousTagSize + Tag組成再愈,其中PreviousTagSize的長(zhǎng)度為4個(gè)字節(jié),用來表示前一個(gè)Tag的長(zhǎng)度护戳;Tag里面的數(shù)據(jù)可能是video翎冲、audio或者scripts,其定義參見E.4.1 FLV Tag媳荒,結(jié)構(gòu)如下:
以FLV文件結(jié)構(gòu)示例1.jpg為例分析Tag結(jié)構(gòu):
- 位置0x0000020E : 0x08, 二進(jìn)制為0000 1000抗悍,第5位為0, 表示為非加擾文件;低5位01000為8钳枕,說明這個(gè)Tag包含的數(shù)據(jù)類型為Audio缴渊;
- 位置0x0000020F - 0x00000211 : 0x00 0x00 0x04,轉(zhuǎn)十進(jìn)制為4么伯,說明Tag的內(nèi)容長(zhǎng)度為4疟暖,與該tag后面的previousTagSize(15) - 11相同卡儒;
- 位置0x00000212 - 0x00000214 : 0x00 0x00 0x00田柔,轉(zhuǎn)十進(jìn)制為0,說明當(dāng)前Audio數(shù)據(jù)的時(shí)間戳為0骨望;
- 位置0x00000215 : 0x00硬爆,擴(kuò)展時(shí)間戳為0,如果擴(kuò)展時(shí)間戳不為0擎鸠,那么該Tag的時(shí)間戳應(yīng)為: Timestamp | TimestampExtended<<24缀磕;
- 位置0x00000216 - 0x00000218 : 0x00 0x00 0x00,StreamID,總是0袜蚕;
- StreamID之后的數(shù)據(jù)每種格式的情況都不一樣糟把,下面會(huì)依次進(jìn)行詳細(xì)解讀。
3.3 Audio Tags
如果TAG包中的TagType等于8牲剃,表示該Tag中包含的數(shù)據(jù)類型為Audio遣疯。StreamID之后的數(shù)據(jù)就是AudioTagHeader,其定義詳見E.4.2.1 AUDIODATA凿傅。結(jié)構(gòu)如下:
需要說明的是缠犀,通常情況下AudioTagHeader之后跟著的就是AUDIODATA數(shù)據(jù)了,但有個(gè)特例聪舒,如果音頻編碼格式為AAC辨液,AudioTagHeader中會(huì)多出1個(gè)字節(jié)的數(shù)據(jù)AACPacketType,這個(gè)字段來表示AACAUDIODATA的類型:
- 0 = AAC sequence header
- 1 = AAC raw箱残。
以 FLV文件結(jié)構(gòu)示例1.jpg 為例分析AudioTag結(jié)構(gòu):
- 位置0x00000219 : 0xAF, 二進(jìn)制表示為1010 1111:
- 高4位為1010滔迈,轉(zhuǎn)十進(jìn)制為10, 表示Audio的編碼格式為AAC被辑;
- 第3亡鼠、2位為11,轉(zhuǎn)十進(jìn)制為3敷待,表示該音頻的采樣率為44KHZ间涵;
- 第1位為1,表示該音頻采樣點(diǎn)位寬為16bits榜揖;
- 第0位為1勾哩,表示該音頻為立體聲。
- 位置0x0000021A : 0x00举哟,十進(jìn)制為0思劳,并且Audio的編碼格式為AAC,說明AACAUDIODATA中存放的是AAC sequence header妨猩;
- 位置0x0000021B - 0x0000021C : AUDIODATA數(shù)據(jù)潜叛,即AAC sequence header。
3.3.1 AudioSpecificConfig
AAC sequence header中存放的是AudioSpecificConfig壶硅,該結(jié)構(gòu)包含了更加詳細(xì)的音頻信息威兜,《ISO-14496-3 Audio》中的1.6.2.1 章節(jié)對(duì)此作了詳細(xì)定義。
通常情況下庐椒,AAC sequence header這種Tag在FLV文件中只出現(xiàn)1次椒舵,并且是第一個(gè)Audio Tag,它存放了解碼AAC音頻所需要的詳細(xì)信息约谈。
有關(guān)AudioSpecificConfig結(jié)構(gòu)的代碼解析笔宿,可以參考ffmpeg/libavcodec/mpeg4audio.c中的avpriv_mpeg4audio_get_config方法犁钟。
為什么AudioTagHeader中定義了音頻的相關(guān)參數(shù),我們還需要傳遞AudioSpecificConfig呢泼橘?
因?yàn)楫?dāng)SoundFormat為AAC時(shí)涝动,SoundType須設(shè)置為1(立體聲),SoundRate須設(shè)置為3(44KHZ)炬灭,但這并不意味著FLV文件中AAC編碼的音頻必須是44KHZ的立體聲捧存。播放器在播放AAC音頻時(shí),應(yīng)忽略AudioTagHeader中的參數(shù)担败,并根據(jù)AudioSpecificConfig來配置正確的解碼參數(shù)昔穴。
3.4 Video Tag
如果TAG包中的TagType等于9,表示該Tag中包含的數(shù)據(jù)類型為Video提前。StreamID之后的數(shù)據(jù)就是VideoTagHeader吗货,其定義詳見E.4.3.1 VIDEODATA,結(jié)構(gòu)如下:
VideoTagHeader之后跟著的就是VIDEODATA數(shù)據(jù)了狈网,但是和AAC音頻一樣宙搬,它也存在一個(gè)特例,就是當(dāng)視頻編碼格式為H.264的時(shí)候拓哺,VideoTagHeader會(huì)多出4個(gè)字節(jié)的信息勇垛,AVCPacketType 和 CompositionTime。
- AVCPacketType用來表示VIDEODATA的內(nèi)容
- CompositonTime 相對(duì)時(shí)間戳士鸥,如果AVCPacketType=0x01闲孤,為相對(duì)時(shí)間戳,其它均為0烤礁;
以 FLV文件結(jié)構(gòu)示例2.jpg 為例分析VideoTagHeader結(jié)構(gòu):
- 位置0x0000022C : 0x17, 二進(jìn)制表示為0001 0111:
- 高4位為0001讼积,轉(zhuǎn)十進(jìn)制為1, 表示當(dāng)前幀為關(guān)鍵幀脚仔;
- 低4位為0111勤众,轉(zhuǎn)十進(jìn)制為7,說明當(dāng)前視頻的編碼格式為AVC鲤脏。
- 位置0x0000022D : 0x00们颜,十進(jìn)制為0,并且Video的編碼格式為AVC猎醇,說明VideoTagBody中存放的是AVC sequence header窥突;
- 位置0x0000022E - 0x00000230 : 轉(zhuǎn)十進(jìn)制為0,表示相對(duì)時(shí)間戳為0姑食;
- 位置0x00000231 - 0x0000021C : VIDEODATA數(shù)據(jù)波岛,即AVC sequence header。
3.4.1 AVCDecoderConfigurationRecord
AVC sequence header中存放的是AVCDecoderConfigurationRecord音半,《ISO-14496-15 AVC file format》對(duì)此作了詳細(xì)定義则拷。它存放的是AVC的編碼參數(shù),解碼時(shí)需設(shè)置給解碼器后方可正確解碼曹鸠。
通常情況下煌茬,AVC sequence header這種Tag在FLV文件中只出現(xiàn)1次,并且是第一個(gè)Video Tag彻桃。
有關(guān)AVCDecoderConfigurationRecord結(jié)構(gòu)的代碼解析坛善,可以參考中的ff_isom_write_avcc 方法。
3.4.2 CompositionTime(相對(duì)時(shí)間戳)
相對(duì)時(shí)間戳的概念需要和PTS邻眷、DTS一起理解:
- DTS : *Decode Time Stamp眠屎,解碼時(shí)間戳,用于告知解碼器該視頻幀的解碼時(shí)間肆饶;
- PTS : Presentation Time Stamp改衩,顯示時(shí)間戳,用于告知播放器該視頻幀的顯示時(shí)間驯镊;
- CTS : Composition Time Stamp葫督,相對(duì)時(shí)間戳,用來表示PTS與DTS的差值板惑。
如果視頻里各幀的編碼是按輸入順序依次進(jìn)行的橄镜,則解碼和顯示時(shí)間相同,應(yīng)該是一致的冯乘。但實(shí)際在大多數(shù)視頻編解碼標(biāo)準(zhǔn)中(如H.264或HEVC)洽胶,輸入順序和編碼順序并不一致,所以才需要PTS和DTS這兩種的時(shí)間戳裆馒。視頻幀的解碼一定是發(fā)生在顯示前妖异,所以視頻幀的PTS,一定是大于等于DTS的领追,因此CTS=PTS-DTS他膳。
FLV Video Tag中的TimeStamp,不是PTS绒窑,而是DTS棕孙,視頻幀的PTS需要我們通過DTS + CTS計(jì)算得到。
為什么Audio Tag不需要CompositionTime呢些膨?
因?yàn)锳udio的編碼順序和輸入順序一致蟀俊,即PTS=DTS,所以它沒有CompositionTime的概念订雾。
3.5 Script Data Tags
如果TAG包中的TagType等于18肢预,表示該Tag中包含的數(shù)據(jù)類型為SCRIPT。
SCRIPTDATA 結(jié)構(gòu)十分復(fù)雜洼哎,定義了很多格式類型烫映,每個(gè)類型對(duì)應(yīng)一種結(jié)構(gòu)沼本,詳細(xì)可參考E.4.4 Data Tags
onMetaData是SCRIPTDATA中一個(gè)非常重要的信息,其結(jié)構(gòu)定義可參考E.5 onMetaData锭沟。它通常是FLV文件中的第一個(gè)Tag抽兆,用來用來表示當(dāng)前文件的一些基本信息: 比如視音頻的編碼類型id、視頻的寬和高族淮、文件大小辫红、視頻長(zhǎng)度、創(chuàng)建日期等祝辣。
4. 結(jié)束語
金山云多媒體SDK團(tuán)隊(duì)在直播/短視頻SDK中深入使用了FFmpeg贴妻,如有任何問題,歡迎參與討論蝙斜。
金山云SDK相關(guān)的QQ交流群:
- 視頻云技術(shù)交流群:574179720
- 視頻云iOS技術(shù)交流:621137661
原文首發(fā)在LiveVideoStack名惩,本文不接受再次轉(zhuǎn)載。