您的位置:首页 > 其它

flv封装ffmpeg编码的视音频笔记(一)

2016-03-21 13:59 253 查看
一、FLV文件结构分析

宏观上看,FLV包括文件头(FileHeader)和文件体(File Body)两部分。文件体由一系列的标签组成,标签又可以分成三类:音频标签、视频标签、脚本标签,且每个标签只能包含一种类型的数据。
FLV文件中最多只能有一个视频流和一个音频流,不能封装两个或以上的同一类型的流数据。

FLV文件使用big-endian字序,也就是我们通常所说的高位在前低位在后。整型数值一般都是UI24类型,使用3字节表示。
整个FLV文件的结构:FLVheader(占用9个byte) + previous tag size0(占用4个byte 默认为00 00 00 00) + Metadata Tag(FLV视频和音频的参数信息,如duration、width、height等,具体尺寸按实际大小计算) + previous tag size(占用4个byte 表示前一个Tag的大小,这里指Metadata的长度) + Video Tag1(视频配置信息,尺寸按实际大小计算) + previous tag size1(占用4个byte 视频配置信息大小) +  Audio Tag2(音频配置信息,尺寸按实际大小计算) + previous tag size2(占用4个byte 音频配置信息大小) + Video Tag3(H264的Nalu) + previous tag size 3(视频tag的长度) + AudioTag4(AAC的音频单元) + previous tag size 4(音频tag的长度) ... +tagN + previous tag sizeN。
二、FLV文件头结构+标签结构+脚本标签数据区
FLV的文件头结构很简单,在当前版本中由9个字节组成,见表1。

表1 文件头的结构
-------------------------------------------------------
项目      长度(字节) 说明
-------------------------------------------------------
格式签名   3   文件标识,必须是“FLV”3个大写字母
版本     1   FLV文件的版本
类型标志位  1   包括四部分说明信息(1表示只有视频,5表示有视频和音频)
文件体偏移量 4   文件体的起始地点,也是文件头的大小
------------------------------------------------------
说明:
  1.类型标志位的1个字节,其中高5位和第2位保留,必须=0,第1位表示是否有视频,第3位表示是否有音频。
  2.数据偏移量的值在版本1中=9。如果头部中包含了其它信息,这个长度就≠9了。
  3.FLV文件中的长度或大小类的数值使用big-endian字序。

标签包括标签头和标签数据区两部分。不同类型的标签,其头结构是相同的,只是标签数据的结构各不相同。标签头的结构见表2。

表2 标签头的数据结构
---------------------------------------
项目        长度(字节) 说明
---------------------------------------
标签类型  1     目前只有3类
数据区大小 3     标签数据区的大小
时间戳   3
扩展时间戳 1
流ID    3     ≡0(恒等于0)
---------------------------------------
说明:
  1.标签类型目前只有3类:8表示音频标签,9表示视频标签,18表示脚本标签。其它值被保留。
  2.时间戳表示该标签从什么时间开始播放,这是一个UI24类型(该类型为3字节的整型数据,VB中没有对应的数据类型)的值。时间戳的单位为ms。标签1的时间戳=0,脚本标签的时间戳也=0(实际上脚本标签一般就是标签1)。
  3.扩展时间戳是当时间戳的3字节24位不够用时,该字节作为最高位(注意是最高位)将时间戳扩展为32位值。
  4.数据区大小不包括标签头的11字节,也不包括表2中的标签大小项目本身占用的4字节,标签头之后紧接着就是标签数据区。
  5.标签大小=数据区大小+标签头长度。

脚本标签数据区的结构
  脚本标签通常又被称为元数据标签(Metadata Tag),包含一些关于FLV视频和音频的参数信息,如持续时间(duration)、视频宽度和高度等。通常脚本标签紧跟在文件头后面作为第一个标签出现,并且一般只有一个。
  一般来说,脚本标签数据区的结构就包含两个AMF包。AMF(Action Message Format)是Adobe设计的一种通用数据封装格式,在Adobe的很多产品中应用。简单来说,AMF将不同类型的数据用统一的格式来描述。
  第一个AMF包封装字符串类型数据,用来装入一个"onMetaData"标志,这个标志与Adobe的一些API调用有关,在此不详述。
  第二个AMF包封装一个数组类型,这个数组中包含了一些媒体信息:例如视频的高、宽和编码ID、帧率;音频的信息如采样率、编码ID、样本大小、是否立体声,还有整个文件的大小等等。
  具体说明见表3。

