Android進階:自定義視頻播放器開發(上)
隨著快手,抖音,西瓜視頻等視頻APP的崛起,視頻播放已經成為主流,此時作為Android研發的你,想要提高自己的能力還不知道怎麽開發視頻播放器怎麽行?所以今天就帶著大家一起開發一個簡易播放器:SmallVideoPlayer
需求分析
我們觀察一個視頻播放器,可以看到視頻播放器除了正在播放的視頻還有很多控件,比如播放按鈕,暫停按鈕,播放進度條,播放計時器等。
這麽多控件顯然無法播放視頻,但是他們都在控制視頻的播放。由此可見視頻播放器可以分為兩層,一層為視頻播放器控制層,一層為真正的視頻播放層。
所以實現視頻播放器的時候就可以分為上層控制層,和底層播放層兩層來實現。
視頻播放器播放層實現
技術沈澱
視頻播放內核
我們知道自己開發視頻播放器內核肯定是不現實的,這需要一定的技術成本,單個人很難達到,所以我們就選擇一個最受歡迎的開源的內核即可:bilibili開源的視頻播放器:ijkplayer
視頻播放器
視頻播放這塊需要給大家普及兩個知識點:
SurfaceView
先來介紹一下大部分軟件如何解析一段視頻流。首先它需要先確定視頻的格式,這個和解碼相關,不同的格式視頻編碼不同,不是這裏的重點。知道了視頻的編碼格式後,再通過編碼格式進行解碼,最後得到一幀一幀的圖像,並把這些圖像快速的顯示在界面上,即為播放一段視頻。SurfaceView在Android中就是完成這個功能的。
既然SurfaceView是配合MediaPlayer使用的,MediaPlayer也提供了相應的方法設置SurfaceView顯示圖片,只需要為MediaPlayer指定SurfaceView顯示圖像即可。它的完整API如下:
void setDisplay(SurfaceHolder sh);
它需要傳遞一個SurfaceHolder對象,SurfaceHolder可以理解為SurfaceView裝載需要顯示的一幀幀圖像的容器,它可以通過SurfaceHolder.getHolder()方法獲得。
使用MediaPlayer配合SurfaceView播放視頻的步驟與播放使用MediaPlayer播放MP3大體一致,只需要額外設置顯示的SurfaceView即可。
SurfaceView雙緩沖
上面有提到,SurfaceView和大部分視頻應用一樣,把視頻流解析成一幀幀的圖像進行顯示,但是如果把這個解析的過程放到一個線程中完成,可能在上一幀圖像已經顯示過後,下一幀圖像還沒有來得及解析,這樣會導致畫面的不流暢或者聲音和視頻不同步的問題。所以SurfaceView和大部分視頻應用一樣,通過雙緩沖的機制來顯示幀圖像。那麽什麽是雙緩沖呢?雙緩沖可以理解為有兩個線程輪番去解析視頻流的幀圖像,當一個線程解析完幀圖像後,把圖像渲染到界面中,同時另一線程開始解析下一幀圖像,使得兩個線程輪番配合去解析視頻流,以達到流暢播放的效果。
下圖為演示了雙緩沖的過程,線程A和線程B配合解析渲染視頻流的幀圖像:
SurfaceHolder
SurfaceView內部實現了雙緩沖的機制,但是實現這個功能是非常消耗系統內存的。因為移動設備的局限性,Android在設計的時候規定,SurfaceView如果為用戶可見的時候,創建SurfaceView的SurfaceHolder用於顯示視頻流解析的幀圖片,如果發現SurfaceView變為用戶不可見的時候,則立即銷毀SurfaceView的SurfaceHolder,以達到節約系統資源的目的。
如果開發人員不對SurfaceHolder進行維護,會出現最小化程序後,再打開應用的時候,視頻的聲音在繼續播放,但是不顯示畫面了的情況,這就是因為當SurfaceView不被用戶可見的時候,之前的SurfaceHolder已經被銷毀了,再次進入的時候,界面上的SurfaceHolder已經是新的SurfaceHolder了。所以SurfaceHolder需要我們開發人員去編碼維護,維護SurfaceHolder需要用到它的一個回調,SurfaceHolder.Callback(),它需要實現三個如下三個方法:
void surfaceDestroyed(SurfaceHolder holder)
:當SurfaceHolder被銷毀的時候回調。void surfaceCreated(SurfaceHolder holder)
:當SurfaceHolder被創建的時候回調。void surfaceChange(SurfaceHolder holder)
:當SurfaceHolder的尺寸發生變化的時候被回調。
在應用中分別為SurfaceHolder實現了這三個方法,先進入應用,SurfaceHolder被創建,創建好之後會改變SurfaceHolder的大小,然後按Home鍵回退到桌面銷毀SurfaceHolder,最後再進入應用,重新創建SurfaceHolder並改變其大小。
SurfaceView的優點:
如上面所說,SurfaceView可以在一個獨立的線程中進行繪制,不會影響主線程,並且使用雙緩沖機制,播放視頻時畫面更流暢。
SurfaceView的缺陷:
因為這個Surface不在View hierachy中,它的顯示也不受View的屬性控制,所以不能進行平移,縮放等變換,也不能放在其它ViewGroup中,一些View中的特性也無法使用。
TextureView
與SurfaceView一樣繼承View,它可以將內容流直接投影到View中,可以用於實現Live preview等功能。
和SurfaceView不同的是,它不會在WMS中單獨創建窗口,而是作為View hierachy中的一個普通View,因此可以和其它普通View一樣進行移動,旋轉,縮放,動畫等變化。
值得註意的是TextureView必須在硬件加速的窗口中。它顯示的內容流數據可以來自App進程或是遠端進程。從類圖中可以看到,TextureView繼承自View,它與其它的View一樣在View hierachy中管理與繪制。
SurfaceTexture
TextureView重載了draw()方法,其中主要SurfaceTexture中收到的圖像數據作為紋理更新到對應的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用於通知TextureView內容流有新圖像到來。SurfaceTextureListener接口用於讓TextureView的使用者知道SurfaceTexture已準備好,這樣就可以把SurfaceTexture交給相應的內容源。
Surface
Surface為BufferQueue的Producer接口實現類,使生產者可以通過它的軟件或硬件渲染接口為SurfaceTexture內部的BufferQueue提供graphic buffer。
TextureView優點
支持移動、旋轉、縮放等動畫,支持截圖
TextureView缺點
必須在硬件加速的窗口中使用,占用內存比SurfaceView高,在5.0以前在主線程渲染,5.0以後有單獨的渲染線程。
Android進階:自定義視頻播放器開發(上)