FFmpeg代碼導(dǎo)讀系列(一)----基礎(chǔ)篇

原文首發(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. 背景介紹

典型的直播框架通常包括三大部分闺魏,如下圖所示:

  1. 推流端:負(fù)責(zé)音視頻數(shù)據(jù)的采集、處理俯画、編碼及封裝后將數(shù)據(jù)推送至至源站析桥;
  2. 服務(wù)端:涵蓋源站和CDN,接收來自推流端的音視頻數(shù)據(jù)艰垂,然后將數(shù)據(jù)分發(fā)至各播放端烹骨;
  3. 播放端:從CDN拉取直播數(shù)據(jù),解復(fù)用材泄、解碼后渲染音視頻數(shù)據(jù)沮焕;
圖1. 直播框架圖.png

引入HEVC編碼,涉及到的變動(dòng)部分如上圖中紅色字體所標(biāo)注:

  1. 編碼模塊:需要支持HEVC格式的編解碼拉宗,該部分不屬于本文的介紹范疇峦树,我們有在其它文章中介紹如何在iOS11上進(jìn)行HEVC的硬編硬解,感興趣的朋友可自行查閱旦事;
  2. 封裝/傳輸模塊: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)為FLVRTMPAdobe發(fā)行的標(biāo)準(zhǔn)畴嘶,而Adobe暫停了對(duì)FLVRTMP標(biāo)準(zhǔn)的更新,HEVC的相關(guān)擴(kuò)展屬于私有標(biāo)準(zhǔn)集晚,所以為了減少國際上不必要的不兼容性麻煩窗悯,官方FFmpeg并不會(huì)對(duì)FLVRTMP中擴(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.cflvenc.c等)都位于該目錄下屹篓。

libavcodec - 音視頻各種格式的編解碼疙渣。各種格式的編解碼代碼(如aacenc.caacdec.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)先閱讀libavformatlibavcodec以及libavutil下面的代碼荆针,它們提供了音視頻開發(fā)的最基本功能敞嗡,應(yīng)用范圍也是最廣的。

2.2 常用結(jié)構(gòu)

FFmpeg里面最常用的數(shù)據(jù)結(jié)構(gòu)航背,按功能可大致分為以下幾類(以下代碼行數(shù)喉悴,以branch: origin/release/3.4為準(zhǔn)):

  1. 封裝格式

    • AVFormatContext - 描述了媒體文件的構(gòu)成及基本信息,是統(tǒng)領(lǐng)全局的基本結(jié)構(gòu)體玖媚,貫穿程序始終箕肃,很多函數(shù)都要用它作為參數(shù);
    • AVInputFormat - 解復(fù)用器對(duì)象今魔,每種作為輸入的封裝格式(例如FLV勺像、MP4TS等)對(duì)應(yīng)一個(gè)該結(jié)構(gòu)體错森,如libavformat/flvdec.cff_flv_demuxer吟宦;
    • AVOutputFormat - 復(fù)用器對(duì)象,每種作為輸出的封裝格式(例如FLV, MP4涩维、TS等)對(duì)應(yīng)一個(gè)該結(jié)構(gòu)體殃姓,如libavformat/flvenc.cff_flv_muxer
    • AVStream - 用于描述一個(gè)視頻/音頻流的相關(guān)數(shù)據(jù)信息瓦阐。
  2. 編解碼

    • AVCodecContext - 描述編解碼器上下文的數(shù)據(jù)結(jié)構(gòu)蜗侈,包含了眾多編解碼器需要的參數(shù)信息;
    • AVCodec - 編解碼器對(duì)象睡蟋,每種編解碼格式(例如H.264踏幻、AAC等)對(duì)應(yīng)一個(gè)該結(jié)構(gòu)體,如libavcodec/aacdec.cff_aac_decoder戳杀。每個(gè)AVCodecContext中含有一個(gè)AVCodec该面;
    • AVCodecParameters - 編解碼參數(shù),每個(gè)AVStream中都含有一個(gè)AVCodecParameters豺瘤,用來存放當(dāng)前流的編解碼參數(shù)吆倦。
  3. 網(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ì)象仔蝌。
  4. 數(shù)據(jù)存放

    • AVPacket - 存放編碼后、解碼前的壓縮數(shù)據(jù)荒吏,即ES數(shù)據(jù)敛惊;
    • AVFrame - 存放編碼前、解碼后的原始數(shù)據(jù)绰更,如YUV格式的視頻數(shù)據(jù)或PCM格式的音頻數(shù)據(jù)等瞧挤;

上述結(jié)構(gòu)體的關(guān)系圖如下所示(箭頭表示 派生出):

圖2. FFmpeg結(jié)構(gòu)體關(guān)系圖.png

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_formatav_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);
}
  1. 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é)議替裆,通過urlschemeprotocol->name相比較,得到正確的protocol窘问。例如本例中URLProtocol最終指向了libavformat/http.c中的ff_http_protocol辆童。

  1. 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)用了AVInputFormatread_packet()方法场晶。AVInputFormatread_packet()是一個(gè)函數(shù)指針,指向當(dāng)前的AVInputFormat的讀取數(shù)據(jù)的函數(shù)怠缸。在本例中诗轻,AVInputFormatff_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文件結(jié)構(gòu)示例1.jpg
FLV文件結(jié)構(gòu)示例2.jpg

FLV文件的分析工具有很多计螺,這里給大家推薦FLV Parser這個(gè)小軟件夯尽,通過它可以很容易的看到文件的組成結(jié)構(gòu)。

3.1 文件結(jié)構(gòu)

從整個(gè)文件上看登馒,F(xiàn)LV是由HeaderFile Body組成匙握,如下圖所示:

