TextureView+SurfaceTexture+OpenGL ES來播放視頻(二)

前言

在使用OpenGL ES 繪制前,我先概括下接下來要做的工作:我先借用一個博主kiffa舉的的一個栗子,我覺得說的恰到好處,遂稍稍概括下來給大家分享分享:
我們想像一下畫家繪畫的過程,在作畫之前他是不是應該先準備準備呢,首先他得準備作畫的畫布(對應TexureView),一些筆,顏料以及輔助工具比如調色板等等(OpenGL ES API),然后他就可以在畫出他的作品了(SurfaceTexture)。
ok,假如他現在希望將它繪制好的畫上傳到電腦上通過顯示器來展示,那他先是不是得要獲取到要顯示設備,好為他的畫創造一個展示的環境,同時我們知道你在顯示器上所看到的一副圖像都是由無數個像素點(其實是一個很小很小的矩形)組成,而每幅畫都又是由各種顏色搭配組合而成,在顯示器上的繪制其實就是將畫的顏色往這些小矩形里面去填充,然而這些顏色都是畫家在調色板上一點點調出來的,與之相對應的是,這些顏色在顯示器上是可以用RGB(三原色)調制出來的,好了這些準備都到位以后,畫家繪制的畫就可以在屏幕上顯示出來了。若畫家以至少24幀/s的頻率將畫的畫不斷的輸送到屏幕上時就形成了一個連續的影像了。
為了將畫能夠呈現給大家,同時考慮到每個人顯示設備都可能不一樣的情況下,維護OpenGL 的khronos組織提供了一個專門抽象層接口(EGL API OpenGL ES 和底層 Native 平臺視窗系統之間的接口) 去適配不同的設備,以保證opengl es的平臺無關性。
下面我就要看看這個EGL是怎么配置的了,OK,又到了“talk is cheap,show me the code“的時刻了

代碼

/**
 * 繪制前,renderer的配置,初始化EGL,開始一個繪制線程.
 * 這個類需要子類去實現相應的繪制工作.
 */
public abstract class TextureSurfaceRenderer implements Runnable{
    public static String LOG_TAG = TextureSurfaceRenderer.class.getSimpleName();

    protected final SurfaceTexture surfaceTexture;
    protected int width;
    protected int height;

    private EGL10 egl;
    private EGLContext eglContext;
    private EGLDisplay eglDisplay;
    private EGLSurface eglSurface;
    /***
     * 是否正在繪制(draw)
     */
    private boolean running = false;

    public TextureSurfaceRenderer(SurfaceTexture surfaceTexture, int width, int height) {
        this.surfaceTexture = surfaceTexture;
        this.width = width;
        this.height = height;
        this.running = true;
        /**開個線程來進行繪制工作*/
        Thread thread = new Thread(this);
        thread.start();
    }
    
    /**
    * 初始化EGL環境
    * 1.獲取display    2.初始畫egl
    * 3.選擇config     4.構造surface
    * 5.創建context    5.進行繪制
    */
    private void initEGL() {
        egl = (EGL10)EGLContext.getEGL();
        //獲取顯示設備
        eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        //version中存放EGL 版本號,int[0]為主版本號,int[1]為子版本號
        int version[] = new int[2];
        egl.eglInitialize(eglDisplay, version);

        EGLConfig eglConfig = chooseEglConfig();
        //創建EGL 的window surface 并且返回它的handles(eslSurface)
        eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null);

        eglContext = createContext(egl, eglDisplay, eglConfig);

        /**綁定context到當前渲染線程并且去繪制(通過opengl去繪制)和讀取surface(通過eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)來顯示)*/
        try {
            if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) {
                throw new RuntimeException("GL error:" + GLUtils.getEGLErrorString(egl.eglGetError()));
            }
            if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
                throw new RuntimeException("GL Make current Error"+ GLUtils.getEGLErrorString(egl.eglGetError()));
            }
        }catch (Exception e) {
            e.printStackTrace();
        }

    @Override
    public void run() {
        initEGL();
        initGLComponents();
        Log.d(LOG_TAG, "OpenGL init OK. start draw...");
        while (running) {
            if (draw()) {
                egl.eglSwapBuffers(eglDisplay, eglSurface);
            }
        }
        deinitGLComponents();
        deinitEGL();
    }

    private void deinitEGL() {
        egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        egl.eglDestroySurface(eglDisplay, eglSurface);
        egl.eglDestroyContext(eglDisplay, eglContext);
        egl.eglTerminate(eglDisplay);
        Log.d(LOG_TAG, "OpenGL deinit OK.");
    }

    /**
     * 主要的繪制函數, 需在子類中去實現繪制
     */
    protected abstract boolean draw();

    /***
     * 抽象函數,初始化opengl繪制所必需的一些組件比如vertextBuffer,sharders,textures等,
     * 通常在Opengl context 初始化以后被調用,需要子類去實現
     */
    protected abstract void initGLComponents();
    /***
    * 于init操作相反,做一些釋放資源,停止繪制工作
    */
    protected abstract void deinitGLComponents();
    /***
    * 獲取生產的 SurfaceTexture,在子類中實現
    */
    public abstract SurfaceTexture getSurfaceTexture();

    /**
     * 為當前渲染的API創建一個渲染上下文
     * @return a handle to the context
     */
    private EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
        int[] attrs = {
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL10.EGL_NONE
        };
        return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrs);
    }

    /***
     *  選擇一個你所期望的配置.
     * @return 一個與你所指定最相近的一個EGL 幀緩存配置.
     */
    private EGLConfig chooseEglConfig() {
        int[] configsCount = new int[1];
        EGLConfig[] configs = new EGLConfig[1];
        int[] attributes = getAttributes();
        int confSize = 1;

        if (!egl.eglChooseConfig(eglDisplay, attributes, configs, confSize, configsCount)) {    //獲取滿足attributes的config個數
            throw new IllegalArgumentException("Failed to choose config:"+ GLUtils.getEGLErrorString(egl.eglGetError()));
        }
        else if (configsCount[0] > 0) {
            return configs[0];
        }

        return null;
    }

    /**
     * 構造你期望的繪制時所需要的特性配置,如ARGB,DEPTH...
     */
    private int[] getAttributes()
    {
        return new int[] {
                EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,  //指定渲染api類別
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_DEPTH_SIZE, 0,
                EGL10.EGL_STENCIL_SIZE, 0,
                EGL10.EGL_NONE      //總是以EGL10.EGL_NONE結尾
        };
    }

    /**
     * 當 當前activity停止時調用,用來停止渲染線程,并釋放資源.
     */
    public void onPause()
    {
        running = false;
    }
    
    @Override
    protected  void finalize() throws Throwable {
        super.finalize();
        running = false;
    }
}

OK,代碼就先甩到這兒了,注釋也算很詳細了,很多EGL相關的API以及EGL的介紹強烈推薦你去官網去查查。其它的有什么問題敬請指正,感激不僅啊! 還有上篇鏈接 TextureView+SurfaceTexture+OpenGL ES來播放視頻(一)
以及下篇TextureView+SurfaceTexture+OpenGL ES來播放視頻(三),若是等不了博客的更新速度,可以先看看整個項目clone下來先睹為快,項目地址在here

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容