SurfaceView與TextureView

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

  1. Surface 可以簡單理解為內(nèi)存中的一段繪圖緩沖區(qū)

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

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

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

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


下面內(nèi)容來自CharonChui

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

一、SurfaceView

在說SurfaceView之前,需要先說一下幾個相關(guān)的部分。

1.1 Surface簡介

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

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

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

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

1.2 SurfaceView 簡介

  • 簡單的說SurfaceView就是一個有SurfaceView里面內(nèi)嵌了一個專門用于繪制的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創(chuàng)建的時候都會創(chuàng)建一個MyWindownew MyWindow(this)中的this正是SurfaceView自身,因此將SurfaceViewwindow綁定在一起,而前面提到過每個window對應一個Surface
所以SurfaceView也就內(nèi)嵌了一個自己的Surface,可以認為SurfaceView是來控制Surface的位置和尺寸。傳統(tǒng)View及其派生類的更新只能在UI線程,然而UI線程還同時處理其他交互邏輯,
這就無法保證view更新的速度和幀率了,而SurfaceView可以用獨立的線程來進行繪制,因此可以提供更高的幀率,例如游戲,攝像頭取景等場景就比較適合用SurfaceView來實現(xiàn)。

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

  • Surfaceview提供了一個可見區(qū)域,只有在這個可見區(qū)域內(nèi)的Surface部分內(nèi)容才可見,可見區(qū)域外的部分不可見,所以可以認為SurfaceView就是展示Surface中數(shù)據(jù)的地方,Surface就是管理數(shù)據(jù)的地方,
    SurfaceView就是展示數(shù)據(jù)的地方,只有通過SurfaceView才能展現(xiàn)Surface中的數(shù)據(jù)。

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

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

SurfaceView的優(yōu)缺點:

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


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

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

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

1.3 SurfaceHolder`簡介

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

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

--》音視頻開發(fā)之旅 標注:這個MVC的理解很精彩

二、 TextureView

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

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

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

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

SurfaceTexture

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

SurfaceTexture是從Android 3.0開始加入,與SurfaceView不同的是,它對圖像流的處理并不直接顯示,而是轉(zhuǎn)為GL外部紋理,因此用于圖像流數(shù)據(jù)的二次處理。

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

SurfaceTexture 包含一個應用是其使用方的BufferQueue。當生產(chǎn)方將新的緩沖區(qū)排入隊列時,onFrameAvailable() 回調(diào)會通知應用。然后,應用調(diào)用updateTexImage(),這會釋放先前占有的緩沖區(qū),從隊列中獲取新緩沖區(qū)并執(zhí)行EGL調(diào)用,從而使GLES可將此緩沖區(qū)作為外部紋理使用

三、SurfaceView vs TextureView

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

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

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

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

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

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

項目 SurfaceView TextureView
內(nèi)存
耗電
繪制 及時 1-3幀延遲
動畫和截圖 不支持 支持
  • 在android 7.0上系統(tǒng)surfaceview的性能比TextureView更有優(yōu)勢,支持對象的內(nèi)容位置和包含的應用內(nèi)容同步更新,平移、縮放不會產(chǎn)生黑邊。 - 在7.0以下系統(tǒng)如果使用場景有動畫效果,可以選擇性使用TextureView
  • 由于失效(invalidation)和緩沖的特性,TextureView增加了額外1~3幀的延遲顯示畫面更新
  • TextureView總是使用GL合成,而SurfaceView可以使用硬件overlay后端,可以占用更少的內(nèi)存
  • TextureView的內(nèi)部緩沖隊列導致比SurfaceView使用更多的內(nèi)存
  • SurfaceView: 內(nèi)部自己持有surface,surface 創(chuàng)建、銷毀、大小改變時系統(tǒng)來處理的,通過surfaceHolder 的callback回調(diào)通知。當畫布創(chuàng)建好時,可以將surface綁定到MediaPlayer中。SurfaceView如果為用戶可見的時候,創(chuàng)建SurfaceView的SurfaceHolder用于顯示視頻流解析的幀圖片,如果發(fā)現(xiàn)SurfaceView變?yōu)橛脩舨豢梢姷臅r候,則立即銷毀SurfaceView的SurfaceHolder,以達到節(jié)約系統(tǒng)資源的目的
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,250評論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,923評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,041評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,475評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,253評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,801評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,882評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,023評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,530評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,494評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,639評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,177評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,890評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,289評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,552評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,242評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,626評論 2 370

推薦閱讀更多精彩內(nèi)容