如何在Android中使用OpenGL ES渲染android.media.Image

问题描述 投票:0回答:1

我正在使用 MediaCodec 来解码视频,由于某种原因,我无法使用 Surface 配置编解码器。所以我使用 EGL 渲染视频。这是我的渲染逻辑。

    videoDecoder.setStateListener(object : DefDecoderStateListener {

        override fun decodeOneFrame(
            codec: MediaCodec, // already set the format MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible
            bufferInfo: MediaCodec.BufferInfo?,
            outputBuffers: Array<ByteBuffer>,
            index: Int
        ) {
            // renderer draw
            val image = codec.getOutputImage(index)!!
            yuvDrawer.setImage(image)
            mRenderer.notifySwap(bufferInfo?.presentationTimeUs ?: 0)
        }
    })

我的想法是从输出图像中获取 YUV 缓冲区并通过 YuvDrawer 渲染它。 但是当我运行代码时,我得到了错误的结果。我的YuvDrawer有问题吗?

package com.cxp.learningvideo.opengl.drawer

import android.graphics.SurfaceTexture
import android.media.Image
import android.opengl.GLES11Ext
import android.opengl.GLES20
import android.opengl.Matrix
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer

class YuvDrawer: IDrawer {


    private val mVertexCoors = floatArrayOf(
        -1f, -1f,
        1f, -1f,
        -1f, 1f,
        1f, 1f
    )


    private val mTextureCoors = floatArrayOf(
        0f, 1f,
        1f, 1f,
        0f, 0f,
        1f, 0f
    )

    private var mWorldWidth: Int = -1
    private var mWorldHeight: Int = -1
    private var mVideoWidth: Int = -1
    private var mVideoHeight: Int = -1

    private var mTextureId: Int = -1

    private var mSurfaceTexture: SurfaceTexture? = null

    private var mSftCb: (() -> Unit)? = null


    private var mProgram: Int = -1


    private var mVertexMatrixHandler: Int = -1

    private var mVertexPosHandler: Int = -1

    private var mTexturePosHandler: Int = -1

    private var mTextureYHandler: Int = -1
    private var mTextureUHandler: Int = -1
    private var mTextureVHandler: Int = -1

    private var mAlphaHandler: Int = -1

    private lateinit var mVertexBuffer: FloatBuffer
    private lateinit var mTextureBuffer: FloatBuffer

    private var mMatrix: FloatArray? = null

    private var mAlpha = 1f

    private val textures = IntArray(3)
    private var bufferY: ByteBuffer? = null
    private var bufferU: ByteBuffer? = null
    private var bufferV: ByteBuffer? = null
    private var image: Image? = null

    init {

        initPos()
    }

    private fun initPos() {
        val bb = ByteBuffer.allocateDirect(mVertexCoors.size * 4)
        bb.order(ByteOrder.nativeOrder())

        mVertexBuffer = bb.asFloatBuffer()
        mVertexBuffer.put(mVertexCoors)
        mVertexBuffer.position(0)

        val cc = ByteBuffer.allocateDirect(mTextureCoors.size * 4)
        cc.order(ByteOrder.nativeOrder())
        mTextureBuffer = cc.asFloatBuffer()
        mTextureBuffer.put(mTextureCoors)
        mTextureBuffer.position(0)
    }

    private var mWidthRatio = 1f
    private var mHeightRatio = 1f

    private fun initDefMatrix() {
        if (mMatrix != null) return
        if (mVideoWidth != -1 && mVideoHeight != -1 &&
            mWorldWidth != -1 && mWorldHeight != -1) {
            mMatrix = FloatArray(16)
            var prjMatrix = FloatArray(16)
            val originRatio = mVideoWidth / mVideoHeight.toFloat()
            val worldRatio = mWorldWidth / mWorldHeight.toFloat()
            if (mWorldWidth > mWorldHeight) {
                if (originRatio > worldRatio) {
                    mHeightRatio = originRatio / worldRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                } else {
                    mWidthRatio = worldRatio / originRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                }
            } else {
                if (originRatio > worldRatio) {
                    mHeightRatio = originRatio / worldRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                } else {
                    mWidthRatio = worldRatio / originRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                }
            }

            val viewMatrix = FloatArray(16)
            Matrix.setLookAtM(
                viewMatrix, 0,
                0f, 0f, 5.0f,
                0f, 0f, 0f,
                0f, 1.0f, 0f
            )
            Matrix.multiplyMM(mMatrix, 0, prjMatrix, 0, viewMatrix, 0)
        }
    }

    fun setImage(image: Image) {

        this.image = image
        try {
            bufferY = this.image!!.planes[0].buffer
            Log.d("testt", "setImage: buffer y $bufferY")
            bufferU = this.image!!.planes[1].buffer
            bufferV = this.image!!.planes[2].buffer
        } catch (e: Exception) {
            e.printStackTrace()
            Log.d("YuvDrawer", "setImage: exception")
        }

    }

    override fun setVideoSize(videoW: Int, videoH: Int) {
        mVideoWidth = videoW
        mVideoHeight = videoH
    }

    override fun setWorldSize(worldW: Int, worldH: Int) {
        mWorldWidth = worldW
        mWorldHeight = worldH
    }

    override fun setAlpha(alpha: Float) {
        mAlpha = alpha
    }

    fun setSurfaceCreateListener(listener: () -> Unit) {
        mSftCb = listener
    }

