FLV 封裝格式解析

本文爲做者原創,轉載請註明出處:http://www.javashuo.com/article/p-zcjsicjj-e.htmlhtml

FLV (Flash Video) 是由 Adobe 公司推出的一種封裝格式,主要用於流媒體系統。FLV 封裝的媒體文件具備體積輕巧、封裝播放簡單等特色,很適合網絡應用。目前各瀏覽器廣泛使用 Flash Player 做爲網頁播放器,使得安裝有瀏覽器的計算機終端不須要另外安裝播放器,這也是 FLV 格式廣爲流行的緣由之一。ios

FLV 封裝格式的文件擴展名爲 .flv。FLV 文件主要由一個 Header 加上由多個 Tag 組成的 Body 構成。以下所述:數組

1. FLV Header

全部 FLV 格式文件都以 FLV Header 開頭。FLV Header 類型是 FLVHEADER,FLVHEADER 定義以下:瀏覽器

字段 類型 說明
Signature UI8 'F' (0x46)
Signature UI8 'L' (0x4C)
Signature UI8 'V' (0x56)
Version UI8 FLV 版本。例如,0x01 表示 FLV 版本 1
TypeFlags UI8 b[0] 是否存在視頻流
b[2] 是否存在音頻流
其餘字段保留,值爲0
DataOffset UI32 FLV Header 長度(字節)

在 FLV 版本 1 中,「數據偏移」字段值爲 9。在 FLV 將來版本中,此字段可兼容更大尺寸的 FLV Header。網絡

typedef struct {
    UI8 Signature;
    UI8 Signature;
    UI8 Signature;
    UI8 Version;
    UI8 TypeFlags;
    UI32 DataOffset;
}   FLVHEADER;

2. FLV Body

一個 FLV 文件,除開頭的 FLV Header 外,剩餘部分就是 FLV Body。FLV Body 由一系列 back-pointer 和 tag 交織構成。back-pointer 表示前一 tag 大小。FLV Body 類型是 FLVBODY,FLVBODY 定義以下:ide

字段 類型 說明
PreviousTagSize0 UI32 值總爲 0
Tag1 FLVTAG 第一個 Tag
PreviousTagSize1 UI32 前一 Tag 大小,單位字節。FLV 版本 1 中, 此值等於前一 Tag 的 DataSize + 11
Tag2 FLVTAG 第二個 Tag
... ... ...
PreviousTagSizeN-1 UI32 倒數第二個 Tag 大小,單位字節
TagN FLVTAG 最後一個 Tag
PreviousTagSizeN UI32 最後一個 Tag 的大小,單位字節
typedef struct {
    UI32 PreviousTagSize0;
    FLVTAG Tag1;
    UI32 PreviousTagSize1;
    FLVTAG Tag2;
    ...
    UI32 PreviousTagSizeN-1;
    FLVTAG TagN;
    UI32 PreviousTagSizeN;
}   FLVBODY;

3. FLV Tag

FLV Tag 包含音頻、視頻或腳本元數據、可選的加密元數據和 payload。FLV Tag 類型是 FLVTAG,FLVTAG 定義以下:ui

字段 類型 說明
Reserved UB [2] 用於 FMS 的保留字段, 值爲 0
Filter UB [1] 指示 packet 是否須要預處理。
0 = 不須要預處理。
1 = packet 在渲染前須要預處理(例如解密)。
未加密文件中此值爲0,加密文件中此值爲1。
TagType UB [5] 8 = 音頻
9 = 視頻
18 = 腳本數據
DataSize UI24 Tag 中除通用頭外的長度,即 Header + Data 字段的長度 (等於 Tag 總長度 – 11)
Timestamp UI24 當前 Tag 的解碼時間戳 (DTS),單位是毫秒。FLV 文件中第一個 Tag 的 DTS 總爲 0
TimestampExtended UI8 和 Timestamp 字段一塊兒構成一個 32 位值, 此字段爲高 8 位。單位毫秒
StreamID UI24 總爲 0
Header IF TagType == 8
 AudioTagHeader
IF TagType == 9
 VideoTagHeader
音頻或視頻 TagHeader,注意腳本沒有 TagHeader
Data IF TagType == 8
 AUDIODATA
