本文爲做者原創,轉載請註明出處:http://www.javashuo.com/article/p-zcjsicjj-e.htmlhtml
FLV (Flash Video) 是由 Adobe 公司推出的一種封裝格式,主要用於流媒體系統。FLV 封裝的媒體文件具備體積輕巧、封裝播放簡單等特色,很適合網絡應用。目前各瀏覽器廣泛使用 Flash Player 做爲網頁播放器,使得安裝有瀏覽器的計算機終端不須要另外安裝播放器,這也是 FLV 格式廣爲流行的緣由之一。ios
FLV 封裝格式的文件擴展名爲 .flv。FLV 文件主要由一個 Header 加上由多個 Tag 組成的 Body 構成。以下所述:數組
全部 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;
一個 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;
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 字段的定義。加密
Audio Tag 包括 AudioTagHeader 和 AudioTagBody 兩部分。code
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; }
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;
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 中定義 |
Video Tag 包含 VideoTagHeader 和 VideoTagBody 兩部分。
字段 | 類型 | 說明 |
---|---|---|
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。
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;
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;
數據 Tag 封裝了單一方法,此方法一般在 Flash 播放器中的網絡流對象上被調用。數據 Tag 包含方法名和一組參數。
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;
一個 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 規範文檔。
SCRIPTDATASTRING 和 SCRIPTDATALONGSTRING 兩種類型用於存儲字符串,兩者可存儲字符串長度不一樣,SCRIPTDATASTRING 用於存儲不超過 65535 個字符的字符串。
SCRIPTDATASTRING 定義以下:
字段 | 類型 | 說明 |
---|---|---|
StringLength | UI16 | StringData 字段的長度,單位字節。 |
StringData | STRING | 字符串實際數據,注意不帶結束符 NUL。 |
typedef struct { UI16 StringLength; STRING StringData; } SCRIPTDATASTRING;
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;
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 類型 ...
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;
[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
2019-03-30 V1.0 初稿