多媒體文件格式之FLV

1. 概述

FLV(Flash Video) 是 Adobe 公司設計開發的一種流媒體格式,其封裝格式的文件後綴一般爲 ".flv"。整體上看,FLV 包括文件頭(File Header)和文件體(File Body)兩部分,其中文件體由一系列的 Tag 組成。一個 FLV 文件,每種類型的 Tag 都屬於一個流,也就是一個 FLV 文件最多隻有一個音頻流,一個視頻流,不存在多個獨立的音頻流/視頻流在一個文件中的狀況。php

注:FLV 文件格式用大端字節序。ios

FLV 總體文件格式圖

image
注:Header 下面的 4 個字節是 PreviousTagSize 0,由於前面的 Header 不是一個 Tag,因此值爲 0。git

2. FLV Header

header 部分記錄了 flv 的類型、版本等信息,佔 9bytes。具體格式以下:github

文件類型      3bytes    通常爲 "FLV" (0x46, 0x4c, 0x66)
-------------------------------------------------------------------------------------------
版本          1byte     通常爲 0x01
-------------------------------------------------------------------------------------------
流信息        1byte     前5位和第7位保留,爲0。第6位和第8位分別表示是否存在音頻 Tag 和視頻 Tag
-------------------------------------------------------------------------------------------
header大小    4bytes    整個header的長度,通常爲 9;大於 9 表示下面還有擴展信息
-------------------------------------------------------------------------------------------

3. FLV Body

body 部分由一個個 Tag 組成,每一個 Tag 的下面有一個 4bytes 的空間,用來記錄這個 Tag 的大小,這個後置是用於逆向讀取處理。數組

3.1 Tag

每一個 Tag 也是由兩部分組成:Tag Header 和 Tag Data。Tag Header 裏存放的是當前 Tag 的類型、數據區(Tag Data)的大小等信息,具體格式以下:服務器

Field               type                    Comment
-------------------------------------------------------------------------------------------
                                8: audio
Tag類型             UI8         9: video
                                18: script data——這裏是一些描述信息。
                                all others: reserved其餘全部值未使用。
-------------------------------------------------------------------------------------------
數據大小            UI24        數據區的大小,不包括Tag Header。Tag Header總大小是11個字節。
-------------------------------------------------------------------------------------------
時戳                UI24        當前幀時戳,單位是毫秒。相對於FLV文件的第一個Tag時戳。
                                第一個tag的時戳老是0。注:不是時戳增量,rtmp中是時戳增量。
-------------------------------------------------------------------------------------------
時戳擴展字段        UI8         若是時戳大於0xFFFFFF,將會使用這個字節。這個字節是時戳的高8位,
                                上面的三個字節是低24位。
-------------------------------------------------------------------------------------------
流ID                U24         老是 0
-------------------------------------------------------------------------------------------
數據區              UI8[n]

數據區根據 Tag 類型的不一樣能夠分爲:Audio Tag,Video Tag,Script Tag。ide

4. Script Data

該類型 Tag 又一般被稱爲 Metadata Tag,會放一些關於 FLV 視頻和音頻的元數據信息如:duration、width、height 等。一般該類型 Tag 會跟在 File Header 後面做爲第一個 Tag 出現,並且只有一個。結構以下圖所示:
函數

  • 第一個 AMF 包:
    • 第 1 個字節表示 AMF 包類型,通常老是 0x02,表示 string;
    • 第 2~3 字節爲 UI16 類型值,標識字符串的長度,通常老是 0x000A("onMetadata" 的長度);
    • 後面的字節爲具體的字符串,通常總爲 "onMetadata" (6F,6E,4D,65,74,61,44,61,74,61)。
  • 第二個 AMF 包:
    • 第 1 個字節表示 AMF 包類型,通常老是 0x08,表示 ECMA array 類型;
    • 第 2~5 個字節爲 UI32 類型值,表示數組元素的個數;
    • 後面即爲每一個數組元素的封裝,數據元素爲元素名稱和值組成對。常見的數組元素以下所示。
      • duration:時長
      • width:視頻寬度
      • height:視頻高度
      • videodatarate:視頻碼率
      • framerate:視頻幀率
      • videocodecid:視頻編碼方式
      • audiosamplerate:音頻採樣率
      • audiosamplesize:音頻採樣精度
      • stereo:是否爲立體聲
      • audiocodecid:音頻編碼方式
      • fileSize:文件大小

