隨著快手,抖音,西瓜視訊等視訊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以後有單獨的渲染執行緒。