IF TagType == 9
 VIDEODATA
IF TagType == 18
 SCRIPTDATA
音頻、視頻或腳本 TagBody
typedef struct {
    UB[2] Reserved;
    UB[1] Filter;
    UB[5] TagType;
    UI24 DataSize;
    UI24 Timestamp;
    UI8 TimestampExtended;
    UI24 StreamID;
  IF TagType == 8
    AudioTagHeader Header;
  IF TagType == 9
    VideoTagHeader Header;
  IF TagType == 8
    AUDIODATA Data;
  IF TagType == 9
    VIDEODATA Data;
  IF TagType == 18
    SCRIPTDATA Data;
}   FLVTAG;

一個 FLVTAG 中,前 11 個字節是通用 TagHeader,後面緊跟跟着音頻 Tag、視頻 Tag 或腳本 Tag,其中音頻 Tag 和視頻 Tag 都包含 TagHeader 和 TagBody 兩部分,腳本 Tag 只有 TagBody 部分。編碼

上面 Timestamp 和 TimestampExtended 兩個字段拼成一個 32 位的時間戳,是當前 Tag 的解碼時間戳 (DTS)。對於音頻幀來講,PTS 和 DTS 相同。對於視頻幀來講,若含 B 幀,則 PTS 和 DTS 不一樣,H264 視頻幀 PTS = DTS + CTS,CTS 就是 CompositionTime 字段,參考 3.2.1 節 CompositionTime 字段的定義。加密

3.1 Audio Tag

Audio Tag 包括 AudioTagHeader 和 AudioTagBody 兩部分。code

3.1.1 AudioTagHeader

AudioTagHeader 定義以下:

字段 類型 說明
SoundFormat UB [4] 聲音格式:
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
SoundRate UB [2] 採樣率:
AAC: 總爲3
0 = 5.5 kHz
1 = 11 kHz
2 = 22 kHz
3 = 44 kHz
SoundSize UB [1] 採樣位深。此參數僅適用未壓縮格式,壓縮格式總在內部被解碼爲16位。
0 = 8位
1 = 16位
SoundType UB [1] 0 = 單聲道
1 = 立體聲
IF SoundFormat == 10
 AACPacketType
UI8 AAC幀類型。僅當聲音格式爲 10 時,存在此字段
0 = AAC sequence header
1 = AAC raw

格式 3,linear PCM,存儲原始 PCM 採樣點。若是採樣位深爲 8,採樣點數據爲無符號型。若是採樣位深爲 16,採樣點數據爲小端存儲的帶符號型。若是是立體聲,左右聲道採樣點交織存放:左-右-左-右-...

格式 0 與格式 3 的不一樣之處只有一點:格式 0 存儲 16 位採樣數據,採用的大小端順序是建立 FLV 文件的平臺所使用的大小端順序。所以,不該使用格式 0,而應使用格式 3。

格式 4 (Nellymoser 16-kHz mono) 和格式 5 (Nellymoser 8 kHz mono),是兩種特殊狀況, 由於採樣率字段沒法表示 8 kHz 和 16 kHz。當採樣格式是格式 4 或格式 5 時,Flash 播放器會忽略採樣率和聲音類型兩個字段。對於其餘採樣率的 Nellymoser 格式, 即格式 6,則正常使用採樣率和聲音類型兩個字段。

格式 10,AAC,聲音類型應爲 1 (立體聲) 且採樣率應爲 3 (44 kHz)。這並不表示 FLV 中的 AAC 音頻老是立體聲、44 kHz的數據。實際上,Flash 播放器會忽略這兩個值,而從已編碼的 AAC 位流中提取出聲道數和採樣率信息。

格式 11,Speex,音頻以 16 kHz採樣率壓縮爲單聲道,採樣率字段值應爲 0,採樣位深字段值應爲 1,聲音類型字段值應爲 0。

格式 7,8,14 和 15 保留。

typedef struct {
    UB [4] SoundFormat;
    UB [2] SoundRate;
    UB [1] SoundSize;
    UB [1] SoundType;
  IF SoundFormat == 10
    UI8 AACPacketType;
}

3.1.2 AudioTagBody/AUDIODATA

