参考网址:
Android native层的opengltest程序学习例子
由于项目需要把 Camera HAL 层的部分算法移植到 APK 的 native 用 OpenGL ES 增加特效和渲染预览。
需要两个步骤:
- 从 APK 传输数据到 native 层: Android camera2 API 获取相机的 YUV 元数据预览
- 在 native 层处理相机的 YUV 数据,并使用 OpenGL ES 增加特效和渲染预览。
本代码需要在 android O 源码环境中编译。
重点是 OpenGL ES 顶点着色器和片源着色器的 program
android 中 OpenGL ES 和 EGL 结合完成预览的任务
/*
* 使用 EGL 一般顺序
* 1. 使用EGL首先必须创建,建立本地窗口系统和OpenGL ES的连接
* eglGetDisplay
* 2. 初始化EGL
* eglInitialize
* 3. 确定可用的渲染表面(Surface)的配置。一旦初始化了 EGL,就可以确定可用渲染表面的类型和配置
* chooseConfig
* 4. 创建渲染表面 surface(4/5步骤可互换)
* eglCreateWindowSurface
* 5. 创建渲染上下文 Context
* eglCreateContext
* 6. 指定某个 EGLContext 为当前上下文, 关联起来
* eglMakeCurrent
* 1~6步完成之后,我们可以通过OpenGL的 API 来绘制东西
*
* 7. 使用OpenGL相关的API进行绘制操作
*
* 8. 交换EGL的Surface的内部缓冲和EGL创建的和平台无关的窗口 diaplay ;
* EGL实际上维护了两个buffer,前台buffer显示的时候,绘制操作会在后台buffer上进行
* eglSwapBuffers
*
* 9. 销毁display
* eglTerminate
* 10. 销毁上下文
* eglDestroyContext
* 11. 销毁surface
* eglDestroySurface
*
* */
程序如下:
Shader.vert 顶点着色器程序
//Shader.vert文件内容
static const char* VERTEX_SHADER =
"attribute vec4 vPosition; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 tc; \n"
"void main() \n"
"{ \n"
"gl_Position = vPosition; \n"
"tc = a_texCoord; \n"
"} \n";
Shader.frag 片源着色器程序
该段程序类似 C 语言中 YUV 转 RGB 的算法
//Shader.frag文件内容
static const char* FRAG_SHADER =
"varying lowp vec2 tc; \n"
"uniform sampler2D dataY; \n"
"uniform sampler2D dataU; \n"
"uniform sampler2D dataV; \n"
"void main(void) \n"
"{ \n"
"mediump vec3 yuv; \n"
"lowp vec3 rgb; \n"
"yuv.x = texture2D(dataY, tc).r; \n"
"yuv.y = texture2D(dataU, tc).r - 0.5; \n"
"yuv.z = texture2D(dataV, tc).r - 0.5; \n"
"rgb = mat3( 1, 1, 1, \n"
"0, -0.39465, 2.03211, \n"
"1.13983, -0.58060, 0) * yuv; \n"
"gl_FragColor = vec4(rgb, 1); \n"
"} \n";
cur_log.h 打印信息
#ifndef CUR_LOG_H
#define CUR_LOG_H
#include <android/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#undef LOG_TAG
#define LOG_TAG "cur_log"
#define cur_logi(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) /* 普通打印信息 */
#define cur_loge(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) /* 错误打印信息 */
// #define cur_logi(fmt, args...) printf("I %s " fmt "\n", LOG_TAG, ##args)
// #define cur_loge(fmt, args...) printf("E %s " fmt "\n", LOG_TAG, ##args)
#define cur_enter() cur_logi("enter, %s", __func__)
#define cur_exit() cur_logi("exit, %s", __func__)
#ifdef __cplusplus
}
#endif
/* CUR_LOG_H */
#endif
yuvTest.h 头文件
#ifndef YUVTEST_H
#define YUVTEST_H
#ifdef __cplusplus
extern "C"{
#endif
enum
{
ATTRIB_VERTEX = 0,
ATTRIB_TEXTURE,
};
typedef struct _gmVector3
{
GLfloat x;
GLfloat y;
GLfloat z;
} gmVector3_s;
typedef struct _gmMatrix4
{
GLfloat m[16];
} gmMatrix4;
typedef struct _gmVector2
{
float x;
float y;
} gmVector2_s;
typedef struct _gmVector4
{
float x;
float y;
float z;
float w;
} gmVector4_s;
static void printGLString(const char *name, GLenum s);
static void checkEglError(const char *op, EGLBoolean returnVal = EGL_TRUE);
static void checkGlError(const char *op);
GLuint buildShader(GLenum shaderType, const char *pSource);
GLuint createProgram(const char *pVertexSource, const char *pFragmentSource);
bool setupGraphics(int w __unused, int h __unused);
void renderFrame();
void printEGLConfiguration(EGLDisplay dpy, EGLConfig config);
int printEGLConfigurations(EGLDisplay dpy);
GLuint bindTexture(GLuint texture, const char *buffer, GLuint w, GLuint h);
#ifdef __cplusplus
}
#endif
#endif // GL2_BASIC_H
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sched.h>
#include <sys/resource.h>
#include <math.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <utils/Timers.h>
#include <WindowSurface.h>
#include <EGLUtils.h>
#include <unistd.h>
#include "yuvTest.h"
#include "cur_log.h"
#include "Shader.vert"
#include "Shader.frag"
using namespace android;
using namespace std;
GLuint g_texYId;
GLuint g_texUId;
GLuint g_texVId;
GLuint gProgram;
GLuint uLocMvp;
static void printGLString(const char *name, GLenum s)
{
const char *v = (const char *) glGetString(s);
fprintf(stderr, "GL %s = %s\n", name, v);
}
static void checkEglError(const char *op, EGLBoolean returnVal)
{
if(returnVal != EGL_TRUE)
{
fprintf(stderr, "%s() returned %d\n", op, returnVal);
}
for(EGLint error = eglGetError(); error != EGL_SUCCESS; error
= eglGetError())
{
fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
error);
}
}
static void checkGlError(const char *op)
{
for(GLint error = glGetError(); error; error
= glGetError())
{
fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
}
}
GLuint buildShader(GLenum shaderType, const char *pSource)
{
// 创建着色器 Shader
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); // 返回着色器对象的信息日志
fprintf(stderr, "Could not compile shader %d:\n%s\n",
shaderType, buf);
free(buf);
}
glDeleteShader(shader); // 删除一个着色器对象
shader = 0;
}
}
}
return shader;
}
GLuint createProgram(const char *pVertexSource, const char *pFragmentSource)
{
// 创建一个顶点着色器对象 Vertex Shader
GLuint vertexShader = buildShader(GL_VERTEX_SHADER, pVertexSource);
if(!vertexShader)
{
return 0;
}
// 创建一个像素着色器对象 Fragment Shader
GLuint pixelShader = buildShader(GL_FRAGMENT_SHADER, pFragmentSource);
if(!pixelShader)
{
return 0;
}
// 创建一个 program 对象
GLuint program = glCreateProgram();
if(program)
{
glAttachShader(program, vertexShader); // 将顶点着色器对象附加到 program 对象
checkGlError("glAttachShader");
glAttachShader(program, pixelShader); // 将像素着色器对象附加到 program 对象
checkGlError("glAttachShader");
glLinkProgram(program); // 连接一个 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);
fprintf(stderr, "Could not link program:\n%s\n", buf);
free(buf);
}
}
glDeleteProgram(program);
program = 0;
}
}
return program;
}
bool setupGraphics(int w __unused, int h __unused)
{
// gProgram = createProgram(gVertexShader, gFragmentShader); // 创建一个顶点着色器,一个像素着色器
gProgram = createProgram(VERTEX_SHADER, FRAG_SHADER); // 创建一个顶点着色器,一个像素着色器
if(!gProgram)
{
return false;
}
// 否则颜色清除错误错误
glUseProgram(gProgram); // 使用 program 对象作为当前 render 渲染状态的一部分
checkGlError("glUseProgram");
uLocMvp = glGetUniformLocation(gProgram, "uMvp"); // 返回统一变量的位置(句柄)
glGenTextures(1, &g_texYId); // 生成纹理名称(ID)
glGenTextures(1, &g_texUId);
glGenTextures(1, &g_texVId);
return true;
}
void renderFrame()
{
#if 1
static GLfloat squareVertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
static GLfloat coordVertices[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
#else
static GLfloat squareVertices[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
static GLfloat coordVertices[] = {
-1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
};
#endif
glClearColor(0.5f, 0.5f, 0.5f, 1); // 指定颜色缓冲区清除时的RGBA值,默认都是0
checkGlError("glClearColor");
glClear(GL_COLOR_BUFFER_BIT); // 清除预设值的缓冲区, GL_COLOR_BUFFER_BIT - 表示当前启用了颜色写入的缓冲区。
checkGlError("glClear");
//PRINTF("setsampler %d %d %d", g_texYId, g_texUId, g_texVId);
GLint tex_y = glGetUniformLocation(gProgram, "dataY"); // 返回 dataY 变量的位置
checkGlError("glGetUniformLocation");
GLint tex_u = glGetUniformLocation(gProgram, "dataU");
checkGlError("glGetUniformLocation");
GLint tex_v = glGetUniformLocation(gProgram, "dataV");
checkGlError("glGetUniformLocation");
glBindAttribLocation(gProgram, ATTRIB_VERTEX, "vPosition"); // 将通用顶点属性索引与命名属性变量绑定
checkGlError("glBindAttribLocation");
glBindAttribLocation(gProgram, ATTRIB_TEXTURE, "a_texCoord");
checkGlError("glBindAttribLocation");
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices); // 定义通用顶点属性数据的数组
checkGlError("glVertexAttribPointer");
glEnableVertexAttribArray(ATTRIB_VERTEX);
checkGlError("glEnableVertexAttribArray");
glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, coordVertices);
checkGlError("glVertexAttribPointer");
glEnableVertexAttribArray(ATTRIB_TEXTURE);
checkGlError("glEnableVertexAttribArray");
glActiveTexture(GL_TEXTURE0); // 激活纹理单元
checkGlError("glActiveTexture");
glBindTexture(GL_TEXTURE_2D, g_texYId); // 将一个指定的纹理ID绑定到一个纹理目标上
checkGlError("glBindTexture");
glUniform1i(tex_y, 0); // 通过 glUniform1i 的设置,保证每个uniform采样器对应着正确的纹理单元
checkGlError("glUniform1i");
glActiveTexture(GL_TEXTURE1);
checkGlError("glActiveTexture");
glBindTexture(GL_TEXTURE_2D, g_texUId);
checkGlError("glBindTexture");
glUniform1i(tex_u, 1);
checkGlError("glUniform1i");
glActiveTexture(GL_TEXTURE2);
checkGlError("glActiveTexture");
glBindTexture(GL_TEXTURE_2D, g_texVId);
checkGlError("glBindTexture");
glUniform1i(tex_v, 2);
checkGlError("glUniform1i");
//glEnable(GL_TEXTURE_2D);
//checkGlError("glEnable");
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 从数组数据中渲染图元
checkGlError("glDrawArrays");
}
void printEGLConfiguration(EGLDisplay dpy, EGLConfig config)
{
#define X(VAL) {VAL, #VAL}
struct
{
EGLint attribute;
const char *name;
} names[] = {
X(EGL_BUFFER_SIZE),
X(EGL_ALPHA_SIZE),
X(EGL_BLUE_SIZE),
X(EGL_GREEN_SIZE),
X(EGL_RED_SIZE),
X(EGL_DEPTH_SIZE),
X(EGL_STENCIL_SIZE),
X(EGL_CONFIG_CAVEAT),
X(EGL_CONFIG_ID),
X(EGL_LEVEL),
X(EGL_MAX_PBUFFER_HEIGHT),
X(EGL_MAX_PBUFFER_PIXELS),
X(EGL_MAX_PBUFFER_WIDTH),
X(EGL_NATIVE_RENDERABLE),
X(EGL_NATIVE_VISUAL_ID),
X(EGL_NATIVE_VISUAL_TYPE),
X(EGL_SAMPLES),
X(EGL_SAMPLE_BUFFERS),
X(EGL_SURFACE_TYPE),
X(EGL_TRANSPARENT_TYPE),
X(EGL_TRANSPARENT_RED_VALUE),
X(EGL_TRANSPARENT_GREEN_VALUE),
X(EGL_TRANSPARENT_BLUE_VALUE),
X(EGL_BIND_TO_TEXTURE_RGB),
X(EGL_BIND_TO_TEXTURE_RGBA),
X(EGL_MIN_SWAP_INTERVAL),
X(EGL_MAX_SWAP_INTERVAL),
X(EGL_LUMINANCE_SIZE),
X(EGL_ALPHA_MASK_SIZE),
X(EGL_COLOR_BUFFER_TYPE),
X(EGL_RENDERABLE_TYPE),
X(EGL_CONFORMANT),
};
#undef X
for(size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++)
{
EGLint value = -1;
EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
EGLint error = eglGetError();
if(returnVal && error == EGL_SUCCESS)
{
printf(" %s: ", names[j].name);
printf("%d (0x%x)", value, value);
}
}
printf("\n");
}
int printEGLConfigurations(EGLDisplay dpy)
{
EGLint numConfig = 0;
EGLint returnVal = eglGetConfigs(dpy, NULL, 0, &numConfig);
checkEglError("eglGetConfigs", returnVal);
if(!returnVal)
{
return false;
}
printf("Number of EGL configuration: %d\n", numConfig);
EGLConfig *configs = (EGLConfig *) malloc(sizeof(EGLConfig) * numConfig);
if(!configs)
{
printf("Could not allocate configs.\n");
return false;
}
returnVal = eglGetConfigs(dpy, configs, numConfig, &numConfig);
checkEglError("eglGetConfigs", returnVal);
if(!returnVal)
{
free(configs);
return false;
}
for(int i = 0; i < numConfig; i++)
{
printf("Configuration %d\n", i);
printEGLConfiguration(dpy, configs[i]);
}
free(configs);
return true;
}
GLuint bindTexture(GLuint texture, const char *buffer, GLuint w , GLuint h)
{
// GLuint texture;
// glGenTextures ( 1, &texture );
checkGlError("glGenTextures");
glBindTexture(GL_TEXTURE_2D, texture); // 指定一个二维的纹理图片
checkGlError("glBindTexture");
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer); // 指定一个二维的纹理图片
checkGlError("glTexImage2D");
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); // 纹理过滤函数, GL_TEXTURE_MIN_FILTER 缩小过滤
checkGlError("glTexParameteri");
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); // 纹理过滤函数, GL_TEXTURE_MAG_FILTER 放大过滤
checkGlError("glTexParameteri");
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); // 纹理过滤函数, GL_TEXTURE_WRAP_S S方向上的贴图模式
checkGlError("glTexParameteri");
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
checkGlError("glTexParameteri");
//glBindTexture(GL_TEXTURE_2D, 0);
return texture;
}
// ****************************************************************************
int main(int argc __unused, char **argv __unused)
{
EGLBoolean returnValue;
EGLConfig myConfig = {0};
EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
//当前我们要的是Windows的surface,在屏幕上是可见的,以EGL_NONE结尾。
EGLint s_configAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE};
EGLint majorVersion;
EGLint minorVersion;
EGLContext context;
EGLSurface surface;
EGLint w, h;
EGLDisplay dpy;
// 第一步: 初始化EGL和创建Open GL上下文环境
checkEglError("<init>");
// 1. 取得显示设备, 获得默认的 Display 。通常我们只有一块屏幕,参数传 EGL_DEFAULT_DISPLAY 就可以了。
dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
checkEglError("eglGetDisplay");
if(dpy == EGL_NO_DISPLAY)
{
printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
return 0;
}
// 2. 初始化 EGL 显示设备
returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
checkEglError("eglInitialize", returnValue);
fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion);
if(returnValue != EGL_TRUE)
{
printf("eglInitialize failed\n");
return 0;
}
// 打印 EGL 配置
if(!printEGLConfigurations(dpy))
{
printf("printEGLConfigurations failed\n");
return 0;
}
checkEglError("printEGLConfigurations");
// 3. 确定可用的渲染表面(Surface)的配置。一旦初始化了 EGL,就可以确定可用渲染表面的类型和配置
WindowSurface windowSurface;
EGLNativeWindowType window = windowSurface.getSurface();
returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig);
if(returnValue)
{
printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue);
return 0;
}
checkEglError("EGLUtils::selectConfigForNativeWindow");
printf("Chose this configuration:\n");
// printEGLConfiguration(dpy, myConfig);
// 4. 创建渲染表面 surface。(4/5步骤可互换)
surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
checkEglError("eglCreateWindowSurface");
if(surface == EGL_NO_SURFACE)
{
printf("gelCreateWindowSurface failed.\n");
return 0;
}
// 5. 创建渲染上下文,使用前面的显示设备和配置信息来创建 OpenGL 上下文环境
context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
checkEglError("eglCreateContext");
if(context == EGL_NO_CONTEXT)
{
printf("eglCreateContext failed\n");
return 0;
}
// 将创建的 context 绑定到当前的线程,使用我们创建的 surface 进行读和写。
// context 绑定到线程上,可不用担心其他进程影响你的 OpenGLES 应用程序。
// 6. 指定某个 EGLContext 为当前上下文
returnValue = eglMakeCurrent(dpy, surface, surface, context);
checkEglError("eglMakeCurrent", returnValue);
if(returnValue != EGL_TRUE)
{
return 0;
}
// Surface 也有一些 attribute
// EGL_HEIGHT EGL_WIDTH EGL_LARGEST_PBUFFER EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET EGL_MIPMAP_TEXTURE EGL_MIPMAP_LEVEL
// 通过 eglSurfaceAttrib() 设置、 eglQuerySurface() 读取。 mat
eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
checkEglError("eglQuerySurface");
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
checkEglError("eglQuerySurface");
// GLint dim = w < h ? w : h;
fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
printGLString("Version", GL_VERSION);
printGLString("Vendor", GL_VENDOR);
printGLString("Renderer", GL_RENDERER);
printGLString("Extensions", GL_EXTENSIONS);
//开始绘制
if(!setupGraphics(w, h))
{
fprintf(stderr, "Could not set up graphics.\n");
return 0;
}
int width = 1440, height = 1080;
FILE *fp = fopen("/sdcard/bb.yuv", "rb");
if(fp == NULL)
{
cur_loge("fopen fp failed");
}
char *buffer = (char*)malloc(width * height * 3 / 2);
if(buffer == NULL)
{
cur_loge("malloc buffer failed");
}
for (;;)
{
if(buffer != NULL && fp != NULL)
{
// glViewport(0, 0, width, height); // 设置视口
int ret = fread(buffer, 1, width * height * 3 / 2, fp);
if(ret == width * height * 3 / 2)
{
bindTexture(g_texYId, buffer, width, height);
bindTexture(g_texUId, buffer + width * height, width/2, height/2);
bindTexture(g_texVId, buffer + width * height * 5 / 4, width/2, height/2);
renderFrame();
usleep(100 * 1000);
eglSwapBuffers(dpy, surface); // EGL实际上维护了两个 buffer ,前台 buffer 显示的时候,绘制操作会在后台 buffer 上进行
checkEglError("eglSwapBuffers");
}
else
{
cur_logi("end read file");
}
}
}
if(fp != NULL)
{
fclose(fp);
fp = NULL;
}
if(buffer != NULL)
{
free(buffer);
buffer = NULL;
}
return 0;
}
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := yuvTest.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libEGL \
libGLESv2 \
libui \
libgui \
libutils
LOCAL_STATIC_LIBRARIES += libglTest
LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
LOCAL_MODULE := yuvopenGL
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS := -lpthread -DGL_GLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Wno-unused-function -Wno-unused-const-variable -Wno-unused-variable
include $(BUILD_EXECUTABLE)
运行方法:
adb push out/xxx/xxx/system/bin/yuvopenGL system/bin/
adb shell
yuvopenGL