Android 自定義相機開發(fā)(三) —— 了解下EGL

胡說八道

如果要使用OpenGl來自定義相機考传,這個還是要了解下的肴焊∏傲可能大多數(shù)開發(fā)者使用過OpengGL但是不知道EGL是什么?EGL的作用是什么娶眷?這其實一點都不奇怪似嗤,因為Android中的GlSurfaceView已經(jīng)將EGL環(huán)境都給配置好了,你一直在使用届宠,只是不知道他的存在罷了烁落。很多人可能在使用OpenGl ES渲染數(shù)據(jù)的時候都帶著一個疑問,渲染的數(shù)據(jù)到底到哪里去了豌注?沒看到畫布伤塌,Android中的自定義view不都是有畫布的嗎?這篇文章就是為了解決這一系列問題而服務的轧铁。當然每聪,如果你對自定義相機,音視頻編碼開發(fā)感興趣齿风,可以了解下我的Chat點這里

正兒八經(jīng)

通過本文你可以了解到如下內(nèi)容:1. 什么是EGL?. 2. EGL和OpenGl ES的關系 3. 使用EGL繪圖的基本步驟药薯。4. GlSurfaceView源碼中分析EGL 5. 總結

1. 了解下什么是EGL?

EGL是什么?EGL是渲染API(如OpenGL, OpenGL ES, OpenVG)和本地窗口系統(tǒng)之間的接口救斑。它處理圖形上下文管理童本,表面/緩沖區(qū)創(chuàng)建,綁定和渲染同步脸候,并使用其他Khronos API實現(xiàn)高性能穷娱,加速,混合模式2D和3D渲染OpenGL / OpenGL ES渲染客戶端API OpenVG渲染客戶端API原生平臺窗口系統(tǒng)纪他。這個稍微了解下就OK鄙煤,你只要知道他是一個用來給OpenGl ES提供繪制界面的接口就可以了晾匠。

EGL的作用:

  1. 與設備的原生窗口系統(tǒng)通信茶袒。
  2. 查詢繪圖表面的可用類型和配置。
  3. 創(chuàng)建繪圖表面凉馆。
  4. 在OpenGL ES 和其他圖形渲染API之間同步渲染薪寓。
  5. 管理紋理貼圖等渲染資源亡资。

這里關于EGL的介紹就講這么多,如果你有興趣的話你可以繼續(xù)到這里去see yi seeUnderstanding Android EGL

2. EGL和OpenGl ES的關系

從上面的講解我們基本上可以知道向叉,EGL是為OpenGl提供繪制表面的锥腻。對的,這就是OpenGl ES數(shù)據(jù)渲染畫布所在了母谎。想必到這里大家也清楚了渲染數(shù)據(jù)去處的問題了瘦黑。EGL還有什么用呢?EGL可以理解為OpenGl ES ES和設備之間的橋梁奇唤。對幸斥,完全可以這么理解。

3. EGL繪圖的基本步驟

先上圖咬扇,再說話甲葬。
簡單講解下各部分的作用:

  1. Display(EGLDisplay) 是對實際顯示設備的抽象。
  2. Surface(EGLSurface)是對用來存儲圖像的內(nèi)存區(qū)FrameBuffer 的抽象,包括Color Buffer,Stencil Buffer,Depth Buffer懈贺。
  3. Context (EGLContext) 存儲 OpenGL ES繪圖的一些狀態(tài)信息经窖。

EGL的基本使用步驟:

  1. 首先我們需要知道繪制內(nèi)容的目標在哪里,EGLDisplayer是一個封裝系統(tǒng)屏幕的數(shù)據(jù)類型梭灿,通常通過eglGetDisplay方法來返回EGLDisplay作為OpenGl ES的渲染目標画侣,eglGetDisplay()
 if ( (mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)) == EGL14.EGL_NO_DISPLAY) {
                throw new RuntimeException("unable to get EGL14 display");
            }
  1. 初始化顯示設備,第一參數(shù)代表Major版本堡妒,第二個代表Minor版本棉钧。如果不關心版本號,傳0或者null就可以了涕蚤。初始化與 EGLDisplay 之間的連接:eglInitialize()
            if (!EGL14.eglInitialize(mEGLDisplay, 0, 0)) {
                throw new RuntimeException("unable to initialize EGL14");
            }
  1. 下面我們進行配置選項宪卿,使用eglChooseConfig()方法,Android平臺的配置代碼如下:
int[] attribList = {
                    EGL14.EGL_RED_SIZE, 8,
                    EGL14.EGL_GREEN_SIZE, 8,
                    EGL14.EGL_BLUE_SIZE, 8,
                    EGL14.EGL_ALPHA_SIZE, 8,
                    EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                    EGL_RECORDABLE_ANDROID, 1,
                    EGL14.EGL_NONE
            };
            EGLConfig[] configs = new EGLConfig[1];
            int[] numConfigs = new int[1];
            EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
                    numConfigs, 0);
  1. 接下來我們需要創(chuàng)建OpenGl的上下文環(huán)境 EGLContext 實例万栅,這里值得留意的是佑钾,OpenGl的任何一條指令都是必須在自己的OpenGl上下文環(huán)境中運行,我們可以通過eglCreateContext()方法來構建上下文環(huán)境:
    int[] attrib_list = {
                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                    EGL14.EGL_NONE
            };
            mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
                    attrib_list, 0);

eglCreateContext中的第三個參數(shù)可以傳入一個EGLContext類型的變量烦粒,改變量的意義是可以與正在創(chuàng)建的上下文環(huán)境共享OpenGl資源休溶,包括紋理ID,FrameBuffer以及其他Buffer資源。如果沒有的話可以填寫Null.

  1. 通過上面四步,獲取OpenGl 上下文之后扰她,說明EGL和OpenGl ES端的環(huán)境已經(jīng)搭建完畢兽掰,也就是說OpengGl的輸出我們可以獲取到了。下面的步驟我們講如何將EGl和設備屏幕連接起來徒役。如果連接呢孽尽?當然,這時候我們就要使用EGLSurface了,我們通過EGL庫提供eglCreateWindowSurface可以創(chuàng)建一個實際可以顯示的surface.當然忧勿,如果需要離線的surface杉女,我們可以通過eglCreatePbufferSurface創(chuàng)建瞻讽。eglCreateWindowSurface()
     private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
      int[] surfaceAttribs = {
                    EGL14.EGL_NONE
            };
            mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
                    surfaceAttribs, 0);
  1. 通過上面的步驟,EGL的準備工作做好了熏挎,一方面我們?yōu)镺penGl ES渲染提供了目標及上下文環(huán)境速勇,可以接收到OpenGl ES渲染出來的紋理,另一方面我們連接好了設備顯示屏(這里指SurfaceView或者TextureView),接下來我們講解如何在創(chuàng)建好的EGL環(huán)境下工作的坎拐。首先我們有一點必須要明確烦磁,OpenGl ES 的渲染必須新開一個線程,并為該線程綁定顯示設備及上下文環(huán)境(Context)哼勇。因為前面有說過OpenGl指令必須要在其上下文環(huán)境中才能執(zhí)行个初。所以我們首先要通過 eglMakeCurrent()方法來綁定該線程的顯示設備及上下文。
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
  1. 當我們綁定完成之后猴蹂,我們就可以進行RenderLoop循環(huán)了院溺。這里簡單說一下,EGL的工作模式是雙緩沖模式磅轻,其內(nèi)部有兩個FrameBuffer(幀緩沖區(qū)珍逸,可以理解為一個圖像存儲區(qū)域),當EGL將一個FrameBuffer顯示到屏幕上的時候聋溜,另一個FrameBuffer就在后臺等待OpenGl ES進行渲染輸出谆膳。知道調(diào)用了eglSwapBuffers這條指令的時候,才會把前臺的FrameBuffers和后臺的FrameBuffer進行交換撮躁,這樣界面呈現(xiàn)的就是OpenGl ES剛剛渲染的結構了漱病。
mInputSurface.swapBuffers();
  1. 當然,在所有的操作都執(zhí)行完之后把曼,我們要銷毀資源杨帽。特別注意,銷毀資源必須在當前線程中進行嗤军,不然會報錯滴注盈。首先我們銷毀顯示設備(EGLSurface),然后銷毀上下文(EGLContext),停止并釋放線程,最后終止與EGLDisplay之間的鏈接叙赚,
 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
                EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
                EGL14.eglReleaseThread();
                EGL14.eglTerminate(mEGLDisplay);

4. GlSurfaceView源碼中分析EGL

上面我們有提到過Android GlSurfaceView中已經(jīng)幫忙配置好了EGL老客,下面我們來看下EGL在GLSurfaceView中的具體實現(xiàn)過程:我們平時使用GlSurfaceView怎么使用的呢?xml中布置震叮,然后setRenderer(this),然后調(diào)用下setRenderMode()就OK了胧砰。特比簡單。但是GlSurfaceView內(nèi)部原理是什么呢苇瓣?我們現(xiàn)在來一探究竟:

特別聲明:下面所有代碼皆為GlSurfaView中的源碼尉间。

  public void setRenderer(Renderer renderer) {
        checkRenderThreadState();
        if (mEGLConfigChooser == null) {
            mEGLConfigChooser = new SimpleEGLConfigChooser(true);
        }
        if (mEGLContextFactory == null) {
            mEGLContextFactory = new DefaultContextFactory();
        }
        if (mEGLWindowSurfaceFactory == null) {
            mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
        }
        mRenderer = renderer;//為當前的View設置一個渲染器
        mGLThread = new GLThread(mThisWeakRef);
        //創(chuàng)建線程并開啟
        mGLThread.start();
    }