AUDIODATA 定義以下:

字段 類型 說明
Body IF Encrypted
 EncryptedBody
ELSE
 AudioTagBody
類型分加密與非加密兩種
typedef struct {
  IF Encrypted
    EncryptedBody Body
  else
    AudioTagBody Body;
}   AUDIODATA;

AUDIODATA 包含 Body 字段。若是採用了加密,Body 的類型是 EncryptedBody,可參考規範文檔「附件 F. FLV 加密」章節得到詳細信息,此處略。若是未採用加密,則 Body 的類型是 AudioTagBody,下面詳述。

AudioTagBody 定義以下:

字段 類型 說明
SoundData IF SoundFormat == 10
 AACAUDIODATA
ELSE
 Varies by format
字段類型根據聲音格式肯定
typedef struct {
  IF SoundFormat == 10
    AACAUDIODATA SoundData;
  ELSE
    Varies by format
}   AudioTagBody;

3.1.3 AACAUDIODATA

Flash 播放器 9.0.115.0 及以上版本支持 AAC 格式。AACAUDIODATA 定義以下:

字段 類型 說明
Data IF AACPacketType == 0
 AudioSpecificConfig
ELSE IF AACPacketType == 1
 Raw AAC frame data in UI8 []
AudioSpecificConfig 在 ISO 14496-3 中定義

3.2 Video Tag

Video Tag 包含 VideoTagHeader 和 VideoTagBody 兩部分。

3.2.1 VideoTagHeader

字段 類型 說明
FrameType UB [4] 幀類型:
1: keyframe (for AVC, a seekable frame)
2: inter frame (for AVC, a non-seekable frame)
3: disposable inter frame (H.263 only)
4: generated keyframe (reserved for server use only)
5: video info/command frame
CodecID UB [4] 編解碼器標識:
1: JPEG (currently unused)
2: Sorenson H.263
3: Screen video
4: On2 VP6
5: On2 VP6 with alpha channel
6: Screen video version 2
7: AVC
IF CodecID == 7
 AVCPacketType
UI8 AVC幀類型:
0 = AVC sequence header
1 = AVC NALU
2 = AVC end of sequence (lower level NALU sequence ender is not required or supported)
IF CodecID == 7
 CompositionTime
UI24 PTS 與 DTS 的時間偏移值,單位 ms,記做 CTS。參考 "ISO 14496-12, 8.15.3"

H.264 的命名遵循了 ITU-T 的命名約定,它是 VCEG 視頻編碼標準 H.26x 線中的一員;MPEG-4 AVC 的命名來自 ISO/IEC MPEG 的命名約定,它是 ISO/IEC 14496 的第 10 部分,該協議族被稱爲 MPEG-4。

3.2.2 VideoTagBody/VIDEODATA

VIDEODATA 定義以下:

字段 類型 說明
Body IF Encrypted
 EncryptedBody
ELSE
 VideoTagBody
類型分加密與非加密兩種
typedef struct {
  IF Encrypted
    EncryptedBody Body
  else
    VideoTagBody Body;
}   VIDEODATA;

VIDEODATA 包含 Body 字段。若是採用了加密,Body 的類型是 EncryptedBody,可參考規範文檔「附件 F. FLV 加密」章節得到詳細信息,此處略。若是未採用加密,則 Body 的類型是 VideoTagBody,下面詳述。

VideoTagBody 包含視頻幀淨荷數據。VideoTagBody 定義以下:

字段 長度 說明
VideoData IF FrameType == 5
 UI8
ELSE (
IF CodecID == 2
 H263VIDEOPACKET
IF CodecID == 3
 SCREENVIDEOPACKET
IF CodecID == 4
 VP6FLVVIDEOPACKET
IF CodecID == 5
 VP6FLVALPHAVIDEOPACKET
IF CodecID == 6
 SCREENV2VIDEOPACKET
IF CodecID == 7
 AVCVIDEOPACKET
)
視頻幀淨荷數據或視頻幀信息
除 AVCVIDEOPACKET 外的全部格式均可以參考 SWF 文件格式規範
typedef struct {
  IF FrameType == 5
    UI8 VideoData;
  ELSE (
  IF CodecID == 2
    H263VIDEOPACKET VideoData;
  IF CodecID == 3
    SCREENVIDEOPACKET VideoData;
  IF CodecID == 4
    VP6FLVVIDEOPACKET VideoData;
  IF CodecID == 5
    VP6FLVALPHAVIDEOPACKET VideoData;
  IF CodecID == 6
    SCREENV2VIDEOPACKET VideoData;
  IF CodecID == 7
    AVCVIDEOPACKET VideoData;
  )  
}   VideoTagBody;

