Android進階:自定義視訊播放器開發(上)

Android入墳之路發表於2019-04-04

隨著快手,抖音,西瓜視訊等視訊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配合解析渲染視訊流的幀影象:

  
Android進階:自定義視訊播放器開發(上)
image

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以後有單獨的渲染執行緒。


相關文章