MKV 文件格式解析

MKV 文件格式解析xx mkv

大家好,欢迎来到IT知识分享网。

MKV 文件格式解析

前言

       MKV(Matroska Video File)是一种Matroska媒体格式的多媒体封装格式(Multimedia Container Format,简称MCF)但Matroska媒体格式除了MKV外,常见的还有MKA (Matroska Audio File)单一音频文件,可以有多条及多种类型的音轨,MKS (Matroska Subtitles)字幕文件,Matroska来自于俄语,影射俄罗斯娃娃,就是下面这个啦,表示一层包着另外一层。

MKV 文件格式解析

     MKV采用可扩展二进制元语言EBML(Extensible Binary Meta Language)来描述其文件结构,EBML用元素(Elements)来描述EBML文档,组织结构如下:

MKV 文件格式解析

    Element元素ID大端方式编码,起始位0的个数代表了ID的长度,ID长度=起始0的个数+1。除起始位0外,其余bit全1的ID为保留 ID,元素ID可以包括其子ID。

MKV 文件格式解析

    Element数据长度也采用大端方式编码,起始位0的个数代表了数据长度(包括头部)占用的字节个数,字节个数=起始0的个数+1。可以是1~8个字节:

MKV 文件格式解析

EBML组成的文件包含EBML头和EBML体两部分。Matroska 文件可以看作是包含EBML头和Segment两部分的文件。

MKV 文件格式解析

MKV文件分析

    MKV文件分析工具有 EBML Tree Viewer  AVI-Mux GUI和MKVToolnix,下面图片是用AVI-Mux解析的某MKV文件

MKV 文件格式解析

EBML头

MKV 文件格式解析

Segment

       Matroska 文件的segment包含了音视频数据和播放音视频数据所需要的信息。Matroska 文件中可以有多个segment,但不是所有的播放器都支持这样做。Segment的结构示意图如下:

MKV 文件格式解析

Segment的第一级子元素元素名及ID如下:

MKV 文件格式解析

MKV 文件格式解析

        SeekPosition对应元素在Segment中的位置。这个位置的base是SeekHead的起始位置,SeekPosition+SeekHead的起始位置才是元素在文件中的绝对位置。也就是说定位头Seekhead存储的信息是剪辑Segment里面的其他Master元素的类型和位置,比如剪辑信息SegmentInfo,轨道Tracks,索引表Cues,标签Tags信息所在的位置,这个位置信息也是个相对值,是定位头Seekhead的位置的相对值,实际地址等于定位头所在绝对位置+定位条目里面的位置SeekPosition。

MKV 文件格式解析

2. Info

包含了Segment的时间戳base和时长。

MKV 文件格式解析

以下是Info元素的解释:

MKV 文件格式解析

MKV 文件格式解析

详细元素解释如下:

MKV 文件格式解析

MKV 文件格式解析

如下是Cluster中元素的详细解释:

MKV 文件格式解析

看一个BlockGroup中包括Block的例子:

MKV 文件格式解析

MKV 文件格式解析

元素解释如下:

MKV 文件格式解析

MKV 文件格式解析

后记:

    在笔者看来,MKV文件格式中如果出现了错误,寻求一个完美的解决方案相当麻烦。MKV中的元素ID不具有唯一性,可能和数据相同,这就造成了在resync的时候,误把数据当成元素ID的可能性。

Gstreamer的MKV parse

