FLV(Flash Video) 是 Adobe 公司設計開發的一種流媒體格式,其封裝格式的文件後綴一般爲 ".flv"。整體上看,FLV 包括文件頭(File Header)和文件體(File Body)兩部分,其中文件體由一系列的 Tag 組成。一個 FLV 文件,每種類型的 Tag 都屬於一個流,也就是一個 FLV 文件最多隻有一個音頻流,一個視頻流,不存在多個獨立的音頻流/視頻流在一個文件中的狀況。php
注:FLV 文件格式用大端字節序。ios
注:Header 下面的 4 個字節是 PreviousTagSize 0,由於前面的 Header 不是一個 Tag,因此值爲 0。git
header 部分記錄了 flv 的類型、版本等信息,佔 9bytes。具體格式以下:github
文件類型 3bytes 通常爲 "FLV" (0x46, 0x4c, 0x66) ------------------------------------------------------------------------------------------- 版本 1byte 通常爲 0x01 ------------------------------------------------------------------------------------------- 流信息 1byte 前5位和第7位保留,爲0。第6位和第8位分別表示是否存在音頻 Tag 和視頻 Tag ------------------------------------------------------------------------------------------- header大小 4bytes 整個header的長度,通常爲 9;大於 9 表示下面還有擴展信息 -------------------------------------------------------------------------------------------
body 部分由一個個 Tag 組成,每一個 Tag 的下面有一個 4bytes 的空間,用來記錄這個 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
該類型 Tag 又一般被稱爲 Metadata Tag,會放一些關於 FLV 視頻和音頻的元數據信息如:duration、width、height 等。一般該類型 Tag 會跟在 File Header 後面做爲第一個 Tag 出現,並且只有一個。結構以下圖所示:
函數
以下圖,爲經過 obs 推送 FLV 文件到 RTMP 服務器上抓到的 FLV Script Tag 數據:
ui
音頻 Tag 的數據區開始的第 1 個字節包含了音頻數據參數信息,從第 2 個字節開始爲音頻流數據。結構以下圖所示:
this
下面針對存儲的爲 AAC 音頻數據進行分析。
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 |
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。
以下圖,爲抓包獲得的一個 obs 推 FLV 文件到 RTMP 服務器的 video raw 音頻包:
和 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
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 和 dts 的時間不同,應該只出如今含有 B 幀的狀況下,也就是 profile main 以上。baseline 是沒有這個問題的,baseline 的 pts 和 dts 一直相同,因此 cts 一直爲 0。
在 FLV Tag 中的時戳就是 dts。
Field type Comment ------------------------------------------------------------------------ 長度 UI32 nalu 單元的長度,不包括長度字段 ------------------------------------------------------------------------ nalu數據 UI8[N] NALU 數據,沒有四個字節的 nalu 單元頭,直接從 h264 頭開始 好比:65 ** ** **,41 ** ** ** ------------------------------------------------------------------------ 長度 UI32 nalu 單元的長度,不包括長度字段。 --------------------------------------------------------------------------- nalu數據 UI8[N] NALU 數據,沒有四個字節的 nalu 單元頭,直接從 h264 頭開始 好比:65 ** ** **,41 ** ** ** ------------------------------------------------------------------------ ...
當 AVC packet 類型的類型爲 0 是,則表示是解碼器配置,即 sps,pps,保存控制信息。此時數據區的格式以下圖:
記錄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...,..
//今後往下就是AVCDecoderConfigurationRecord
//sps[N]:sps數組。
//由於只有一個sps,跳過這些長度,而後就是pps的個數信息:
//pps[n] pps 的個數
以下圖,爲經過 obs 推 FLV 文件到 RTMP 服務器抓包獲得的 video sps,pps 的包:
在 6.1 中,若 AVC packet 類型爲 1 的話,則爲 nalu 單元。對於 H264 的 nalu 數據有兩中封裝格式:
對於該格式的 H264,能夠經過如下代碼提取 NALU。
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; }
/* * 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) 分割。
提取該封裝格式的 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 字節。