[Android] OpenGL ES渲染YUV

6 篇文章 0 订阅
4 篇文章 0 订阅

    • 目的:视频直播多以YUV格式输出,显示到屏幕上需要转换为RGB。OpenGL在GPU中对YUV转换为RGB再进行渲染,可降低CPU负载,从而提高渲染效率。
    • 建立渲染机制:首先必须准备一个GLSurfaceView(网络上例子较多,这里就不展开描述)初始化EGL,并通过jni,分别在onSurfaceChanged() 和 onDrawFrame() 分别调用glInit(), glRender();并提供调用this.requestRender()的接口

	@Override
	public void onDrawFrame(GL10 gl) {
		nativeFunctionLock.lock();
		if (!surfaceCreated) {
			nativeFunctionLock.unlock();
			return;
		}

		if (!openGLCreated) {
			if (!glInit(this, viewWidth, viewHeight)) {
				nativeFunctionLock.unlock();
				return; // Failed to create OpenGL
			}
			openGLCreated = true; // Created OpenGL successfully
		}
		glRender(); // Draw the new frame
		nativeFunctionLock.unlock();
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		surfaceCreated = true;
		viewWidth = width;
		viewHeight = height;

		nativeFunctionLock.lock();
		if (glInit(this, width, height)) {
			openGLCreated = true;
		}
		nativeFunctionLock.unlock();
	}
	
	public void redraw() {// jni层解码以后的数据回调,然后由系统调用onDrawFrame显示
		if (surfaceCreated) {
			// Request the renderer to redraw using the render thread context.
			this.requestRender();
		}
	}

gl_renderer.c

#include  "gl_renderer.h"

#define ANDROID_LOG

const char g_indices[] = { 0, 3, 2, 0, 2, 1 };

const char g_vertexShader[] = {
		"attribute vec4 aPosition;\n"
		"attribute vec2 aTextureCoord;\n"
		"varying vec2 vTextureCoord;\n"
		"void main() {\n"
		"  gl_Position = aPosition;\n"
		"  vTextureCoord = aTextureCoord;\n"
		"}\n" };

// The fragment shader.
// Do YUV to RGB565 conversion.
const char g_fragmentShader[] = {
		"precision mediump float;\n"
		"uniform sampler2D Ytex;\n"
		"uniform sampler2D Utex,Vtex;\n"
		"varying vec2 vTextureCoord;\n"
		"void main(void) {\n"
		"  float nx,ny,r,g,b,y,u,v;\n"
		"  mediump vec4 txl,ux,vx;"
		"  nx=vTextureCoord[0];\n"
		"  ny=vTextureCoord[1];\n"
		"  y=texture2D(Ytex,vec2(nx,ny)).r;\n"
		"  u=texture2D(Utex,vec2(nx,ny)).r;\n"
		"  v=texture2D(Vtex,vec2(nx,ny)).r;\n"

		//"  y = v;\n"+
		"  y=1.1643*(y-0.0625);\n"
		"  u=u-0.5;\n"
		"  v=v-0.5;\n"

		"  r=y+1.5958*v;\n"
		"  g=y-0.39173*u-0.81290*v;\n"
		"  b=y+2.017*u;\n"
		"  gl_FragColor=vec4(r,g,b,1.0);\n"
		"}\n" };

const GLfloat _vertices[20] = {
    // X, Y, Z, U, V
//	-1,-1, 0, 1, 0, // Bottom Left
//	1, -1, 0, 0, 0, //Bottom Right
//	1, 1, 0, 0, 1, //Top Right
//	-1, 1, 0, 1, 1, //Top Left

	1, 1, 0, 1, 0, // Top Right
    -1, 1, 0, 0, 0, //Top Left
    -1, -1, 0, 0, 1, //Bottom Left
    1, -1, 0, 1, 1, //Bottom Right
};

GLuint _program;
GLuint _textureIds[3];
GLuint _textureWidth;
GLuint _textureHeight;
//GLfloat _vertices[20];

jobject viewObj;
char *g_buffer;
int g_bufferSize;
int32_t g_width;
int32_t g_height;


void gl_renderer_init(JNIEnv *env) {
	viewObj = NULL;
	g_buffer = NULL;
	g_width = 0;
	g_height = 0;

	_textureWidth = -1;
	_textureHeight = -1;

	return;
}

