當前位置:編程學習大全網 - 源碼下載 - 轉:小窗播放視頻的原理和實現(上)

轉:小窗播放視頻的原理和實現(上)

轉到, 在手機上看著方便

由於文章篇幅較長,將分為上、下兩篇。上篇主要介紹小窗播放視頻的原理,下篇主要介紹小窗播放視頻的實現。

目前很多視頻類App都有小窗播放功能,比如Youtube(如圖1)、Facebook(如圖2)等,不過它們的實現方式卻不同。Youtube 是將視頻播放View內嵌到應用內,優點是交互好;Facebook則是通過WindowManager添加視頻播放View,同時支持應用內部和外部播放。

小窗播放視頻功能在小窗和大屏之間切換時,視頻類App通常壹邊執行交互動作壹邊播放視頻。交互動作包括移動、縮放或者動畫;這些App在播放時期望給用戶平滑的過渡體驗,流暢加載視頻,不能有明顯的卡頓。

<figure>

<figcaption>(圖1 Youtube小窗播放視頻)</figcaption>

</figure>

<figure>

<figcaption>(圖2 Facebook小窗播放視頻)</figcaption>

</figure>

二、SurfaceView 和 GLSurfaceView

Android 中使用 MediaPlayer 播放視頻時,壹般采用 SurfaceView 、 GLSurfaceView 和 TextureView 。Youtube、Facebook用不同技術方案實現了小窗播放視頻功能,它們***同點是都可以使用 SurfaceView 、 GLSurfaceView 和 TextureView 來播放視頻。接下來分析三個視圖用於小窗播放視頻的原理。

SurfaceView繼承自類View,但與其他View的子類不同的是它有獨立的Surface,如下源碼可以看出它包含壹個Surface屬性,即它不與宿主窗口***享同壹個繪圖表面。因此SurfaceView的UI可以在壹個獨立的線程中進行繪制。由於不會占用主線程資源,因此SurfaceView可以實現復雜而高效的UI。GLSurfaceView繼承SurfaceView,作為SurfaceView的補充,加入了EGL的管理,並自帶了渲染線程。它用於小窗播放時效果和SurfaceView類似。本文以下內容以SurfaceView為例分析獨立的Surface對小窗播放會有什麽影響。

通過Surface的類註釋可以知道,Surface處理壹塊由Screen compositor管理的Raw buffer。而Screen compositor其實就是SurfaceFlinger服務。Surface字面意思就是繪圖表面,可以理解為是UI的畫布。

Android應用程序窗口需要請求SurfaceFlinger服務創建繪圖表面(也就是Surface對象),同時窗口還需要被WindowManagerService管理;所以實際上窗口的繪圖表面是通過兩個Surface對象來描述,壹個是應用程序進程創建的,另壹個是由WindowManagerService創建的,這兩個Surface對象對應於SurfaceFlinger服務的同壹個Layer對象。

在應用程序進程這壹側,每壹個應用程序窗口,如Activity,都有壹個Surface對象,就是在ViewRootImpl對象的mSurface屬性,這個Surface用來繪制應用程序窗口的UI,如下ViewRootImpl源碼所示。當界面需要刷新時,窗口調用draw方法,向Surface請求canvas,執行繪畫操作後再次提交給Surface完成屏幕顯示。

在WindowManagerService服務這壹側,每壹個窗口,都有壹個對應的WindowState對象。其有壹個屬性mSurface,它負責設置窗口的位置、大小屬性。例如,壹個窗口的Z軸坐標大小要考慮到它的窗口類型,以及它與系統中的其它窗口的關系[2]。

SurfaceView有獨立的繪圖表面,那麽SurfaceView又是怎麽繪制在宿主窗口的呢?

上面說到每壹個窗口在SurfaceFlinger服務中都對應有壹個Layer,用來描述它的繪圖表面。同時每壹個SurfaceView在SurfaceFlinger服務中還對應有壹個獨立的Layer或者LayerBuffer,用來單獨描述它的繪圖表面,以區別於它的宿主窗口的繪圖表面[3]。

<figure>

</figure>

(圖3 SurfaceView的Surface的創建過程)

