Android OpenGL开发实践 - GLSurfaceView对YUV格式数据的处理

一、创建OpenGL ES 环境

在清单中声明OpenGL ES

为了使您的应用程序能够使用OpenGL ES 2.0 API,您必须在清单中添加以下声明:

<uses-feature android:glEsVersion="0x00020000" android:required="true" />
构建GLSurfaceView对象
class MyGLSurfaceView(context: Context, attributeSet: AttributeSet?) : GLSurfaceView(context, attributeSet) {
   
    companion object {
   
        private const val TAG = "MyGLSurfaceView"
    }

    constructor(context: Context) : this(context, null)

    private val renderer: MyGLRenderer

    init {
   

        // Create an OpenGL ES 2.0 context
        setEGLContextClientVersion(2)

        renderer = MyGLRenderer()

        // Set the Renderer for drawing on the GLSurfaceView
        setRenderer(renderer)

        // Render the view only when there is a change in the drawing data
        renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
    }
    
    /**
     * 设置显示方向
     * @param degrees 显示旋转角度(逆时针),有效值是(0, 90, 180, and 270.)
     */
    fun setDisplayOrientation(degrees: Int) {
   
        renderer.setDisplayOrientation(degrees)
    }
    
    /**
     * 设置渲染的YUV数据的宽高
     * @param width 宽度
     * @param height 高度
     */
    fun setYuvDataSize(width: Int, height: Int) {
   
        Log.d(TAG, "setYuvDataSize $width * $height")
        renderer.setYuvDataSize(width, height)
    }

    /**
     * 填充预览YUV格式数据
     * @param yuvData yuv格式的数据
     * @param type YUV数据的格式 0 -> I420  1 -> NV12  2 -> NV21
     */
    fun feedData(yuvData: ByteArray?, type: Int = 0) {
   
        if (yuvData == null) {
   
            return
        }
        renderer.feedData(yuvData, type)
        // 请求渲染新的YUV数据
        requestRender()
    }
}

主要工作:
1、指定OpenGL ES Context版本
2、设置渲染的 Renderer
3、设定渲染模式为RENDERMODE_WHEN_DIRTY,只有在调用requestRender()后才触发redraw工作
4、传入YUV数据的宽度、高度
5、传输需要渲染的YUV格式的数据(I420、NV12、NV21)

构建Renderer类
class MyGLRenderer : GLSurfaceView.Renderer {
   
    companion object {
   
        private const val TAG = "MyGLRenderer"
    }

    private lateinit var mProgram: MyGLProgram
    // GLSurfaceView宽度
    private var mScreenWidth: Int = 0
    // GLSurfaceView高度
    private var mScreenHeight: Int = 0
    // 预览YUV数据宽度
    private var mVideoWidth: Int = 0
    // 预览YUV数据高度
    private var mVideoHeight: Int = 0

    // vPMatrix is an abbreviation for "Model View Projection Matrix"
    private val vPMatrix = FloatArray(16)
    private val projectionMatrix = FloatArray(16)
    private val viewMatrix = FloatArray(16)

    // y分量数据
    private var y: ByteBuffer = ByteBuffer.allocate(0)
    // u分量数据
    private var u: ByteBuffer = ByteBuffer.allocate(0)
    // v分量数据
    private var v: ByteBuffer = ByteBuffer.allocate(0)
    // uv分量数据
    private var uv: ByteBuffer = ByteBuffer.allocate(0)

    // YUV数据格式 0 -> I420  1 -> NV12  2 -> NV21
    private var type: Int = 0
    // 标识GLSurfaceView是否准备好
    private var hasVisibility = false