static GstFlowReturn gst_matroska_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { ……   /* 非连续,清除adapter里面的buffer及信息 */   if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buffer))) {     GST_DEBUG_OBJECT (parse, "got DISCONT");     gst_adapter_clear (parse->common.adapter);     GST_OBJECT_LOCK (parse);     gst_matroska_read_common_reset_streams (&parse->common,         GST_CLOCK_TIME_NONE, FALSE);     GST_OBJECT_UNLOCK (parse);   }   gst_adapter_push (parse->common.adapter, buffer);   buffer = NULL; next:   available = gst_adapter_available (parse->common.adapter);   /* 获取下一个id及元素的大小length,needed是元素id本身占用的字节数加上元素长度length所占用的字节数(比如:    * 00000000  1a 45 df a3 a3 42 86 81  .E...B..    * 00000008  01 42 f7 81 01 42 f2 81  .B...B..    * 00000010  04 42 f3 81 08 42 82 88  .B...B..    * 00000018  6d 61 74 72 6f 73 6b 61  matroska    * 00000020  42 87 81 04 42 85 81 02  B...B...    * EBML头ID为0x1A45DFA3,占用4字节,它的长度字段0xa3占用一个字节,    * 头里面的内容占用35个字节,0xa开头没有0,故用一字节,则ID=0x1A45DFA3,length=0x23,need=5),    如果长度字段全部是FF的话,则设置成最大长度G_MAXUINT64    */   ret = gst_matroska_read_common_peek_id_length_push (&parse->common,       GST_ELEMENT_CAST (parse), &id, &length, &needed);   /* 返回值一共三种,OK,EOS或者ERROR,ERROR时代表长度或者ID错误 */   if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)) {     /* ebml_segment_length未设置默认值,但遇到ID_SEGMENT时,会配置这个参数,文件中这个参数为0时,会配置成G_MAXUINT64,ebml_segment_start就是segment在文件中开始的位置 */     if (parse->common.ebml_segment_length != G_MAXUINT64         && parse->common.offset >=         parse->common.ebml_segment_start + parse->common.ebml_segment_length) {       /* 代表处理过的数据已经超过了段segment的长度。 */       return GST_FLOW_EOS;     } else {       /*        * 获取ID或者长度出现错误,设置成SCANNING,事实上gstreamer只有在CLUSTER时,才支持,非CLUSTER相关的ID在scanning的时候会被丢弃,所以如果其他信息错误,是无法播放的。        * parsing error: we need to flush a byte from the adapter if the id is        * not a cluster and so on until we found a new cluser or the        * INVALID_DATA_THRESHOLD is exceeded, we reuse gst_matroska_parse_parse_id        * setting the state to GST_MATROSKA_READ_STATE_SCANNING so the bytes        * are skipped until a new cluster is found        */       gint64 bytes_scanned;       if (parse->common.start_resync_offset == -1) {         /* 标记开始查找下一个ID的开始位置 */         parse->common.start_resync_offset = parse->common.offset;         parse->common.state_to_restore = parse->common.state;       }       bytes_scanned = parse->common.offset - parse->common.start_resync_offset;       /* 重新同步的数据量不大,还可以继续同步 */       if (bytes_scanned <= INVALID_DATA_THRESHOLD) {         GST_WARNING_OBJECT (parse,             "parse error, looking for next cluster, actual offset %"             G_GUINT64_FORMAT ", start resync offset %" G_GUINT64_FORMAT,             parse->common.offset, parse->common.start_resync_offset);         parse->common.state = GST_MATROSKA_READ_STATE_SCANNING;         ret = GST_FLOW_OK;       } else {         /* 重新同步的数据量太大了,返回错误 */         GST_WARNING_OBJECT (parse,             "unrecoverable parse error, next cluster not found and threshold "             "exceeded, bytes scanned %" G_GINT64_FORMAT, bytes_scanned);         return ret;       }     }   }   GST_LOG_OBJECT (parse, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "       "size %" G_GUINT64_FORMAT ", needed %d, available %d",       parse->common.offset, id, length, needed, available);   /* ID长度及长度字段的字节数大于可用数据量,返回 */   if (needed > available)     return GST_FLOW_OK;   ret = gst_matroska_parse_parse_id (parse, id, length, needed);   if (ret == GST_FLOW_EOS) {     /* need more data */     return GST_FLOW_OK;   } else if (ret != GST_FLOW_OK) {     return ret;   } else     goto next; }

参考网页:

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/118207.html

(0)
上一篇 2025-11-14 22:20
下一篇 2025-11-14 22:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信