表3 脚本标签数据区的结构
------------------------------------------
字节序 项目及意义
------------------------------------------
  ***第一个AMF包***
1   AMF包类型,一般总是02,表示字符串
2-3  包内容长度,一般总是00 0A
4-13  包内容,一般总是"onMetaData"

  ***第二个AMF包***
14   AMF包类型,一般总是08,表示ECMA数组
15-18 ECMA数组元素个数
19-  数组元素1
    数组元素2
    ……
    数组元素N
最后3字节为数组结束标志,≡00 00 09
-------------------------------------------
说明:
  1.AMF包的类型见表6。
  2.从第19字节起,后面即为各数组元素。
  3.这里的数组与我们在VB编程中的数组大不一样,别弄混了。ECMA数组元素的结构,见表4。

表4 ECMA数组元素的结构
------------------------------------------
字节序  项目及含义
------------------------------------------
1-2   数组元素名称的长度,假设为L
3-L+2  数组元素名称(字符串)
L+3   元素值类型
L+4-   元素值
*L+4-L+5 元素值长度(如果元素值是字符串)
*L+6-  元素值(字符串)
------------------------------------------
说明
  1.元素值占用字节数取决于值的类型。
  2.如果元素值类型为字符串,那么在元素值类型之后就还有2字节的长度(表11中星号所表示的项目)。如果为长字符串,那么在元素值类型之后就有4字节的长度,相应地,元素值的字节序要后推4字节。
  3.常用的数组元素名称见表5。
  4.元素值类型见表6。

表5 常用数组元素名称
-----------------------------------------------------
名称         中译义       数据类型
-----------------------------------------------------
audiocodecid      音频编码ID      双精度
audiodatarate     音频位率(千字节/秒) 双精度
audiodelay       音频延迟       双精度
audiosamplerate    音频采样率      双精度
audiosamplesize    音频采样大小     双精度
audiosize       音频大小       双精度
canSeekToEnd      能否查找到结束    布尔
creator        创作者        字符串
cuePoints       提示点        ECMA数组
datasize        数据大小       双精度
duration        持续时间(秒)    双精度
encoder        编码器        字符串
filepositions     文件位置       精确数组
filesize        文件大小       双精度
framerate       帧速率(帧/秒)    双精度
hasAudio        是否有音频      布尔
hasKeyframes      是否有关键帧     布尔
hasMetadata      是否有元数据     布尔
hasVideo        是否有视频      布尔
height         视频高度       双精度
keyframes       关键帧        对象
lastkeyframelocation  最后一个关键帧位置  双精度
lastkeyframetimestamp 最后一个关键帧时间戳 双精度
lasttimestamp     最后一个时间戳    双精度
metadatacreator    元数据创建者     字符串
stereo         是否立体声      布尔
times         概述(次数)     精确数组
width         视频宽度       双精度
videocodecid      视频编码ID      双精度
videodatarate     视频位率(千位/秒)   双精度
videosize       视频大小       双精度
-----------------------------------------------------
说明:
  1.数组元素名称一般是小写字母。
  2.canSeekToEnd的意思是:如果FLV文件是用最后一帧(它允许定位到渐进式下载影片剪辑的末尾)上的关键帧编码的,则该值为1,如果FLV文件不是用最后一帧上的关键帧编码的,则该值为0。
  3.只有duration(持续时间)这个元素是必要的,其它的元素可以都可选的。