    //  Called once to set up the view's OpenGL ES environment.
    override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
   
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)

        // 配置OpenGL ES 环境
        mProgram = MyGLProgram()
    }

    //  Called if the geometry of the view changes, for example when the device's screen orientation changes.
    override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
   
        GLES20.glViewport(0, 0, width, height)

        mScreenWidth = width
        mScreenHeight = height

        mScreenWidth = width
        mScreenHeight = height
        val ratio: Float = width.toFloat() / height.toFloat()

        // this projection matrix is applied to object coordinates
        // in the onDrawFrame() method
        Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)

        // Set the camera position (View matrix)
        Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 1.0f, 0.0f, 0.0f)
        
        if (mVideoWidth > 0 && mVideoHeight > 0) {
   
            createBuffers(mVideoWidth, mVideoHeight)
        }
        hasVisibility = true
        Log.d(TAG, "onSurfaceChanged width:$width * height:$height")
    }

    //  Called for each redraw of the view.
    override fun onDrawFrame(unused: GL10) {
   
        synchronized(this) {
   
            if (y.capac
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
要在 GLSurfaceView 中显示 YUV 数据,你需要将 YUV 数据转换为 RGB 数据,然后将 RGB 数据传递给 OpenGL ES,最后在 GLSurfaceView 中显示。这个过程可以通过 JNI 来完成。 以下是一个简单的示例代码: 1. Java 代码: ``` public class YuvRenderer implements GLSurfaceView.Renderer { private static final String TAG = "YuvRenderer"; private int mTextureId; private int mProgram; private int mPositionHandle; private int mTexCoordHandle; private int mYuvWidth; private int mYuvHeight; private ByteBuffer mYuvBuffer; public YuvRenderer() { mYuvWidth = 0; mYuvHeight = 0; mYuvBuffer = null; } public void setYuvData(int width, int height, byte[] yuvData) { mYuvWidth = width; mYuvHeight = height; mYuvBuffer = ByteBuffer.wrap(yuvData); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); GLES20.glDisable(GLES20.GL_DEPTH_TEST); mProgram = createProgram(); mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); int textureUniformHandle = GLES20.glGetUniformLocation(mProgram, "uTexture"); int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); mTextureId = textureIds[0]; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mYuvWidth / 2, mYuvHeight / 2, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); GLES20.glUseProgram(mProgram); GLES20.glVertexAttribPointer(mPositionHandle, 2, GLES20.GL_FLOAT, false, 0, createVertexBuffer()); GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0, createTexCoordBuffer()); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glEnableVertexAttribArray(mTexCoordHandle); GLES20.glUniform1i(textureUniformHandle, 0); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 gl) { if (mYuvBuffer == null) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); return; } GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); byte[] yuvData = mYuvBuffer.array(); int[] rgbData = new int[mYuvWidth * mYuvHeight]; YuvUtils.convertYUV420ToRGB8888(yuvData, rgbData, mYuvWidth, mYuvHeight); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mYuvWidth / 2, mYuvHeight / 2, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(rgbData)); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } private int createProgram() { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE); int program = GLES20.glCreateProgram(); GLES20.glAttachShader(program, vertexShader); GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); return program; } private int loadShader(int shaderType, String shaderCode) { int shader = GLES20.glCreateShader(shaderType); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } private FloatBuffer createVertexBuffer() { float[] vertexData = new float[] { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertexData.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); FloatBuffer buffer = byteBuffer.asFloatBuffer(); buffer.put(vertexData); buffer.position(0); return buffer; } private FloatBuffer createTexCoordBuffer() { float[] texCoordData = new float[] { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, }; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(texCoordData.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); FloatBuffer buffer = byteBuffer.asFloatBuffer(); buffer.put(texCoordData); buffer.position(0); return buffer; } private static final String VERTEX_SHADER_CODE = "attribute vec4 aPosition;\n" + "attribute vec2 aTexCoord;\n" + "varying vec2 vTexCoord;\n" + "void main() {\n" + " gl_Position = aPosition;\n" + " vTexCoord = aTexCoord;\n" + "}"; private static final String FRAGMENT_SHADER_CODE = "precision mediump float;\n" + "uniform sampler2D uTexture;\n" + "varying vec2 vTexCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTexture, vTexCoord);\n" + "}"; } ``` 2. JNI 代码: ``` JNIEXPORT void JNICALL Java_com_example_yuvrenderer_YuvRenderer_setYuvData(JNIEnv *env, jobject obj, jint width, jint height, jbyteArray yuvData) { jclass clazz = env->GetObjectClass(obj); jfieldID yuvWidthField = env->GetFieldID(clazz, "mYuvWidth", "I"); jint yuvWidth = env->GetIntField(obj, yuvWidthField); jfieldID yuvHeightField = env->GetFieldID(clazz, "mYuvHeight", "I"); jint yuvHeight = env->GetIntField(obj, yuvHeightField); jbyte* yuvDataPtr = env->GetByteArrayElements(yuvData, NULL); jsize yuvDataSize = env->GetArrayLength(yuvData); if (yuvWidth != width || yuvHeight != height) { env->SetIntField(obj, yuvWidthField, width); env->SetIntField(obj, yuvHeightField, height); jclass byteBufferClazz = env->FindClass("java/nio/ByteBuffer"); jmethodID allocateDirectMethod = env->GetStaticMethodID(byteBufferClazz, "allocateDirect", "(I)Ljava/nio/ByteBuffer;"); jobject yuvBuffer = env->CallStaticObjectMethod(byteBufferClazz, allocateDirectMethod, yuvDataSize); env->SetObjectField(obj, env->GetFieldID(clazz, "mYuvBuffer", "Ljava/nio/ByteBuffer;"), yuvBuffer); } jobject yuvBuffer = env->GetObjectField(obj, env->GetFieldID(clazz, "mYuvBuffer", "Ljava/nio/ByteBuffer;")); env->GetDirectBufferAddress(yuvBuffer); memcpy(yuvBufferPtr, yuvDataPtr, yuvDataSize); env->ReleaseByteArrayElements(yuvData, yuvDataPtr, JNI_ABORT); } ``` 这个示例代码中假设 YUV 数据NV21 格式的,你需要根据你的 YUV 数据格式进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值