多媒体文件格式之FLV

1. 概述

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

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

FLV 总体文件格式图

image
注:Header 下面的 4 个字节是 PreviousTagSize 0,由于前面的 Header 不是一个 Tag,因此值为 0。git

2. FLV Header

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

文件类型      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。ide

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 数据:
ui

5. Audio Data

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

  • 音频格式(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 字节。

相关文章
相关标签/搜索