setRenderer函數(shù)主要干了2件事,一個是給View設置一個渲染器對象二是創(chuàng)建并開啟渲染線程。線程start后執(zhí)行guardedRun乌妒,run函數(shù)一個while(true)循環(huán),渲染數(shù)據(jù)外邓。

 public void setRenderer(Renderer renderer) {
        checkRenderThreadState();
        if (mEGLConfigChooser == null) {
            mEGLConfigChooser = new SimpleEGLConfigChooser(true);
        }
        if (mEGLContextFactory == null) {
            mEGLContextFactory = new DefaultContextFactory();
        }
        if (mEGLWindowSurfaceFactory == null) {
            mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
        }
        mRenderer = renderer;//為當前的View設置一個渲染器
        mGLThread = new GLThread(mThisWeakRef);
        //創(chuàng)建線程并開啟
        mGLThread.start();
    }

到這里為止撤蚊,是不是感覺特別熟悉?我們已經(jīng)看到了onSurfaceCreated()损话,onSurfaceChanged()侦啸,onDrawFrame()的回調(diào)了。在這些方法之前有個EglHelper類的創(chuàng)建丧枪,接下來進入EGL的配置環(huán)節(jié)光涂。請系好安全帶:

public void start(){
        ……
     mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);//1. 獲取 EGL Display 對象
        ……
        //2. 初始化與 EGLDisplay 之間的連接
         if(!mEgl.eglInitialize(mEglDisplay, version)) {
                throw new RuntimeException("eglInitialize failed");
            }
         ……
           mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);//3. 獲取 EGLConfig 對象
            /*
                * Create an EGL context. We want to do this as rarely as we can, because an
                * EGL context is a somewhat heavy object.
                */
                mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);//4 創(chuàng)建 EGLContext 實例
               ……
 }

好了,后面的請按照上面EGL的繪制步驟并對照下GlSurfaceView的源碼拧烦。老衲就不在這里啰嗦了忘闻。不要問我為什么?


總結

總結下恋博,看完上面應該對開篇提到的問題有了比較明確的認識了齐佳,我相信你心中已經(jīng)有答案了≌冢總結點啥呢炼吴?還是說下感慨吧,學習編程的最好方式還是看源碼吧疫衩,沒有什么比閱讀源碼學習更有效了硅蹦。今天重新看GlSurfaceView又有了與以往不同的理解。最后闷煤,如果你在自定義相機的學習上面有什么問題童芹,可以給我留言,一塊交流學習鲤拿。微信公公眾號:aserbao辐脖。還有最后打下廣告:如果你在學習Android 自定義相機的開發(fā),可以了解下我的自定義相機的ChatAndroid 零基礎開發(fā)相機皆愉。

如果覺得文章寫得好嗜价,麻煩賞個贊唄,如果你覺得寫的不好幕庐,歡迎留言批評指正久锥,方便我及時復查修改。

這里寫圖片描述
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末异剥,一起剝皮案震驚了整個濱河市瑟由,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冤寿,老刑警劉巖歹苦,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件青伤,死亡現(xiàn)場離奇詭異,居然都是意外死亡殴瘦,警方通過查閱死者的電腦和手機狠角,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚪腋,“玉大人投队,你說我怎么就攤上這事瞧挤≌儇” “怎么了单山?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悠砚。 經(jīng)常有香客問我晓勇,道長,這世上最難降的妖魔是什么灌旧? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任宵蕉,我火速辦了婚禮,結果婚禮上节榜,老公的妹妹穿的比我還像新娘羡玛。我一直安慰自己,他們只是感情好宗苍,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布稼稿。 她就那樣靜靜地躺著,像睡著了一般讳窟。 火紅的嫁衣襯著肌膚如雪让歼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天丽啡,我揣著相機與錄音谋右,去河邊找鬼。 笑死补箍,一個胖子當著我的面吹牛改执,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坑雅,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辈挂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了裹粤?” 一聲冷哼從身側(cè)響起终蒂,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拇泣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體噪叙,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年霉翔,在試婚紗的時候發(fā)現(xiàn)自己被綠了睁蕾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡早龟,死狀恐怖惫霸,靈堂內(nèi)的尸體忽然破棺而出猫缭,到底是詐尸還是另有隱情葱弟,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布猜丹,位于F島的核電站芝加,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏射窒。R本人自食惡果不足惜藏杖,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脉顿。 院中可真熱鬧蝌麸,春花似錦、人聲如沸艾疟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔽莱。三九已至弟疆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盗冷,已是汗流浹背怠苔。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仪糖,地道東北人柑司。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像锅劝,于是被迫代替她去往敵國和親帜羊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

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