使用 GLSurfaceView 和 OpenGl 通过JNI 显示 YUV 数据
时间: 2024-02-11 14:05:08 浏览: 157
要在 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 数据格式进行相应的修改。
阅读全文