基于Qt的嵌入式媒體播放器系統(tǒng)的設(shè)計(jì)
1引言
隨著用戶要求的不斷提高,越來(lái)越多的嵌入式設(shè)備使用功能強(qiáng)大、價(jià)格低廉的嵌入式Linux作為操作系統(tǒng)并開始采用較為復(fù)雜的圖形用戶界面。Qt以其強(qiáng)大的功能、良好的可移植性逐漸成為一種被廣泛使用的GUI系統(tǒng)。正是由于嵌入式操作系統(tǒng)及其相應(yīng)圖形用戶界面的不斷發(fā)展,嵌入式軟件的開發(fā)顯得越來(lái)越重要。其中嵌入式媒體播放器由于能夠滿足人們的視聽(tīng)享受已經(jīng)逐漸成為了系統(tǒng)中不可或缺的重要組成部分,在嵌入式系統(tǒng)上開發(fā)媒體播放器已經(jīng)成為了一個(gè)技術(shù)熱點(diǎn),當(dāng)前許多嵌入式產(chǎn)品中都包含媒體播放器。因此在基于Qt的嵌入式系統(tǒng)中實(shí)現(xiàn)媒體播放器具有深刻的意義和實(shí)用價(jià)值。
2 嵌入式媒體播放器系統(tǒng)設(shè)計(jì)
2.1架構(gòu)設(shè)計(jì)
嵌入式媒體播放器架構(gòu)設(shè)計(jì)方案如圖1所示。通過(guò)使用純C++語(yǔ)言開發(fā)來(lái)支持嵌入式Linux系統(tǒng),采用Qt/Embedded作為GUI來(lái)提供強(qiáng)大的用戶界面,實(shí)現(xiàn)一個(gè)開放式的插件接口來(lái)增強(qiáng)擴(kuò)展性,利用內(nèi)核幀緩沖來(lái)輸出,消除對(duì)特定架構(gòu)的依賴,從而保證可移植性。媒體播放器屬于上層應(yīng)用程序,位于Linux用戶空間。這樣設(shè)計(jì)的目的是為了系統(tǒng)移植性。
圖形用戶界面窗口以Qt/Embedded為基礎(chǔ)開發(fā),通過(guò)調(diào)用Qt/Embedded提供的類庫(kù)根據(jù)需要設(shè)計(jì)可以管理多媒體文件的基本窗口,包括打開、刪除、顯示文件長(zhǎng)度、顯示播放時(shí)間等窗口,以及為方便用戶設(shè)定的管理播放列表、進(jìn)行播放控制的窗口,這些都是直接和用戶打交道的。由于采用了Qt/Embedded作為GUI,移植性可以得到保證。
文件輸入主要是對(duì)用戶指定的文件進(jìn)行讀取和解析,將獲得的文件長(zhǎng)度、播放時(shí)間、編碼格式、音視頻幀率、文件標(biāo)題等內(nèi)容,結(jié)合MIME的處理,顯示在預(yù)先設(shè)計(jì)的窗口中。
插件接口調(diào)用主要是把所有對(duì)解碼器的操作整合到一個(gè)統(tǒng)一的開放式接口當(dāng)中,根據(jù)上一部分解析出的文件信息去查找相應(yīng)的解碼器插件并調(diào)用,如果沒(méi)有找到可用的解碼器可以返回信息提醒用戶添加相應(yīng)的插件。通過(guò)實(shí)現(xiàn)這樣一個(gè)接口可以使播放器的擴(kuò)展性大大提高,因此本部分是媒體播放器的核心。
文件解碼和輸出主要負(fù)責(zé)通過(guò)調(diào)用解碼器對(duì)音視頻數(shù)據(jù)流進(jìn)行解碼,然后利用QT/Embedded可以直接操縱內(nèi)核幀緩沖FrameBuffer的特性,將解碼之后的數(shù)據(jù)通過(guò)FrameBuffer直接送到輸出設(shè)備輸出,避免對(duì)DirectShow、OpenGL等特定架構(gòu)的依賴,進(jìn)一步增強(qiáng)可移植性。
圖1 嵌入式媒體播放器的架構(gòu)
3 插件接口模塊和解碼庫(kù)模塊
3.1插件接口模塊設(shè)計(jì)
插件接口模塊是整個(gè)播放器的核心部分,它封裝了對(duì)具體解碼器的操作,從而在輸入和輸出模塊之間搭起一座橋梁,確保數(shù)據(jù)的正常流動(dòng)。插件接口模塊主要提供了以下方法來(lái)控制解碼器:
1)文件支持性函數(shù) bool isFileSupported(const QString&filename);
通過(guò)檢查文件的擴(kuò)展名來(lái)確定待播放的文件是否被播放器支持,若是返回真,否則返回假??勺R(shí)別的擴(kuò)展名有asf、avi、dat、mp2、mp3、mpeg、mpg、ogg、wav等。如果添加了新的解碼器插件以后可以識(shí)別新的文件格式,只需要將其擴(kuò)展名添加到此函數(shù)的支持列表中[!--empirenews.page--]
2)獲取文件信息函數(shù) const QString& fileInfo();
用于獲得文件的各種信息并將結(jié)果保存在一個(gè)常量字符串中,便于其他函數(shù)調(diào)用。這些信息包括:播放時(shí)間、音頻格式、音頻比特率、音頻通道、音頻頻率、視頻格式、視頻比特率、視頻高度、視頻寬度等。
3)讀取音頻采樣函數(shù)
bool audioReadSamples(short* output , int channels, long samples, long& samplesRead, int);
調(diào)用解碼器對(duì)音頻采樣數(shù)據(jù)進(jìn)行讀取,是音頻數(shù)據(jù)處理的核心部分。output表示待輸出文件指針,channels表示通道數(shù),samples表示采樣數(shù),samplesRead表示待讀取采樣數(shù)
4)讀取視頻幀函數(shù)
bool videoReadScaledFrame(unsigned char** output_rows, int, int, int in_w, int in_h,int out_w,int out_h,ColorFormat fmt,int);
調(diào)用解碼器對(duì)視頻幀進(jìn)行讀取,是視頻數(shù)據(jù)處理的核心部分。參數(shù)output_rows表示輸出列地址的指針,in_w、in_h、out_w、out_h分別表示輸入和輸出幀數(shù)據(jù)的寬度和高度,fmt表示采用的色彩模式,返回值用來(lái)判斷執(zhí)行是否成功。
5)音視頻同步函數(shù)定義:int Sync(File*fp,int auIndex,struct timeval*vtime);
fp為打開的多媒體文件指針,vtime為當(dāng)前正在播放的視頻文件的幀頭中提取的時(shí)間, auIndex指出當(dāng)前的音頻幀計(jì)數(shù),即當(dāng)前播放到了第幾幀。通過(guò)這些參數(shù)就可以計(jì)算出希望跳到的幀數(shù)和當(dāng)前幀數(shù)的差值,然后根據(jù)這個(gè)差值將音頻流向前(滯后)或向后(超前)跳即可。同時(shí)Sync函數(shù)還會(huì)將此差值int反饋給音頻解碼器,讓音頻解碼器修正數(shù)據(jù)流的時(shí)間戳,如此循環(huán),從而達(dá)到較好的音視頻同步效果。此函數(shù)的總體思想是在播放視頻數(shù)據(jù)流的同時(shí)啟動(dòng)另一線程,打開對(duì)應(yīng)的音頻數(shù)據(jù)流播放,然后在視頻線程中來(lái)同步音頻數(shù)據(jù)。
此外還有插件初始化和注冊(cè)函數(shù) void pluginInit()、文件初始化函數(shù) void fileInit()、查找函數(shù) bool seek(long pos)、清空視頻數(shù)據(jù)函數(shù)flushVideoPackets()和清空音頻數(shù)據(jù)函數(shù)flushAudioPackets()、獲取下一數(shù)據(jù)包函數(shù) MediaPacket*getAnotherPacket(int stream)等,不再做詳細(xì)介紹。
3.2解碼庫(kù)模塊
解碼庫(kù)模塊的主要作用是為插件接口模塊提供解碼器,考慮到播放器的可移植性和可擴(kuò)展性,本系統(tǒng)采用了ffmpeg解碼庫(kù)。FFmpeg解碼庫(kù)是Linux下的一個(gè)開源解碼器集合,它支持多種音頻和視頻編解碼標(biāo)準(zhǔn),還支持轉(zhuǎn)文件格式、制作avi等,功能十分強(qiáng)大。可以在windows下使用的ffshow插件,linux下的mplayer播放器都是使用的ffmpeg解碼庫(kù)。
解碼庫(kù)又包含解碼器和分離器。解碼器就是對(duì)音視頻數(shù)據(jù)流進(jìn)行解碼的組件,分離器就是把文件流中的數(shù)據(jù)分離為音頻數(shù)據(jù)流和視頻數(shù)據(jù)流的組件,音頻數(shù)據(jù)和視頻數(shù)據(jù)是分開解碼的,所以二者缺一不可。
3 嵌入式媒體播放器系統(tǒng)實(shí)現(xiàn)
3.1 數(shù)據(jù)流程總體設(shè)計(jì)
圖2為系統(tǒng)數(shù)據(jù)流程:首先輸入模塊從數(shù)據(jù)源(多媒體文件)讀入數(shù)據(jù),此時(shí)它將讀入文件頭,做一些基本的處理,如讀出文件長(zhǎng)度,獲取此文件的編碼類型、比特率,判斷能否播放等;然后插件接口模塊會(huì)調(diào)用分離器插件將多媒體數(shù)據(jù)切分為視頻數(shù)據(jù)流和音頻數(shù)據(jù)流;再經(jīng)過(guò)視頻FIFO和音頻FIFO,排序處理;最后送入視、音頻解碼器調(diào)用相應(yīng)的解碼器進(jìn)行解碼,對(duì)于音頻數(shù)據(jù)就會(huì)進(jìn)行重采樣,對(duì)于視頻數(shù)據(jù)就會(huì)讀取相應(yīng)的幀,逐幀解碼;之后經(jīng)過(guò)采樣的音頻數(shù)據(jù)和經(jīng)過(guò)渲染覆蓋的視頻數(shù)據(jù)先進(jìn)行音視頻同步,再分別通過(guò)視、音頻輸出模塊輸出。這其中,數(shù)據(jù)的讀入、分離、解碼、輸出都是通過(guò)Qt提供的類庫(kù)以多線程同時(shí)進(jìn)行的,在解碼得同時(shí)程序也在不斷將數(shù)據(jù)讀入緩沖區(qū)并排序等待處理,以提高效率。
輸入模塊的主要功能是將用戶指定的多媒體文件讀入。由于不同格式的多媒體文件需要調(diào)用不同的解碼器才能正常打開,因此考慮到程序的模塊化將實(shí)際的文件打開工作交給插件接口模塊調(diào)用相應(yīng)的解碼器進(jìn)行,輸入模塊只對(duì)文件進(jìn)行一些基本的處理并對(duì)文件內(nèi)容進(jìn)行緩存,然后為插件接口模塊輸送原始數(shù)據(jù)流。用戶首先通過(guò)圖形用戶界面選定待播放文件發(fā)出打開指令,這將會(huì)使輸入模塊接收到一個(gè)信號(hào)并通過(guò)用戶界面?zhèn)骰氐男畔@得待播放文件的文件路徑和文件名。接下來(lái)輸入模塊會(huì)檢查文件路徑是否合法、文件是否為空,之后會(huì)向插件接口模塊發(fā)出信號(hào),通知插件接口模塊查找可用的解碼器,為文件解碼做好準(zhǔn)備。下一步就是進(jìn)行調(diào)用播放初始化函數(shù)init(),其具體過(guò)程下面會(huì)詳細(xì)介紹,最后就是將工作移交給插件接口模塊,讓它調(diào)用對(duì)應(yīng)文件格式的解碼器的open()函數(shù)。
輸出模塊的主要功能是將通過(guò)解碼器解碼之后的音頻、視頻數(shù)據(jù)送到輸出設(shè)備(如LCD顯示屏、揚(yáng)聲器)輸出。根據(jù)輸出內(nèi)容的不同可以將輸出模塊劃分為音頻輸出和視頻輸出兩個(gè)子部分。這兩個(gè)部分基本上是相互獨(dú)立輸出的,通過(guò)插件接口模塊的同步控制讓它們?cè)谳敵鰰r(shí)保持同步。視頻輸出和音頻輸出稍有不同,它利用Qt/Embedded可以直接控制FrameBuffer的特性來(lái)輸出視頻數(shù)據(jù)。幀緩沖區(qū)是顯卡上的內(nèi)存,使用幀緩沖區(qū)可以提高繪圖的速度和整體性能,與幀緩沖區(qū)有關(guān)的設(shè)備是/dev/fb0(主設(shè)備號(hào)29,次設(shè)備號(hào)0)。[!--empirenews.page--]
圖2 系統(tǒng)數(shù)據(jù)流程
4.2嵌入式音視頻同步設(shè)計(jì)
本方法的基本思想是以視頻流為主媒體流,音頻流為從媒體流,視頻的播放速率保持不變,根據(jù)本地系統(tǒng)時(shí)鐘確定的實(shí)際顯示時(shí)間,通過(guò)調(diào)整音頻播放速度來(lái)達(dá)到音視頻同步。整個(gè)系統(tǒng)的音視頻同步數(shù)據(jù)流程見(jiàn)圖3。首先選擇一個(gè)本地系統(tǒng)時(shí)鐘參考(LSCR),要求本地系統(tǒng)時(shí)鐘參考上的時(shí)間是線性遞增的。然后將LSCR分送到視頻解碼器和音頻解碼器,由這兩個(gè)解碼器根據(jù)各幀的PTS值對(duì)照本地系統(tǒng)時(shí)鐘參考產(chǎn)生各幀準(zhǔn)確的顯示或回放的時(shí)間。也就是說(shuō),生成輸出數(shù)據(jù)流時(shí)依據(jù)本地參考時(shí)鐘上的時(shí)間給每個(gè)數(shù)據(jù)塊都打上時(shí)間戳(一般包括開始時(shí)間和結(jié)束時(shí)間)。在播放時(shí),讀取數(shù)據(jù)塊上的時(shí)間戳,同時(shí)根據(jù)本地系統(tǒng)時(shí)鐘參考上的時(shí)間來(lái)安排播放。
圖3 音視頻同步數(shù)據(jù)流程
基于時(shí)間戳的播放過(guò)程中,僅僅對(duì)早到的或晚到的數(shù)據(jù)塊進(jìn)行等待或快速處理,往往是不夠的。如果想要更加主動(dòng)并且有效地調(diào)節(jié)播放性能,就需要引入反饋機(jī)制,也就是通過(guò)對(duì)比音視頻的時(shí)間戳將當(dāng)前數(shù)據(jù)流的播放狀態(tài)反饋給上層的“源”。如果音頻流滯后,就即時(shí)通知音頻解碼器加快音頻流輸出,但是如果滯后太多,則直接將當(dāng)前數(shù)據(jù)丟棄,直接跳到下一幀;如果視頻流滯后,就通知音頻解碼器減慢音頻輸出速度等待視頻流,如滯后太多也直接進(jìn)行跳幀。數(shù)據(jù)流首先通過(guò)分離器分解為視頻數(shù)據(jù)流和音頻數(shù)據(jù)流,然后經(jīng)過(guò)對(duì)應(yīng)的解碼器,同時(shí)由本地系統(tǒng)時(shí)鐘來(lái)進(jìn)行時(shí)間戳控制;獲得準(zhǔn)確顯示或回放時(shí)間以后進(jìn)行時(shí)間戳比較;若同步則直接輸出,不同步則進(jìn)行音頻跳幀或等待,直到同步后輸出。
5 總結(jié)
本文的創(chuàng)新點(diǎn)是系統(tǒng)具有很好的可移植性,它的實(shí)現(xiàn)過(guò)程以及核心的代碼對(duì)類似應(yīng)用具有很好的可重用性,只需通過(guò)較小的修改就能移植到不同的操作系統(tǒng)和平臺(tái)上,可以廣泛使用在各種嵌入式系統(tǒng)中,如PDA,智能手機(jī)等方面,具有較高的經(jīng)濟(jì)價(jià)值,同時(shí)也可以為開發(fā)其他嵌入式系統(tǒng)軟件提供參考意見(jiàn);其次,本文圍繞用戶的基本需求,提出了一個(gè)基于嵌入式Linux操作系統(tǒng)和圖形用戶界面QT/Embedded的媒體播放器設(shè)計(jì)方案。該設(shè)計(jì)方案具有低耦合、高內(nèi)聚、可擴(kuò)展、可移植等良好特點(diǎn),并在設(shè)計(jì)的基礎(chǔ)上將該方案實(shí)現(xiàn)。該媒體播放器支持編碼格式為MPEG-1、MPEG-2和MPEG-4的多媒體文件。同時(shí)具有存儲(chǔ)空間小,響應(yīng)速度快的性能特點(diǎn),并支持播放控制、播放列表等功能,可自由切換中英文雙語(yǔ)界面,用戶可以選擇打開任意位置的文件。項(xiàng)目經(jīng)濟(jì)效益50萬(wàn)元。