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)。