zoukankan      html  css  js  c++  java
  • 多媒体文件格式之FLV

    1. 概述

    FLV(Flash Video) 是 Adobe 公司设计开发的一种流媒体格式,其封装格式的文件后缀通常为 ".flv"。总体上看,FLV 包括文件头(File Header)和文件体(File Body)两部分,其中文件体由一系列的 Tag 组成。一个 FLV 文件,每种类型的 Tag 都属于一个流,也就是一个 FLV 文件最多只有一个音频流,一个视频流,不存在多个独立的音频流/视频流在一个文件中的情况。

    注:FLV 文件格式用大端字节序。

    FLV 整体文件格式图

    image
    注:Header 下面的 4 个字节是 PreviousTagSize 0,因为前面的 Header 不是一个 Tag,所以值为 0。

    2. FLV Header

    header 部分记录了 flv 的类型、版本等信息,占 9bytes。具体格式如下:

    文件类型      3bytes    一般为 "FLV" (0x46, 0x4c, 0x66)
    -------------------------------------------------------------------------------------------
    版本          1byte     一般为 0x01
    -------------------------------------------------------------------------------------------
    流信息        1byte     前5位和第7位保留,为0。第6位和第8位分别表示是否存在音频 Tag 和视频 Tag
    -------------------------------------------------------------------------------------------
    header大小    4bytes    整个header的长度,一般为 9;大于 9 表示下面还有扩展信息
    -------------------------------------------------------------------------------------------
    

    3. FLV Body

    body 部分由一个个 Tag 组成,每个 Tag 的下面有一个 4bytes 的空间,用来记录这个 Tag 的大小,这个后置是用于逆向读取处理。

    3.1 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。

    4. Script Data

    该类型 Tag 又通常被称为 Metadata Tag,会放一些关于 FLV 视频和音频的元数据信息如:duration、width、height 等。通常该类型 Tag 会跟在 File Header 后面作为第一个 Tag 出现,而且只有一个。结构如下图所示:

    • 第一个 AMF 包:
      • 第 1 个字节表示 AMF 包类型,一般总是 0x02,表示 string;
      • 第 2~3 字节为 UI16 类型值,标识字符串的长度,一般总是 0x000A("onMetadata" 的长度);
      • 后面的字节为具体的字符串,一般总为 "onMetadata" (6F,6E,4D,65,74,61,44,61,74,61)。
    • 第二个 AMF 包:
      • 第 1 个字节表示 AMF 包类型,一般总是 0x08,表示 ECMA array 类型;
      • 第 2~5 个字节为 UI32 类型值,表示数组元素的个数;
      • 后面即为每个数组元素的封装,数据元素为元素名称和值组成对。常见的数组元素如下所示。
        • duration:时长
        • width:视频宽度
        • height:视频高度
        • videodatarate:视频码率
        • framerate:视频帧率
        • videocodecid:视频编码方式
        • audiosamplerate:音频采样率
        • audiosamplesize:音频采样精度
        • stereo:是否为立体声
        • audiocodecid:音频编码方式
        • fileSize:文件大小

    如下图,为通过 obs 推送 FLV 文件到 RTMP 服务器上抓到的 FLV Script Tag 数据:

    5. Audio Data

    音频 Tag 的数据区开始的第 1 个字节包含了音频数据参数信息,从第 2 个字节开始为音频流数据。结构如下图所示:

    • 音频格式(4bits):
      • 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
      • 7, 8, 14, and 15:内部保留使用
      • flv 是不支持 g711a 的,如果要用,可能要用线性音频
    • 采样率(2bits):
      • 0 = 5.5-kHz
      • 1 = 11-kHz
      • 2 = 22-kHz
      • 3 = 44-kHz,For AAC: always 3
    • 采样大小(1bit):
      • 0 = snd8Bit
      • 1 = snd16Bit
      • 压缩过的音频都是 16bit
    • 声道(1bit):
      • 0 = 单声道
      • 1 = 立体声,即双声道。AAC 永远是 1
    • AACPacketType(1byte):实际上,若为 AAC 数据,则还有一个字节表示 AACPacketType,否则没有这个字节。这个字段表示 AACAudioData 的类型:
      • 0 = AAC sequence header,类似 H.264 的 sps,pps,在 FLV 文件头部中出现一次
      • 1 = AAC raw
    • 数据:如果是 PCM 线性数据,存储的时候每个 16bits 小端存储,有符号。如果音频格式是 AAC,则存储的数据是 AAC AUDIO DATA,否则为线性数组。

    下面针对存储的为 AAC 音频数据进行分析。

    5.1 AAC sequence header

    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 |
    
    • audio object type,即 AAC Profile(5bits):下面列举常用的
      • 1:Main,主规格,包含了除增益控制之外的全部功能,其音质最好
      • 2:LC,低复杂度规格(Low Complexity),现在的手机比较常见的 MP4 文件中的音频部分就包括了该规格音频文件
      • 3:SSR
      • 5:AAC HE = LC + SBR,高效率规格(High Efficiency),这种规格适合用于低码率编码,有 Nero AAC 编码器支持
      • 29:AAC HEv2 = LC + SBR + PS
    • Sampling Frequency Index(4bits):用来表示采样率表中的索引号
      • 0x00: 96000 Hz
      • 0x01: 88200 Hz
      • 0x02: 64000 Hz
      • 0x03: 48000 Hz
      • 0x04: 44100 Hz
      • 0x05: 32000 Hz
      • 0x06: 24000 Hz
      • 0x07: 22050 Hz
      • 0x08: 16000 Hz
      • 0x09: 12000 Hz
      • 0x0A: 11025 Hz
      • 0x0B: 8000 Hz
      • 0x0C: 7350 Hz
    • channel configuraion(4bits):声道数
      • 0: Defined in AOT Specifc Config
      • 1: 1 channel: front-center
      • 2: 2 channels: front-left, front-right
      • 3: 3 channels: front-center, front-left, front-right
      • 4: 4 channels: front-center, front-left, front-right, back-center
      • 5: 5 channels: front-center, front-left, front-right, back-left, back-right
      • 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
      • 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
      • 8-15: Reserved
    • 其他 3bits 设置为 0 即可

    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。

    5.2 AAC raw

    如下图,为抓包得到的一个 obs 推 FLV 文件到 RTMP 服务器的 video raw 音频包:

    6. Video Data

    和 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
    

    6.1 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: 显示时间,也就是接收方在显示器显示这帧的时间。单位为 1/90000 秒。
    • dst:解码时间,也就是 rtp 包 或 rtmp 包中传输的时间戳,表明解码的顺序。单位为 1/90000 秒。根据后面的理解,cts 就是标准中的 CompositionTime。
    • ctx 偏移:cts = (pts - dts) / 90. cts 的单位是毫秒。

    pts 和 dts 的时间不一样,应该只出现在含有 B 帧的情况下,也就是 profile main 以上。baseline 是没有这个问题的,baseline 的 pts 和 dts 一直相同,所以 cts 一直为 0。

    在 FLV Tag 中的时戳就是 dts。

    6.2 AVCVIDEOPACKET 中 data 格式

    Field       type      Comment
    ------------------------------------------------------------------------
    长度        UI32      nalu 单元的长度,不包括长度字段
    ------------------------------------------------------------------------
    nalu数据    UI8[N]    NALU 数据,没有四个字节的 nalu 单元头,直接从 h264 头开始
                          比如:65 ** ** **,41 **  ** **
    ------------------------------------------------------------------------
    长度        UI32      nalu 单元的长度,不包括长度字段。
    ---------------------------------------------------------------------------
    nalu数据    UI8[N]    NALU 数据,没有四个字节的 nalu 单元头,直接从 h264 头开始
                          比如:65 ** ** **,41 **  ** ** 
    ------------------------------------------------------------------------
    ...      
    

    6.3 AVCDecoderConfigurationRecord

    当 AVC packet 类型的类型为 0 是,则表示是解码器配置,即 sps,pps,保存控制信息。此时数据区的格式如下图:
    image
    记录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...,..
    
    • 17: 表示h264IDR data
    • 00:表示是AVC序列头
    • 00 00 00 :cts为0

    //从此往下就是AVCDecoderConfigurationRecord

    • 01 :版本号
    • 64 00 2a:profile level id,sps的三个字节,64 表示是h264 high profile,2a表示level。
    • FF:NALU长度,为3?不知道这个长度用在哪里。
    • E1:表示下面紧跟SPS有一个。

    //sps[N]:sps数组。

    • 00 1e: 前面是两个字节的sps长度,表示后面的sps的长度是1e大小。
    • 6764 002a acd9 4078 0227 e5ff c389 4388 0400 0003 0028 0000 0978 3c60 c658:sps的数据。

    //因为只有一个sps,跳过这些长度,然后就是pps的个数信息:

    • 01 :pps个数,1

    //pps[n] pps 的个数

    • 00 05:表示pps的大小是5个字节。
    • 68 eb ec b2 2c:pps的数据
    • 00 00 …….这是下一个tag 的内容了

    如下图,为通过 obs 推 FLV 文件到 RTMP 服务器抓包得到的 video sps,pps 的包:

    6.4 NALU 数据

    在 6.1 中,若 AVC packet 类型为 1 的话,则为 nalu 单元。对于 H264 的 nalu 数据有两中封装格式:

    • AnnexB
    • ISO Base Media File Format

    6.4.1 Annexb

    对于该格式的 H264,可以通过以下代码提取 NALU。

    avc_demux_annexb_format

    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;
    }
    

    srs_avc_startswith_annexb

    /* 
     * 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) 分割。

    6.4.2 ISO Base Media File Format

    提取该封装格式的 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 字节。

  • 相关阅读:
    vim高亮
    mengning
    4.4内核osal
    tmpvalgrind
    为什么引入协程
    alloc_call_show(转)
    TSAN
    如何查看哪些进程占用Buffer和Cache高(转)
    ASAN详解其他参考链接
    Linux系统与程序监控工具atop教程(转)
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/8992425.html
Copyright © 2011-2022 走看看