显示成果

背景

在利用的开发过程中,咱们防止不了要应用Opengl共享context的技术。比方咱们须要在预览的同时还要将预览的画面传递给MediaCodec进行编码。有的敌人会说Camera2能够设置多个surface,这样就能够把预览的surface和MediaCodec的surface同时传递给Camera2。这种只是实现比较简单的录制性能。当初少数有录制性能的利用都反对滤镜性能,能够把你拍得更丑陋。因为退出了滤镜的性能,所以咱们要通过Opengl技术来实现。

当咱们退出opengl的滤镜性能后就有一个须要解决的问题,如何让预览和MediaCo dec同时利用滤镜。这时共享 context技术就派上用场了,通过共享context使创立在不同线程的texture id能够互相共享。通常共享的texture id绑定的是一个frame buffer,滤镜成果都绘制在这个frame buffer上,而后将frame buffer别离绘制在预览和MediaCodec。这里不对共享context的实现进行具体的介绍,这里重点关注的是有没有其余的计划来实现这个性能。

对于Camera2的思考(SurfaceTextureRenderer)

大家都晓得Camera2是反对设置多个surface的,那么他是如何把camera数据绘制到这些surface上的呢?实现形式就在SurfaceTextureRenderer中,通过查看这个文件咱们理解到他结构了一个opengl环境用于绘制camera数据。在构建opengl环境的过程中创立了EGLDisplay、EGLContext、EGLSurface。这里对于EGLSurface的创立有些非凡,咱们能够看到代码中创立了多个EGLSurface。每个EGLSurface都与一个surface进行绑定。

    private void configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces) {        if (surfaces == null || surfaces.size() == 0) {            throw new IllegalStateException("No Surfaces were provided to draw to");        }        int[] surfaceAttribs = {                EGL14.EGL_NONE        };        for (EGLSurfaceHolder holder : surfaces) {            holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs,                    holder.surface, surfaceAttribs, /*offset*/ 0);            checkEglError("eglCreateWindowSurface");        }    }

上面的代码是绘制camera数据到各个EGLSurface上,具体绘制到哪个EGLSurface上是通过 makeCurrent办法管制的。看到这里大家大略就明确了,通过EGL14.eglMakeCurrent办法就能够实现绘制内容到不同的surface上。当然在切换surface的时候要注意绘制的viewport大小也会发送变动,所以mvp matrix也要进行相应的更新。

public void drawIntoSurfaces(CaptureCollector targetCollector) {       ...               for (EGLSurfaceHolder holder : mSurfaces) {            if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {                try{                    LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,                            holder.height);                    makeCurrent(holder.eglSurface);                    LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);                    drawFrame(mSurfaceTexture, holder.width, holder.height,                            (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?                                    FLIP_TYPE_HORIZONTAL : FLIP_TYPE_NONE);                    swapBuffers(holder.eglSurface);                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {                    Log.w(TAG, "Surface abandoned, dropping frame. ", e);                    request.setOutputAbandoned();                }            }        }                ...    } }         private void makeCurrent(EGLSurface surface)            throws LegacyExceptionUtils.BufferQueueAbandonedException {        EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);        checkEglDrawError("makeCurrent");    }

通过增加两个SurfaceView验证计划

这里咱们创立两个surfaceview用于验证结构多个eglsurface的计划。

 surfaceView1.holder.addCallback(object:SurfaceHolder.Callback{            override fun surfaceCreated(holder: SurfaceHolder?) {            }            override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {                if (eglSurfaceHolder1 == null) {                    eglSurfaceHolder1 = EGLCore.EGLSurfaceHolder(holder!!.surface, width.toFloat(), height.toFloat())                    renderScope.addSurfaceHolder(eglSurfaceHolder1)                }            }            override fun surfaceDestroyed(holder: SurfaceHolder?) {                renderScope.removeSurfaceHolder(eglSurfaceHolder1)                eglSurfaceHolder1 = null            }        })        surfaceView2.holder.addCallback(object:SurfaceHolder.Callback{            override fun surfaceCreated(holder: SurfaceHolder?) {            }            override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {                if (eglSurfaceHolder2 == null) {                    eglSurfaceHolder2 = EGLCore.EGLSurfaceHolder(holder!!.surface, width.toFloat(), height.toFloat())                    renderScope.addSurfaceHolder(eglSurfaceHolder2)                }            }            override fun surfaceDestroyed(holder: SurfaceHolder?) {                renderScope.removeSurfaceHolder(eglSurfaceHolder2)                eglSurfaceHolder2 = null            }        })