以下圖,爲經過 obs 推送 FLV 文件到 RTMP 服務器上抓到的 FLV Script Tag 數據:
ui

5. Audio Data

音頻 Tag 的數據區開始的第 1 個字節包含了音頻數據參數信息,從第 2 個字節開始爲音頻流數據。結構以下圖所示:
this

  • 音頻格式(4bits):
    • 0 = Linear PCM, platform endian
    • 1 = ADPCM
    • 2 = MP3
    • 3 = Linear PCM, little endian
    • 4 = Nellymoser 16-kHz mono
    • 5 = Nellymoser 8-kHz mono
    • 6 = Nellymoser
    • 7 = G.711 A-law logarithmic PCM
    • 8 = G.711 mu-law logarithmic PCM
    • 9 = reserved
    • 10 = AAC
    • 11 = Speex
    • 14 = MP3 8-Khz
    • 15 = Device-specific sound
    • 7, 8, 14, and 15:內部保留使用
    • flv 是不支持 g711a 的,若是要用,可能要用線性音頻
  • 採樣率(2bits):
    • 0 = 5.5-kHz
    • 1 = 11-kHz
    • 2 = 22-kHz
    • 3 = 44-kHz,For AAC: always 3
  • 採樣大小(1bit):
    • 0 = snd8Bit
    • 1 = snd16Bit
    • 壓縮過的音頻都是 16bit
  • 聲道(1bit):
    • 0 = 單聲道
    • 1 = 立體聲,即雙聲道。AAC 永遠是 1
  • AACPacketType(1byte):實際上,若爲 AAC 數據,則還有一個字節表示 AACPacketType,不然沒有這個字節。這個字段表示 AACAudioData 的類型:
    • 0 = AAC sequence header,相似 H.264 的 sps,pps,在 FLV 文件頭部中出現一次
    • 1 = AAC raw
  • 數據:若是是 PCM 線性數據,存儲的時候每一個 16bits 小端存儲,有符號。若是音頻格式是 AAC,則存儲的數據是 AAC AUDIO DATA,不然爲線性數組。

下面針對存儲的爲 AAC 音頻數據進行分析。

5.1 AAC sequence header

AAC sequence header 這個音頻包有些 FLV 文件裏面沒有也能夠正確解碼。但對於 RTMP 播放,必需要在發送第一個 AAC raw 包以前發送這個 AAC sequence header 包。

AAC Sequence header 定義爲 Audio Specific Config,Audio Specific Config 包含着一些更加詳細的音頻信息,它的定義在 ISO14496-3 中的 1.6.2.1。具體能夠參考 MPEG-4 Audio

Audio Specific Config 音頻包數據區的格式以下:

5 bits: object type
if (object type == 31)
    6 bits + 32: object type
4 bits: frequency index
if (frequency index == 15)
    24 bits: frequency
4 bits: channel configuration
var bits: AOT Specific Config

簡化版的 Audio Specific Config 的 2 字節定義以下:

AAC Profile 5bits | 採樣率表索引 4bits | 聲道數 4bits | 其餘 3bits |
  • audio object type,即 AAC Profile(5bits):下面列舉經常使用的
    • 1:Main,主規格,包含了除增益控制以外的所有功能,其音質最好
    • 2:LC,低複雜度規格(Low Complexity),如今的手機比較常見的 MP4 文件中的音頻部分就包括了該規格音頻文件
    • 3:SSR
    • 5:AAC HE = LC + SBR,高效率規格(High Efficiency),這種規格適合用於低碼率編碼,有 Nero AAC 編碼器支持
    • 29:AAC HEv2 = LC + SBR + PS
  • Sampling Frequency Index(4bits):用來表示採樣率表中的索引號
    • 0x00: 96000 Hz
    • 0x01: 88200 Hz
    • 0x02: 64000 Hz
    • 0x03: 48000 Hz
    • 0x04: 44100 Hz
    • 0x05: 32000 Hz
    • 0x06: 24000 Hz
    • 0x07: 22050 Hz
    • 0x08: 16000 Hz
    • 0x09: 12000 Hz
    • 0x0A: 11025 Hz
    • 0x0B: 8000 Hz
    • 0x0C: 7350 Hz
  • channel configuraion(4bits):聲道數
    • 0: Defined in AOT Specifc Config
    • 1: 1 channel: front-center
    • 2: 2 channels: front-left, front-right
    • 3: 3 channels: front-center, front-left, front-right
    • 4: 4 channels: front-center, front-left, front-right, back-center
    • 5: 5 channels: front-center, front-left, front-right, back-left, back-right
    • 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
    • 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
    • 8-15: Reserved
  • 其餘 3bits 設置爲 0 便可

