多媒體那些事-播放器漫談 如何選擇更合適的音視頻播放器
前言
近年來,隨著HTML5技術(shù)普及、移動(dòng)設(shè)備迅猛增長和流量資費(fèi)不斷下降,視頻已成為人們了解世界、獲取知識(shí)的主要途徑之一。現(xiàn)在音視頻播放器已隨處可見,但這個(gè)過程到底如何實(shí)現(xiàn)?播放器的原理又是什么?或者是作為生產(chǎn)者,應(yīng)該依據(jù)哪些標(biāo)準(zhǔn)來選擇適合自己的播放器?UCloud將基于多年的實(shí)踐經(jīng)驗(yàn),為客戶做出相應(yīng)解答或建議。
一.播放器功能架構(gòu)
首先來看常規(guī)播放器的功能架構(gòu):
應(yīng)用層:UI/統(tǒng)計(jì)/DRM(數(shù)字版權(quán)保護(hù))/多碼率/彈幕/廣告等。
底層:數(shù)據(jù)接收模塊/解復(fù)用模塊/音視頻解碼、濾鏡、渲染模塊/字幕/應(yīng)用層結(jié)合功能:DRM、多碼率/統(tǒng)計(jì)等。
播放器的功能比較多,本文就不再一一贅述,下面將重點(diǎn)講解多媒體引擎模塊。
二.多媒體引擎
作為播放器的核心部分,多媒體引擎主要負(fù)責(zé)音視頻數(shù)據(jù)的加載、處理和展現(xiàn)。以FFmpeg為例,它的基本運(yùn)作流程如下圖所示:
(圖:多媒體播放流程圖)
我們?cè)敿?xì)了解一下每個(gè)環(huán)節(jié):
1、數(shù)據(jù)接收(Source)
作為數(shù)據(jù)入口,這里的文件可以通過本地文件(File://協(xié)議開頭)輸入,也可以通過網(wǎng)絡(luò)協(xié)議(如HTTP、RTMP、RTSP等)輸入。在找到對(duì)應(yīng)的傳輸協(xié)議之后,F(xiàn)Fmpeg會(huì)根據(jù)協(xié)議特性與本機(jī)或服務(wù)器建立連接并獲取到流數(shù)據(jù)。
2、解復(fù)用(Demux)
通過對(duì)文件的特征碼分析,可以找到文件的封裝格式,如常見的MP4、FLV、TS、AVI等。根據(jù)封裝格式的標(biāo)準(zhǔn)對(duì)其拆封,可以得到編碼的音視頻數(shù)據(jù),一般稱之為“packet”。
3、解碼(Decode)
解碼器初始化時(shí),利用之前源數(shù)據(jù)分析獲得的音視頻信息,分別設(shè)置對(duì)應(yīng)的音頻解碼器和視頻解碼器。目前互聯(lián)網(wǎng)中比較常見的音頻編碼方式有AAC(Advanced Audio Coding)、MP3,視頻編碼方式有H.264、H.265。對(duì)packet分別進(jìn)行解碼后,音頻解碼獲得的數(shù)據(jù)是PCM(Pulse Code Modulation,脈沖編碼調(diào)制)采樣數(shù)據(jù),一般稱為“sample”。視頻解碼獲得的數(shù)據(jù)是一幅YUV或RGB圖像數(shù)據(jù),一般稱為“picture”。
4、音視頻同步(Synchronizing)
音視頻解碼時(shí)是兩個(gè)獨(dú)立線程,因此獲得的音視頻數(shù)據(jù)是分開的,并無任何關(guān)聯(lián)。理想狀態(tài)下,音視頻按照自己固有頻率渲染輸出就能達(dá)到音視頻同步的效果。但是在現(xiàn)實(shí)中,斷網(wǎng)、弱網(wǎng)、丟幀、緩沖、音視頻不同的解碼耗時(shí)等情況都會(huì)妨礙實(shí)現(xiàn)同步,很難達(dá)到預(yù)期效果,所以要保證視頻內(nèi)容和音頻內(nèi)容對(duì)得上,必須做音視頻同步。
5、音視頻渲染(Render)
經(jīng)過音視頻同步調(diào)整之后,需要把sample和picture分別輸送給聲卡和顯卡,這部分工作建議由成熟的庫來完成。常見的音頻庫有SDL、OpenAL/OpenAL ES、DirectSound、ALSA(Advanced Linux Sound Architecture)、OSS(Open Source System)等;視頻庫有SDL、OpenGL/OpenGL ES、DirectDraw、FrameBuffer等。
經(jīng)過以上5個(gè)步驟,基本的播放流程就完成了。
三.各平臺(tái)視頻技術(shù)概況
要實(shí)現(xiàn)全平臺(tái)覆蓋的播放器,自然少不了技術(shù)支撐。這里主要討論Web、Android、iOS上的音視頻技術(shù),講述一部分音視頻常用方案。
1、Web-HTML5
技術(shù)語言:JavaScript,HTML/CSS
使用環(huán)境:所有可以支持HTML5 Video標(biāo)簽的終端瀏覽器(PC Windows/Mac/Linux/Unix、Android、iOS)即可播放視頻,目前覆蓋率已達(dá)95%。
相關(guān)方案:
1.1 標(biāo)簽
從代碼示例來看,HTML5的Video標(biāo)簽非常簡單易用,controls= " controls "可以使用瀏覽器默認(rèn)的播放器UI(不同瀏覽器UI不一樣),如果不對(duì)controls賦值,則可以通過CSS和Video消息來實(shí)現(xiàn)自定義UI及控制。
1.2 MSE(Media Source Extensions)
Media Source Extensions (MSE) 是一個(gè)主流瀏覽器支持的新Web API,而且是W3C標(biāo)準(zhǔn),允許JavaScript動(dòng)態(tài)構(gòu)建 和 媒體流。它定義了對(duì)象, 允許JavaScript傳輸媒體流片段到一個(gè)HTMLMediaElement。
通過使用MSE,可以動(dòng)態(tài)地修改媒體流而不需要任何插件。這使前端JavaScript可以做更多事情, 比如可以在JavaScript進(jìn)行轉(zhuǎn)封裝、處理,甚至轉(zhuǎn)碼,因此像ABR(Adaptive Bitrate)、hls.js、dash.js、flv.js也是基于這點(diǎn)來實(shí)現(xiàn)的。
雖然MSE不能讓流直接傳輸?shù)絤edia tags上, 但是MSE提供了構(gòu)建跨瀏覽器播放器的核心技術(shù), 讓瀏覽器通過JavaScript API來推音視頻到media tags上,目前MSE的覆蓋率已達(dá)77.93%。
2、Web-Flash
技術(shù)語言:ActionStript
使用環(huán)境:安裝了FlashPlayer的瀏覽器(PC Windows/Mac/Linux/Unix、Android、iOS)即可播放視頻。曾經(jīng)是互聯(lián)網(wǎng)時(shí)代早期富媒體老大的Web-Flash,因HTML5普及已慢慢淡出人們的視野,現(xiàn)在更多是作為HTML5播放器的互補(bǔ)方案來使用。
相關(guān)方案:
2.1 FLVPlayBack
Flash自帶封裝好的播放器,跟HTML5的瀏覽器默認(rèn)播放器差不多,系統(tǒng)了一些播放器UI樣式供選擇,簡單幾句代碼就可以快速構(gòu)建精美的播放器,但是靈活度較差。
2.2 NetStream
Flash處理多媒體的核心類,它提供了appendbytes這個(gè)API,允許外部傳入FLV封裝格式的數(shù)據(jù)進(jìn)來播放,類似于HTML5的MSE。同理,可以用此接口實(shí)現(xiàn)視頻的轉(zhuǎn)封裝、處理、轉(zhuǎn)碼等操作,再配合RTMFP協(xié)議,還可以實(shí)現(xiàn)P2P的視頻傳輸播放。
2.3 CrossBridge & FFmpeg
曾幾何時(shí),F(xiàn)lash還有個(gè)叫CrossBridge(又叫Alchemy、Flascc)的黑科技,允許在Flash規(guī)定范圍內(nèi)跑自己編寫的C/C++代碼,這樣就徹底提升了運(yùn)算效率,如果配合FFmpeg的多媒體處理能力,可能會(huì)有意想不到的效果。
3、Andorid
技術(shù)語言:Java、JNI(C/C++)
使用環(huán)境:Andorid系統(tǒng)(Andorid phone、Android pad、Andorid TV、Android Box等)。
相關(guān)方案:
3.1 MediaPlayer
Android提供的默認(rèn)多媒體播放器,可以用來播放本地或網(wǎng)絡(luò)上的音頻、視頻數(shù)據(jù),主要負(fù)責(zé)解協(xié)議、解碼,但不包含顯示部分。顯示部分需要配合SurfaceView或者SurfaceTexture來處理。
3.2 MeidaCodec (API 16+ ,Jelly Bean 4.1.x)
Android提供的硬編解碼類,第一次使用是在Android 4.1版本(API16),主要對(duì)音視頻數(shù)據(jù)進(jìn)行編碼或解碼操作。在Android 4.3(API18)中,MediaCodec被擴(kuò)展為包含一種通過 Surface 提供輸入的方法(即createInputSurface),這允許輸入來自于相機(jī)的預(yù)覽或是經(jīng)過OpenGL ES呈現(xiàn)。
3.3 JNI & FFmpeg
JNI是Java Native Interface的縮寫,一開始是為了本地已編譯語言,尤其是C和C++而設(shè)計(jì)的,但并不妨礙使用其他編程語言,只要調(diào)用約定受支持就可以了。使用java與本地已編譯的代碼交互,通常會(huì)喪失平臺(tái)可移植性,但有些情況下這樣做是可以接受的,甚至是必須的。例如,使用一些舊的庫與硬件、操作系統(tǒng)進(jìn)行交互,或者為了提高程序的性能。JNI配合FFmpeg這個(gè)強(qiáng)大引擎,使Android平臺(tái)具備了較好的多媒體處理能力。
4、iOS
- 技術(shù)語言:Objective C、C/C++、Swift
- 使用環(huán)境:iOS系統(tǒng)(iphone、ipad)
相關(guān)方案:
4.1 AVFoundation.framework:AVPlayer、AVPlayerLayer
AVPlayer類只包含音視頻的數(shù)據(jù)接收、解碼、處理工作,不包含顯示部分,需要配合AVPlayerLayer來使用,可定制化強(qiáng)度大、靈活,消息也比較豐富,官方推薦使用,取代了MPMoviePlayerController的位置。
4.2 AVFoundation.framework:AVPlayerViewController
提供了默認(rèn)的可視化控制界面,整合了一個(gè)完整的播放器,可以作為控制器進(jìn)行操作顯示。官方推薦使用,取代了MPMoviePlayerViewController的位置。
4.3 MediaPlayer.framework:MPMoviePlayerController
iOS的基礎(chǔ)播放器,使用幾個(gè)簡單的API就能完成視頻文件播放,其內(nèi)部已包含數(shù)據(jù)接收、解碼功能,如果要在UI中展示視頻,需要將view屬性添加到界面中,播放器UI方面需要自行開發(fā)。iOS 9.0以后,蘋果官方已棄用。
4.4 MediaPlayer.framework:MPMoviePlayerViewController
iOS的基礎(chǔ)播放器視圖控制器,MPMoviePlayerViewController在MPMoviePlayerController基礎(chǔ)上封裝了一層View,默認(rèn)全屏模式展示,彈出后自動(dòng)播放,作為模態(tài)窗口展示時(shí)如果點(diǎn)擊“Done”按鈕會(huì)自動(dòng)退出模態(tài)窗口等,有一套自己的UI。iOS 9.0以后蘋果官方已棄用。
4.5 VideoToolbox.framework
VideoToolbox是一個(gè)底層框架,iOS 8.0以后,官方正式放開了硬件編解碼API,它為視頻壓縮和解壓縮提供服務(wù),并存儲(chǔ)在緩沖區(qū)corevideo像素柵格圖像格式之中。這些服務(wù)以會(huì)話對(duì)象的形式提供(壓縮、解壓和像素傳輸),應(yīng)用程序不需要直接訪問硬件編碼器和解碼器相關(guān)內(nèi)容,而只需要直接使用videotoolbox即可實(shí)現(xiàn)編解碼。iOS設(shè)備在硬件編解碼這塊的質(zhì)量有一定保證,可以優(yōu)先使用硬編解碼,F(xiàn)Fmpeg軟解碼為互補(bǔ)方案。
4.6 FFmpeg
因Objective-C支持 C/C++混合編譯,所以很容易就可以把FFmpeg使用起來,讓iOS具備強(qiáng)大的多媒體處理能力。
5、小結(jié)
從HTML5、Flash、iOS、Android四個(gè)平臺(tái)上看,它們似乎都具備音視頻播放能力,但它們之間到底有些什么差別?我們應(yīng)如何選擇合適自己的方案?
先對(duì)比一下播放方面的技術(shù)差異:
我們嘗試把產(chǎn)品場(chǎng)景分為四種,以下是對(duì)各自的建議:
四、幾個(gè)常見問題
做一個(gè)播放器遠(yuǎn)沒有想象的那么簡單,來看一下播放器的幾個(gè)常見問題。
1、 怎么做音視頻同步?
對(duì)于系統(tǒng)封裝好的播放器,則無須關(guān)心這個(gè)問題,但如果要實(shí)現(xiàn)一個(gè)完全可控的播放器,從數(shù)據(jù)接收->解復(fù)用->解碼->音視頻同步->渲染都需要去干預(yù)。處理音視頻同步的原因已在前面章節(jié)講過了,這里著重講實(shí)現(xiàn)原理。
做音視頻同步之前,先補(bǔ)充個(gè)知識(shí)點(diǎn),DTS與PTS(I幀、P幀、B幀請(qǐng)自行補(bǔ)充)。
- DTS(Decode Time Stamp):解碼時(shí)間戳,這個(gè)時(shí)間戳的意義在于告訴播放器該在什么時(shí)候解碼這一幀的數(shù)據(jù)。
- PTS(Presentation Time Stamp):顯示時(shí)間戳,這個(gè)時(shí)間戳用來告訴播放器該在什么時(shí)候顯示這一幀的數(shù)據(jù)。
視頻編碼中必存在I幀,可能存在B幀、P幀,B幀是雙向參考幀,因此會(huì)打亂幀的解碼和顯示順序,因此引入了DTS和PTS來應(yīng)對(duì)這種情況。普遍情況下B幀壓縮率較I幀、P幀高,在點(diǎn)播視頻中比較常見,直播視頻中一般沒有B幀,在此情況下,DTS和PTS應(yīng)該相同。音頻編碼因?yàn)闆]有參考幀的概念,是不需要DTS的。
在清楚了DTS和PTS之后,可見PTS對(duì)音視頻同步起關(guān)鍵作用,音視頻常見的同步方式有三種,分別是:以音頻為主線同步、以視頻為主線同步和以外部時(shí)鐘同步。第一種最為常見,也是比較符合人類視覺聽覺感官的方式(音頻變速會(huì)導(dǎo)致變聲),同時(shí)音頻的播放速率比較固定,比較容易作為主線管理,其同步過程總結(jié)如下:
a) 解碼器從輸入的數(shù)據(jù)封裝中解出音頻PTS、視頻PTS;
b) 創(chuàng)建音頻播放線程,音頻按固有的采樣頻率播放;
c) 創(chuàng)建視頻刷新線程,按一定頻率(10ms)定時(shí)檢測(cè);
d) 通過音頻、視頻的時(shí)間基(time_base)換算成相同時(shí)間基準(zhǔn)的PTS;
e) 在視頻刷新線程中計(jì)算當(dāng)前真實(shí)的音頻PTS、視頻PTS:
- currentRealVideoPTS = 當(dāng)前準(zhǔn)備顯示的視頻幀PTS + 當(dāng)前視頻幀流逝系統(tǒng)時(shí)間;
- currentRealAudioPTS = 最新已播放的音頻幀PTS + 當(dāng)前音頻幀流逝系統(tǒng)時(shí)間;
f) 計(jì)算currentRealVideoPTS - currentRealAudioPTS的差值diff;
g) 若diff <= 0,說明視頻顯示慢了,這時(shí)需要馬上顯示該幀數(shù)據(jù);
- 若diff > 0,則視頻酌情做適當(dāng)?shù)难訒r(shí)顯示;
h) 一幀數(shù)據(jù)的音視頻同步檢測(cè)就此結(jié)束。
以上方法的核心理念是通過延時(shí)評(píng)估算法來控制視頻幀慢放或快放,以此做音視頻在合理范圍內(nèi)保持同步。
2、 怎么做到視頻秒開?
有以下幾點(diǎn)可以優(yōu)化:
- a) 服務(wù)器端下發(fā)關(guān)鍵幀數(shù)據(jù),保證播放器第一時(shí)間解碼到有用的數(shù)據(jù);
- b) 播放器可以在選擇音視頻解碼器環(huán)節(jié)做優(yōu)化,通過外部預(yù)設(shè)解碼器相關(guān)參數(shù)來減少解碼器類型探測(cè)耗時(shí);
- c) 在業(yè)務(wù)層面上優(yōu)化,使用HTTP-DNS服務(wù)做到精準(zhǔn)調(diào)度,選擇最優(yōu)服務(wù)器獲取播放數(shù)據(jù);
- d) 提升音視頻服務(wù)節(jié)點(diǎn)質(zhì)量;
3、 怎么做到低延時(shí)?
延時(shí)這詞是針對(duì)直播而言,先看直播系統(tǒng)的架構(gòu)圖:
從圖中可以看出,可能產(chǎn)生延時(shí)的環(huán)節(jié)有:
a) 主播端推流至上傳節(jié)點(diǎn)的延時(shí);
可以通過接入HTTP-DNS為主播選擇最優(yōu)節(jié)點(diǎn),減少傳輸延時(shí)。
b) 主播端發(fā)包的延時(shí);
主播端應(yīng)盡快發(fā)送本地?cái)?shù)據(jù)至上傳節(jié)點(diǎn),對(duì)于弱網(wǎng)環(huán)境,應(yīng)該有合理的控制策略,盡量避免大量過期數(shù)據(jù)阻塞在本地。
c) 上傳節(jié)點(diǎn)到服務(wù)器集群的數(shù)據(jù)分發(fā)、轉(zhuǎn)封裝、轉(zhuǎn)碼等耗時(shí);
分發(fā)的層級(jí)越多,延時(shí)則越大,這塊也主要依賴于服務(wù)器間的網(wǎng)絡(luò)狀況。
d) 觀眾端到下載節(jié)點(diǎn)的延時(shí);
可以通過接入HTTP-DNS為觀眾選擇最優(yōu)節(jié)點(diǎn),減少傳輸延時(shí)。
e) 播放器本地存在buffer;
常規(guī)的播放器都會(huì)有本地緩存,合理的緩存設(shè)置可以為用戶帶來較好的觀看體驗(yàn),減少視頻的卡頓次數(shù)。如果對(duì)延時(shí)有較高要求,可以減少buffer的緩沖時(shí)長,甚至可以把buffer去掉。
4、 播放器卡頓的原因有哪些?
再看上圖,卡頓可能的原因有:
- a) 主播端推流與上傳節(jié)點(diǎn)傳輸不順暢;
- b) 服務(wù)器間傳輸出了問題;
- c) 觀眾與下載節(jié)點(diǎn)傳輸不順暢。
五、總結(jié)
看完這些,相信大家對(duì)基礎(chǔ)的播放流程及各平臺(tái)上音視頻處理技術(shù)已有初步了解,但是要做好多個(gè)平臺(tái)的播放器并非易事,要做大量兼容性適配、容災(zāi)、性能調(diào)優(yōu)、播放質(zhì)量統(tǒng)計(jì)反饋、新功能迭代等工作。因此,一款好的播放器需要不斷打磨,UCloud也將持續(xù)投入更多資源去支持該領(lǐng)域的運(yùn)作。
作者簡介
【劉宇峰,現(xiàn)任UCloud流媒體終端研發(fā)部經(jīng)理,熱愛多媒體行業(yè)和專注于Web、Android、iOS方面的技術(shù),擁有近十年的互聯(lián)網(wǎng)和多媒體領(lǐng)域的研發(fā)經(jīng)驗(yàn),現(xiàn)主要負(fù)責(zé)UCloud視頻云SDK、直播云SDK的架構(gòu)設(shè)計(jì)、研發(fā)管理和運(yùn)營工作。此前,曾任職于騰訊,主要負(fù)責(zé)Web前端多媒體方面的研發(fā)工作?!?nbsp;