(本文基本逻辑:FLV 封装格式概览 → Audio Tags 解析 → Video Tags 解析 → Data Tags 解析)
FLV(Flash Video)是 Adobe 公司推出的一种流媒体格式,它的特点是封装后的音视频文件较小、封装规范简单,因此适合在互联网上进行传输和使用。在浏览器普遍支持 Flash 插件的时代,FLV 格式的视频非常流行。但是随着主流的浏览器平台逐步放弃了对 Flash 插件的支持后,以及移动互联网的兴起,App 取代浏览器成为更多内容的载体,在短视频领域 FLV 的地位逐步被 MP4 取代。但是,在直播领域,由于 RTMP 推流、HTTP-FLV 播放的整套方案低延时的特性,以及服务端普遍提供 HTTP Web 服务,能更广泛的兼容 HTTP-FLV,使得 FLV 仍然是大多数直播产品的首选流媒体格式。
1、FLV 格式概览
FLV 文件由一个 FLV Header 和一个 FLV Body 组成,在 FLV Body 中则由多组(PreviousTagSize + Tag)组成。
FLV 中 Tag 的类型有三种:
- Audio Tags
- Video Tags
- Data Tags
Tag 中包含着 audio、video、scripts 的元信息,加密信息(可选)以及对应的实际数据。
总体来讲,FLV 的结构大致如下表所示:

2、Audio Tags 解析
Audio Tag 的结构大致如下所示:

