视频文件头解析--mkv

来源:互联网 发布:华为数据银行 编辑:程序博客网 时间:2024/05/20 16:44

MKV 的文件格式的目标是,成为多媒休包容格式的标准。它基于EBML(扩展二进制多媒体语言)。与XML标记语言有点相似。

EBML是类似于XML那样的层次化结构,每一个元素都有它的ID(就是元素名)和值。另外由于是二进制存储,还含有一个长度值来记录它的值的长度,每个元素的排列是ID,长度,值
     然后它的ID和长度的表示方法很有趣,是采用了UTF-8那样的不定长前缀表示法。转换成二进制后,如果以1开头,那么长度是1字节,如果以01开头就是2字节,001开头是三字节……依此类推。前缀之后的值就是其值(当然,ID就和值无关,只是拿来确定ID的长度了)。
     对于元素的值,就采用普通的方法存储。值得注意的是,整数之类的值保存与一般二进制会前后颠倒不同,是按从高到低的顺序存储(例如:对于数0xFE65C4,一般的二进制保存到文件后会变成C4 65 FE,而在EBML里就是以FE 65 C4的形式保存的)
     举个例子,这是某MKV文件里的一段:

……42 82 88 6D61 74 72 6F 73 6B 61 42 87 81 01……

于是首先看42,转换为2进制值为01000010(注意这里的42是十六进制,别错当成10进制转换成00101010了XD,另外不足8位的用0补齐)。由于是01开头所以知道ID的长度是2字节,于是42 82就是它的ID。之后是长度:88转换为2进制值为10001000,以1开头,于是长度只有1字幕,去掉前缀1后值为0001000,也就是8,即是说后8个字节都是它的值。查表可知ID 42 82也就是DocType,它的值是字符串,于是把后面8字节6D 61 74 72 6F 73 6B 61按UTF-8解码可得“matroska”(由于是英文字符,UTF-8和ASCII码一样XD)。
     之后又是一个新元素,同理得知42 87是其ID,长度是81,也就是长为1字节,后面的01就是它的值。同样可查得4287代表DocTypeVersion,值类型为整数,于是这一段EBML码解析结果为:
DocType=matroska
DocTypeVersion=1
     说了半天,EBML是什么?是Extensible Binary Meta Language(可扩展二进制元语言)的缩写,主要是设计用来描述媒体文件的。

MKV中的元素(类似于XML中的标记):

EBML

               DocType

               DocTypeVersion

               DocTypeReadVersion    etc...

Segement

             SegementInformation

             SeekHead

             Tracks

             Clusters

             Cues

             Chapters

             Attachments

              tags

 

数据结构:

typedef struct

{

   D_UINT8 video_track_type[STREAM_MAX_NUM];

   D_UINT8 audio_track_type[STREAM_MAX_NUM];

   D_UINT8 other_track_type[STREAM_MAX_NUM];

   D_UINT8 reserved;

   D_UINT32 video_codec[STREAM_MAX_NUM];

   D_UINT32 audio_codec[STREAM_MAX_NUM];

   D_UINT32 other_codec[STREAM_MAX_NUM];

}MKV_FILE_INFO;

 

操作步骤: 

1、首先打开文件指针,读取2k自己的文件内容到headbuffer里面

2、通过mkv_stream_verify函数解析当前文件头中是否有ebml元素的id号(EBML元素的ID被编码成一个不定长整数,数据宽度的最大值默认为4字节)。如果id号不等于0X1a45dfa3 ,报错。EBML元素是EBMLheader的一个容器,EBML:=0X1a45dfa3 container

3、EBML是类似于XML那样的层次化结构,包含多个结构,每一个元素都有它的ID(就是元素名)和值,通过mkv_file_head_parser函数解析每一个元素的详细信息。具体操作为:

1)初始化mkv各参数的存储变量,置0,打开mkv文件返回文件指针,申请header空间(300k)

2)读取300k的mkv文件头数据到内存中,如果文件没有申请的空间大,置FeofFlag为1(暂时不知道FeofFlag是用来干嘛的)

3)通过matroska_ebml_head_read函数解析当前文件是不是Matroska视频容器格式。EBML是类似于XML那样的层次化结构,每一个元素都有它的ID(就是元素名)和值。一个EBML文档是各个EBML元素的列表。每个元素有三个部分:ID,SIZE,DATA。其中,ID和SIZE的表示方法,采用的是UTF-8编码那样的不定长前缀表示法。而DATA采用普通的方法存储,具体操作:

A)ebml_matroska_id_read,读取embl head id看是否是mkv的

B)ebml_matroska_size_read,读取embl size大小.

C)如果解析出来的size大小大于0,表明,在emblsize数据之后有有用数据需要解析(多组数据)

D)判断解析出来的id是哪种类型,如果是DOCTYPE,解析后面的值,如果值是"matroska",表明这个是mkv文件头。此文件就是mkv文件。

E)其他类型的信息不解析。跳过数据

4)跳过EBML部分,解析Segement。matroska_segment_read函数进行解析。解析mkv文件里面包含的多条track信息,包括audio_pid、video_pid、编码方式等.解析出来存在mkv_video_pid这种的全局变量里。segment里包括了多媒体数据和回放时所需要的信息头.一个文件可以包括多个信息头,但很多的库不支持多个Segment。具体操作为:

A)同样是读取id(ebml_matroska_id_read),如果获得的id不是EBML中Segment元素的标志,那么报错。

B)读取size大小(ebml_matroska_size_read)

C)如果FeofFlag不为1的情况下,调用ebml_matroska_id_read读取id,判断id是什么类型。

D)如果id=0x1654AE6B(Master Tracks ID.描述了包含在Cluster中的数据轨道信息,可以在文件生成多个备份。如果没有Cluster元素,Tracks元素也可以不存在)表明后面数据中是track信息。调用segment_master_track_read进行解析track。具体操作为:

a)   读取之前id后面的size大小(ebml_matroska_size_read)

b)   在size信息之后解析后面的数据获取后面数据紧接着的那个id号,如果 id=0xAE(每个轨道的标示符,描述一个在Segment元素中的轨道.网上也有说是0x4E的,所有的轨道信息包含在Tracks元素中,每个轨道用一个TrackEntry元素来表示),调用segment_master_track_info函数来读取tarck的序列号,轨道类型,编码类型ID,解压时所需要的数据,其他的都跳过。此函数中能得到mkv封装格式的编码算法,audio和video pid等相关信息。主要用到的方法跟之前一样,一层层去解析,如果遇到了id,判断当前id是轨道序列号或者轨道类型,或者轨道使用的编码类型,解压时所使用的私有数据等信息,对后面的参数进行解析,得到的数据对应的存入全局变量,就会得到当前mkv文件中的所有track信息。

E)其他的id不解析,跳过后面的数据区

5)Segement解析完,track也解析完了,那么mkv文件里包含的所有音视频信息就都解析出来了,把他们赋值给DG_MEDIA_FILE_INFO类型的file_info结构体,供底层播放文件解码时使用。

6)去初始化。