查看: 60|回复: 0

flv格式要怎么看?

[复制链接]
  • TA的每日心情
    奋斗
    2022-7-25 00:26
  • 签到天数: 1 天

    [LV.1]初来乍到

    5万

    主题

    5万

    帖子

    16万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    167923
    发表于 2022-8-28 08:09:54 | 显示全部楼层 |阅读模式

    (本文基本逻辑: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 格式:为什么直播首选这个流媒体格式?丨音视频基础 - 资料 - 音视频开发中文网 - 构建全国最权威的音视频技术交流分享论坛

    回复

    使用道具 举报

    懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    客服QQ/微信
    921439866 周一至周日:09:00 - 21:00
    致力打造互联网创业第一品牌,学习网上创业赚钱,首选泓嘉网络创业,值得信赖! 泓嘉网络科技 版权所有!

    本站内容均转载于互联网,并不代表泓嘉网立场! 拒绝任何人以任何形式在本站发表与中华人民共和国法律相抵触的言论!。

    信息产业部备案号 豫ICP备2022016396号-1

    QQ|免责声明|广告服务|小黑屋|泓嘉网创 ( 豫ICP备2022016396号-1 )|网站地图

    GMT+8, 2026-4-26 11:43 , Processed in 1.198756 second(s), 26 queries .

    快速回复 返回顶部 返回列表