这里把两个surface都增加到RenderScope中,RenderScope创立opengl线程环境,opengl描绘都是产生在这个线程的。

class RenderScope(private val render: Render) : BackgroundScope by HandlerThreadScope() {    private var eglCore: EGLCore? = null    init {        runInBackground {            eglCore = EGLCore()            render.onCreate()        }    }    fun addSurfaceHolder(eglSurfaceHolder: EGLCore.EGLSurfaceHolder?) = runInBackground {        eglCore?.addSurfaceHolder(eglSurfaceHolder)    }    fun removeSurfaceHolder(eglSurfaceHolder: EGLCore.EGLSurfaceHolder?) = runInBackground {        eglCore?.removeSurfaceHolder(eglSurfaceHolder)    }    fun removeSurfaceHolder(surface: Surface?) = runInBackground {        eglCore?.removeSurfaceHolder(surface)    }    fun release() = runInBackground {        render.onDestroy()        eglCore?.releaseEGLContext()        eglCore = null        quit()    }    fun requestRender() = runInBackground {        eglCore?.render { render.onDrawFrame(it) }    }    interface Render {        fun onCreate()        fun onDestroy()        fun onDrawFrame(eglSurfaceHolder: EGLCore.EGLSurfaceHolder)    }}

EGLCore这个类是用来结构opengl环境的,结构的过程这里不细说了。咱们重点关注下addSurfaceHolder这个办法。它是用于增加surface的,通过这个办法增加surface后,渲染过程中就能够把内容绘制到这个surface上。render办法用于渲染应用,看他的实现次要就是通过makecurrent来切换eglsurface,而后调用block 进行描绘。block调用的次数与增加的surface数量雷同。

