基于Qt的嵌入式媒体播放器系统的设计
扫描二维码
随时随地手机看文章
1引言
随着用户要求的不断提高,越来越多的嵌入式设备使用功能强大、价格低廉的嵌入式Linux作为操作系统并开始采用较为复杂的图形用户界面。Qt以其强大的功能、良好的可移植性逐渐成为一种被广泛使用的GUI系统。正是由于嵌入式操作系统及其相应图形用户界面的不断发展,嵌入式软件的开发显得越来越重要。其中嵌入式媒体播放器由于能够满足人们的视听享受已经逐渐成为了系统中不可或缺的重要组成部分,在嵌入式系统上开发媒体播放器已经成为了一个技术热点,当前许多嵌入式产品中都包含媒体播放器。因此在基于Qt的嵌入式系统中实现媒体播放器具有深刻的意义和实用价值。
2 嵌入式媒体播放器系统设计
2.1架构设计
嵌入式媒体播放器架构设计方案如图1所示。通过使用纯C++语言开发来支持嵌入式Linux系统,采用Qt/Embedded作为GUI来提供强大的用户界面,实现一个开放式的插件接口来增强扩展性,利用内核帧缓冲来输出,消除对特定架构的依赖,从而保证可移植性。媒体播放器属于上层应用程序,位于Linux用户空间。这样设计的目的是为了系统移植性。
图形用户界面窗口以Qt/Embedded为基础开发,通过调用Qt/Embedded提供的类库根据需要设计可以管理多媒体文件的基本窗口,包括打开、删除、显示文件长度、显示播放时间等窗口,以及为方便用户设定的管理播放列表、进行播放控制的窗口,这些都是直接和用户打交道的。由于采用了Qt/Embedded作为GUI,移植性可以得到保证。
文件输入主要是对用户指定的文件进行读取和解析,将获得的文件长度、播放时间、编码格式、音视频帧率、文件标题等内容,结合MIME的处理,显示在预先设计的窗口中。
插件接口调用主要是把所有对解码器的操作整合到一个统一的开放式接口当中,根据上一部分解析出的文件信息去查找相应的解码器插件并调用,如果没有找到可用的解码器可以返回信息提醒用户添加相应的插件。通过实现这样一个接口可以使播放器的扩展性大大提高,因此本部分是媒体播放器的核心。
文件解码和输出主要负责通过调用解码器对音视频数据流进行解码,然后利用QT/Embedded可以直接操纵内核帧缓冲FrameBuffer的特性,将解码之后的数据通过FrameBuffer直接送到输出设备输出,避免对DirectShow、OpenGL等特定架构的依赖,进一步增强可移植性。
图1 嵌入式媒体播放器的架构
3 插件接口模块和解码库模块
3.1插件接口模块设计
插件接口模块是整个播放器的核心部分,它封装了对具体解码器的操作,从而在输入和输出模块之间搭起一座桥梁,确保数据的正常流动。插件接口模块主要提供了以下方法来控制解码器:
1)文件支持性函数 bool isFileSupported(const QString&filename);
通过检查文件的扩展名来确定待播放的文件是否被播放器支持,若是返回真,否则返回假。可识别的扩展名有asf、avi、dat、mp2、mp3、mpeg、mpg、ogg、wav等。如果添加了新的解码器插件以后可以识别新的文件格式,只需要将其扩展名添加到此函数的支持列表中[!--empirenews.page--]
2)获取文件信息函数 const QString& fileInfo();
用于获得文件的各种信息并将结果保存在一个常量字符串中,便于其他函数调用。这些信息包括:播放时间、音频格式、音频比特率、音频通道、音频频率、视频格式、视频比特率、视频高度、视频宽度等。
3)读取音频采样函数
bool audioReadSamples(short* output , int channels, long samples, long& samplesRead, int);
调用解码器对音频采样数据进行读取,是音频数据处理的核心部分。output表示待输出文件指针,channels表示通道数,samples表示采样数,samplesRead表示待读取采样数
4)读取视频帧函数
bool videoReadScaledFrame(unsigned char** output_rows, int, int, int in_w, int in_h,int out_w,int out_h,ColorFormat fmt,int);
调用解码器对视频帧进行读取,是视频数据处理的核心部分。参数output_rows表示输出列地址的指针,in_w、in_h、out_w、out_h分别表示输入和输出帧数据的宽度和高度,fmt表示采用的色彩模式,返回值用来判断执行是否成功。
5)音视频同步函数定义:int Sync(File*fp,int auIndex,struct timeval*vtime);
fp为打开的多媒体文件指针,vtime为当前正在播放的视频文件的帧头中提取的时间, auIndex指出当前的音频帧计数,即当前播放到了第几帧。通过这些参数就可以计算出希望跳到的帧数和当前帧数的差值,然后根据这个差值将音频流向前(滞后)或向后(超前)跳即可。同时Sync函数还会将此差值int反馈给音频解码器,让音频解码器修正数据流的时间戳,如此循环,从而达到较好的音视频同步效果。此函数的总体思想是在播放视频数据流的同时启动另一线程,打开对应的音频数据流播放,然后在视频线程中来同步音频数据。
此外还有插件初始化和注册函数 void pluginInit()、文件初始化函数 void fileInit()、查找函数 bool seek(long pos)、清空视频数据函数flushVideoPackets()和清空音频数据函数flushAudioPackets()、获取下一数据包函数 MediaPacket*getAnotherPacket(int stream)等,不再做详细介绍。
3.2解码库模块
解码库模块的主要作用是为插件接口模块提供解码器,考虑到播放器的可移植性和可扩展性,本系统采用了ffmpeg解码库。FFmpeg解码库是Linux下的一个开源解码器集合,它支持多种音频和视频编解码标准,还支持转文件格式、制作avi等,功能十分强大。可以在windows下使用的ffshow插件,linux下的mplayer播放器都是使用的ffmpeg解码库。
解码库又包含解码器和分离器。解码器就是对音视频数据流进行解码的组件,分离器就是把文件流中的数据分离为音频数据流和视频数据流的组件,音频数据和视频数据是分开解码的,所以二者缺一不可。
3 嵌入式媒体播放器系统实现
3.1 数据流程总体设计
图2为系统数据流程:首先输入模块从数据源(多媒体文件)读入数据,此时它将读入文件头,做一些基本的处理,如读出文件长度,获取此文件的编码类型、比特率,判断能否播放等;然后插件接口模块会调用分离器插件将多媒体数据切分为视频数据流和音频数据流;再经过视频FIFO和音频FIFO,排序处理;最后送入视、音频解码器调用相应的解码器进行解码,对于音频数据就会进行重采样,对于视频数据就会读取相应的帧,逐帧解码;之后经过采样的音频数据和经过渲染覆盖的视频数据先进行音视频同步,再分别通过视、音频输出模块输出。这其中,数据的读入、分离、解码、输出都是通过Qt提供的类库以多线程同时进行的,在解码得同时程序也在不断将数据读入缓冲区并排序等待处理,以提高效率。
输入模块的主要功能是将用户指定的多媒体文件读入。由于不同格式的多媒体文件需要调用不同的解码器才能正常打开,因此考虑到程序的模块化将实际的文件打开工作交给插件接口模块调用相应的解码器进行,输入模块只对文件进行一些基本的处理并对文件内容进行缓存,然后为插件接口模块输送原始数据流。用户首先通过图形用户界面选定待播放文件发出打开指令,这将会使输入模块接收到一个信号并通过用户界面传回的信息获得待播放文件的文件路径和文件名。接下来输入模块会检查文件路径是否合法、文件是否为空,之后会向插件接口模块发出信号,通知插件接口模块查找可用的解码器,为文件解码做好准备。下一步就是进行调用播放初始化函数init(),其具体过程下面会详细介绍,最后就是将工作移交给插件接口模块,让它调用对应文件格式的解码器的open()函数。
输出模块的主要功能是将通过解码器解码之后的音频、视频数据送到输出设备(如LCD显示屏、扬声器)输出。根据输出内容的不同可以将输出模块划分为音频输出和视频输出两个子部分。这两个部分基本上是相互独立输出的,通过插件接口模块的同步控制让它们在输出时保持同步。视频输出和音频输出稍有不同,它利用Qt/Embedded可以直接控制FrameBuffer的特性来输出视频数据。帧缓冲区是显卡上的内存,使用帧缓冲区可以提高绘图的速度和整体性能,与帧缓冲区有关的设备是/dev/fb0(主设备号29,次设备号0)。[!--empirenews.page--]
图2 系统数据流程
4.2嵌入式音视频同步设计
本方法的基本思想是以视频流为主媒体流,音频流为从媒体流,视频的播放速率保持不变,根据本地系统时钟确定的实际显示时间,通过调整音频播放速度来达到音视频同步。整个系统的音视频同步数据流程见图3。首先选择一个本地系统时钟参考(LSCR),要求本地系统时钟参考上的时间是线性递增的。然后将LSCR分送到视频解码器和音频解码器,由这两个解码器根据各帧的PTS值对照本地系统时钟参考产生各帧准确的显示或回放的时间。也就是说,生成输出数据流时依据本地参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间)。在播放时,读取数据块上的时间戳,同时根据本地系统时钟参考上的时间来安排播放。
图3 音视频同步数据流程
基于时间戳的播放过程中,仅仅对早到的或晚到的数据块进行等待或快速处理,往往是不够的。如果想要更加主动并且有效地调节播放性能,就需要引入反馈机制,也就是通过对比音视频的时间戳将当前数据流的播放状态反馈给上层的“源”。如果音频流滞后,就即时通知音频解码器加快音频流输出,但是如果滞后太多,则直接将当前数据丢弃,直接跳到下一帧;如果视频流滞后,就通知音频解码器减慢音频输出速度等待视频流,如滞后太多也直接进行跳帧。数据流首先通过分离器分解为视频数据流和音频数据流,然后经过对应的解码器,同时由本地系统时钟来进行时间戳控制;获得准确显示或回放时间以后进行时间戳比较;若同步则直接输出,不同步则进行音频跳帧或等待,直到同步后输出。
5 总结
本文的创新点是系统具有很好的可移植性,它的实现过程以及核心的代码对类似应用具有很好的可重用性,只需通过较小的修改就能移植到不同的操作系统和平台上,可以广泛使用在各种嵌入式系统中,如PDA,智能手机等方面,具有较高的经济价值,同时也可以为开发其他嵌入式系统软件提供参考意见;其次,本文围绕用户的基本需求,提出了一个基于嵌入式Linux操作系统和图形用户界面QT/Embedded的媒体播放器设计方案。该设计方案具有低耦合、高内聚、可扩展、可移植等良好特点,并在设计的基础上将该方案实现。该媒体播放器支持编码格式为MPEG-1、MPEG-2和MPEG-4的多媒体文件。同时具有存储空间小,响应速度快的性能特点,并支持播放控制、播放列表等功能,可自由切换中英文双语界面,用户可以选择打开任意位置的文件。项目经济效益50万元。