蔡曉麗 程曉蘭
(四川長虹網絡科技有限責任公司 四川省綿陽市 621000)
長期以來數字機頂盒上多媒體播放器嚴重的依耐于各個芯片SDK 提供的媒體播放接口實現(xiàn)。在機頂盒應用開發(fā)中,對于每一個芯片平臺都需要適配媒體播放接口。加之流媒體的興起,各種芯片平臺對流媒體的支持能力更是參差不齊?;诖嗽O計可跨平臺的播放器可以減少對芯片平臺的依賴,引進新平臺時可以快速的實現(xiàn)播放器成型。通過集成統(tǒng)一的流媒體接口可以更好的規(guī)劃產品定型。
多媒體涉及MKV, AVI, FLV, WMV, TS 等容器格式以及流媒體中的HLS, Smooth streaming, DASH 等協(xié)議內容。媒體完成解析后便形成獨立的字幕流,音頻流,視頻流。將音頻流,視頻流分別注入芯片底層的解碼器實現(xiàn)解碼完成播放。利用開源軟件實現(xiàn)對多媒體解析,并封裝統(tǒng)一的音視頻同步控制接口,寫解碼器控制接口和文件讀寫控制接口最大程度的弱化媒體播放器對芯片平臺的依耐性。另外現(xiàn)有的VLC,F(xiàn)FMPEG 等開源代碼均能很好完成多媒體容器解析。而VLC 作為優(yōu)秀的開源播放器支持多種媒體封裝格式解析,而且適用于多個平臺集成。
本文提出一種基于開源軟件VLC 的可跨平臺應用的多媒體播放器系統(tǒng)。以C/S 模式,VLC 進程作為服務器端等待多媒體解析請求,應用平臺端作為客戶端向服務端發(fā)起播放請求,經進程通信控制模塊(IPC)傳遞播放地址,開啟音視頻讀數據線程并打開底層解碼器,之后等待共享內存中視頻PES 數據量達到起播值SIZE_READ_START。VLC 進程獲取媒體播放地址后開啟媒體容器解析線程。一方面將獲取的媒體解碼器信息包括音視頻編碼格式,音視頻PID 值和文件時長等信息通過IPC 接口傳回給客戶端。另一方面將音視頻流PES 數據寫入分配的共享內存BUFFER 供客戶端取用??蛻舳嗽讷@取媒體解碼信息后配置音視頻解碼器解碼格式。讀視頻數據線程等待共享內存內數據達到起播閾值后立即開始將數據整塊寫入解碼器緩存,并開啟音頻流讀數據線程。
如圖1 所示,播放器系統(tǒng)由兩個進程協(xié)調實現(xiàn)平臺應用客戶端進程與VLC 服務端進程。平臺應用客戶端進程內包括播放器UI 模塊,讀共享內存控制模塊,音視頻同步控制模塊,解碼器模塊,輸出控制模塊,平臺抽象層模塊。VLC 服務端進程包括媒體解析模塊,寫共享內存控制模塊。各個功能模塊的具體實現(xiàn)功能如下。
播放器UI 模塊,負責視頻元信息展示及視頻播放;并與用戶交付如暫停,播放,快進快退,SEEK 等播放控制指令。
讀共享內存控制模塊,負責從共享內存中讀視頻數據與讀音頻數據。讀取的同時寫入解碼器控制模塊。讀視頻數據控制等待視頻緩存內可用數據量超過閾值SIZE_READ_START 開啟讀操作。SIZE_READ_START 值的大小會影響起播時間與起播后的流暢性?,F(xiàn)在改值量化為20KByte,播放器的起播時間小于15 秒。
讀共享內存控制模塊,判定當前已寫入的數據量Video_writetotalsize 與可讀數據量Video_readablesize 進行比較確定讀取的數據量Inject_Data_Size。將視頻PES 緩存設置成一個循環(huán)BUFFER。已寫入的數據量Video_writetotalsize 為寫入的循環(huán)次數Video_writecycle 乘以分配的內存大小VIDEO_BUFFER_MAX_SIZE 在與當前的寫地址偏移Video_writesize 求和而得。以讀的數據量Video_readtotalsize 為已讀的循環(huán)次數Video_readcycle 乘以分配的內存大小VIDEO_BUFFER_MAX_SIZE 再與當前的讀地址偏移量Video_readsize 求和而得。如圖2 所示,根據上述幾者的關系確定Inject_Data_Size。讀音頻數據控制流程與讀視頻數據控制流程類似不在贅述。
音視頻同步控制模塊,平臺應用第一次讀取到視頻PES 數據時提取視頻第一幀的PTS 值V_PTS。并將此值作為初始值寫入平臺底層系統(tǒng)同步時間系統(tǒng),從而初次同步到系統(tǒng)時間戳,再以系統(tǒng)時間戳作為時間基準進行音視頻同步。每隔時間T0 抽取音頻幀與視頻幀的的PTS 時間值與系統(tǒng)時間戳進行對比,如果音頻幀PTS 與時間戳的差值遠遠大于視頻幀PTS 值與時間戳的差值則重新收取下一個比較接近PTS 數據的音頻幀寫入底層解碼器實現(xiàn)同步。
解碼器模塊,如圖3 所示,解碼器模塊下設置音頻解碼器寫控制與視頻解碼器寫控制。以視頻解碼器寫控制為例,讀共享內存控制模塊獲取Inject_Data_Size 后,從共享內存拷貝大小Inject_Data_Size 的數據量寫入底層解碼器。最終根據解碼器驅動接口返回的寫入數據值作為真正寫入的數據量,并更新Video_readtotalsize 作為下一次的判定標準。輸出模塊,對接音頻輸出驅動和視頻輸出驅動。
平臺抽象層模塊,根據媒體播放對平臺的依賴性,該模塊需要提供內存管理,文件系統(tǒng),內核管理等平臺抽象封裝。對平臺抽象層的每個模塊進行類定義,在類的設計采用C++虛函數機制,該種機制能夠實現(xiàn)運行時綁定的特性,因此派生類的行為不影響基類以及基類同一基類的派生類的行為。
媒體解析模塊,VLC2.2.0 在DecoderThread 線程內查詢共享內存的視頻數據的緩存量確定是否繼續(xù)向共享內存數據寫入。Video_readtotalsize 減去Video_writetotalsize 得到的差值Sub_videosize,如果差值大于分配的視頻數據緩存量VIDEO_BUFFER_MAX_SIZE*(3/5)就需要暫停從解析線程中讀取數據到共享內存。寫共享內存控制模塊,根據音視頻的PID 值分別將音視頻PES 數據寫入共享內存。
圖1:基于VLC 的可跨平臺應用的播放器架構圖
圖3:播放器系統(tǒng)中解析解碼功能結構框圖
該播放系統(tǒng)的一個重要組成是PES 數據的進程交付控制。一種VLC 提取媒體文件PES 數據以共享內存方式交付另一進程的方法包括以下步驟。
(1)根據需要設置VLC 以文件寫入方式輸出媒體文件解析的ES 數據到移動硬盤,具體控制命令為:argv[argc++]= "-vvv";argv[argc++]= "--no-loop";argv[argc++]= "--sout";argv[argc++]= "#es{access=file, dst-video=/mnt/usb/video_%d.%c, dst-audio=/mnt/usb/audio_%d.%c}";該控制指令為將解析輸出的ES 數據寫入到存儲盤/mnt/usb。
(2)VLC 進程運行初始化階段關聯(lián)共享內存地址到本地內存地址,內存大小分配為視頻PES 數據緩存為3.768MByte,音頻PES數據緩存為1.88MByte,控制信息緩存1024KByte。
(3)VLC 進程接收播放請求后,首先獲取通道音視頻編解碼信息。其次開啟媒體播放線程。在獲取文件播放地址后調用libvlc_media_get_duration 獲取媒體的時長信息,通過接口libvlc_media_get_tracks_info 獲取音視頻通道解碼信息。通過調用接口libvlc_media_player_play 開啟播放流程。
(4)VLC 將解析獲取的通道音視頻解碼信息,寫入共享內存控制信息緩存。VLC 需要將解析的視頻ES 數據轉換成視頻PES 數據并寫入視頻數據緩存,解析的音頻ES 數據轉換成音頻PES 數據寫入音頻數據緩存。在ES 輸出的指令驅使下,VLC2.2.0 在線程DecoderThread 中將解析器輸出的數據包經接口DecoderProcessSout寫入到對應的存儲盤地址/mnt/usb/。此處需要阻斷上述過程,修改函數DecoderProcessSout 在得到解析ES 包數據后經重新封裝的接口sout_AccessOutMemoryWrite 寫入到共享內存。重新封裝的接口sout_AccessOutMemoryWrite 首先將ES 數據轉換成PES 數據然后寫入共享內存。音視頻PES 數據寫入以PID 信息作為區(qū)分標志。VLC2.2.0 中線程DecoderThread 需輪詢共享內存區(qū)域狀態(tài),根據狀態(tài)確定是否繼續(xù)向共享內存寫入數據,否則會造成數據溢出花屏問題; 輪詢平臺解碼器狀態(tài),確定是否繼續(xù)向共享內存寫入數據。
本文提出的可跨平臺的播放系統(tǒng)將多媒體播放器劃分為媒體解析,音視頻同步,解碼,渲染等幾個組成部分。以多進程的方式利用開源軟件VLC2.2.0 實現(xiàn)媒體解析,以共享內存方式進行數據通信,僅利用芯片平臺提供的解碼器解碼獨立的音視頻數據流實現(xiàn)多媒體播放器。利用該方法可以方便在不同芯片平臺上集成具有統(tǒng)一播放體驗的多媒體播放器。