表6 AMF包及数组元素的常用类型
-----------------------------------
值 类型     说明  占用字节
-----------------------------------
0  Number    双精度  8
1  Boolean    布尔   1
2  String    字串   不定
3  Object    对象   2
4  MovieClip   视频剪辑
5  Null
6  Undefined   未定义
7  Reference   参考
8  ECMA array  ECMA数组
A  Strict array 精确数组
B  Date     日期
C  Long string  长字串  不定
-----------------------------------
说明:
  1.笔者经过多次实验,发现FLV文件的双精度值虽然也占用8个字节,但字节的排列顺序与VB的双精度值完全相反,如果用VB的双精度变量去读硬盘上的FLV文件的双精度值,你只会得到一个莫名其妙的数值,所以必须使用用CopyMemory这个API函数把8个字节复制到Double变量里面,具体做法见下面的代码。
  2.由双精度值的字节排列顺序,以及文件体中的所谓“标签0大小”这个项目,笔者推测,FLV解码时可能是从后向前的,这样双精度值的字节排列顺序就正好符合要求,而“标签0大小”的4个为0的字节可能是文件体的结束标志。

三、视频配置信息
第一个VideoTag是视频相关的参数信息,包含PPS和SPS(缺少了PPS和SPS,FLV不能正常播放)。
  视频标签数据区的第1个字节包含了视频数据的参数信息,从第2个字节开始为视频流数据。
  第1个字节的含义见表7、表8,数据位从左至右。

表7 前4位的数值表示帧类型
-------------------------------------------------------------
值  含义
-------------------------------------------------------------
1  关键帧(keyframe)
2  中间帧(inner frame)
3  可任意使用的中间帧(disposableinner frame (H.263 only))
-------------------------------------------------------------
说明:
  1.关键帧存储的是整个画面完整的数据,可以提取它来生成图片。
  2.中间帧是关键帧之间的状态,不完整的画面数据,需要依靠前面帧的数据生成。

表8 后4位的数值表示视频编码ID
-------------------------------------------------
值  含义
-------------------------------------------------
1  JPEG(现已不用)
2  SeronsonH.263(mencoder转换所使用的视频编码)
3  Screen video
4  On2 VP6
5  On2 VP6 withoutchannel
6  Screen videoversion 2
7    AVC
[code]-------------------------------------------------
视频配置Tag的数据区结构如表9

表9 视频配置Tag的数据区结构
---------------------------------------
项目 长度(字节) 说明
---------------------------------------
类别   1     值为23,表示AVC的关键帧
AVC类型  1    值为0,表示AVC sequence header
时间戳   3 ≡0(恒等于0)
AVC版本  1 值为1
AVC配置 3 三个byte,值分别为SPS[1], SPS[2], SPS[3]
nalu尺寸 1 值为0xff,前六位保留,后两位表示nal size length - 1
SPS数量 1 值为0xe1,前三位保留,后五位表示SPS的数量
SPS尺寸 2
SPS内容 SPS尺寸 下一小节将介绍SPS的数据来源
PPS尺寸 2
PPS内容 PPS尺寸 下一小节将介绍PPS的数据来源
---------------------------------------
接下来是该Tag的大小。

四、ffmpeg编码后的视频
H.264原始码流(又称为“裸流”)是由一个一个的NALU组成的。他们的结构如下图所示。
其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU对应的Slice为一帧的开始就用0x00000001,否则就用0x000001。
H.264码流解析的步骤就是首先从码流中搜索0x000001和0x00000001,分离出NALU;然后再分析NALU的各个字段。
通过分析ffmpeg编码后得到的H264数据,第一次获得的数据是H264的sei和一个关键帧数据。直接去掉起始码,填到flv的视频tag数据区即可。依此类推。第一次存储AVPacket之前需要在前面加上H.264的SPS和PPS。这些信息存储在AVCodecContext的extradata里面。

五、封装视频Tag
视频Tag标签头结构与视频配置Tag标签头一样,视频Tag标签数据区如表10。

表10 视频Tag的数据区结构
---------------------------------------
项目 长度(字节) 说明
---------------------------------------
类别   1     值为23或39,表示AVC的关键帧或中间帧
AVC类型  1    值为1,表示AVC NALU
时间戳   3
保留   2 值为0,保留的2个byte
NALU大小 2 NALU的数据大小(该值不对,封装的FVL不能正常播放)
NALU内容 NALU大小 第四节中介绍的ffmpeg编码得到的H264数据
---------------------------------------
填上NALU内容后,别忘了加上视频Tag的大小,依次类推,就得到一个能正常播放的FVL视频文件(无音频)。目前存在一个问题,得到的视频文件有花屏现象。
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: