SurfaceView與TextureView 一、SurfaceView 二、 TextureView 三、SurfaceView vs TextureView

SurfaceView與TextureView是Android做視頻開發是必定要用到的兩個控件,它們的特性和使用場景有什麼區別。先來概括如下:

  1. Surface 可以簡單理解爲內存中的一段繪圖緩衝區

  2. SurfaceView是一個有自己Surface的View。它的渲染可以放在單獨線程而不是主線程中。其缺點是不能做變形和動畫。從Android7.0開始,SurfaceView的窗口位置與其他View渲染同步更新。 這意味着在屏幕上平移和縮放SurfaceView不會導致渲染失真。

  3. SurfaceTexture可以用作非直接輸出的內容流,這樣就提供二次處理的機會。與SurfaceView直接輸出相比,這樣會有若干幀的延遲。同時,由於它本身管理BufferQueue,因此內存消耗也會稍微大一些。

  4. TextureView是一個可以把內容流作爲外部紋理輸出在上面的View。它本身需要是一個硬件加速層。事實上TextureView本身也包含了SurfaceTexture。它與SurfaceView+SurfaceTexture組合相比可以完成類似的功能(即把內容流上的圖像轉成紋理,然後輸出)。

  5. TextureView是在View hierachy中做繪製,因此一般它是在主線程上做的(在Android 5.0引入渲染線程後,它是在渲染線程中做的)。而SurfaceView+SurfaceTexture在單獨的Surface上做繪製,可以是用戶提供的線程,而不是系統的主線程或是渲染線程。


下面內容來自CharonChui

作者:CharonChui
文章鏈接:https://github.com/CharonChui/AndroidNote/blob/master/VideoDevelopment/SurfaceView%E4%B8%8ETextureView.md

一、SurfaceView

在說SurfaceView之前,需要先說一下幾個相關的部分。

1.1 Surface簡介

  • Surface就是“表面”的意思,可以簡單理解爲內存中的一段繪圖緩衝區。在SDK的文檔中,對Surface的描述是這樣的:“Handle onto a raw buffer that is being managed by the screen compositor”,
    翻譯成中文就是“由屏幕顯示內容合成器(screen compositor)所管理的原生緩衝器的句柄”, 這句話包括下面兩個意思:

    • 通過Surface(因爲Surface是句柄)就可以獲得原生緩衝器以及其中的內容。就像在C語言中,可以通過一個文件的句柄,就可以獲得文件的內容一樣;
    • 原生緩衝器(rawbuffer)是用於保存當前窗口的像素數據的。
  • 簡單的說Surface對應了一塊屏幕緩衝區,每個Window對應一個Surface,任何View都是畫在Surface上的,傳統的view共享一塊屏幕緩衝區,所有的繪製必須在UI線程中進行

  • 我們不能直接操作Surface實例,要通過SurfaceHolder,在SurfaceView中可以通過getHolder()方法獲取到SurfaceHolder實例。

  • Surface是一個用來畫圖形的地方,但是我們知道畫圖都是在一個Canvas對象上面進行的,Surface中的Canvas成員,是專門用於提供畫圖的地方,就像黑板一樣,其中的原始緩衝區是用來保存數據的地方,
    Surface本身的作用類似一個句柄,得到了這個句柄就可以得到其中的Canvas、原始緩衝區以及其他方面的內容,所以簡單的說Surface是用來管理數據的(句柄)

1.2 SurfaceView 簡介

  • 簡單的說SurfaceView就是一個有SurfaceView裏面內嵌了一個專門用於繪製的Surface,SurfaceView控制這個Surface的格式和尺寸以及繪製位置。

SurfaceView就是在Window上挖一個洞,它就是顯示在這個洞裏,其他的View是顯示在Window上,所以View可以顯式在 SurfaceView之上,你也可以添加一些層在SurfaceView之上。

if (mWindow == null) {  ß
    mWindow = new MyWindow(this);  
    mLayout.type = mWindowType;  
    mLayout.gravity = Gravity.LEFT|Gravity.TOP;  
    mSession.addWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,  
    mVisible ? VISIBLE : GONE, mContentInsets);  
}  

很明顯,每個SurfaceView創建的時候都會創建一個MyWindownew MyWindow(this)中的this正是SurfaceView自身,因此將SurfaceViewwindow綁定在一起,而前面提到過每個window對應一個Surface
所以SurfaceView也就內嵌了一個自己的Surface,可以認爲SurfaceView是來控制Surface的位置和尺寸。傳統View及其派生類的更新只能在UI線程,然而UI線程還同時處理其他交互邏輯,
這就無法保證view更新的速度和幀率了,而SurfaceView可以用獨立的線程來進行繪製,因此可以提供更高的幀率,例如遊戲,攝像頭取景等場景就比較適合用SurfaceView來實現。

  • Surface是縱深排序(Z-ordered)的,這表明它總在自己所在窗口的後面。

  • Surfaceview提供了一個可見區域,只有在這個可見區域內的Surface部分內容纔可見,可見區域外的部分不可見,所以可以認爲SurfaceView就是展示Surface中數據的地方,Surface就是管理數據的地方,
    SurfaceView就是展示數據的地方,只有通過SurfaceView才能展現Surface中的數據。

  • Surface的排版顯示受到視圖層級關係的影響,它的兄弟視圖結點會在頂端顯示。這意味者Surface的內容會被它的兄弟視圖遮擋,這一特性可以用來放置遮蓋物(overlays)(例如,文本和按鈕等控件)。
    注意,如果Surface上面有透明控件,那麼它的每次變化都會引起框架重新計算它和頂層控件的透明效果,這會影響性能。surfaceview變得可見時,surface被創建;surfaceview隱藏前,surface被銷燬。
    這樣能節省資源。如果你要查看surface被創建和銷燬的時機,可以重載surfaceCreated(SurfaceHolder)surfaceDestroyed(SurfaceHolder)

    SurfaceView的核心在於提供了兩個線程:UI線程和渲染線程,兩個線程通過“雙緩衝”機制來達到高效的界面適時更新。
    這個雙緩衝可以理解爲,SurfaceView在更新視圖時用到了兩張Canvas,一張frontCanvas和一張backCanvas。每次實際顯示的是frontCanvas,backCanvas存儲的是上一次更改前的視圖,當使用lockCanvas()獲取畫布時,得到的實際上是backCanvas而不是正在顯示的frontCanvas,之後你在獲取到的backCanvas上繪製新視圖,再unlockCanvasAndPost(canvas)此視圖,那麼上傳的這張canvas將替換原來的frontCanvas作爲新的frontCanvas,原來的frontCanvas將切換到後臺作爲backCanvas。例如,如果你已經先後兩次繪製了視圖A和B,那麼你再調用lockCanvas()獲取視圖,獲得的將是A而不是正在顯示的B,之後你將重繪的C視圖上傳,那麼C將取代B作爲新的frontCanvas顯示在SurfaceView上,原來的B則轉換爲backCanvas。()
    不用畫布,直接在窗口上進行繪圖叫做無緩衝繪圖。用了一個畫布,將所有內容都先畫到畫布上,在整體繪製到窗口上,就該叫做單緩衝繪圖,那個畫布就是一個緩衝區。用了兩個畫布,一個進行臨時的繪圖,一個進行最終的繪圖,這樣就叫做雙緩衝。)

SurfaceView的優缺點:

一般的Activity包含的多個View會組成View hierachy的樹形結構,只有最頂層的DectorView纔是對WMS可見的,這個DecorView在WMS中有一個對應的WindowState,再SurfaceFlinger中有對應的Layer,而SurfaceView正因爲它有自己的Surface,有自己的Window,它在WMS中有對應的WindowState,在SurfaceFlinger中有Layer。雖然在App端它仍在View hierachy中,但在Server端(WMS和SurfaceFlinger)中,它與宿主窗口是分離的。


--》音視頻開發之旅標註:添加了SurfaceView及TextureView區別(https://blog.csdn.net/while0/article/details/81481771)的一張圖片

這樣的好處是對這個Surface的渲染可以放到單獨的線程中去做,渲染時可以有自己的GL context。因爲它不會影響主線程對時間的響應。所以它的優點就是可以在獨立的線程中繪製,不影響主線程,而且使用雙緩衝機制,播放視頻時畫面更順暢。
但是這也有缺點,因爲這個Surface不在View hierachy中,它的顯示也不受View的屬性控制,所以不能進行平移、縮放等動畫,它也不能放在其它ViewGroup中,SurfaceView不能嵌套使用,而且不能使用某些View的特性,例如View.setAlpha()。

從Android7.0開始,SurfaceView的窗口位置與其他View渲染同步更新。 這意味着在屏幕上平移和縮放SurfaceView不會導致渲染失真。

1.3 SurfaceHolder`簡介

顯示一個Surface的抽象接口,使你可以控制Surface的大小和格式以及在Surface上編輯像素,和監視Surace的改變。這個接口通常通過SurfaceView類實現。
簡單的說就是我們無法直接操作Surface只能通過SurfaceHolder這個接口來獲取和操作Surface
SurfaceHolder中提供了一些lockCanvas():獲取一個Canvas對象,並鎖定之。所得到的Canvas對象,其實就是Surface中一個成員。加鎖的目的其實就是爲了在繪製的過程中,
Surface中的數據不會被改變。lockCanvas是爲了防止同一時刻多個線程對同一canvas寫入。

從設計模式的角度來看,Surface、SurfaceView、SurfaceHolder實質上就是MVC(Model-View-Controller)Model就是模型或者說是數據模型,更簡單的可以理解成數據,在這裏也就是Surface
View就是視圖,代表用戶交互界面,這裏就是SurfaceView,SurfaceHolder就是Controller.

--》音視頻開發之旅 標註:這個MVC的理解很精彩

二、 TextureView

因爲上面所說的SurfaceView不在主窗口中,它沒法做動畫沒法使用一些View的特性方法,所以在Android 4.0中引入了TextureView,它是一個結合了View和SurfaceTexture的View對象。它不會在WMS中單獨創建窗口,而是作爲View hierachy中的一個普通view,因此它可以和其他普通View一樣進行平移、旋轉等動畫。但是TextureView必須在硬件加速的窗口中,它顯示的內容流數據可以來自App進程或者遠程進程。

TextureView重載了draw()方法,其中主要SurfaceTexture中收到的圖像數據作爲紋理更新到對應的HardwareLayer中。

SurfaceTexture.OnFrameAvailableListener用於通知TextureView內容流有新圖像到來。SurfaceTextureListener接口用於讓TextureView的使用者知道SurfaceTexture已準備好,這樣就可以把SurfaceTexture交給相應的內容源。Surface爲BufferQueue的Producer接口實現類,使生產者可以通過它的軟件或硬件渲染接口爲SurfaceTexture內部的BufferQueue提供graphic buffer。

SurfaceTexture可以用作非直接輸出的內容流,這樣就提供二次處理的機會。與SurfaceView直接輸出相比,這樣會有若干幀的延遲。同時,由於它本身管理BufferQueue,因此內存消耗也會稍微大一些。
TextureView是一個可以把內容流作爲外部紋理輸出在上面的View, 它本身需要是一個硬件加速層。

SurfaceTexture

SurfaceTexture是Surface和OpenGL ES(GLES)紋理的組合。SurfaceTexture用於提供輸出到GLES 紋理的Surface。

SurfaceTexture是從Android 3.0開始加入,與SurfaceView不同的是,它對圖像流的處理並不直接顯示,而是轉爲GL外部紋理,因此用於圖像流數據的二次處理。

比如Camera的預覽數據,變成紋理後可以交給GLSurfaceView直接顯示,也可以通過SurfaceTexture交給TextureView作爲View heirachy中的一個硬件加速層來顯示。首先,SurfaceTexture從圖像流(來自Camera預覽、視頻解碼、GL繪製場景等)中獲得幀數據,當調用updateTexImage()時,根據內容流中最近的圖像更新SurfaceTexture對應的GL紋理對象。

SurfaceTexture 包含一個應用是其使用方的BufferQueue。當生產方將新的緩衝區排入隊列時,onFrameAvailable() 回調會通知應用。然後,應用調用updateTexImage(),這會釋放先前佔有的緩衝區,從隊列中獲取新緩衝區並執行EGL調用,從而使GLES可將此緩衝區作爲外部紋理使用

三、SurfaceView vs TextureView

SurfaceView是一個有自己Surface的View。它的渲染可以放在單獨線程而不是主線程中。其缺點是不能做變形和動畫。

SurfaceTexture可以用作非直接輸出的內容流,這樣就提供二次處理的機會。與SurfaceView直接輸出相比,這樣會有若干幀的延遲。同時,由於它本身管理BufferQueue,因此內存消耗也會稍微大一些。

TextureView是一個可以把內容流作爲外部紋理輸出在上面的View。它本身需要是一個硬件加速層。事實上TextureView本身也包含了SurfaceTexture。它與SurfaceView+SurfaceTexture組合相比可以完成類似的功能(即把內容流上的圖像轉成紋理,然後輸出)。

TextureView是在View hierachy中做繪製,因此一般它是在主線程上做的(在Android 5.0引入渲染線程後,它是在渲染線程中做的)。而SurfaceView+SurfaceTexture在單獨的Surface上做繪製,可以是用戶提供的線程,而不是系統的主線程或是渲染線程。

與 SurfaceView 相比,TextureView 具有更出色的 Alpha 版和旋轉處理能力,但在視頻上以分層方式合成界面元素時,SurfaceView 具有性能方面的優勢。當客戶端使用 SurfaceView 呈現內容時,SurfaceView 會爲客戶端提供單獨的合成層。如果設備支持,SurfaceFlinger 會將單獨的層合成爲硬件疊加層。當客戶端使用 TextureView 呈現內容時,界面工具包會使用 GPU 將 TextureView 的內容合成到 View 層次結構中。對內容進行的更新可能會導致其他 View 元素重繪,例如,如果其他 View 位於 TextureView 上方。View 呈現完成後,SurfaceFlinger 會合成應用界面層和所有其他層,以至每個可見像素合成兩次。

注意:受 DRM 保護的視頻只能在疊加平面上呈現。支持受保護內容的視頻播放器必須使用 SurfaceView 進行實現。

項目 SurfaceView TextureView
內存
耗電
繪製 及時 1-3幀延遲
動畫和截圖 不支持 支持
  • 在android 7.0上系統surfaceview的性能比TextureView更有優勢,支持對象的內容位置和包含的應用內容同步更新,平移、縮放不會產生黑邊。 - 在7.0以下系統如果使用場景有動畫效果,可以選擇性使用TextureView
  • 由於失效(invalidation)和緩衝的特性,TextureView增加了額外1~3幀的延遲顯示畫面更新
  • TextureView總是使用GL合成,而SurfaceView可以使用硬件overlay後端,可以佔用更少的內存
  • TextureView的內部緩衝隊列導致比SurfaceView使用更多的內存
  • SurfaceView: 內部自己持有surface,surface 創建、銷燬、大小改變時系統來處理的,通過surfaceHolder 的callback回調通知。當畫布創建好時,可以將surface綁定到MediaPlayer中。SurfaceView如果爲用戶可見的時候,創建SurfaceView的SurfaceHolder用於顯示視頻流解析的幀圖片,如果發現SurfaceView變爲用戶不可見的時候,則立即銷燬SurfaceView的SurfaceHolder,以達到節約系統資源的目的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章