void gl_renderer_free(JNIEnv *env) {
	if (viewObj != NULL) {
		(*env)->DeleteGlobalRef(env, viewObj);
	}
	glDeleteTextures(3, _textureIds);
	free(g_buffer);
	g_buffer = NULL;
	return;
}

void gl_renderer_render(JNIEnv *env, char *data, int len, int32_t width, int32_t height) {
	if (viewObj == NULL) {
		return;
	}
	if (g_buffer == NULL || g_width != width || g_height != height) {
		if (g_buffer != NULL) {
			free(g_buffer);
			g_buffer = NULL;
		}
		g_buffer = malloc(sizeof(unsigned char) * len);
	}
	/* 经过JNI调用,可能处于不同线程,data不能直接使用,需要把数组拷贝出来 */
	g_buffer = memcpy(g_buffer, data, len);
	g_bufferSize = len;
	g_width = width;
	g_height = height;
	jclass clazz = (*env)->GetObjectClass(env, viewObj);
	jmethodID method = (*env)->GetMethodID(env, clazz, "redraw", "()V");
	(*env)->CallVoidMethod(env, viewObj, method);
	return;
}

static GLuint loadShader(GLenum shaderType, const char* pSource) {
	GLuint shader = glCreateShader(shaderType);
	if (shader) {
		glShaderSource(shader, 1, &pSource, NULL);
		glCompileShader(shader);
		GLint compiled = 0;
		glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
		if (!compiled) {
			GLint infoLen = 0;
			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
			if (infoLen) {
				char* buf = (char *)malloc(infoLen);
				if (buf) {
					glGetShaderInfoLog(shader, infoLen, NULL, buf);
					free(buf);
				}
				glDeleteShader(shader);
				shader = 0;
			}
		}
	}
	return shader;
}

static void printGLString(const char *name, GLenum s) {
	const char *v = (const char *) glGetString(s);
	LOGI("GL %s = %s\n", name, v);
}

void checkGlError(const char* op) {
#ifdef ANDROID_LOG
	GLint error;
	for (error = glGetError(); error; error = glGetError()) {
		LOGE("after %s() glError (0x%x)\n", op, error);
	}
#else
	return;
#endif
}

static GLuint createProgram(const char* pVertexSource,
												const char* pFragmentSource) {
	GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
	if (!vertexShader) {
		return 0;
	}
	GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
	if (!pixelShader) {
		return 0;
	}

	GLuint program = glCreateProgram();
	if (program) {
		glAttachShader(program, vertexShader);
		checkGlError("glAttachShader");
		glAttachShader(program, pixelShader);
		checkGlError("glAttachShader");
		glLinkProgram(program);
		GLint linkStatus = GL_FALSE;
		glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
		if (linkStatus != GL_TRUE) {
			GLint bufLength = 0;
			glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
			if (bufLength) {
				char* buf = (char*) malloc(bufLength);
				if (buf) {
					glGetProgramInfoLog(program, bufLength, NULL, buf);
					free(buf);
				}
			}
			glDeleteProgram(program);
			program = 0;
		}
	}
	return program;
}

static void InitializeTexture(int name, int id, int width, int height) {
	glActiveTexture(name);
	glBindTexture(GL_TEXTURE_2D, id);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE,
			GL_UNSIGNED_BYTE, NULL);
}

static void setupTextures(int32_t width, int32_t height) {
	glDeleteTextures(3, _textureIds);
	glGenTextures(3, _textureIds); //Generate  the Y, U and V texture
	InitializeTexture(GL_TEXTURE0, _textureIds[0], width, height);
	InitializeTexture(GL_TEXTURE1, _textureIds[1], width / 2, height / 2);
	InitializeTexture(GL_TEXTURE2, _textureIds[2], width / 2, height / 2);

	checkGlError("SetupTextures");

	_textureWidth = width;
	_textureHeight = height;
}

/*
 * glTexSubImage2D(): 在一个已经存在的纹理A中,嵌入另外一幅纹理B贴图
 */
static void updateTextures(char *data, int32_t width, int32_t height) {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, _textureIds[0]);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE,
                    data);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, _textureIds[1]);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE,
                    GL_UNSIGNED_BYTE, (char *)data + width * height);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, _textureIds[2]);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE,
                    GL_UNSIGNED_BYTE, (char *)data + width * height * 5 / 4);

    checkGlError("UpdateTextures");
}