class EGLCore {    private var mEGLDisplay = EGL14.EGL_NO_DISPLAY    private var mEGLContext = EGL14.EGL_NO_CONTEXT    private var mConfigs: EGLConfig? = null    private val mEGLSurfaces = SparseArray<EGLSurfaceHolder>()    init {        log("initEGLContext")        releaseEGLContext()        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)        check(!(mEGLDisplay === EGL14.EGL_NO_DISPLAY)) { "No EGL14 display" }        val version = IntArray(2)        check(EGL14.eglInitialize(mEGLDisplay, version,  /*offset*/0, version,  /*offset*/1)) { "Cannot initialize EGL14" }        val attributeList = intArrayOf(            EGL14.EGL_RED_SIZE, EGL_COLOR_BIT_LENGTH,            EGL14.EGL_GREEN_SIZE, EGL_COLOR_BIT_LENGTH,            EGL14.EGL_BLUE_SIZE, EGL_COLOR_BIT_LENGTH,            EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,            EGL_RECORDABLE_ANDROID, 1,            EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT or EGL14.EGL_WINDOW_BIT,            EGL14.EGL_NONE        )        val configs = arrayOfNulls<EGLConfig>(1)        val numConfigs = IntArray(1)        EGL14.eglChooseConfig(            mEGLDisplay,            attributeList,  /*offset*/0,            configs,  /*offset*/0, configs.size,            numConfigs,  /*offset*/0        )        checkEglError("eglCreateContext RGB888+recordable ES2")        mConfigs = configs[0]        val contextAttributeList = intArrayOf(            EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,            EGL14.EGL_NONE        )        mEGLContext = EGL14.eglCreateContext(            mEGLDisplay,            configs[0],            EGL14.EGL_NO_CONTEXT,            contextAttributeList,  /*offset*/0        )        checkEglError("eglCreateContext")        check(!(mEGLContext === EGL14.EGL_NO_CONTEXT)) { "No EGLContext could be made" }        EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, mEGLContext)    }    fun addSurfaceHolder(eglSurfaceHolder: EGLSurfaceHolder?) {        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {            log("addSurfaceHolder mEGLDisplay == EGL14.EGL_NO_DISPLAY")            return        }        eglSurfaceHolder ?: return        check(eglSurfaceHolder.surface.isValid) { "addSurface surface is not valid!!" }        val surfaceAttribute = intArrayOf(            EGL14.EGL_NONE        )        eglSurfaceHolder.eglSurface = EGL14.eglCreateWindowSurface(            mEGLDisplay,            mConfigs,            eglSurfaceHolder.surface,            surfaceAttribute,  /*offset*/0        )        checkEglError("eglCreateWindowSurface")        mEGLSurfaces.put(eglSurfaceHolder.surface.hashCode(), eglSurfaceHolder)    }    fun removeSurfaceHolder(eglSurfaceHolder: EGLSurfaceHolder?) {        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {            log("removeSurfaceHolder mEGLDisplay == EGL14.EGL_NO_DISPLAY")            return        }        eglSurfaceHolder ?: return        mEGLSurfaces[eglSurfaceHolder.surface.hashCode()]?.let {            EGL14.eglDestroySurface(mEGLDisplay, it.eglSurface)            it.eglSurface = null            mEGLSurfaces.remove(it.surface.hashCode())        }    }        fun removeSurfaceHolder(surface: Surface?) {        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {            log("removeSurfaceHolder mEGLDisplay == EGL14.EGL_NO_DISPLAY")            return        }        surface ?: return        mEGLSurfaces[surface.hashCode()]?.let {            EGL14.eglDestroySurface(mEGLDisplay, it.eglSurface)            it.eglSurface = null            mEGLSurfaces.remove(surface.hashCode())        }    }    fun render(block: (EGLSurfaceHolder) -> Unit) {        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {            log("render mEGLDisplay == EGL14.EGL_NO_DISPLAY")            return        }        mEGLSurfaces.forEach { _, holder ->            if (!holder.available) {                return@forEach            }            EGL14.eglMakeCurrent(mEGLDisplay, holder.eglSurface, holder.eglSurface, mEGLContext)            checkEglError("makeCurrent")            block.invoke(holder)            val result = EGL14.eglSwapBuffers(mEGLDisplay, holder.eglSurface)            when (val error = EGL14.eglGetError()) {                EGL14.EGL_SUCCESS -> result                EGL14.EGL_BAD_NATIVE_WINDOW, EGL14.EGL_BAD_SURFACE -> throw IllegalStateException(                    "swapBuffers: EGL error: 0x" + Integer.toHexString(error)                )                else -> throw IllegalStateException(                    "swapBuffers: EGL error: 0x" + Integer.toHexString(error)                )            }        }    }    fun releaseEGLContext() {        if (mEGLDisplay !== EGL14.EGL_NO_DISPLAY) {            log("releaseEGLContext")            EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)            mEGLSurfaces.forEach { _, holder ->                if (holder.eglSurface != null) {                    EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface)                    holder.eglSurface = null                }            }            mEGLSurfaces.clear()            EGL14.eglDestroyContext(mEGLDisplay, mEGLContext)            EGL14.eglReleaseThread()            EGL14.eglTerminate(mEGLDisplay)        }        mConfigs = null        mEGLDisplay = EGL14.EGL_NO_DISPLAY        mEGLContext = EGL14.EGL_NO_CONTEXT    }    private fun checkEglError(msg: String) {        val error = EGL14.eglGetError()        check(error == EGL14.EGL_SUCCESS) { msg + ": EGL error: 0x" + Integer.toHexString(error) }    }    private fun log(msg: String) {        Log.d("EGLCore", msg)    }    class EGLSurfaceHolder(        val surface: Surface,        val width: Float,        val height: Float    ) {        var eglSurface: EGLSurface? = null        var available = true        val mvpMatrix = MVPMatrix().updateViewport(width, height)        val viewPortRectF = RectF(0f, 0f, width, height)    }    companion object {        private const val GLES_VERSION = 2        private const val EGL_COLOR_BIT_LENGTH = 8        const val EGL_RECORDABLE_ANDROID = 0x3142 // from EGL/eglext.h    }}

Git

https://github.com/mjlong1231...