SRS 中對 Audio Specific Config(即 AAC Sequence header)的解碼以下:

u_int8_t profile_ObjectType = stream->read_1bytes();
u_int8_t samplingFrequencyIndex = stream->read_1bytes();
    
aac_channels = (samplingFrequencyIndex >> 3) & 0x0f;
samplingFrequencyIndex = ((profile_ObjectType << 1) & 0x0e) | 
                         ((samplingFrequencyIndex >> 7) & 0x01);
profile_ObjectType = (profile_ObjectType >> 3) & 0x1f;

下圖是經過 obs 推 FLV 到 RTMP 服務器後抓包到的第一個 Audio Tag,即 AAC Sequence header:

注:如上圖所示,0xaf 接下來的 0x00 即爲 AACPacketType,爲 0 表示這是 AAC Sequence header。

5.2 AAC raw

以下圖,爲抓包獲得的一個 obs 推 FLV 文件到 RTMP 服務器的 video raw 音頻包:

6. Video Data

和 Audio Tag 的數據區同樣,Video Tag 的第一個字節是視頻信息,第二個字節開始纔是視頻數據。第一個字節格式以下:

名稱              長度              介紹
-------------------------------------------------------------------------------------------
                                    1: keyframe (for AVC, a seekable frame)
                                    2: inter frame (for AVC, a non-seekable frame)
幀類型           4 bits             3: disposable inter frame (H.263 only)
                                    4: generated keyframe (reserved for server use only)
                                    5: video info/command frame
-------------------------------------------------------------------------------------------
                                     使用哪一種編碼類型
                                    1: JPEG (currently unused)
                                    2: Sorenson H.263
                                    3: Screen video
編碼ID           4 bits             4: On2 VP6
                                    5: On2 VP6 with alpha channel
                                    6: Screen video version 2
                                    7: AVC 
-------------------------------------------------------------------------------------------
視頻數據         UI[N]              若是是 avc,則參考下面的介紹:AVCVIDEOPACKET

6.1 AVCVIDEOPACKET

Field              type       Comment
------------------------------------------------------------------------
                              0:AVC序列頭
AVC packet 類型    UI8        1:AVC NALU單元
                              2:AVC序列結束。低級別avc不須要。
------------------------------------------------------------------------
CTS                SI24       若是 AVC packet 類型是 1,則爲 cts 偏移(見下面的解釋),爲 0 則爲 0
------------------------------------------------------------------------
數據               UI8[n]     若是AVC packet類型是0,則是解碼器配置,sps,pps。
                              若是是1,則是nalu單元,能夠是多個

關於CTS:這是一個比較難以理解的概念,須要和pts,dts配合一塊兒理解。

  • pts: 顯示時間,也就是接收方在顯示器顯示這幀的時間。單位爲 1/90000 秒。
  • dst:解碼時間,也就是 rtp 包 或 rtmp 包中傳輸的時間戳,代表解碼的順序。單位爲 1/90000 秒。根據後面的理解,cts 就是標準中的 CompositionTime。
  • ctx 偏移:cts = (pts - dts) / 90. cts 的單位是毫秒。

pts 和 dts 的時間不同,應該只出如今含有 B 幀的狀況下,也就是 profile main 以上。baseline 是沒有這個問題的,baseline 的 pts 和 dts 一直相同,因此 cts 一直爲 0。

在 FLV Tag 中的時戳就是 dts。

