FLV (Flash Video) 是由 Adobe 公司推出的一种封装格式,主要用于流媒体系统。FLV 封装的媒体文件具有体积轻巧、封装播放简单等特点,很适合网络应用。目前各浏览器普遍使用 Flash Player 作为网页播放器,使得安装有浏览器的计算机终端不需要另外安装播放器,这也是 FLV 格式广为流行的原因之一。FLV 封装格式的文件扩展名为 .flv。FLV 文件主要由一个 Header 加上由多个 Tag 组成的 Body 构成。如下所述:1. FLV Header所有 FLV 格式文件都以 FLV Header 开头。FLV Header 类型是 FLVHEADER,FLVHEADER 定义如下:在 FLV 版本 1 中,“数据偏移”字段值为 9。在 FLV 未来版本中,此字段可兼容更大尺寸的 FLV Header。typedef struct {
UI8 Signature;
UI8 Signature;
UI8 Signature;
UI8 Version;
UI8 TypeFlags;
UI32 DataOffset;
} FLVHEADER;2. FLV Body一个 FLV 文件,除开头的 FLV Header 外,剩余部分就是 FLV Body。FLV Body 由一系列 back-pointer 和 tag 交织构成。back-pointer 表示前一 tag 大小。FLV Body 类型是 FLVBODY,FLVBODY 定义如下:typedef struct {
UI32 PreviousTagSize0;
FLVTAG Tag1;
UI32 PreviousTagSize1;
FLVTAG Tag2;
…
UI32 PreviousTagSizeN-1;
FLVTAG TagN;
UI32 PreviousTagSizeN;
} FLVBODY;领取音视频开发资料包:音视频流媒体高级开发FFmpegWebRTCRTMPRTSPHLSRTP播放器企鵝君羊994289133领取资料企鵝君羊994289133领取资料3. FLV TagFLV Tag 包含音频、视频或脚本元数据、可选的加密元数据和 payload。FLV Tag 类型是 FLVTAG,FLVTAG 定义如下:typedef struct {
UB[2] Reserved;
UB[1] Filter;
UB[5] TagType;
UI24 DataSize;
UI24 Timestamp;
UI8 TimestampExtended;
UI24 StreamID;
IF TagType == 8
AudioTagHeader Header;
IF TagType == 9
VideoTagHeader Header;
IF TagType == 8
AUDIODATA Data;
IF TagType == 9
VIDEODATA Data;
IF TagType == 18
SCRIPTDATA Data;
} FLVTAG;一个 FLVTAG 中,前 11 个字节是通用 TagHeader,后面紧跟跟着音频 Tag、视频 Tag 或脚本 Tag,其中音频 Tag 和视频 Tag 都包含 TagHeader 和 TagBody 两部分,脚本 Tag 只有 TagBody 部分。上面 Timestamp 和 TimestampExtended 两个字段拼成一个 32 位的时间戳,是当前 Tag 的解码时间戳 (DTS)。对于音频帧来说,PTS 和 DTS 相同。对于视频帧来说,若含 B 帧,则 PTS 和 DTS 不同,H264 视频帧 PTS = DTS + CTS,CTS 就是 CompositionTime 字段,参考 3.2.1 节 CompositionTime 字段的定义。3.1 Audio TagAudio Tag 包括 AudioTagHeader 和 AudioTagBody 两部分。3.1.1 AudioTagHeaderAudioTagHeader 定义如下:格式 3,linear PCM,存储原始 PCM 采样点。如果采样位深为 8,采样点数据为无符号型。如果采样位深为 16,采样点数据为小端存储的带符号型。如果是立体声,左右声道采样点交织存放:左-右-左-右-…格式 0 与格式 3 的不同之处只有一点:格式 0 存储 16 位采样数据,采用的大小端顺序是创建 FLV 文件的平台所使用的大小端顺序。因此,不应使用格式 0,而应使用格式 3。格式 4 (Nellymoser 16-kHz mono) 和格式 5 (Nellymoser 8 kHz mono),是两种特殊情况, 因为采样率字段无法表示 8 kHz 和 16 kHz。当采样格式是格式 4 或格式 5 时,Flash 播放器会忽略采样率和声音类型两个字段。对于其他采样率的 Nellymoser 格式, 即格式 6,则正常使用采样率和声音类型两个字段。格式 10,AAC,声音类型应为 1 (立体声) 且采样率应为 3 (44 kHz)。这并不表示 FLV 中的 AAC 音频总是立体声、44 kHz的数据。实际上,Flash 播放器会忽略这两个值,而从已编码的 AAC 位流中提取出声道数和采样率信息。格式 11,Speex,音频以 16 kHz采样率压缩为单声道,采样率字段值应为 0,采样位深字段值应为 1,声音类型字段值应为 0。格式 7,8,14 和 15 保留。typedef struct {
UB [4] SoundFormat;
UB [2] SoundRate;
UB [1] SoundSize;
UB [1] SoundType;
IF SoundFormat == 10
UI8 AACPacketType;
}3.1.2 AudioTagBody/AUDIODATAAUDIODATA 定义如下:typedef struct {
IF Encrypted
EncryptedBody Body
else
AudioTagBody Body;
} AUDIODATA;AUDIODATA 包含 Body 字段。如果采用了加密,Body 的类型是 EncryptedBody,可参考规范文档“附件 F. FLV 加密”章节获得详细信息,此处略。如果未采用加密,则 Body 的类型是 AudioTagBody,下面详述。AudioTagBody 定义如下:typedef struct {
IF SoundFormat == 10
AACAUDIODATA SoundData;
ELSE
Varies by format
} AudioTagBody;3.1.3 AACAUDIODATAFlash 播放器 9.0.115.0 及以上版本支持 AAC 格式。AACAUDIODATA 定义如下:3.2 Video TagVideo Tag 包含 VideoTagHeader 和 VideoTagBody 两部分。3.2.1 VideoTagHeaderH.264 的命名遵循了 ITU-T 的命名约定,它是 VCEG 视频编码标准 H.26x 线中的一员;MPEG-4 AVC 的命名来自 ISO/IEC MPEG 的命名约定,它是 ISO/IEC 14496 的第 10 部分,该协议族被称为 MPEG-4。3.2.2 VideoTagBody/VIDEODATAVIDEODATA 定义如下:typedef struct {
IF Encrypted
EncryptedBody Body
else
VideoTagBody Body;
} VIDEODATA;VIDEODATA 包含 Body 字段。如果采用了加密,Body 的类型是 EncryptedBody,可参考规范文档“附件 F. FLV 加密”章节获得详细信息,此处略。如果未采用加密,则 Body 的类型是 VideoTagBody,下面详述。VideoTagBody 包含视频帧净荷数据。VideoTagBody 定义如下:typedef struct {
IF FrameType == 5
UI8 VideoData;
ELSE (
IF CodecID == 2
H263VIDEOPACKET VideoData;
IF CodecID == 3
SCREENVIDEOPACKET VideoData;
IF CodecID == 4
VP6FLVVIDEOPACKET VideoData;
IF CodecID == 5
VP6FLVALPHAVIDEOPACKET VideoData;
IF CodecID == 6
SCREENV2VIDEOPACKET VideoData;
IF CodecID == 7
AVCVIDEOPACKET VideoData;
)
} VideoTagBody;3.2.3 AVCVIDEOPACKETAVCVIDEOPACKET 包含 AVC(H264) 视频净荷数据。AVCVIDEOPACKET 定义如下:typedef struct {
IF AVCPacketType == 0
AVCDecoderConfigurationRecord Data;
IF AVCPacketType == 1
One or more NALUs
} AVCVIDEOPACKET;3.3 Data Tag数据 Tag 封装了单一方法,此方法通常在 Flash 播放器中的网络流对象上被调用。数据 Tag 包含方法名和一组参数。3.3.1 ScriptTagBody/SCRIPTDATASCRIPTDATA 定义如下:typedef struct {
IF Encrypted
EncryptedBody Body
else
ScriptTagBody Body;
} SCRIPTDATA;SCRIPTDATA 包含 Body 字段。如果采用了加密,Body 的类型是 EncryptedBody,可参考规范文档“附件 F. FLV 加密”章节获得详细信息,此处略。如果未采用加密,则 Body 的类型是 ScriptTagBody,下面详述。ScriptTagBody 包含以 AMF(Action Message Format) 编码的 SCRIPTDATA。AMF 是一种紧凑二进制格式,用于序列化 ActionScript 对象图。ScriptTagBody 定义如下:typedef struct {
SCRIPTDATAVALUE Name;
SCRIPTDATAVALUE Value;
} ScriptTagBody;3.3.2 SCRIPTDATAVALUE一个 SCRIPTDATAVALUE 记录包含一个特定类型的 ActionScript 值。SCRIPTDATAVALUE 定义如下:SCRIPTDATAVALUE 的两个字段,Type 是类型,ScriptDataValue 是值。Type 的值确定 ScriptDataValue 的类型。因为 ScriptDataValue 的类型是动态的,由运行时解析得到的 Type 的值确定,所以这里类型和值用了两个字段。如果是静态类型,显然只用一个字段就可以了。typedef struct {
UI8 Type;
IF Type == 0
DOUBLE ScriptDataValue;
IF Type == 1
UI8 ScriptDataValue;
IF Type == 2
SCRIPTDATASTRING ScriptDataValue;
IF Type == 3
SCRIPTDATAOBJECT ScriptDataValue;
IF Type == 7
UI16 ScriptDataValue;
IF Type == 8
SCRIPTDATAECMAARRAY ScriptDataValue;
IF Type == 10
SCRIPTDATASTRICTARRAY ScriptDataValue;
IF Type == 11
SCRIPTDATADATE ScriptDataValue;
IF Type == 12
SCRIPTDATALONGSTRING ScriptDataValue;
} SCRIPTDATAVALUE;3.3.1 节中 Name 字段和 Value 字段的类型都是SCRIPTDATAVALUE。Name 表示方法名,实际类型通常是SCRIPTDATASTRING。Value 字段表示方法的一组参数,实际类型通常是SCRIPTDATAECMAARRAY。后文将介绍 SCRIPTDATASTRING 和 SCRIPTDATAECMAARRAY 两种类型。其他类型略,详情可参考 FLV 规范文档。3.3.3 SCRIPTDATASTRINGSCRIPTDATASTRING 和 SCRIPTDATALONGSTRING 两种类型用于存储字符串,二者可存储字符串长度不同,SCRIPTDATASTRING 用于存储不超过 65535 个字符的字符串。SCRIPTDATASTRING 定义如下:typedef struct {
UI16 StringLength;
STRING StringData;
} SCRIPTDATASTRING;3.3.4 SCRIPTDATAECMAARRAYSCRIPTDATAECMAARRAY 记录存储 ECMA 数组(下表中的 Variables 字段)。ECMA 数组是一个关联数组,应在 ActionScript 数组包含无序索引时使用。所有索引(无序或有序)都是字符串而不是整数。出于序列化的目的,SCRIPTDATAECMAARRAY 类型与匿名 ActionScript 对象非常相似。SCRIPTDATAECMAARRAY 定义如下:typedef struct {
UI32 ECMAArrayLength;
SCRIPTDATAOBJECTPROPERTY[] Variables;
SCRIPTDATAOBJECTEND ListTerminator;
} SCRIPTDATAECMAARRAY;其中,SCRIPTDATAOBJECTPROPERTY 类型定义了 ActionScript 对象或关联数组变量的对象属性。SCRIPTDATAOBJECTPROPERTY 定义如下:typedef struct {
SCRIPTDATASTRING PropertyName;
SCRIPTDATAVALUE PropertyData;
} SCRIPTDATAOBJECTPROPERTY;3.3.5 实例:onMetaData 对象FLV 元数据对象应在名为 onMetadata 的 SCRIPTDATA 标签中携带。各种属性对通过 NetStream.onMetaData 属性运行的 ActionScript 程序有效。可用的属性根据创建 FLV 文件的软件而有所不同。典型属性包括:onMetaData 标签通常会成为 FLV Body 中的第一个标签,紧跟在 FLV Header 之后。onMetaData 标签中存储的是一些视频、音频及文件相关的元数据信息:如视频帧率,音频采样率、文件长度等。结合 3.3.1 节,onMetaData 标签的 Name 字段主要就是存储 “onMetaData” 字符串。具体为:第 1 个字节值是 0x02,表示 Name 字段是字符串类型。第 2-3 个字节为 UI16 类型值,标识字符串的长度,值为 0x000A (“onMetaData” 这个字符串的长度)。后面跟着的数据为具体的字符串,值为 “onMetaData”。onMetaData 标签的 Value 字段存储上表所示的各属性键值对。具体为:第 1 个字节值是 0x08,表示 Value 字段是数组类型。第 2-5 个字节为UI32类型值,表示数组元素个数。后面紧跟着数组,数组元素为属性名称和值组成的对(键值对)。最后是数组的结束符。ScriptTagBody onMetaData;
onMetaData.Name.Type == 0x02
onMetaData.Name.ScriptDataValue.StringLength == 0x000A
onMetaData.Name.ScriptDataValue.StringData == "onMetaData"
onMetaData.Value.Type == 0x08
onMetaData.Value.ScriptDataValue.ECMAArrayLength ==
onMetaData.Value.ScriptDataValue.Variables[0].PropertyName == {0x0005, "width"} // SCRIPTDATASTRING 类型
onMetaData.Value.ScriptDataValue.Variables[0].PropertyData == {0x00, 1280.0} // SCRIPTDATAVALUE 类型
onMetaData.Value.ScriptDataValue.Variables[1].PropertyName == {0x0005, "height"} // SCRIPTDATASTRING 类型
onMetaData.Value.ScriptDataValue.Variables[1].PropertyData == {0x00, 720.0} // SCRIPTDATAVALUE 类型
…4. 总结FLV 结构如下图所示:在 C 语言中定义 FLV 文件结构,一目了然:/*
* @brief flv file header 9 bytes
*/
typedef struct flv_header {
uint8_t signature[3];
uint8_t version;
uint8_t type_flags;
uint32_t data_offset; // header size, always 9
} __attribute__((__packed__)) flv_header_t;
/*
* @brief flv tag general header 11 bytes
*/
typedef struct flv_tag {
uint8_t tag_type;
uint32_t data_size;
uint32_t timestamp;
uint8_t timestamp_ext;
uint32_t stream_id;
void *data; // will point to an audio_tag or video_tag
} flv_tag_t;
typedef struct audio_tag {
uint8_t sound_format; // 0 – raw, 1 – ADPCM, 2 – MP3, 4 – Nellymoser 16 KHz mono, 5 – Nellymoser 8 KHz mono, 10 – AAC, 11 – Speex
uint8_t sound_rate; // 0 – 5.5 KHz, 1 – 11 KHz, 2 – 22 KHz, 3 – 44 KHz
uint8_t sound_size; // 0 – 8 bit, 1 – 16 bit
uint8_t sound_type; // 0 – mono, 1 – stereo
void *data;
} audio_tag_t;
typedef struct video_tag {
uint8_t frame_type;
uint8_t codec_id;
void *data;
} video_tag_t;
typedef struct avc_video_tag {
uint8_t avc_packet_type; // 0x00 – AVC sequence header, 0x01 – AVC NALU
uint32_t composition_time;
uint32_t nalu_len;
void *data;
} avc_video_tag_t;