    private fun createGLPrg() {
        if (mProgram == -1) {
            val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, getVertexShader())
            val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, getFragmentShader())

            mProgram = GLES20.glCreateProgram()
            GLES20.glAttachShader(mProgram, vertexShader)
            GLES20.glAttachShader(mProgram, fragmentShader)
            GLES20.glLinkProgram(mProgram)

            mVertexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix")
            mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "a_Position")
            mTextureYHandler = GLES20.glGetUniformLocation(mProgram, "textureY")
            mTextureUHandler = GLES20.glGetUniformLocation(mProgram, "textureU")
            mTextureVHandler = GLES20.glGetUniformLocation(mProgram, "textureV")

            mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate")
            mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha")
        }
        GLES20.glUseProgram(mProgram)
    }

    private fun activateTexture() {
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        // y
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0])
        GLES20.glTexImage2D(
            GLES20.GL_TEXTURE_2D,
            0,
            GLES20.GL_LUMINANCE,
            mVideoWidth,
            mVideoHeight,
            0,
            GLES20.GL_LUMINANCE,
            GLES20.GL_UNSIGNED_BYTE,
            bufferY
        )
        // u
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[1])
        GLES20.glTexImage2D(
            GLES20.GL_TEXTURE_2D,
            0,
            GLES20.GL_LUMINANCE,
            mVideoWidth / 2,
            mVideoHeight / 2,
            0,
            GLES20.GL_LUMINANCE,
            GLES20.GL_UNSIGNED_BYTE,
            bufferU
        )
        //v
        GLES20.glActiveTexture(GLES20.GL_TEXTURE2)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[2])
        GLES20.glTexImage2D(
            GLES20.GL_TEXTURE_2D,
            0,
            GLES20.GL_LUMINANCE,
            mVideoWidth / 2,
            mVideoHeight / 2,
            0,
            GLES20.GL_LUMINANCE,
            GLES20.GL_UNSIGNED_BYTE,
            bufferV
        )


        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR.toFloat())
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
//        bufferY?.clear()
//        bufferU?.clear()
//        bufferV?.clear()
    }

    override fun draw() {
        initDefMatrix()
        activateTexture()
        doDraw()

        bufferY = null
        bufferU = null
        bufferV = null
        image?.close()
    }


    private fun doDraw() {
        GLES20.glEnableVertexAttribArray(mVertexPosHandler)
        GLES20.glEnableVertexAttribArray(mTexturePosHandler)
        GLES20.glUniformMatrix4fv(mVertexMatrixHandler, 1, false, mMatrix, 0)
        GLES20.glVertexAttribPointer(mVertexPosHandler, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer)
        GLES20.glVertexAttribPointer(mTexturePosHandler, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer)
        GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha)
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
    }

    override fun setTextureID(id: Int) {
        createGLPrg()
        GLES20.glUniform1i(mTextureYHandler, 0)
        GLES20.glUniform1i(mTextureUHandler, 1)
        GLES20.glUniform1i(mTextureVHandler, 2)

        GLES20.glGenTextures(3, textures, 0)
        for(i in 0..2) {
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[i])

            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT)
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT)

            GLES20.glTexParameteri(
                GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_NEAREST
            )
            GLES20.glTexParameteri(
                GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_LINEAR
            )

            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
        }
        mSftCb?.invoke()
    }

    override fun release() {
        GLES20.glDisableVertexAttribArray(mVertexPosHandler)
        GLES20.glDisableVertexAttribArray(mTexturePosHandler)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
        GLES20.glDeleteTextures(1, intArrayOf(mTextureId), 0)
        GLES20.glDeleteProgram(mProgram)
    }

    private fun loadShader(type: Int, shaderCode: String): Int {
        val shader = GLES20.glCreateShader(type)
        GLES20.glShaderSource(shader, shaderCode)
        GLES20.glCompileShader(shader)

        return shader
    }

    fun translate(dx: Float, dy: Float) {
        Matrix.translateM(mMatrix, 0, dx*mWidthRatio*2, -dy*mHeightRatio*2, 0f)
    }

    fun scale(sx: Float, sy: Float) {
        Matrix.scaleM(mMatrix, 0, sx, sy, 1f)
        mWidthRatio /= sx
        mHeightRatio /= sy
    }

    private fun getVertexShader(): String {
        return """#version 300 es
            layout(location = 0) in vec4 a_Position;
            layout(location = 1) in vec2 aTexture;
            out vec2 vTexture;
            void main()
            {

                gl_Position = a_Position;
                vTexture = aTexture;
                
            }
        """
    }

    private fun getFragmentShader(): String {
        return """#version 300 es
            precision mediump float;
            out vec4 FragColor;
            in vec2 vTexture;
            uniform sampler2D textureY;
            uniform sampler2D textureU;
            uniform sampler2D textureV;
            void main() {

                float y,u,v;

                vec3 rgb;

                y = texture(textureY, vTexture).r;
                u = texture(textureU, vTexture).g - 0.5;
                v = texture(textureV, vTexture).b - 0.5;

                rgb.r = y + 1.540 * v;
                rgb.g = y - 0.183*u - 0.459*v;
                rgb.b = y + 1.818*u;
                FragColor = vec4(rgb, 1.0);
            }
        """
    }
}

这个渲染逻辑有意义吗还是我应该考虑另一种方式。

android opengl-es opengl-es-2.0
1个回答
0
投票

mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate") -> mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aTexture")

© www.soinside.com 2019 - 2024. All rights reserved.