- 目的:视频直播多以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