直播協議 HTTP-FLV 詳解

又拍雲發表於2017-08-24

傳統的直播協議要麼使用 Adobe 的基於 TCP 的 RTMP 協議,要麼使用 Apple 的基於 HTTP 的 HLS 協議。

今天我要向大家介紹另外一種結合了 RTMP 的低延時,以及可以複用現有 HTTP 分發資源的流式協議 HTTP-FLV。

FLV

首先介紹一下 FLV 檔案格式的細節。

FLV Adobe 官方標準

FLV 檔案格式標準是寫在 F4V/FLV file format spec v10.1 的附錄 E 裡面的 FLV File Format。

單位說明

型別

說明

Unit

data

types

 

SI8

Signed 8-bit integer

SI16

Signed 16-bit integer

SI24

Signed 24-bit integer

SI32

Signed 32-bit integer

SI64

Signed 32-bit integer

UI8

Unsigned 8-bit integer

UI16

Unsigned 16-bit integer

UI24

Unsigned 24-bit integer

UI32

Unsigned 32-bit integer

UI64

Unsigned 64-bit integer

xxx[]

Slice of type xxx

xxx[n]

Array of type xxx

STRING

Sequence of Unicode 8-bit characters (UTF-8), terminated with 0x00

FLV 檔案頭和檔案體 (E.2, E.3)

從整個檔案上看,FLV = FLV File Header + FLV File Body。

欄位

型別

說明

FLV File Header

   

Signature

UI8[3]

簽名,總是 “FLV” (0x464C56)

Version

UI8

版本,總是 0x01,表示 FLV version 1

TypeFlagsReserved

UB [5]

全 0

TypeFlagsAudio

UB[1]

1 = 有音訊

TypeFlagsReserved

UB[1]

全 0

TypeFlagsVideo

UB[1]

1 = 有視訊

DataOffset

UI32

整個檔案頭長度,對於FLV v1,總是 9

FLV File Body

   

PreviousTagSize0

UI32

總是 0

Tag1

FLVTAG

第一個 tag

PreviousTagSize1

UI32

前一個 tag 的大小, 包括他的 header,即:11 + 前一個 tag 的大小

Tag2

FLVTAG

第二個 tag

   

PreviousTagSizeN-1

UI32

前一個 tag 大小

TagN

FLVTAG

最後一個 tag

PreviousTagSizeN

UI32

最後一個 tag 大小,包括他的 header

通常,FLV 的前 13 個位元組(flv header + PreviousTagSize0)完全相同,所以,程式中會單獨定義一個常量來指定。

FLV Tag (E.4)

欄位

型別

說明

FLV Tag

   

Reserved

UB[2]

保留給FMS, 應為 0

Filter

UB[1]

0 = unencrypted tags,1 = encrypted tags

TagType

UB [5]

型別,0x08 = audio,0x09 = video,0x12 = script data

DataSize

UI24

message 長度,從 StreamID 到 tag 結束(len(tag) - 11)

Timestamp

UI24

相對於第一個 tag 的時間戳(unit: ms),第一個 tag 總是 0

TimestampExtended

UI8

Timestamp 的高 8 位。 擴充套件 Timestamp 為 SI32 型別

StreamID

UI24

總是 0,至此為 11 bytes

AudioTagHeader

 

IF TagType == 0x08

VideoTagHeader

 

IF TagType == 0x09

EncryptionHeader

 

IF Filter == 1

FilterParams

 

IF Filter == 1

Data

 

AUDIODATA 或者 VIDEODATA 或者 SCRIPTDATA

Timestamp 和 TimestampExtended 組成了這個 TAG 包資料的 PTS 資訊,PTS = Timestamp | TimestampExtended << 24。

AudioTag (E.4.2)

由於 AAC 編碼的特殊性,這裡著重說明了 AAC 編碼的 Tag 格式。

欄位

型別

說明

Audio Tag

   

AudioTagHeader

   

SoundFormat

UB[4]

音訊編碼格式。2 = MP3,10 = AAC,11 = Speex

SoundRate

UB[2]

取樣率。0 = 5.5 kHz,1 = 11 kHz,2 = 22 kHz,3 = 44 kHz

SoundSize

UB[1]

取樣大小。 0 = 8-bit,1 = 16-bit

SoundType

UB[1]

音訊聲道數。0 = Mono,1 = Stereo

AACPacketType

UI8

只有當 SoundFormat 為 10 時,才有該欄位。0 = AAC sequence header,1 = AAC raw

AACAUDIODATA

   

Data

AudioSpecificConfig

IF AACPacketType == 0,包含著一些更加詳細音訊的資訊

Data

Raw AAC frame data in UI8 [n]

IF AACPacketType == 1,audio payload,n = [AAC Raw data length] - ([has CRC] ? 9:7)