static int32_t onSetup(int32_t width, int32_t height) {
	printGLString("Version", GL_VERSION);
	printGLString("Vendor", GL_VENDOR);
	printGLString("Renderer", GL_RENDERER);
	printGLString("Extensions", GL_EXTENSIONS);

	int maxTextureImageUnits[2];
	int maxTextureSize[2];
	glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, maxTextureImageUnits);
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize);

	_program = createProgram(g_vertexShader, g_fragmentShader);
	if (!_program) {
		return -1;
	}

	int positionHandle = glGetAttribLocation(_program, "aPosition");
	checkGlError("glGetAttribLocation aPosition");
	if (positionHandle == -1) {
		return -1;
	}

	int textureHandle = glGetAttribLocation(_program, "aTextureCoord");
	checkGlError("glGetAttribLocation aTextureCoord");
	if (textureHandle == -1) {
		return -1;
	}

	// set the vertices array in the shader
	// _vertices contains 4 vertices with 5 coordinates.
	// 3 for (xyz) for the vertices and 2 for the texture
	glVertexAttribPointer(positionHandle, 3, GL_FLOAT, B_FALSE,
			5 * sizeof(GLfloat), _vertices);
	checkGlError("glVertexAttribPointer aPosition");

	glEnableVertexAttribArray(positionHandle);
	checkGlError("glEnableVertexAttribArray positionHandle");

	// set the texture coordinate array in the shader
	// _vertices contains 4 vertices with 5 coordinates.
	// 3 for (xyz) for the vertices and 2 for the texture
	glVertexAttribPointer(textureHandle, 2, GL_FLOAT, B_FALSE,
			5 * sizeof(GLfloat), &_vertices[3]);
	checkGlError("glVertexAttribPointer maTextureHandle");
	glEnableVertexAttribArray(textureHandle);
	checkGlError("glEnableVertexAttribArray textureHandle");

	glUseProgram(_program);
	int i = glGetUniformLocation(_program, "Ytex");
	checkGlError("glGetUniformLocation");
	glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */
	checkGlError("glUniform1i Ytex");

	i = glGetUniformLocation(_program, "Utex");
	checkGlError("glGetUniformLocation Utex");
	glUniform1i(i, 1); /* Bind Utex to texture unit 1 */
	checkGlError("glUniform1i Utex");

	i = glGetUniformLocation(_program, "Vtex");
	checkGlError("glGetUniformLocation");
	glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */
	checkGlError("glUniform1i");

	glViewport(0, 0, width, height);
	checkGlError("glViewport");
	return 0;
}

static int32_t onRender(char *data, int32_t width, int32_t height) {
	glUseProgram(_program);
	checkGlError("glUseProgram");

	if (_textureWidth != (GLsizei) width || _textureHeight != (GLsizei) height) {
		setupTextures(width, height);
	}

	updateTextures(data, width, height);

	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, g_indices);
	checkGlError("glDrawArrays");

	return 0;
}

JOWW(jboolean, glInit) (JNIEnv *env, jobject obj, jobject glView, jint width, jint height) {
	if (viewObj != NULL) {
		(*env)->DeleteGlobalRef(env, viewObj);
	}
	viewObj = (*env)->NewGlobalRef(env, glView);
	onSetup(width, height);
	return B_TRUE;
}

JOWW(void, glRender)(JNIEnv *env, jobject obj) {
	if (g_buffer != NULL) {
		onRender(g_buffer, g_width, g_height);
	}
	return;
}

gl_renderer.h:

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
JNIEXPORT jboolean JNICALL Java_com_package_glInit(JNIEnv *env, jobject obj, jobject glView, jint width, jint height);
JNIEXPORT void JNICALL Java_com_package_glRender(JNIEnv *env, jobject obj);
void gl_renderer_init(JNIEnv *env);
void gl_renderer_free(JNIEnv *env);
void gl_renderer_render(JNIEnv *env, char *data, int len, int32_t width, int32_t height);

    • 渲染一帧画面:直接调用 gl_renderer_render() 函数


参考:

http://blog.csdn.net/cjj198561/article/details/34136187
http://www.xuebuyuan.com/510409.html

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值