MKV是一种新的多媒体封装格式,支持多种视频和音频编码格式,能够将多达16路不同格式的音频和不同语言的字幕流封装到一个文件中,在高清影片中得到了广泛的应用,越来越多的视频和影片采用MKV作为其封装格式。能否支持MKV封装格式是高清播放机性能的一个重要指标。本文提出了一种基于SMP8654平台的MKV播放器设计与实现方案,并针对嵌入式系统和高清媒体的特点做了进一步优化,能够提供对MKV文件的流畅播放。
1 MKV封装格式
MKV全称为Matroska Video,是一种新的多媒体封装格式。多媒体封装格式也称多媒体容器(MulTImedia Container),它不同于H264、MPEG-2、MPEG-4这类编码格式,它只是为多媒体编码提供了一个“外壳”,本身不涉及编码。MKV是由开源组织Matroska Development Team制定的一个标准,总共包括三部分:MKV(Matroska Video)、MKA(Matroska Audio)和MKS(Matroska SubTItles),后两种格式分别针对音频和字幕,应用较少。MKV的目的是代替AVI等传统封装格式。AVI是Microsoft于1992年推出一种封装格式。其含义是Audio Video InteracTIve,就是把视频和音频编码混合在一起储存。微软在1996年推出了AVI的改进版本AVl2.0。AVI格式上限制比较多,只能有一个视频轨道和一个音频轨道,还可以有一些附加轨道,如文字等。AVI格式不提供任何控制功能。总体而言,AVI为主的传统封装格式结构陈旧,只能包含少数几种音视频格式,并且不够开放,可扩展性差。正因为如此,才促成了Matroska这类新的多媒体封装格式的诞生。
MKV相对于传统的封装格式,有如下优点:支持可变比特率(VBR),支持错误检测以及修复软字幕,支持流式传输,强大的开放性和跨平台兼容性,支持16 路以上的音频流和字幕流等。Matroska最大的特点是能容纳几乎所有类型的视频、音频及字幕流,除H.264以外,也可包括MPEG4、MPEG2、 Ac3、AAC等其他视频和音频格式,即使是非常封闭的RealMedia及QuicklTIme也被它包括进去了,并将它们的音视频进行了重新组织来达到更好的效果。
由于MKV封装格式本身具有众多优点,随着互联网和高清影片的流行,MKV格式得到了广泛的应用,互联网上越来越多的高清影片采用MKV格式。然而,MKV是一个由开源组织制定和推广的标准,缺乏大商业公司的支持,导致MKV文件的播放缺乏一个完整和有效的设计实现。在性能和资源受限的嵌入式平台上,这个问题尤为严重。虽然目前有很多高清播放机都提供了对MKV格式的支持,但是大部分实现方案都存在支持不完善、播放效率比较低的问题,播放高码率的影片时会出现不流畅、画面卡顿等问题,影响观看效果。本文提出了一种基于SMP8654平台的MKV播放器的设计与实现方案,并针对嵌入式系统的特点做了优化,较好地达到了MKV文件流畅播放的要求。
2 硬件平台和软件总体设计
硬件平台以SMP8654芯片为核心,通过总线与RAM、SATA硬盘、Flash闪存、输入输出设备等外设相连。SMP8654是Sigma Design公司推出的多媒体播放SoC解决方案,集成了一个强大的多媒体处理器、强健的内容保障系统、新的DDR2内存控制器、多个片上CPU以及完备的系统外围设备接口。从媒体播放角度看,smp8654提供了一个完整支持高清晰度视频解码的先进解码引擎,能够支持对MPEGl、MPEG-2、 MPEG-4、H.264、WMV9、VCl以及AVS等格式的硬件解码,支持高效能的图形加速,支持多标准音频解码和先进的显示处理能力。Siena Design公司为方便第三方厂商开发应用,提供了与芯片相关的开发工具包和开发框架。本文的工作也基于这个框架进行二次开发。软件平台方面,由于一个完善的播放系统已经相当复杂,不适合直接操纵底层硬件来完成功能,需要操作系统的支持。本项目中操作系统采用uclinux,文件系统采用Romfs。 uclinux是专为嵌入式系统定制的一款Linux,它具有标准Linux操作系统的稳定性、强大网络功能等主要优点,但是却不像标准Li-nux那样复杂,主要针对没有MMU(内存管理单元)的微控制器。Romfs是一款专门为嵌入式系统设计的文件系统,体积小、可靠性好、读取速度快,是嵌入式系统常用的文件系统。
媒体文件的播放流程一般包括如下几个步骤:系统初始化、判断文件类型、文件解析、设置硬件解码器和音视频解码,其中文件解析和音视频解码是关键部分。由于 SMP8654集成了完善的音视频硬件解码器,解码工作主要由硬件完成,我们只需将音视频数据按要求送入相应的解码缓冲区即可。整体的软件架构如图2所示。
3 系统关键技术设计和实现
3.1 MKV文件解析
MKV文件解析主要是对MKV格式的各个组成元素进行解析,以获得必需的音视频参数和媒体数据。MKV作为一种封装格式,实际的视频和音频数据都被封装到某一个子模块中,要想获得实际的数据,必须首先对文件进行解析,并且文件解析贯穿播放的全过程。能否有效并正确的解析,关系到读取数据的准确性,进而影响播放的效果。MKV格式采用可变长编码,能够减少存储空间,另一方面,也给解析带来了新问题。
MKV文件格式建立在EBML(Extensible Binary MetaLanguagel基础上,EBML是一种类似于XML格式的可扩展二进制元语言,使用可变长度的整数存储,以节省空间。EBML的基本结构是典型的TLV结构,有三部分组成:
ID标志属性类型,size为后面data部分的大小,data部分为ID所标识属性的实际数据,ID和size均为可变长编码的整数。整数的长度为 length="1"+[number of leADIng zero bits]。前面的零的个数最多为7个,即最多能表示56个比特的整数。文件中不允许出现大于56比特的数。
MKV文件格式的显著特点是模块化、结构化存储。每一个高一级的元素由若干次一级的元素组成,直至最基本的组成元素,每个元素都是一个TLV结构。一个标准的MKV文件有两部分组成:EBML Header和Segment。EBML Header由EBMLVersion、DocType等子元素组成,包含了文件的版本、文档类型等相关信息。Segment部分保存了媒体文件的视频和音频的实际数据,其data部分又可以分为SeekHead、Tracks、Cluster等若干子元素(表1)。所有元素的处理都可以按照一个统一的流程来进行。我们可以仿照TCP/IP协议分层的思想,对每一层的每个功能都用一个函数来完成,使用更底层的函数完成此项功能,并可被更高层的函数调用。文件解析时,从文件顶层开始,每当上一级的元素解析到有某个子元素时,调用此函数进行次一级的解析,依次直至文件结束,即可完成对文件的解析处理。整个 MKV的解析调用过程如图3所示。Hea-der Parse和Segment Parse为文件最上层的元素解析函数,Cluster Parse、Tracks_Parse等为次一级组成元素的解析函数。ebml_read_ele-ment_idebml read element length为最底层基本组成元素的解析函数。
3.2 设置硬件解码器音视频核心参数
Tracks用来描述文件中包含的每一路多媒体流的信息。一路多媒体流用一个TrackEntry描述,所有的track都要在一个Tracks中进行描述。一个TrackEntry主要包含:TrackNumber(判定属于哪一路流的ID)、TrackType(video、audio或者 subTItle)、TimeScale(时间戳单位)、CodecID(编码格式);CodecPrivate(不同的编码格式所需的私有数据)等;对于视频,还包含以下信息:PixelWidth、PixelHeight等。对于音频,track还包含以下信息:channels、Sampling Frequency等。这些是关于音视频能否正确解码播放的关键参数,需要在解析时获得,然后通过硬件操纵函数设置。
Cluster包含实际的数据,一个Cluster块,通常是几秒钟时间跨度的媒体数据,一个文件有数以千计的Cluster。每个Cluster又有若干个BlockGroup。根据Cluster和BlockGroup的起始pts和持续时间,可以计算出当前Block的实际pts。PTS是用来确定播放时间的重要数据,也是音视频同步的关键信息。这部分信息要在送入视频或音频数据的同时设置硬件解码器。
3.3 性能优化
MKV封装的影片通常为高清影片,分辨率在1920×1080,即使采用H.264等先进编码格式,码率依然非常高。同时,MKV支持可变码率,可变码率能够减少文件的体积,但是剧烈波动的码率会使播放不能流畅进行。在高清文件中码率一般在10~30M/ps之间,最高可达60Mp/ s,如此高的码率,如果不做特殊处理,播放时很容易出现卡顿,播放不流畅等问题。为解决这个问题,我们从两方面考虑。
在解析方面,解析的效率关系到能否尽快将数据读入缓冲区,如果处理时间过长,造成一段时间内缓冲区为空,这时候就会出现卡顿。 MKV文件中通常包含一路视频、多路音频和多路字幕,播放时只选中其中一路音频和一路字幕,其他路的数据可以被视为无效数据。在解析时,可以根据 Block头的标记判断出这路数据是当前播放需要的有效数据还是无效数据。如果是有效数据,则继续解析,并将音视频数据送入缓冲区,如果是无效数据,不进行解析,直接移动文件指针到下一个Block,这样可大大加快文件解析和数据读取速度。
在播放方面,通常播放时的处理流程是读取一帧数据,然后送入硬件解码器,等到收到硬件解码器为空的信号,再读取下一帧的数据。如果是处理低码率文件的播放,这样做不会有问题,但是当文件分辨率比较高,码率比较高时,解析读取时问和解码时间都会增加,这样做就会造成卡顿。为解决这个问题,我们在内存中设计了一个缓冲FIFO,相当于一个滑动窗口(图4),缓冲区可以存放若干个帧(一帧就是一个Block,根据帧的大小缓冲区存放的个数不等)。当缓冲区未满时,读取文件中的一个Block并解析,然后将实际数据到缓冲区的队尾。当发现硬件缓冲区空闲时,将FIFO队首的数据从内存直接送入硬件缓冲区,不需要再去读取文件。由于缓冲区中有多个帧,能够提供一定的缓冲,这样在码率波动时就仍然能够及时提供数据,避免出现硬件缓冲区为空造成的卡顿,播放不流畅等问题。
4 结语
本文详细介绍了MKV封装格式的特点。并基于SMP8654提出了一种MKV播放器的设计与实现方案,经验证,能够达到对高清MKV文件的流畅播放,并已经实际应用到产品上。接下来将做进一步研究,在MKV播放器的基础上,设计一种针对多种封装格式的通用媒体播放器框架,将FLV、FLAC等其他格式也融合进来,并提供较好的可扩展性,方便后续扩充其他的封装格式。