FLV文件總體結(jié)構(gòu).png

  1. FLV Header - 長(zhǎng)度為9, 其結(jié)構(gòu)的標(biāo)準(zhǔn)定義參見標(biāo)準(zhǔn)定義見E.2 The FLV header陈轿;

  2. FLV File Body - 由一連串的PreviousTagSize + Tag構(gòu)成圈纺。previoutsTagSize是4個(gè)字節(jié)的數(shù)據(jù),表示前一個(gè)tagsize麦射。標(biāo)準(zhǔn)定義參見E.3 The FLV File Body蛾娶。

FLV文件結(jié)構(gòu)示例1.jpg為例分析整體結(jié)構(gòu):

  1. 位置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软驰。
  2. 位置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 Tag 結(jié)構(gòu).png

FLV文件結(jié)構(gòu)示例1.jpg為例分析Tag結(jié)構(gòu):

  1. 位置0x0000020E : 0x08, 二進(jìn)制為0000 1000抗悍,第5位為0, 表示為非加擾文件;低5位01000為8钳枕,說明這個(gè)Tag包含的數(shù)據(jù)類型為Audio缴渊;
  2. 位置0x0000020F - 0x00000211 : 0x00 0x00 0x04,轉(zhuǎn)十進(jìn)制為4么伯,說明Tag的內(nèi)容長(zhǎng)度為4疟暖,與該tag后面的previousTagSize(15) - 11相同卡儒;
  3. 位置0x00000212 - 0x00000214 : 0x00 0x00 0x00田柔,轉(zhuǎn)十進(jìn)制為0,說明當(dāng)前Audio數(shù)據(jù)的時(shí)間戳為0骨望;
  4. 位置0x00000215 : 0x00硬爆,擴(kuò)展時(shí)間戳為0,如果擴(kuò)展時(shí)間戳不為0擎鸠,那么該Tag的時(shí)間戳應(yīng)為: Timestamp | TimestampExtended<<24缀磕;
  5. 位置0x00000216 - 0x00000218 : 0x00 0x00 0x00,StreamID,總是0袜蚕;
  6. 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)如下:

FLV Audio Tag結(jié)構(gòu).png

需要說明的是缠犀,通常情況下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):

  1. 位置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勾哩,表示該音頻為立體聲。
  2. 位置0x0000021A : 0x00举哟,十進(jìn)制為0思劳,并且Audio的編碼格式為AAC,說明AACAUDIODATA中存放的是AAC sequence header妨猩;
  3. 位置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)如下:

FLV Video Tag結(jié)構(gòu).png

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):

  1. 位置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鲤脏。
  2. 位置0x0000022D : 0x00们颜,十進(jìn)制為0,并且Video的編碼格式為AVC猎醇,說明VideoTagBody中存放的是AVC sequence header窥突;
  3. 位置0x0000022E - 0x00000230 : 轉(zhuǎn)十進(jìn)制為0,表示相對(duì)時(shí)間戳為0姑食;
  4. 位置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這種TagFLV文件中只出現(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一起理解:

  1. DTS : *Decode Time Stamp眠屎,解碼時(shí)間戳,用于告知解碼器該視頻幀的解碼時(shí)間肆饶;
  2. PTS : Presentation Time Stamp改衩,顯示時(shí)間戳,用于告知播放器該視頻幀的顯示時(shí)間驯镊;
  3. CTS : Composition Time Stamp葫督,相對(duì)時(shí)間戳,用來表示PTS與DTS的差值板惑。

如果視頻里各幀的編碼是按輸入順序依次進(jìn)行的橄镜,則解碼和顯示時(shí)間相同,應(yīng)該是一致的冯乘。但實(shí)際在大多數(shù)視頻編解碼標(biāo)準(zhǔn)中(如H.264HEVC)洽胶,輸入順序和編碼順序并不一致,所以才需要PTSDTS這兩種的時(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)載。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乍炉,一起剝皮案震驚了整個(gè)濱河市绢片,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岛琼,老刑警劉巖底循,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異槐瑞,居然都是意外死亡熙涤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門困檩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祠挫,“玉大人,你說我怎么就攤上這事悼沿〉忍颍” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵糟趾,是天一觀的道長(zhǎng)慌植。 經(jīng)常有香客問我,道長(zhǎng)义郑,這世上最難降的妖魔是什么蝶柿? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮非驮,結(jié)果婚禮上交汤,老公的妹妹穿的比我還像新娘。我一直安慰自己劫笙,他們只是感情好芙扎,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布星岗。 她就那樣靜靜地躺著,像睡著了一般纵顾。 火紅的嫁衣襯著肌膚如雪伍茄。 梳的紋絲不亂的頭發(fā)上栋盹,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天施逾,我揣著相機(jī)與錄音,去河邊找鬼例获。 笑死汉额,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的榨汤。 我是一名探鬼主播蠕搜,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼收壕!你這毒婦竟也來了妓灌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤蜜宪,失蹤者是張志新(化名)和其女友劉穎虫埂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體圃验,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掉伏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澳窑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斧散。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖摊聋,靈堂內(nèi)的尸體忽然破棺而出鸡捐,到底是詐尸還是另有隱情,我是刑警寧澤麻裁,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布箍镜,位于F島的核電站,受9級(jí)特大地震影響悲立,放射性物質(zhì)發(fā)生泄漏鹿寨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一薪夕、第九天 我趴在偏房一處隱蔽的房頂上張望脚草。 院中可真熱鬧,春花似錦原献、人聲如沸馏慨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽写隶。三九已至倔撞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間慕趴,已是汗流浹背痪蝇。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冕房,地道東北人躏啰。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像耙册,于是被迫代替她去往敵國和親给僵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容