AudioTagHeader 的第一個位元組,也就是接跟著 StreamID 的 1 個位元組包含了音訊型別,取樣率等的基本資訊。

AudioTagHeader 之後跟著的就是 AUDIODATA 部分了。但是,這裡有個特例,如果音訊格式(SoundFormat)是 AAC,AudioTagHeader 中會多出 1 個位元組的資料 AACPacketType,這個欄位來表示 AACAUDIODATA 的型別:0 = AAC sequence header,1 = AAC raw。

AudioSpecificConfig 結構描述非常複雜,在標準文件中是用虛擬碼描述的,這裡先假定要編碼的音訊格式,做一下簡化。

音訊編碼為:AAC-LC,音訊取樣率為 44100。

欄位

型別

說明

AudioSpecificConfig

   

audioObjectType

UB[5]

編碼結構型別,AAC-LC 為 2

samplingFrequencyIndex

UB[4]

音訊取樣率索引值,44100 對應值 4

channelConfiguration

UB[4]

音訊輸出聲道,2

GASpecificConfig

   

frameLengthFlag

UB[1]

標誌位,用於表明 IMDCT 視窗長度,0

dependsOnCoreCoder

UB[1]

標誌位,表明是否依賴於 corecoder,0

extensionFlag

UB[1]

選擇了 AAC-LC,這裡必須為 0

在 FLV 的檔案中,一般情況下 AAC sequence header 這種包只出現1次,而且是第一個 audio tag,為什麼需要這種 tag,因為在做 FLV demux 的時候,如果是 AAC 的音訊,需要在每幀 AAC ES 流前邊新增 7 個位元組 ADST 頭,ADST 是解碼器通用的格式,也就是說 AAC 的純 ES 流要打包成 ADST 格式的 AAC 檔案,解碼器才能正常播放。就是在打包 ADST 的時候,需要 samplingFrequencyIndex 這個資訊,samplingFrequencyIndex 最準確的資訊是在 AudioSpecificConfig 中,這樣,你就完全可以把 FLV 檔案中的音訊資訊及資料提取出來,送給音訊解碼器正常播放了。

VideoTag (E.4.3)

由於 AVC(H.264) 編碼的特殊性,這裡著重說明了 AVC(H.264) 編碼的 Tag 格式。

欄位

型別

說明

Video Tag

   

VideoTagHeader

   

FrameType

UB[4]

1 = key frame,2 = inter frame

CodecID

UB[4]

7 = AVC

AVCPacketType

UI8

IF CodecID == 7,0 = AVC sequence header(AVCDecoderConfigurationRecord),1 = One or more AVC NALUs (Full frames are required),2 = AVC end of sequence

CompositionTime

SI24

IF AVCPacketType == 1 Composition time offset ELSE 0

VideoTagHeader 的第一個位元組,也就是接跟著 StreamID 的 1 個位元組包含著視訊幀型別及視訊 CodecID 等最基本資訊。

VideoTagHeader 之後跟著的就是 VIDEODATA 部分了。但是,這裡有個特例,如果視訊格式(CodecID)是 AVC,VideoTagHeader 會多出 4 個位元組的資訊。

AVCDecoderConfigurationRecord 包含著是 H.264 解碼相關比較重要的 SPS 和 PPS 資訊,在給 AVC 解碼器送資料流之前一定要把 SPS 和 PPS 資訊送出,否則的話,解碼器不能正常解碼。而且在解碼器 stop 之後再次 start 之前,如 seek,快進快退狀態切換等,都需要重新送一遍 SPS 和 PPS 的資訊。AVCDecoderConfigurationRecord 在 FLV 檔案中一般情況也只出現 1 次,也就是第一個 video tag。

AVCDecoderConfigurationRecord 長度為 sizeof(UI8) * (11 + sps_size + pps_size)。

欄位

型別

說明

AVCDecoderConfigurationRecord

   

configurationVersion

UI8

版本號,1

AVCProfileIndication

UI8

SPS[1]

profileCompatibility

UI8

SPS[2]

AVCLevelIndication

UI8

SPS[3]

reserved

UB[6]

111111

lengthSizeMinusOne

UB[2]

NALUnitLength - 1,一般為 3

reserved

UB[3]

111

numberOfSequenceParameterSets

UB[5]

SPS 個數, 一般為 1

sequenceParameterSetNALUnits

UI8[sps_size + 2]

sps_size(16bits) + sps(UI8[sps_size])

numberOfPictureParameterSets

UI8

PPS 個數, 一般為 1

pictureParameterSetNALUnits

UI8[pps_size + 2]

pps_size(16bits) + pps(UI8[pps_size])

SCRIPTDATA (E.4.4)

ScriptTagBody 內容用 AMF 編碼

欄位

型別

說明

SCRIPTDATA

   

ScriptTagBody

   

Name

SCRIPTDATAVALUE

Method or object name. SCRIPTDATAVALUE.Type = 2 (String)