接下來了解Surface創建過程,如圖3的時序圖所示,每當壹個窗口需要刷新UI時,就會調用ViewRootImpl類的performTraversals方法。如果當前窗口的Surface還沒有創建,或者已經失效,SurfaceView就會請求WindowManagerService服務創建壹個新的Surface,它最終會調用updateWindow來完成Surface的創建。

如SurfaceView源碼所示,它有壹個mSurface屬性,相比之下TextView或者Button等普通View會***用ViewRootImpl的Surface。

SurfaceView類的屬性mWindowType描述的是SurfaceView的窗口類型,它的默認值等於 TYPE_APPLICATION_MEDIA ,用來顯示多媒體的,如視頻。SurfaceView還有另外壹個類型 TYPE_APPLICATION_MEDIA_OVERLAY ,它是在視頻上面顯示Overlay的,它可以顯示視字幕等信息。宿主窗口會遮擋這兩個類型的SurfaceView,如果窗口嵌入這兩類SurfaceView,那麽它們的Z軸位置會低於該窗口的Z軸位置,顯示在該窗口下面。如果mWindow等於null的話,那麽就說明該SurfaceView還沒有添加到WindowManagerService服務中去,然後調用addWithoutInputChannel添加到WindowManagerService服務中。

mSession.relayout()請求WindowManagerService服務對SurfaceView的UI進行布局。如果宿主窗口的繪制表面還未創建,或者需要重新創建,那麽就會請求SurfaceFlinger服務為它創建壹個新的Surface。由於這壹步可能會修改SurfaceView的Surface,所以添加了mSurfaceLock鎖,避免其它線程同時修改該Surface的內容。

執行完成上述步驟之後,SurfaceView的Surface的創建完成了。但是mWindowType為 TYPE_APPLICATION_MEDIA 或 TYPE_APPLICATION_MEDIA_OVERLAY 的SurfaceView會被宿主窗口擋住,如何解決這個問題,這就要了解SurfaceView“挖洞”原理。

當SurfaceView附加宿主窗口時,它的onAttachedToWindow會被調用。這個方法調用requestTransparentRegion請求在宿主窗口上設置透明區域,即請求在宿主窗口上挖洞,其實就是設置ViewRootImpl中Surface的透明度。而每當其宿主窗口刷新自己的UI的時候,就會調用ViewGroup的gatherTransparentRegion將所有嵌入在它裏面的SurfaceView所設置的透明區域收集起來。然後再通知WindowManagerService為SurfaceView的gatherTransparentRegion方法設置壹個總的透明區域。這就是SurfaceViewd的“挖洞”原理,如圖4時序圖所示。

<figure>

</figure>

(圖4 SurfaceView“挖洞”原理)

接下來結合ViewRootImpl類的requestTransparentRegion源碼,來分析請求在宿主窗口上設置透明區域的過程。

當mView等於子View時,將mPrivateFlags的 View.REQUEST_TRANSPARENT_REGIONS 位設置為1,表示該窗口被設置了壹塊透明區域。當壹個窗口被請求設置了壹塊透明區域之後,它的窗口屬性就發生了變化,這時候除了要將與它所關聯的ViewRootImpl對象的mWindowAttributesChanged值設置為true之外,還要調用該ViewRootImpl對象的requestLayout方法對窗口的UI進行重新布局和繪制。requestLayout最終會調用到另外壹個方法performTraversals來實際執行刷新窗口UI的操作。

ViewRootImpl類的方法performTraversals是用來收集嵌入在它裏面的SurfaceView所設置的透明區域的。它處於窗口的UI布局完成之後,窗口的UI繪制之前。這是因為窗口的UI布局完成之後,各個子視圖的大小和位置才能確定下來,進而才能確定SurfaceView的透明區域的位置和大小。從頂層視圖開始,從上到下收集每壹個子視圖所要設置的區域,最終收集到的總透明區域並保存在ViewRootImpl類的成員變量mTransparentRegion中。其中host是DecorView,它的gatherTransparentRegion方法重載了父類ViewGroup的gatherTransparentRegion方法。

ViewGroup的gatherTransparentRegion方法中,檢測到有透明區域時,調用父類View的方法gatherTransparentRegion來檢查當前視圖容器是否需要繪制。如果需要繪制,說明當前視圖的前景需要繪制,就會將它所占據的區域從參數region所占據的區域移除,以便可以顯示當前視圖的前景。然後調用每壹個子視圖的成員函數gatherTransparentRegion來繼續往下收集透明區域。