6.2 AVCVIDEOPACKET 中 data 格式

Field       type      Comment
------------------------------------------------------------------------
長度        UI32      nalu 單元的長度,不包括長度字段
------------------------------------------------------------------------
nalu數據    UI8[N]    NALU 數據,沒有四個字節的 nalu 單元頭,直接從 h264 頭開始
                      好比:65 ** ** **,41 **  ** **
------------------------------------------------------------------------
長度        UI32      nalu 單元的長度,不包括長度字段。
---------------------------------------------------------------------------
nalu數據    UI8[N]    NALU 數據,沒有四個字節的 nalu 單元頭,直接從 h264 頭開始
                      好比:65 ** ** **,41 **  ** ** 
------------------------------------------------------------------------
...

6.3 AVCDecoderConfigurationRecord

當 AVC packet 類型的類型爲 0 是,則表示是解碼器配置,即 sps,pps,保存控制信息。此時數據區的格式以下圖:
image
記錄sps,pps信息。通常出如今第二個tag中,緊跟在onMeta以後。

一個典型的序列:

0000190: 0900 0033 0000 0000 0000 0017 0000 0000  ...3............

00001a0: 0164 002a ffe1 001e 6764 002a acd9 4078  .d.*....gd.*..@x

00001b0: 0227 e5ff c389 4388 0400 0003 0028 0000  .'....C......(..

