关闭

多媒体封装格式学习:H264封装成FLV(一)

标签: 多媒体h264flv
2079人阅读 评论(0) 收藏 举报
分类:

         搞了好几天的FLV封装,话说封装真是个苦力活,有时候思路不是很清晰的时候,真心有点乱。

         网上关于H264封装成FLV的文档,都分析的很详细了,但是有几个点没有考虑到,一会在下面我会一一跟大家说明。图什么的我就不画了,网上一搜应该有很多,那先看下面一个结构体吧。

typedef struct FLVHeader
{
  unsigned char First;// "F"
  unsigned char Second;//"L"
  unsigned char Last;//"V"
  unsigned char Version; //0x01
  unsigned char Flags;//"前五位保留且为0,第六位表示是否存在音频Tag,第七位保留为0,第八位表示是否有视频Tag"
  unsigned char HeaderLenth[4];//header 长度,一共9bytes
}FLVHeader;

了解过FLV的朋友都应该知道FLV是由FLVHeader和FLVBody两大部分构成的,而FLVBody又可以划分成N个更小的单元:Tag。我们先从简单的看起,上述代码是一个FLVHeader的结构体,其前三个字节分别是F,L,V 第四个字节是Version也就是FLV的版本号,当前为1。第五个字节是一个标志位Flags,代表当前FLV是否包含音频或者视频Tag,此Flags的第六位代表是否含有音频Tag,第八位表示是否含有视频Tag,其他位为0;举个例子,当Flag = 0X01的时候,代表只包含视频Tag,等于0X04的时候代表只含有音频Tag,当Flag等于0X05时,就代表此FLV既包含音频Tag,也包含视频Tag。最后有一个四字节的HeaderLenth,代表着当前Header的长度。大家有没有注意到,我用char[4]来表示这个四字节的变量,而没有用unsigned int类型吗?是有一定道理的,大家可以自己去尝试一下。

下面具体介绍一下FLVBody部分。FLVBody由多个previousdata size + Tag构成,其中Tag的类型多种多样,由于我对音频部分不是很熟悉,我这里就不讨论音频Tag了。而previous data size 用四个字节代表上一个Tag所占字节数,但是FLVBody和第一个Tag之间的previous data size为0.

那么一个Tag是由哪几个部分组成的呢?如果简单分开来:Tag = TagHeader + TagData,如下结构体说明了TagHeader的结构:

typedef struct TagHeader
{
  unsigned char TagType;//tag类型,音频为(0X08),视频(0X09),script data(0x12),其他值保留
  unsigned char DataSize[3];//3字节表示Tagdata的大小
  unsigned char Ts[3]      ;     //3字节,为Tag的时间戳,  
  unsigned char TsEx     ;//时间戳的拓展位,当24位不够用时,此八位作为时间戳最高位,将时间戳变为32位
  unsigned char StreamID[3];//一直为0;
}TagHeader;

Tagheader的第一个字节为TagType,此字节意在说明此Tag的类型,如果是音频,TagType=0X08,视频的话,TagType = 0X09;如果是Script data的话,TagType = 0X12;其他值保留。第2-4字节为DataSize,表示TagHeader后面接着的TagData的数据大小。Ts和TsEx结构地内注释已经很清楚了,就不重复了。StreamID恒定为0.

         对于一个Tag来说,TagHeader的结构都是一样的,而TagData的结构就很复杂了,首先,针对不同的TagType,对应的TagData肯定不一样,分为AudioTagData,VideoTagData,ScriptTagData。AudioTagData在本文中不做分析,因为这并不会影响H264码流的封装。

         当我们向一个FLV文件中写入FLVHeader后,紧接着的肯定是4个字节的previousdata size,且为0。那下一步,我们应该来写我们的Tag了,但是第一个是什么Tag呢,是AudioTag,VideoTag,还是ScriptTag呢。我们第一个应该写入的Tag是ScriptTag,那我们看一下它的结构是如何的:


ypedef struct ScriptTagData
{
  unsigned char MetaDataType;//0x02
  unsigned char StringLenth[2];//一般位10,即0x000A;
  unsigned char MetaString[10];//值为onMetaDat
  unsigned char InfoDataType;//0x08表示数组,也就是第二个AMF包
  unsigned char EnumNum[4];//4bytes有多少个元素//18bytes
  //1
  unsigned char DurationLenth[2];//2bytes,duration的长度
  unsigned char DurationName[8];
  unsigned char DurationType;
  unsigned char DurationData[8];
  //2
  unsigned char WidthLenth[2];//
  unsigned char WidthName[5];
  unsigned char WidthType;
  unsigned char WidthData[8];
  //3
  unsigned char HeightLenth[2];
  unsigned char HeightName[6];
  unsigned char HeightType;
  unsigned char HeightData[8];
  //4
  unsigned char FrameRateLenth[2];
  unsigned char FrameRateName[9];
  unsigned char FrameRateType;
  unsigned char FrameRateData[8];
  //5
  unsigned char FileSizeLenth[2];
  unsigned char FileSizeName[8];
  unsigned char FileSizeType;
  unsigned char FileSizeData[8];
  
  unsigned char End[3];//0x000009
}ScriptTagData;

上面的结构是ScriptTag的TagData,ScriptTag的TagHeader按之前的结构来写。ScriptTagData中的MetaDataType,StringLenth,MetaString,InfoDataType都是恒定不变的固定值,注释里都有他们的值,在这里就不多做解释了。接下来就是EnumNum[4]了,它包含4个字节,它代表ScriptTagData中含有多少个字段,上述结构体中我只定义了五个字段:Duration,width,height,framrate和filesize,所以EnumNum[4] = 0x00 00 00 05,当然也可以定义更多的字段,具体得去查看FLV的封装文档。接下来就是定义的字段了,以durantion为例子,DurationLenth[2]代表duration这个字符串的长度8,不包含’\0’所以DurationLenth[2]=0x00 08;  DurationName[8]代表这个字符串”duration”也不包含’\0’。 DurationType代表着duration这个字段所对应的值DurationData的类型,这里DurationType为0,代表DurationData是Double型的,所以在结构体中我将其定义为了unsigned char DurationData[8],为8个字节的unsignedchar 型,与Double所占字节数一样。其他的几个字段也是一样的,就不累述了。最后当字段定义结束后,需要结束符End[3],为0X00 00 09。

既然SciptTag结束了,那么就轮到VideoTag了,这里确实有点恶心。搞过视频的朋友应该都知道不管是H264还是H265都是以Nalu来封装的吧,每条Nalu都是以0X 00 00 01或者0X 00 00 00 01开头的,在这个头之后的1Byte内,包含了此Nalu的Type,在这个字节的低五位中,具体关于Nalu的请去查看H264或者H265的说明。先写到这里了,明儿接着。


对了,有几个不错的链接,这里给大家发一下:

http://blog.csdn.net/leixiaohua1020/article/details/17934487

http://niulei20012001.blog.163.com/blog/static/751472112012111901130404/




0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4048次
    • 积分:90
    • 等级:
    • 排名:千里之外
    • 原创:5篇
    • 转载:0篇
    • 译文:0篇
    • 评论:1条
    文章分类
    文章存档
    最新评论
  • RTSP协议详解(一)

    zahngjialiang: 写得非常详细,非常好,受教了!不过有个小疑问,就是RTP header之后的一个或者两个字节,专业术...