3.2.3 AVCVIDEOPACKET

AVCVIDEOPACKET 包含 AVC(H264) 視頻淨荷數據。AVCVIDEOPACKET 定義以下:

字段 長度 說明
Data IF AVCPacketType == 0
 AVCDecoderConfigurationRecord
IF AVCPacketType == 1
 One or more NALUs (Full frames are required)
參考 ISO 14496-15, 5.2.4.1 中對 AVCDecoderConfigurationRecord 的描述
typedef struct {
  IF AVCPacketType == 0
    AVCDecoderConfigurationRecord Data;
  IF AVCPacketType == 1
    One or more NALUs
}   AVCVIDEOPACKET;

3.3 Data Tag

數據 Tag 封裝了單一方法,此方法一般在 Flash 播放器中的網絡流對象上被調用。數據 Tag 包含方法名和一組參數。

3.3.1 ScriptTagBody/SCRIPTDATA

SCRIPTDATA 定義以下:

字段 類型 說明
Body IF Encrypted
 EncryptedBody
ELSE
 ScriptTagBody
類型分加密與非加密兩種
typedef struct {
  IF Encrypted
    EncryptedBody Body
  else
    ScriptTagBody Body;
}   SCRIPTDATA;

SCRIPTDATA 包含 Body 字段。若是採用了加密,Body 的類型是 EncryptedBody,可參考規範文檔「附件 F. FLV 加密」章節得到詳細信息,此處略。若是未採用加密,則 Body 的類型是 ScriptTagBody,下面詳述。

ScriptTagBody 包含以 AMF(Action Message Format) 編碼的 SCRIPTDATA。AMF 是一種緊湊二進制格式,用於序列化 ActionScript 對象圖。ScriptTagBody 定義以下:

字段 類型 說明
Name SCRIPTDATAVALUE 方法名或對象名
Value SCRIPTDATAVALUE AMF 參數或對象屬性

這裏的 Name 就是上面提到的數據 Tag 中的方法名,Value 是此方法的一組參數。

typedef struct {
    SCRIPTDATAVALUE Name;
    SCRIPTDATAVALUE Value;
}   ScriptTagBody;

3.3.2 SCRIPTDATAVALUE

一個 SCRIPTDATAVALUE 記錄包含一個特定類型的 ActionScript 值。

SCRIPTDATAVALUE 定義以下:

字段 類型 說明
Type UI8 ScriptDataValue 的類型:
0 = Number
1 = Boolean
2 = String
3 = Object
4 = MovieClip (保留,不支持)
5 = Null
6 = Undefined
7 = Reference
8 = ECMA array
9 = Object end marker
10 = Strict array
11 = Date
12 = Long string
ScriptDataValue Type 字段值 -> 本字段類型:
0 -> DOUBLE
1 -> UI8
2 -> SCRIPTDATASTRING
3 -> SCRIPTDATAOBJECT
7 -> UI16
8 -> SCRIPTDATAECMAARRAY
10 -> SCRIPTDATASTRICTARRAY
11 -> SCRIPTDATADATE
12 -> SCRIPTDATALONGSTRING
腳本數據值

SCRIPTDATAVALUE 的兩個字段,Type 是類型,ScriptDataValue 是值。Type 的值肯定 ScriptDataValue 的類型。由於 ScriptDataValue 的類型是動態的,由運行時解析獲得的 Type 的值肯定,因此這裏類型和值用了兩個字段。若是是靜態類型,顯然只用一個字段就能夠了。

typedef struct {
    UI8 Type;
  IF Type == 0
    DOUBLE ScriptDataValue;
  IF Type == 1
    UI8 ScriptDataValue;
  IF Type == 2
    SCRIPTDATASTRING ScriptDataValue;
  IF Type == 3
    SCRIPTDATAOBJECT ScriptDataValue;
  IF Type == 7
    UI16 ScriptDataValue;
  IF Type == 8
    SCRIPTDATAECMAARRAY ScriptDataValue;
  IF Type == 10
    SCRIPTDATASTRICTARRAY ScriptDataValue;
  IF Type == 11
    SCRIPTDATADATE ScriptDataValue;
  IF Type == 12
    SCRIPTDATALONGSTRING ScriptDataValue;
}   SCRIPTDATAVALUE;

3.3.1 節中 Name 字段和 Value 字段的類型都是SCRIPTDATAVALUE。Name 表示方法名,實際類型一般是SCRIPTDATASTRING。Value 字段表示方法的一組參數,實際類型一般是SCRIPTDATAECMAARRAY。後文將介紹 SCRIPTDATASTRING 和 SCRIPTDATAECMAARRAY 兩種類型。其餘類型略,詳情可參考 FLV 規範文檔。

3.3.3 SCRIPTDATASTRING

SCRIPTDATASTRING 和 SCRIPTDATALONGSTRING 兩種類型用於存儲字符串,兩者可存儲字符串長度不一樣,SCRIPTDATASTRING 用於存儲不超過 65535 個字符的字符串。

SCRIPTDATASTRING 定義以下:

字段 類型 說明
StringLength UI16 StringData 字段的長度,單位字節。
StringData STRING 字符串實際數據,注意不帶結束符 NUL。
typedef struct {
    UI16 StringLength;
    STRING StringData;
}   SCRIPTDATASTRING;

3.3.4 SCRIPTDATAECMAARRAY

SCRIPTDATAECMAARRAY 記錄存儲 ECMA 數組(下表中的 Variables 字段)。ECMA 數組是一個關聯數組,應在 ActionScript 數組包含無序索引時使用。全部索引(無序或有序)都是字符串而不是整數。出於序列化的目的,SCRIPTDATAECMAARRAY 類型與匿名 ActionScript 對象很是類似。

SCRIPTDATAECMAARRAY 定義以下:

字段 類型 說明
ECMAArrayLength UI32 ECMA 數組元素數量(近似)
Variables SCRIPTDATAOBJECTPROPERTY[] 變量名和變量值的列表,即 ECMA 數組
ListTerminator SCRIPTDATAOBJECTEND 列表終止符
typedef struct {
    UI32 ECMAArrayLength;
    SCRIPTDATAOBJECTPROPERTY[] Variables;
    SCRIPTDATAOBJECTEND ListTerminator;
}   SCRIPTDATAECMAARRAY;

其中,SCRIPTDATAOBJECTPROPERTY 類型定義了 ActionScript 對象或關聯數組變量的對象屬性。

SCRIPTDATAOBJECTPROPERTY 定義以下:

字段 類型 說明
PropertyName SCRIPTDATASTRING 對象屬性或變量的名稱
PropertyData SCRIPTDATAVALUE 對象屬性或變量的值和類型
typedef struct {
    SCRIPTDATASTRING PropertyName;
    SCRIPTDATAVALUE PropertyData;
}   SCRIPTDATAOBJECTPROPERTY;

3.3.5 實例:onMetaData 對象

FLV 元數據對象應在名爲 onMetadata 的 SCRIPTDATA 標籤中攜帶。各類屬性對經過 NetStream.onMetaData 屬性運行的 ActionScript 程序有效。可用的屬性根據建立 FLV 文件的軟件而有所不一樣。典型屬性包括:

字段 類型 說明
audiocodecid Number 音頻編解碼器 ID
audiodatarate Number 音頻碼率,單位 kbps
audiodelay Number 由音頻編解碼器引入的延時,單位秒
audiosamplerate Number 音頻採樣率
audiosamplesize Number 音頻採樣點尺寸
canSeekToEnd Boolean 指示最後一個視頻幀是不是關鍵幀
creationdate String 建立日期與時間
duration Number 文件總時長,單位秒
filesize Number 文件總長度,單位字節
framerate Number 視頻幀率
height Number 視頻高度,單位像素
stereo Boolean 音頻立體聲標誌
videocodecid Number 視頻編解碼器 ID
videodatarate Number 視頻碼率,單位 kbps
width Number 視頻寬度,單位像素

onMetaData 標籤一般會成爲 FLV Body 中的第一個標籤,緊跟在 FLV Header 以後。onMetaData 標籤中存儲的是一些視頻、音頻及文件相關的元數據信息:如視頻幀率,音頻採樣率、文件長度等。

結合 3.3.1 節,onMetaData 標籤的 Name 字段主要就是存儲 「onMetaData」 字符串。具體爲:第 1 個字節值是 0x02,表示 Name 字段是字符串類型。第 2-3 個字節爲 UI16 類型值,標識字符串的長度,值爲 0x000A (「onMetaData」 這個字符串的長度)。後面跟着的數據爲具體的字符串,值爲 「onMetaData」。

onMetaData 標籤的 Value 字段存儲上表所示的各屬性鍵值對。具體爲:第 1 個字節值是 0x08,表示 Value 字段是數組類型。第 2-5 個字節爲UI32類型值,表示數組元素個數。後面緊跟着數組,數組元素爲屬性名稱和值組成的對(鍵值對)。最後是數組的結束符。

ScriptTagBody onMetaData;

onMetaData.Name.Type == 0x02
onMetaData.Name.ScriptDataValue.StringLength == 0x000A
onMetaData.Name.ScriptDataValue.StringData == "onMetaData"

onMetaData.Value.Type == 0x08
onMetaData.Value.ScriptDataValue.ECMAArrayLength == 
onMetaData.Value.ScriptDataValue.Variables[0].PropertyName == {0x0005, "width"}   // SCRIPTDATASTRING 類型
onMetaData.Value.ScriptDataValue.Variables[0].PropertyData == {0x00, 1280.0}      // SCRIPTDATAVALUE 類型
onMetaData.Value.ScriptDataValue.Variables[1].PropertyName == {0x0005, "height"}  // SCRIPTDATASTRING 類型
onMetaData.Value.ScriptDataValue.Variables[1].PropertyData == {0x00, 720.0}       // SCRIPTDATAVALUE 類型
...

4. 總結

FLV 結構以下圖所示:
FLV格式示意圖

在 C 語言中定義 FLV 文件結構,一目瞭然:

/*
 * @brief flv file header 9 bytes
 */
typedef struct flv_header {
    uint8_t signature[3];
    uint8_t version;
    uint8_t type_flags;
    uint32_t data_offset; // header size, always 9
} __attribute__((__packed__)) flv_header_t;

/*
 * @brief flv tag general header 11 bytes
 */
typedef struct flv_tag {
    uint8_t tag_type;
    uint32_t data_size;
    uint32_t timestamp;
    uint8_t timestamp_ext;
    uint32_t stream_id;
    void *data; // will point to an audio_tag or video_tag
}   flv_tag_t;

typedef struct audio_tag {
    uint8_t sound_format; // 0 - raw, 1 - ADPCM, 2 - MP3, 4 - Nellymoser 16 KHz mono, 5 - Nellymoser 8 KHz mono, 10 - AAC, 11 - Speex
    uint8_t sound_rate; // 0 - 5.5 KHz, 1 - 11 KHz, 2 - 22 KHz, 3 - 44 KHz
    uint8_t sound_size; // 0 - 8 bit, 1 - 16 bit
    uint8_t sound_type; // 0 - mono, 1 - stereo
    void *data;
}   audio_tag_t;

typedef struct video_tag {
    uint8_t frame_type;
    uint8_t codec_id;
    void *data;
}   video_tag_t;

typedef struct avc_video_tag {
    uint8_t avc_packet_type; // 0x00 - AVC sequence header, 0x01 - AVC NALU
    uint32_t composition_time;
    uint32_t nalu_len;
    void *data;
}   avc_video_tag_t;

5. 參考資料

[1] Adobe Flash Video File Format Specification Version 10.1, "Annex E. The FLV File Format"
[2] FLV 存在 B 幀狀況下的 DTS 和 PTS, https://www.jianshu.com/p/1cbe31baa711

6. 修改記錄

2019-03-30 V1.0 初稿

相關文章
相關標籤/搜索