SurfaceView類的方法gatherTransparentRegion中,先檢測是否用作窗口面板以及mPrivateFlags的SKIP_DRAW位是不是1。如果都是,將它所占據的區域從參數region所描述的區域移除,region中剩下的就是透明區域。最後判斷Surface的像素格式是否設置有透明值。如果有,返回false給ViewRootImpl,然後ViewRootImpl調用 sWindowSession.setTransparentRegion(mWindow, mTransparentRegion); 設置窗口為透明。這樣就能看到SurfaceView了。

SurfaceView雖然具有獨立的Surface,不過它仍然是宿主窗口的視圖結構中的壹個結點,因此,它仍然是可以參與到宿主窗口的繪制流程中去的。

在SurfaceView的draw和dispatchDraw方法中,參數canvas是建立在宿主窗口的Surface上的畫布,因此在這塊畫布上繪制任何UI都是出現在宿主窗口的Surface上的。但如果當前正在處理的SurfaceView不是用作宿主窗口面板的時候,即屬性mWindowType的值不等於 TYPE_APPLICATION_PANEL 的時候,SurfaceView的這兩個方法只是簡單地將它所占據的區域繪制為黑色。另外dispatchDraw還會調用另外壹個方法updateWindow更新的UI,繪制自己的Sueface。

通過了解SurfaceView的創建過程、“挖洞”原理和繪制過程,可以了解到Surface對小窗播放視頻的影響如下:

1)SurfaceView在宿主窗口下面,通過“挖洞”原理顯示Surface。SurfaceView在做旋轉時,畫面不會跟隨SurfaceView旋轉。

2)同理,設置透明度或者執行透明值動畫時,SurfaceView顯示有問題。

3)SurfaceView繪制時會先繪制黑邊,所以在移動或者縮放過程,在更新不及時時會看到黑邊。

4)SurfaceView具有獨立的Surface,它的UI繪制可以在獨立的線程中進行,可以進行復雜的UI繪制。

由於SurfaceView不在View hierarchy中,View的壹些縮放,透明度變化等方法無法使用。要實現這些功能就得使用TextureView,但TextureView有個缺點就是性能低耗電高。Android N對SurfaceView進行了更改,它對SurfaceView自身和它的內容改變做了同步處理,播放視頻時不會出現之前難看的黑色條。SurfaceView因這個新特性不會出現黑色條,但它旋轉時畫面仍然不會跟隨旋轉,仍然不支持透明度。

如下源碼所示,TextureView繼承於View,並重載了View的draw()方法,它與其它的View壹樣在View hierarchy中管理與繪制。draw()方法中主要把SurfaceTexture中收到的圖像數據作為紋理更新到對應的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用於通知TextureView有新數據。

根據以上信息,可以知道TextureView不同於SurfaceView,沒有單獨創建Surface,而是作為View hierarchy中的壹個普通View,來進行移動,旋轉,縮放,動畫等,沒有SurfaceView執行旋轉、縮放時的缺點。值得註意的是TextureView必須在硬件加速的窗口中,通過HardwareLayer更新視圖。它需要硬件加速層,這使得TextureView比SurfaceView更耗性能。 Android N上SurfaceView新特性的說明上,官方也推薦在不執行旋轉、透明度、縮放時使用SurfaceView。

SurfaceView有獨立的Surface,通過“挖洞”原理顯示它。以致它在執行旋轉時,畫面不會跟隨旋轉;同時設置透明度或者執行透明值動畫時,顯示有問題。Android N以上的SurfaceView在視頻進行縮放旋轉時會同步變化,不會看到黑色邊,官方推薦使用SurfaceView。TextureView作為普通View在View hierarchy中管理與繪制,更適用於小窗播放視頻功能。但TextureView需要硬件加速層,使得TextureView比SurfaceView和GLSurfaceView更耗性能。

[1]、Surface( /reference/android/view/Surface.html )

[2]、Android應用程序窗口(Activity)的繪圖表面(Surface)的創建過程分析 ( /reference/android/view/SurfaceView.html )

小窗播放視頻的原理和實現(下) /developer/article/1047885

  • 上一篇:2022年,全面“太空旅行商業”時代的開始,人類走向太空
  • 下一篇:Cmrp計算源代碼
  • copyright 2024編程學習大全網