通常在 AudioTagHeader 后面跟着就是 AUDIODATA 数据了,但是对于 AAC 格式的音频数据来说,AudioTagHeader 会多一个字段 AACPacketType 来表示 AACAUDIODATA 的类型:如果 AACPacketType 为 0,那么数据对应的是 AudioSpecificConfig;如果 AACPacketType 为 1,那么数据对应的为 Raw AAC frame data。
为什么 AudioTagHeader 中已经有了音频的相关参数,还需要在这里来一个 AudioSpecificConfig 呢?这是因为当 SoundFormat 是 AAC 时,SoundType 需要设置为 1(立体声),SoundRate 需要设置为 4(44k Hz),但这并不说明文件中 AAC 编码的音频必须是 44k Hz 的立体声。播放器在处理 AAC 音频时,需要忽略 AudioTagHeader 中的音频参数,而使用 AudioSpecificConfig 的参数来初始化解码器。
在 FLV 的文件中,一般情况下 AudioSpecificConfig 只会出现一次,即第一个 Audio Tag。如果音频使用 AAC,那么这个 Tag 就是 AAC sequence header,即 AAC 音频同步包。AudioSpecificConfig 的结构在ISO/IEC-14496-3 Audio标准中有做说明。
下面是ISO/IEC 14496-3, 1.6.2.1 AudioSpecificConfig:
AudioSpecificConfig() {3 {6 u& Z) L% M% s% j- F
audioObjectType = GetAudioObjectType();) u1 g* h* K, } H7 |8 L1 E" Q
samplingFrequencyIndex; // 4 bslbf5 t5 S J; z7 l0 l4 ?4 e
if (samplingFrequencyIndex == 0xf) {) U; ?: S; P% Y' z# c
samplingFrequency; // 24 uimsbf
7 x3 o5 `5 r- Y6 k } s' u* V$ D: n; P1 I" x0 x
channelConfiguration; // 4 bslbf( y4 O$ ?! s e3 \2 a+ q
sbrPresentFlag = -1; v' S* }1 D6 V7 r9 V
psPresentFlag = -1;! n: L$ Q: E. ?; X6 }
if (audioObjectType == 5 || audioObjectType == 29) {
, Q" g V8 S! W // ...
! B& Y {4 U. {* x6 y }
& N" ]! i, d$ J3 s7 b+ a5 Z else {
. |- j# X! |+ p/ R extensionAudioObjectType = 0;
* k" D% J }5 ^- `1 R }% B! Z1 E3 P9 ~7 e+ Q: x# R0 ]
switch (audioObjectType) {
* c% f2 o( f$ b5 J( o case 1: case 2: case 3: case 4: //...7 g, r; f' b4 O K. Y+ R$ i
GASpecificConfig();
' e* f% k5 W! W% F) k break:
. n$ `# P- f8 w3 u6 J$ g case ...:
0 ^# x+ q; ~& A( b; ?+ b //...
9 Y3 H( q6 ^; t/ s# d6 `8 w }, P* k( ]% t3 X8 v3 _
if (extensionAudioObjectType != 5 && bits_to_decode() >= 16) {
" I+ f+ `0 d, M) _. h N //...
4 V& c. ~$ b# G }
+ l/ O- k% g& U; e4 k1 c& R // ...
% t$ e; ]% z% `! U" A$ H# c
7 D! F$ ~0 z- }1 d/ CGetAudioObjectType() {
9 h3 I4 i0 A& v' R" w+ X. V audioObjectType; // 5 uimsbf- W/ D& o( H1 N2 d2 G- z
if (audioObjectType == 31) {
- k0 V( @5 F* { audioObjectType = 32 + audioObjectTypeExt; // 6uimsbf}
# y4 v7 N- ~$ q: H8 f return audioObjectType;. m0 p7 S. P5 B7 x$ R/ I
}
下面是ISO/IEC 14496-3, 1.5.1.1 Audio object type definition:

下面是ISO/IEC 14496-3, 1.6.3.4 samplingFrequencyIndex:

【相关学习资料推荐,点击下方链接免费报名,先码住不迷路~】
【免费分享】我整理了一些比较好的面试题、学习资料、教学视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击788280672加群领取~


我们常用的 AAC 音频同步包的大小固定为 4 字节,前两个字节被称为AACDecoderSpecificInfo,用于描述这个音频包应当如何被解析,后两个字节称为AudioSpecificConfig,更加详细的指定了音频格式。下图是一个 AAC 音频同步包的示例:

在完成 AAC 音频同步包的发送后,我们就可以向服务器推送普通的 AAC 数据包了。在发送数据包时,AACDecoderSpecificInfo则变为0xAF01,向服务器说明这个包是普通 AAC 数据包。如果这里的 AAC 数据有包含 7 个字节 ADTS 头(若存在 CRC 校验,则是 9 个字节),那么要去掉这个头后,把裸数据放到这里。如果这里是采集到的裸数据,没有 ADTS 头,那么这里就不需要这样处理了。下图是一个 AAC 音频数据包的示例:

对应的,在解析 FLV 时,如果封装的是 AAC 的音频,要在每帧 AAC ES 流前把 7 个字节 ADTS 头添加回来,这是因为 ADTS 是解码器通用的格式,纯的 AAC ES 流要打包成 ADTS 格式的 AAC 文件,解码器才能正常解码。在打包 ADTS 的时候,需要用到 samplingFrequencyIndex 这个信息,samplingFrequencyIndex 最准确的信息是存储在 AudioSpecificConfig 中。
有关 AudioSpecificConfig 结构解析的代码,可以参考ffmpeg/libavcodec/mpeg4audio.c中的avpriv_mpeg4audio_get_config函数。
int avpriv_mpeg4audio_get_config(MPEG4AudioConfig *c, const uint8_t *buf, int bit_size, int sync_extension)* r' O) {. M9 P
{$ @% i9 |6 x7 }. K* _% u# _
GetBitContext gb;6 [3 D" U, I' j, H
int ret;
( L' J X: p+ F( |/ u+ V' Q: p; M( M& B4 e0 m2 ?: ]* `3 C
if (bit_size <= 0). c/ c, g' T! Z* y3 Q+ F) x! [- Q
return AVERROR_INVALIDDATA;% [: F2 P* h7 P7 k" G- t+ q
. w/ J5 j) d+ @6 `$ Q ret = init_get_bits(&gb, buf, bit_size);( L' T- ^* u: l0 l, P* {6 M; D
if (ret < 0)
% [- J& ~3 E0 V7 w" r return ret;
: q8 s' M( m% p. `+ q
9 B2 q' i4 g, N; b* {' |2 r. e return ff_mpeg4audio_get_config_gb(c, &gb, sync_extension);5 s _2 S4 j2 m7 [6 O4 O7 y
}
# S. h. w5 {. a- o" D' b6 S! ?0 r3 z: {1 n
int ff_mpeg4audio_get_config_gb(MPEG4AudioConfig *c, GetBitContext *gb, int sync_extension)
{6 z* h1 m$ ~4 V/ Q9 X! O{7 z/ ^4 U9 D8 _8 f/ a! a! M
int specific_config_bitindex, ret;
w4 L+ ^3 d7 e# `, L3 {2 Q int start_bit_index = get_bits_count(gb);
* S; y h) [3 L c->object_type = get_object_type(gb);
: u- b4 L7 T0 ]) A5 n c->sample_rate = get_sample_rate(gb, &c->sampling_index);
" g+ o5 Y ^6 J) T* { c->chan_config = get_bits(gb, 4);, q7 e5 y, M0 G" x/ c
if (c->chan_config < FF_ARRAY_ELEMS(ff_mpeg4audio_channels)): H* Y0 K- u: I, L
c->channels = ff_mpeg4audio_channels[c->chan_config];8 D0 T8 [7 J+ j& j4 |
c->sbr = -1;
. I$ U) E3 {: d/ D0 J/ A8 ]) N c->ps = -1;
( N* `8 i* F& U8 x7 F" `( ` if (c->object_type == AOT_SBR || (c->object_type == AOT_PS && g9 @! t" x/ T6 y0 p
// check for W6132 Annex YYYY draft MP3onMP4
" [9 A) {! A5 o, q, h !(show_bits(gb, 3) & 0x03 && !(show_bits(gb, 9) & 0x3F)))) {3 K6 c) W, z4 ?6 P! f& v
if (c->object_type == AOT_PS): B! @. d$ F6 @3 ]7 u/ V( m) `& N
c->ps = 1;, D/ ^, J, i% X9 \* Q0 W r5 Y4 G
c->ext_object_type = AOT_SBR;$ {0 x5 ~9 b% U5 {
c->sbr = 1;
; T2 O( t) ~8 U: Y/ N1 V c->ext_sample_rate = get_sample_rate(gb, &c->ext_sampling_index);8 A2 ~* S$ b& t5 b" x
c->object_type = get_object_type(gb);7 W0 P0 R5 L7 ?% A4 ^, @3 Y
if (c->object_type == AOT_ER_BSAC)
8 I( W$ l7 y: Y6 g N( U1 w. f c->ext_chan_config = get_bits(gb, 4);, y5 J& O6 k) v+ E) V- R) x1 A
} else {
6 ^1 D2 r3 p0 J% ~ c->ext_object_type = AOT_NULL;! U7 g! \2 ~ R* G: a/ R% D
c->ext_sample_rate = 0;
1 ~+ i6 }1 X1 A! ]( { }
% Z+ Z: H2 q, v+ z- ?1 m0 ` specific_config_bitindex= get_bits_count(gb);6 l$ T. o) b# P4 _
! Z9 v; @- l/ x+ z3 W* t
if (c->object_type == AOT_ALS) {
6 A7 T) X& T2 H* J" s: \( a+ g skip_bits(gb, 5);
7 q( E7 {( ]$ _' u' i! O if (show_bits_long(gb, 24) != MKBETAG(\0,A,L,S))
1 w; ? S% Z, T- b: t. v skip_bits_long(gb, 24);
4 O- h$ ^2 w( b4 J, ]8 g
. q% X% C6 @3 V' b specific_config_bitindex = get_bits_count(gb);% w# S! U) n; w8 s
1 I0 X, O2 [9 D: v3 ^& T
ret = parse_config_ALS(gb, c);9 {, D1 S- S+ M6 [
if (ret < 0)
* c' J+ [9 m: q9 @( [8 e return ret;* ]+ k/ }& p2 k. W# q' F! e# i m# N/ @
}
% {& O, W! A7 n& A& W% b$ O, J. {/ u1 e3 X
if (c->ext_object_type != AOT_SBR && sync_extension) {
5 s& G( N7 y. Y+ N: r while (get_bits_left(gb) > 15) {4 m* i5 n- o$ w( R
if (show_bits(gb, 11) == 0x2b7) { // sync extension6 b( y u2 K( T1 Z4 I
get_bits(gb, 11);- }( b% C$ l6 L8 \4 D, ~
c->ext_object_type = get_object_type(gb);
% ~, S( w9 t1 O- k; {0 g1 t; } if (c->ext_object_type == AOT_SBR && (c->sbr = get_bits1(gb)) == 1) {
! x; T$ K# d- x3 H, K( `. x8 ?. L c->ext_sample_rate = get_sample_rate(gb, &c->ext_sampling_index);
$ y9 d- w# \0 k4 ^( v& m if (c->ext_sample_rate == c->sample_rate)
5 N! x$ j1 Q* L c->sbr = -1;
% D! J1 X/ z1 E" t* Q9 T7 `% G$ }% y } H* ^+ U2 O; z
if (get_bits_left(gb) > 11 && get_bits(gb, 11) == 0x548)
0 m6 V/ Z, ]* v+ M" l c->ps = get_bits1(gb);( Z, s. o! ^+ F- O& L
break;$ F0 w8 o$ j4 H6 o! H+ J. `. \) @: e: E
} else0 |5 l6 j6 U; l& i; @6 o; f
get_bits1(gb); // skip 1 bit
1 f5 n6 j2 }& ?' ^3 T: } }) h' T" m- {$ W- ]0 W A; s
}- u. g$ U. Y) I G2 w e2 L0 {8 F
' ?. \7 x3 r8 g9 `, i //PS requires SBR/ E' z; H& B, ~0 E9 W
if (!c->sbr)( B0 s7 |7 y9 m3 _8 ^' Q3 P# q# t
c->ps = 0;( t0 R6 O/ a: _2 @1 T: K
//Limit implicit PS to the HE-AACv2 Profile
7 i9 t0 y- q, F6 u$ e if ((c->ps == -1 && c->object_type != AOT_AAC_LC) || c->channels & ~0x01)
" j9 R1 N1 N' \6 B# p9 k0 x c->ps = 0;
3 v5 P( J+ ?& ?; G5 `* w5 n, W
. V9 Q) x- d, {- Z5 e& s9 q return specific_config_bitindex - start_bit_index;2 m7 {' I5 ~$ ^5 Y; P! U
}
3、Video Tags 解析
Video Tag 的结构大致如下所示:

如上图所示,一般在 VideoTagHeader 后面跟着的就是 VIDEODATA 数据了,但是对于 AVC(H.264) 的编码格式来说,VideoTagHeader 会多出两个字段 AVCPacketType 和 CompositionTime。AVCPacketType 是表示后面 VIDEODATA 的类型,CompositionTime 则表示 pts 和dts的差值。
如果 AVCPacketType 为 0,那么这里的数据对应的是 AVCDecoderConfigurationRecord;如果 AVCPacketType 为 1,那么这里的数据对应的是 One or more NALUs(Full frames are required)。
AVCDecoderConfigurationRecord 记录的是 AVC(H.264)解码相关比较重要的 sps 和 pps 信息,解码器在解码数据之前需要首先获取的sps和 pps 的信息。在做 seek 或者断流重连等操作引起解码器重启时,也需要给解码器再传一遍 sps 和 pps 信息。
在 FLV 的文件中,一般情况下 AVCDecoderConfigurationRecord 只会出现一次,即第一个 Video Tag。如果视频使用 AVC,那么这个 Tag 就是 AVC sequence header,即 AVC 视频同步包。AVCDecoderConfigurationRecord 结构的在ISO/IEC-14496-15 AVC file format标准中有做说明。
下面是ISO/IEC 14496-15, 5.3.3.1.2 AVCDecoderConfigurationRecord:
aligned(8) class AVCDecoderConfigurationRecord {
1 V0 c7 A6 u; L, a; K/ P" J. b unsigned int(8) configurationVersion = 1;# O( V* P( ^+ c# Q6 s! y* A
unsigned int(8) AVCProfileIndication;) }/ D$ D) q( i: D3 g/ L
unsigned int(8) profile_compatibility;% \3 o- |6 ^) w% L/ R5 d6 t
unsigned int(8) AVCLevelIndication;
3 M5 Y; Y1 N6 P# {' k$ o, j bit(6) reserved = 111111b;3 }+ |0 \+ R* c7 m$ }% ?+ c
unsigned int(2) lengthSizeMinusOne; 5 r: O" @3 m$ K% s' O, n
bit(3) reserved = 111b;
% X7 p% d9 |# { unsigned int(5) numOfSequenceParameterSets;+ u& L D7 p% |; U6 l4 }+ m
for (i = 0; i < numOfSequenceParameterSets; i++) {' J( M" u: H* F. s
unsigned int(16) sequenceParameterSetLength ;
A7 ^3 V, Q; ^, N; N9 K bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;) K0 w3 h' N: v8 j7 J1 I
}& F' k! Q4 R# x6 w- M
unsigned int(8) numOfPictureParameterSets;, Y7 {3 Q1 f. U2 N- B. }
for (i = 0; i < numOfPictureParameterSets; i++) {# \9 ^5 Y4 p! t, H' ^, G3 \! m/ a
unsigned int(16) pictureParameterSetLength;
1 P: p) x* K9 s bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;, x. x: f9 r3 u+ P+ y$ V# U
}: s C: ]1 t6 l( b
if (profile_idc == 100 || profile_idc == 110 ||. N. b2 B) v6 w
profile_idc == 122 || profile_idc == 144)( \/ Q, U9 O6 a- U L3 B5 Y, W
{0 J5 @8 E" l1 f! L
bit(6) reserved = 111111b;( K' b/ ^/ {0 d, l
unsigned int(2) chroma_format;
" S2 S) f. }; }6 q# h- j0 T bit(5) reserved = 11111b;+ |# m: o$ s) i8 b% `( x3 J( a
unsigned int(3) bit_depth_luma_minus8;. ]/ x& B) o1 d. V- O1 K
bit(5) reserved = 11111b;, E/ o; ^. v% Y4 K, w! J
unsigned int(3) bit_depth_chroma_minus8;
9 ]6 n7 I1 h* } W6 u1 l* Q unsigned int(8) numOfSequenceParameterSetExt;
8 F K1 d7 }- Q for (i = 0; i < numOfSequenceParameterSetExt; i++) {
* Q& s; p' B# }1 {0 o7 ]; N9 b7 \6 G: s unsigned int(16) sequenceParameterSetExtLength;1 j6 E: G" A& \* S( z2 c
bit(8*sequenceParameterSetExtLength) sequenceParameterSetExtNALUnit;
7 `2 F! t) `4 C7 Z6 Q3 ^7 V }
0 z0 o+ o& |7 Z( f }
' Q3 d7 F2 ~, H# E) H}
下面是ITU-T H.264(ISO/IEC 14496-10), A.2 Profiles:

下面是ITU-T H.264(ISO/IEC 14496-10), A.3 Levels:
Defined level_idc: 10, 9[^1], 11, 12, 13, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52( j5 y9 v$ R) h7 G& T
' E( p* }- ?. d[^1]: "level_idc==9" represents "Leve 1b" unless Baseline(Constrained Baseline), Main, or Extended profiles.
下面是ISO/IEC 14496-15, 8.3.3.1.2 HEVCDecoderConfigurationRecord:
aligned(8) class HEVCDecoderConfigurationRecord {' s r3 r$ a8 v* z0 I Z. }# c
unsigned int(8) configurationVersion = 1;: Q2 i- H9 v8 `8 i5 i
unsigned int(2) general_profile_space;" P6 L6 y& {" ^2 }5 e3 t
unsigned int(1) general_tier_flag;7 y) ]! `# R0 G- C; J, E4 c& T
unsigned int(5) general_profile_idc;; m8 q8 \% T) E7 a8 o4 L3 m: y. X
unsigned int(32) general_profile_compatibility_flags;
7 ?0 X) V# b9 ` unsigned int(48) general_constraint_indicator_flags;( }3 K- V3 H3 Y0 Z; j
unsigned int(8) general_level_idc;0 m& J# {4 Z( T
bit(4) reserved = ‘1111’b;6 n, t9 F8 b- ^% m+ }; ~
unsigned int(12) min_spatial_segmentation_idc;
, r1 n: H- G z* y4 s bit(6) reserved = ‘111111’b;# p* k' z$ ?4 w) B% N
unsigned int(2) parallelismType;
" x1 ?4 R; k$ w bit(6) reserved = ‘111111’b;
0 ^' L: c1 b! f8 t0 M0 ~0 p0 u unsigned int(2) chromaFormat;0 d/ f' y$ Q" E6 }/ q8 b w
bit(5) reserved = ‘11111’b;! i4 M% B6 U+ K; ]" ~
unsigned int(3) bitDepthLumaMinus8;
* f8 d) T6 \0 N( f/ s3 b bit(5) reserved = ‘11111’b;
# }- E$ Y5 ?$ w* D \# } unsigned int(3) bitDepthChromaMinus8;
0 v6 B5 w: a/ ^3 |- b% }! ]8 E bit(16) avgFrameRate;$ c+ ?7 \6 c8 g! u5 y) h$ j) J
bit(2) constantFrameRate;% y! e5 I6 w5 m/ Q6 X
bit(3) numTemporalLayers;
, k. P) X# ?2 X bit(1) temporalIdNested;* t$ I3 E0 f# V
unsigned int(2) lengthSizeMinusOne; " g5 ^6 p# i, K
unsigned int(8) numOfArrays;+ L9 I0 b; y. [: j8 ?
for (j=0; j < numOfArrays; j++) {
% A C2 f* e2 g bit(1) array_completeness;
0 {/ d9 n2 w6 }" d unsigned int(1) reserved = 0;0 L9 I2 a4 z% N/ {
unsigned int(6) NAL_unit_type;% }+ G, r( {7 _6 F$ N
unsigned int(16) numNalus;
+ U3 j& n& V1 r2 d+ u& ]; m for (i=0; i< numNalus; i++) {
5 }- E% i, B" v8 o) t7 W4 n0 K unsigned int(16) nalUnitLength;, c* j V& X. W) W
bit(8*nalUnitLength) nalUnit;: a; d9 g! q& D" S+ u2 M; f& m
}
( G3 v, R8 R' t4 S3 d }
* Y( n5 v6 ?$ ?}
下面是ITU-T H.265(ISO/IEC 23008-2), A.3 Profiles:

下面是ITU-T H.265(ISO/IEC 23008-2), A.4 Tiers and levels:


下图是一个 AVC 视频同步包的示例,其中红框部分对应的是 VIDEODATA:


下图是一个 AVC 视频数据包的示例,其中红框部分对应的是 VIDEODATA:


有关 AVCDecoderConfigurationRecord 结构解析的代码,可以参考ffmpeg/libavformat/avc.c中的ff_isom_write_avcc函数。
int ff_isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len)
" y6 o- }- e* t8 _+ ?{8 G$ D4 F- ]* N4 ~2 y3 l
if (len > 6) {
7 @( |8 @8 D. a! \7 d /* check for H.264 start code */7 O0 p" W$ f4 ?( g" Y5 q- t
if (AV_RB32(data) == 0x00000001 ||
! X4 ~) S7 c4 ^8 n9 A$ `3 q7 @ AV_RB24(data) == 0x000001) {
2 `: L" ~3 V% i/ } uint8_t *buf=NULL, *end, *start;2 H. Q1 c6 X; O! @
uint32_t sps_size=0, pps_size=0;
( z7 X' v( R, ?% Z2 R/ ~ uint8_t *sps=0, *pps=0;
+ [/ E" g/ b8 Z' D' H+ d* N7 l" P, X! s( K0 [" C# q
int ret = ff_avc_parse_nal_units_buf(data, &buf, &len);
2 E& e# N# q8 R# D8 A: z c0 P if (ret < 0)& ]7 i! u1 N0 _5 ~/ s+ w
return ret;
3 B% u+ W* F/ `/ S6 k8 M' G start =buf;9 }8 F* O; g/ \ H
end = buf + len;
* e/ i L. N1 ]3 W) ] K# F2 Y& ?: |
/* look for sps and pps */
1 B! R! ^) G& y1 B while (end - buf > 4) {
2 t7 W Z8 l. F( n uint32_t size; t& j/ b2 E! ]7 B. R( S+ R, f! i
uint8_t nal_type;
% H+ ~9 w( w+ E) ?4 z; |- { size = FFMIN(AV_RB32(buf), end - buf - 4);- p7 C6 S) g, ~4 ]8 N$ x6 r
buf += 4;
5 @* b9 T) R1 @$ w R nal_type = buf[0] & 0x1f;7 m: ?) ? M/ t3 ~: T
5 X+ i. M9 i x
if (nal_type == 7) { /* SPS */+ y T; U! l9 m" p1 S% ]- O
sps = buf;9 [1 _9 t; X) K# y
sps_size = size;* e' x$ K- B4 ]7 I
} else if (nal_type == 8) { /* PPS */( s: B% i( b0 H5 A
pps = buf;+ T4 [# w5 l: n$ a1 X+ s I0 V+ F
pps_size = size;5 A2 Y, b6 [" P
}. [7 L Z. p J& m' R, m
7 A: N, K/ H5 z buf += size;: j# ^0 h" m# \1 [) g' H
}
S Z) U9 b) X( J8 L) m* j( J8 D- Q. j9 l1 J
if (!sps || !pps || sps_size < 4 || sps_size > UINT16_MAX || pps_size > UINT16_MAX)
( F K K' b/ l, S1 R return AVERROR_INVALIDDATA;
# a! `4 a* a, |9 J5 S& n# _: ^$ u
avio_w8(pb, 1); /* version */
9 J& A |* S% c# b: y3 s avio_w8(pb, sps[1]); /* profile */) @) \ s- p/ f, E% x* O) n9 K
avio_w8(pb, sps[2]); /* profile compat */
, I1 ~8 X, J v' [. \- V) r" Y( D avio_w8(pb, sps[3]); /* level */
% e# A A' ^6 W) j. [% y avio_w8(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */
$ v, t# ]. ]3 H( K* |( _/ t4 x7 p- S avio_w8(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */) @3 y: Q# M( z
; j0 T, J. U/ ] avio_wb16(pb, sps_size);
- J0 H2 D0 q1 a; ?6 m avio_write(pb, sps, sps_size);/ \! f' i. H$ X: K2 f, q; R: q
avio_w8(pb, 1); /* number of pps */. k' y6 C% F" W! N
avio_wb16(pb, pps_size);% P1 j* m) @4 q
avio_write(pb, pps, pps_size);
0 K# f- y' B" ]( T% r; z+ [" e av_free(start);; S( n) [1 A( x3 `6 ]. |
} else {
- F+ f% n0 t# N6 C, r avio_write(pb, data, len);! P5 B! D. V' a3 N9 `
}" {/ F7 Z2 N, q+ ]% H l) R8 D
}
* P+ T K1 l( Z return 0;
/ P3 F$ L, l6 i8 U7 F}
4、Data Tags 解析
Data Tag 的结构大致如下所示:

其中 ScriptTagBody 中的 Name 和 Value 字段都是 SCRIPTDATAVALUE 类型。Name 最终对应的是 String 类型,Value 最终对应的是 ECMA array 类型。
SCRIPTDATAVALUE 包含两个字段:Type 和 ScriptDataValue,前者表示数据的类型,后者装载实际数据。
- SCRIPTDATAVALUE:
- 0 = Number
- 1 = Boolean
- 2 = String
- 3 = Object
- 4 = MovieClip (reserved, not supported)
- 5 = Null
- 6 = Undefined
- 7 = Reference
- 8 = ECMA array
- 9 = Object end marker
- 10 = Strict array
- 11 = Date
- 12 = Long string
- Type:定义数据的类型
- ScriptDataValue:数据值
这些数据都是以 AMF(Action Message Format) 的形式编码。
Data Tag 里可以承载不同的数据,其中我们最关心是的音视频的 metadata 元信息数据,这些信息是以一个 Name 为onMetadata的 SCRIPTDATA Tag 来存储的。
4.1、onMetadata 解析
onMetadata 包含着不同的属性,这些属性对于不同的 FLV 文件可能各不相同。

本文参考
1)Adobe Flash Video File Format Specification
https://www.adobe.com/content/dam/acom/en/devnet/flv/video_file_format_spec_v10_1.pdf
2)基于 libRTMP 的流媒体直播之 AAC、H264 推送
https://blog.51cto.com/billhoo/1557646
3)RTMP 协议发送 H.264 编码及 AAC 编码的音视频https://www.cnblogs.com/haibindev/archive/2011/12/29/2305712.html
4)ISO IEC 14496-15-2017http://www.doc88.com/p-8951310719017.html
(通过上文的介绍,我们了解了 FLV 视频封装格式,并探讨了其中 Audio Tags、Video Tags、Data Tags 等模块的具体结构。我们将在后面继续探讨其他常见的媒体封装格式)
FLV 格式:为什么直播首选这个流媒体格式?丨音视频基础 - 资料 - 音视频开发中文网 - 构建全国最权威的音视频技术交流分享论坛