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 字节。