00001c0: 0978 3c60 c658 0100 0568 ebec b22c 0000  .x<`.X...h...,..
  • 17: 表示h264IDR data
  • 00:表示是AVC序列頭
  • 00 00 00 :cts爲0

//今後往下就是AVCDecoderConfigurationRecord

  • 01 :版本號
  • 64 00 2a:profile level id,sps的三個字節,64 表示是h264 high profile,2a表示level。
  • FF:NALU長度,爲3?不知道這個長度用在哪裏。
  • E1:表示下面緊跟SPS有一個。

//sps[N]:sps數組。

  • 00 1e: 前面是兩個字節的sps長度,表示後面的sps的長度是1e大小。
  • 6764 002a acd9 4078 0227 e5ff c389 4388 0400 0003 0028 0000 0978 3c60 c658:sps的數據。

//由於只有一個sps,跳過這些長度,而後就是pps的個數信息:

  • 01 :pps個數,1

//pps[n] pps 的個數

  • 00 05:表示pps的大小是5個字節。
  • 68 eb ec b2 2c:pps的數據
  • 00 00 …….這是下一個tag 的內容了

以下圖,爲經過 obs 推 FLV 文件到 RTMP 服務器抓包獲得的 video sps,pps 的包:

6.4 NALU 數據

在 6.1 中,若 AVC packet 類型爲 1 的話,則爲 nalu 單元。對於 H264 的 nalu 數據有兩中封裝格式:

  • AnnexB
  • ISO Base Media File Format

6.4.1 Annexb

對於該格式的 H264,能夠經過如下代碼提取 NALU。

avc_demux_annexb_format

int SrsAvcAacCodec::avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    /* not annexb, try others */
    if (!srs_avc_startswith_annexb(stream, NULL)) {
        return ERROR_HLS_AVC_TRY_OTHERS;
    }
    
    /* AnnexB
     * B.1.1 Byte stream NAL unit syntax,
     * H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
     */
    while (!stream->empty()) {
        /* find start code */
        int nb_start_code = 0;
        if (!srs_avc_startswith_annexb(stream, &nb_start_code)) {
            return ret;
        }
        
        /* skip the start code. */
        if (nb_start_code > 0) {
            stream->skip(nb_start_code);
        }
        
        /* the NALU start bytes. */
        char* p = stream->data() + stream->pos();
        
        /* get the last matched NALU */
        while (!stream->empty()) {
            if (srs_avc_startswith_annexb(stream, NULL)) {
                break;
            }
            
            stream->skip(1);
        }
        
        /* 此時 pp 指向下一個 NALU start bytes */
        char* pp = stream->data() + stream->pos();
        
        /* skip the empty. */
        if (pp - p <= 0) {
            continue;
        }
        
        /* 獲取到一個 NALU 後,將該 NALU 添加到 sample 中的 sample_units 數組中 */
        /* got the NALU. */
        if ((ret = sample->add_sample_unit(p, pp - p)) != ERROR_SUCCESS) {
            srs_error("annexb add video sample failed. ret=%d", ret);
            return ret;
        }
    }
    
    return ret;
}

srs_avc_startswith_annexb

/* 
 * whether stream starts with the avc NALU in "AnnexB"
 * from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
 * start code must be "N[00] 00 00 01" where N>=0
 * @param pnb_start_code, output the size of start code, must >=3.
 *       NULL to ignore.
 */
bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code)
{
    char* bytes = stream->data() + stream->pos();
    char* p = bytes;
    
    for ( ;; ) {
        if (!stream->require(p - bytes + 3)) {
            return false;
        }
        
        /* not match */
        if (p[0] != (char)0x00 || p[1] != (char)0x00) {
            return false;
        }
        
        /* match N[00] 00 00 01, where N>=0 */
        if (p[2] == (char)0x01) {
            if (pnb_start_code) {
                *pnb_start_code = (int)(p - bytes) + 3;
            }
            return true;
        }
        
        p++;
    }
    
    return false;
}

經過以上代碼可知,若 H264 爲 Annexb 封裝格式,則 NALU 之間是以 0x000001(3bytes) 或者 0x00000001(4bytes) 分割。

6.4.2 ISO Base Media File Format

提取該封裝格式的 NALU 以下代碼所示:

/*
 * demux the avc NALU in "ISO Base Media File Format" 
 * from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
 */
int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    int PictureLength = stream->size() - stream->pos();
    
    /*
     * 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
     * 5.2.4.1 AVC decoder configuration record
     * 5.2.4.1.2 Semantics
     * The value of this field shall be one of 0, 1, or 3 corresponding to a
     * length encoded with 1, 2, or 4 bytes, respectively.
     */
    srs_assert(NAL_unit_length != 2);
    
    /* 
     * 該 NAL_unit_length 的值即爲解析 sps 的獲取到的 lengthSizeMinusOne 字段值
     */
    
    /* 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 20 */
    for (int i = 0; i < PictureLength; ) {
        /* unsigned int((NAL_unit_length+1)*8) NALUnitLength; */
        if (!stream->require(NAL_unit_length + 1)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("avc decode NALU size failed. ret=%d", ret);
            return ret;
        }
        int32_t NALUnitLength = 0;
        if (NAL_unit_length == 3) {
            NALUnitLength = stream->read_4bytes();
        } else if (NAL_unit_length == 1) {
            NALUnitLength = stream->read_2bytes();
        } else {
            NALUnitLength = stream->read_1bytes();
        }
        
        /* maybe stream is invalid format. 
         * see: https://github.com/ossrs/srs/issues/183 */
        if (NALUnitLength < 0) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("maybe stream is AnnexB format. ret=%d", ret);
            return ret;
        }
        
        /* NALUnit */
        if (!stream->require(NALUnitLength)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("avc decode NALU data failed. ret=%d", ret);
            return ret;
        }
        /* 7.3.1 NAL unit syntax, H.264-AVC-ISO_IEC_14496-10.pdf, page 44. */
        if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), NALUnitLength)) 
            != ERROR_SUCCESS) {
            srs_error("avc add video sample failed. ret=%d", ret);
            return ret;
        }
        stream->skip(NALUnitLength);
        
        i += NAL_unit_length + 1 + NALUnitLength;
    }
    
    return ret;
}

由該函數源碼可知,若 H264 爲 "ISO Base Media File Format",則各個 NALUnit 之間是以 1byte 或 2bytes 或 4bytes 分割的,這 1byte 或 2bytes 或 4bytes 即爲 NALUnitLength 所佔的字節數,具體爲 1byte 仍是 2bytes 或者 4bytes 是由 sps 中的 lengthSizeMinusOne 值決定的。若 lengthSizeMinusOne 值爲 3,則 NALUnitLength 佔 4bytes;若 lengthSizeMinusOne 值爲 1,則 NALUnitLength 佔 2bytes;若 lengthSizeMinusOne 值爲 0,則 NALUnitLength 佔 1 字節。

相關文章
相關標籤/搜索