Vale

SCRIPTDATAVALUE

AMF arguments or object properties.

SCRIPTDATAVALUE

   

Type

UI8

ScriptDataValue 的型別

ScriptDataValue

各種型別

Script data 值

一個 SCRIPTDATAVALUE 記錄包含一個有型別的 ActionScript 值。

onMetadata (E.5)

FLV metadata object 儲存在 SCRIPTDATA 中, 叫 onMetaData。不同的軟體生成的 FLV 的 properties 不同。

欄位

型別

說明

onMetaData

   

audiocodecid

Number

Audio codec ID used in the file

audiodatarate

Number

Audio bit rate in kilobits per second

audiodelay

Number

Delay introduced by the audio codec in seconds

audiosamplerate

Number

Frequency at which the audio stream is replayed

audiosamplesize

Number

Resolution of a single audio sample

canSeekToEnd

Boolean

Indicating the last video frame is a key frame

creationdate

String

Creation date and time

duration

Number

Total duration of the file in seconds

filesize

Number

Total size of the file in bytes

framerate

Number

Number of frames per second

height

Number

Height of the video in pixels

stereo

Boolean

Indicating stereo audio

videocodecid

Number

Video codec ID used in the file (see E.4.3.1 for available CodecID values)

videodatarate

Number

Video bit rate in kilobits per second

width

Number

Width of the video in pixels

keyframes 索引資訊

官方的文件中並沒有對 keyframes index 做描述,但是,flv 的這種結構每個 tag 又不像 TS 有同步頭,如果沒有 keyframes index 的話,需要按順序讀取每一個tag, seek 及快進快退的效果會非常差。後來在做 flv 檔案合成的時候,發現網上有的 flv 檔案將 keyframes 資訊隱藏在 Script Tag 中。

keyframes 幾乎是一個非官方的標準, 也就是民間標準。兩個常用的操作 metadata 的工具是 flvtool2 和 FLVMDI,都是把 keyframes 作為一個預設的元資訊專案。在 FLVMDI 的主頁上有描述:

 

keyframes: (Object) This object is added only if you specify the /k switch. 'keyframes' is known to FLVMDI and if /k switch is not specified, 'keyframes' object will be deleted.

 'keyframes' object has 2 arrays: 'filepositions' and 'times'. Both arrays have the same number of elements, which is equal to the number of key frames in the FLV. Values in times array are in 'seconds'. Each correspond to the timestamp of the n'th key frame. Values in filepositions array are in 'bytes'. Each correspond to the fileposition of the nth key frame video tag (which starts with byte tag type 9).

 

也就是說 keyframes 中包含著 2 個內容 “filepositions” 和 “times”分別指的是關鍵幀的檔案位置和關鍵幀的 PTS。通過 keyframes 可以建立起自己的 Index,然後在 seek 和快進快退的操作中,快速有效地跳轉到你想要找的關鍵幀位置進行處理。

FLV 分析工具

  • http://www.flvmeta.com/

  • yamdi:將flv轉成帶索引的flv,yamdi -i i.flv -o o.flv

  • flvlib: pip install flvlib, 檢視索引資訊:debug-flv --metadata file.flv

  • flvcheck:http://www.adobe.com/products/adobe-media-server-family/tool-downloads.html

HTTP-FLV

HTTP-FLV,即將音視訊資料封裝成 FLV,然後通過 HTTP 協議傳輸給客戶端。

HLS 其實是一個 “文字協議”,而並非流媒體協議。那麼,什麼樣的協議才能稱之為流媒體協議呢?

流(stream): 資料在網路上按時間先後次序傳輸和播放的連續音/視訊資料流。之所以可以按照順序傳輸和播放連續是因為在類似 RTMP、FLV 協議中,每一個音視訊資料都被封裝成了包含時間戳資訊頭的資料包。而當播放器拿到這些資料包解包的時候能夠根據時間戳資訊把這些音視訊資料和之前到達的音視訊資料連續起來播放。MP4、MKV 等等類似這種封裝,必須拿到完整的音視訊檔案才能播放,因為裡面的單個音視訊資料塊不帶有時間戳資訊,播放器不能將這些沒有時間戳資訊資料塊連續起來,所以就不能實時的解碼播放。

延遲分析

理論上(除去網路延遲外),FLV 可以做到僅僅一個音視訊 tag 的延遲。

相比 RTMP 的優點:

  • 可以在一定程度上避免防火牆的干擾 (例如, 有的機房只允許 80 埠通過);

  • 可以很好的相容 HTTP 302 跳轉,做到靈活排程;

  • 可以使用 HTTPS 做加密通道;

  • 很好的支援移動端(Android,IOS);

 

推薦閱讀:

基於 WEBRTC 技術的實時通訊服務開發實踐

直播卡頓原因詳解及優化

從Html5直播到互動直播,看直播協議的選擇

相關文章