上一篇中讲了ANativeWindow的绘制流程,这一篇我们接着跟一下使用OpenGL的绘制流程。
1.配置OpenGL绘制
如果想在ijk中使用OpenGL进行绘制,则需要进行播放器的配置,如下面代码所示,这里不能配置硬解码,因为硬解码是直接将编码器与Surface进行绑定,解码后的数据直接被进行Surface中,就不需要OpenGL进行绘制了。
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", "fcc-_es2");
{ "overlay-format", "fourcc of overlay format",
OPTION_OFFSET(overlay_format), OPTION_INT(SDL_FCC_RV32, INT_MIN, INT_MAX),
.unit = "overlay-format" },
{ "fcc-_es2", "", 0, OPTION_CONST(SDL_FCC__GLES2), .unit = "overlay-format" },
{ "fcc-i420", "", 0, OPTION_CONST(SDL_FCC_I420), .unit = "overlay-format" },
{ "fcc-yv12", "", 0, OPTION_CONST(SDL_FCC_YV12), .unit = "overlay-format" },
{ "fcc-rv16", "", 0, OPTION_CONST(SDL_FCC_RV16), .unit = "overlay-format" },
{ "fcc-rv24", "", 0, OPTION_CONST(SDL_FCC_RV24), .unit = "overlay-format" },
{ "fcc-rv32", "", 0, OPTION_CONST(SDL_FCC_RV32), .unit = "overlay-format" },
2.相关格式的转换
我们配置之后,ffp->overlay_format的值就是SDL_FCC__GLES2l了,所以在一些流程上会与上一篇中有些差异,我们大致看一下有哪些区别。这里还有一个条件是解码后的像素格式,我们以最常见的YUV420P作为判断条件进行跟踪。
在SDL_VoutFFmpeg_CreateOverlay中会将overlay_format设置为SDL_FCC_YV12,目标像素为AV_PIX_FMT_YUV420P,其有3个分量(yuv)。
SDL_VoutOverlay *SDL_VoutFFmpeg_CreateOverlay(int width, int height, int frame_format, SDL_Vout *display)
{
//需要显示的格式
Uint32 overlay_format = display->overlay_format;
switch (overlay_format) {
// 使用opengl进行渲染
//根据像素格式重新设置overlay_format格式
case SDL_FCC__GLES2: {
switch (frame_format) {
case AV_PIX_FMT_YUV444P10LE:
overlay_format = SDL_FCC_I444P10LE;
break;
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P:
default:
#if defined(__ANDROID__)
overlay_format = SDL_FCC_YV12;
#else
overlay_format = SDL_FCC_I420;
#endif
break;
}
break;
}
}
switch (overlay_format) {
case SDL_FCC_I420:
case SDL_FCC_YV12: {
//SDL_FCC_I420与SDL_FCC_YV12 相同,都属于yuv,y u v分别存储,4个y对应1个uv分量
// U、V 平面的 strides, width 和 height 都是 Y 平面的一半
//I420 现存u再存v,YV12 先存v再存u
ff_format = AV_PIX_FMT_YUV420P;
// FIXME: need runtime config
#if defined(__ANDROID__)
// 16 bytes align pitch for arm-neon image-convert
buf_width = IJKALIGN(width, 16); // 1 bytes per pixel for Y-plane
#elif defined(__APPLE__)
// 2^n align for width
buf_width = width;
if (width > 0)
buf_width = 1 << (sizeof(int) * 8 - __builtin_clz(width));
#else
buf_width = IJKALIGN(width, 16); // unknown platform
#endif
//存在3个分量
opaque->planes = 3;
break;
}
}
所以在绘制过程中会满足条件走IJK_EGL_display绘制。
static int func_display_overlay_l(SDL_Vout *vout, SDL_VoutOverlay *overlay)
{
SDL_Vout_Opaque *opaque = vout->opaque;
ANativeWindow *native_window = opaque->native_window;
if (!native_window) {
if (!opaque->null_native_window_warned) {
opaque->null_native_window_warned = 1;
ALOGW("func_display_overlay_l: NULL native_window");
}
return -1;
} else {
opaque->null_native_window_warned = 1;
}
if (!overlay) {
ALOGE("func_display_overlay_l: NULL overlay");
return -1;
}
if (overlay->w <= 0 || overlay->h <= 0) {
ALOGE("func_display_overlay_l: invalid overlay dimensions(%d, %d)", overlay->w, overlay->h);
return -1;
}
switch(overlay->format) {
case SDL_FCC__AMC: {
// only ANativeWindow support
IJK_EGL_terminate(opaque->egl);
//渲染为true就会渲染到surface
return SDL_VoutOverlayAMediaCodec_releaseFrame_l(overlay, NULL, true);
}
case SDL_FCC_RV24:
case SDL_FCC_I420:
case SDL_FCC_I444P10LE: {
// only GLES support
if (opaque->egl)
return IJK_EGL_display(opaque->egl, native_window, overlay);
break;
}
case SDL_FCC_YV12:
case SDL_FCC_RV16:
case SDL_FCC_RV32: {
// both GLES & ANativeWindow support
// 使用OpenGL进行绘制
if (vout->overlay_format == SDL_FCC__GLES2 && opaque->egl)
return IJK_EGL_display(opaque->egl, native_window, overlay);
break;
}
}
// fallback to ANativeWindow
IJK_EGL_terminate(opaque->egl);
return SDL_Android_NativeWindow_display_l(native_window, overlay);
}
3.绘制流程
由于OpenGL的调用流程都是大同小异的,这里就只把代码步骤贴出来,不做详细的解释了。
EGLBoolean IJK_EGL_display(IJK_EGL* egl, EGLNativeWindowType window, SDL_VoutOverlay *overlay)
{
EGLBoolean ret = EGL_FALSE;
if (!egl)
return EGL_FALSE;
IJK_EGL_Opaque *opaque = egl->opaque;
if (!opaque)
return EGL_FALSE;
if (!IJK_EGL_makeCurrent(egl, window))
return EGL_FALSE;
ret = IJK_EGL_display_internal(egl, window, overlay);
eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglReleaseThread(); // FIXME: call at thread exit
return ret;
}
创建EGL环境
static EGLBoolean IJK_EGL_makeCurrent(IJK_EGL* egl, EGLNativeWindowType window)
{
//如果已初始化egl,则只需要调用eglMakeCurrent
if (window && window == egl->window &&
egl->display &&
egl->surface &&
egl->context) {
if (!eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context)) {
ALOGE("[EGL] elgMakeCurrent() failed (cached)\n");
return EGL_FALSE;
}
return EGL_TRUE;
}
//重置egl状态
IJK_EGL_terminate(egl);
egl->window = window;
if (!window)
return EGL_FALSE;
//获取display
//Display是一个连接,用于连接设备上的底层窗口系统
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
ALOGE("[EGL] eglGetDisplay failed\n");
return EGL_FALSE;
}
EGLint major, minor;
//初始化display
if (!eglInitialize(display, &major, &minor)) {
ALOGE("[EGL] eglInitialize failed\n");
return EGL_FALSE;
}
ALOGI("[EGL] eglInitialize %d.%d\n", (int)major, (int)minor);
//指定egl配置 rgb都为8位
static const EGLint configAttribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_NONE//结束标记
};
static const EGLint contextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLConfig config;
EGLint numConfig;
//传入预设配置,让OpenGL自己选择一个最佳的配置
if (!eglChooseConfig(display, configAttribs, &config, 1, &numConfig)) {
ALOGE("[EGL] eglChooseConfig failed\n");
eglTerminate(display);
return EGL_FALSE;
}
#ifdef __ANDROID__
{
EGLint native_visual_id = 0;
//从debug的角度来看,更像是获取配置给出的像素格式
//然后再设置到window之上
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
ALOGE("[EGL] eglGetConfigAttrib() returned error %d", eglGetError());
eglTerminate(display);
return EGL_FALSE;
}
int32_t width = ANativeWindow_getWidth(window);
int32_t height = ANativeWindow_getWidth(window);
ALOGI("[EGL] ANativeWindow_setBuffersGeometry(f=%d);", native_visual_id);
int ret = ANativeWindow_setBuffersGeometry(window, width, height, native_visual_id);
if (ret) {
ALOGE("[EGL] ANativeWindow_setBuffersGeometry(format) returned error %d", ret);
eglTerminate(display);
return EGL_FALSE;
}
}
#endif
//创建EGLSurface
//Surface则是设计来存储渲染相关的输出数据
//EGL 将创建一个新的 EGLSurface 对象,并将其连接到窗口对象的 BufferQueue 的生产方接口
EGLSurface surface = eglCreateWindowSurface(display, config, window, NULL);
if (surface == EGL_NO_SURFACE) {
ALOGE("[EGL] eglCreateWindowSurface failed\n");
eglTerminate(display);
return EGL_FALSE;
}
//创建EGLContext
//Context是设计来存储渲染相关的输入数据,OpenGL 调用是异步的
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT) {
ALOGE("[EGL] eglCreateContext failed\n");
eglDestroySurface(display, surface);
eglTerminate(display);
return EGL_FALSE;
}
//eglMakeCurrent把context绑定到当前的渲染线程以及draw和read指定的Surface
if (!eglMakeCurrent(display, surface, surface, context)) {
ALOGE("[EGL] elgMakeCurrent() failed (new)\n");
eglDestroyContext(display, context);
eglDestroySurface(display, surface);
eglTerminate(display);
return EGL_FALSE;
}
#if 0
#if defined(__ANDROID__)
{
const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
if (extensions) {
char *dup_extensions = strdup(extensions);
if (dup_extensions) {
char *brk = NULL;
char *ext = strtok_r(dup_extensions, " ", &brk);
while (ext) {
if (0 == strcmp(ext, "GL_EXT_texture_rg"))
egl->gles2_extensions[IJK_GLES2__GL_EXT_texture_rg] = 1;
ext = strtok_r(NULL, " ", &brk);
}
free(dup_extensions);
}
}
}
#elif defined(__APPLE__)
egl->gles2_extensions[IJK_GLES2__GL_EXT_texture_rg] = 1;
#endif
ALOGI("[EGL] GLES2 extensions begin:\n");
ALOGI("[EGL] GL_EXT_texture_rg: %d\n", egl->gles2_extensions[IJK_GLES2__GL_EXT_texture_rg]);
ALOGI("[EGL] GLES2 extensions end.\n");
#endif
//清屏
IJK_GLES2_Renderer_setupGLES();
egl->context = context;
egl->surface = surface;
egl->display = display;
return EGL_TRUE;
}
创建Shader
static EGLBoolean IJK_EGL_display_internal(IJK_EGL* egl, EGLNativeWindowType window, SDL_VoutOverlay *overlay)
{
IJK_EGL_Opaque *opaque = egl->opaque;
if (!IJK_EGL_prepareRenderer(egl, overlay)) {
ALOGE("[EGL] IJK_EGL_prepareRenderer failed\n");
return EGL_FALSE;
}
if (!IJK_GLES2_Renderer_renderOverlay(opaque->renderer, overlay)) {
ALOGE("[EGL] IJK_GLES2_render failed\n");
return EGL_FALSE;
}
//更新缓冲器
//会去触发queueBuffer和dequeueBuffer
eglSwapBuffers(egl->display, egl->surface);
return EGL_TRUE;
}
static EGLBoolean IJK_EGL_prepareRenderer(IJK_EGL* egl, SDL_VoutOverlay *overlay)
{
assert(egl);
assert(egl->opaque);
IJK_EGL_Opaque *opaque = egl->opaque;
if (!IJK_GLES2_Renderer_isValid(opaque->renderer) ||
!IJK_GLES2_Renderer_isFormat(opaque->renderer, overlay->format)) {
IJK_GLES2_Renderer_reset(opaque->renderer);
IJK_GLES2_Renderer_freeP(&opaque->renderer);
//创建render
opaque->renderer = IJK_GLES2_Renderer_create(overlay);
if (!opaque->renderer) {
ALOGE("[EGL] Could not create render.");
return EGL_FALSE;
}
if (!IJK_GLES2_Renderer_use(opaque->renderer)) {
ALOGE("[EGL] Could not use render.");
IJK_GLES2_Renderer_freeP(&opaque->renderer);
return EGL_FALSE;
}
}
//按像素宽高重置surface的宽高
if (!IJK_EGL_setSurfaceSize(egl, overlay->w, overlay->h)) {
ALOGE("[EGL] IJK_EGL_setSurfaceSize(%d, %d) failed\n", overlay->w, overlay->h);
return EGL_FALSE;
}
//重新设置画布大小
glViewport(0, 0, egl->width, egl->height); IJK_GLES2_checkError_TRACE("glViewport");
return EGL_TRUE;
}
这里我们跟yuv420p的逻辑。shader里面会拿到三个纹理的引用,后面会传入纹理即yuv三个分量,再在片元着色器中转换成rgb。
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create(SDL_VoutOverlay *overlay)
{
if (!overlay)
return NULL;
IJK_GLES2_printString("Version", GL_VERSION);
IJK_GLES2_printString("Vendor", GL_VENDOR);
IJK_GLES2_printString("Renderer", GL_RENDERER);
IJK_GLES2_printString("Extensions", GL_EXTENSIONS);
IJK_GLES2_Renderer *renderer = NULL;
//根据输出像素格式构建render
switch (overlay->format) {
case SDL_FCC_RV16: renderer = IJK_GLES2_Renderer_create_rgb565(); break;
case SDL_FCC_RV24: renderer = IJK_GLES2_Renderer_create_rgb888(); break;
case SDL_FCC_RV32: renderer = IJK_GLES2_Renderer_create_rgbx8888(); break;
#ifdef __APPLE__
case SDL_FCC_NV12: renderer = IJK_GLES2_Renderer_create_yuv420sp(); break;
case SDL_FCC__VTB: renderer = IJK_GLES2_Renderer_create_yuv420sp_vtb(overlay); break;
#endif
case SDL_FCC_YV12: renderer = IJK_GLES2_Renderer_create_yuv420p(); break;
case SDL_FCC_I420: renderer = IJK_GLES2_Renderer_create_yuv420p(); break;
case SDL_FCC_I444P10LE: renderer = IJK_GLES2_Renderer_create_yuv444p10le(); break;
default:
ALOGE("[GLES2] unknown format %4s(%d)\n", (char *)&overlay->format, overlay->format);
return NULL;
}
renderer->format = overlay->format;
return renderer;
}
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_yuv420p()
{
ALOGI("create render yuv420p\n");
IJK_GLES2_Renderer *renderer = IJK_GLES2_Renderer_create_base(IJK_GLES2_getFragmentShader_yuv420p());
if (!renderer)
goto fail;
renderer->us2_sampler[0] = glGetUniformLocation(renderer->program, "us2_SamplerX"); IJK_GLES2_checkError_TRACE("glGetUniformLocation(us2_SamplerX)");
renderer->us2_sampler[1] = glGetUniformLocation(renderer->program, "us2_SamplerY"); IJK_GLES2_checkError_TRACE("glGetUniformLocation(us2_SamplerY)");
renderer->us2_sampler[2] = glGetUniformLocation(renderer->program, "us2_SamplerZ"); IJK_GLES2_checkError_TRACE("glGetUniformLocation(us2_SamplerZ)");
renderer->um3_color_conversion = glGetUniformLocation(renderer->program, "um3_ColorConversion"); IJK_GLES2_checkError_TRACE("glGetUniformLocation(um3_ColorConversionMatrix)");
renderer->func_use = yuv420p_use;
renderer->func_getBufferWidth = yuv420p_getBufferWidth;
renderer->func_uploadTexture = yuv420p_uploadTexture;
return renderer;
fail:
IJK_GLES2_Renderer_free(renderer);
return NULL;
}
// 片元着色器
static const char g_shader[] = IJK_GLES_STRING(
precision highp float;
varying highp vec2 vv2_Texcoord;
uniform mat3 um3_ColorConversion;
uniform lowp sampler2D us2_SamplerX;
uniform lowp sampler2D us2_SamplerY;
uniform lowp sampler2D us2_SamplerZ;
void main()
{
mediump vec3 yuv;
lowp vec3 rgb;
yuv.x = (texture2D(us2_SamplerX, vv2_Texcoord).r - (16.0 / 255.0));
yuv.y = (texture2D(us2_SamplerY, vv2_Texcoord).r - 0.5);
yuv.z = (texture2D(us2_SamplerZ, vv2_Texcoord).r - 0.5);
rgb = um3_ColorConversion * yuv;
gl_FragColor = vec4(rgb, 1);
}
);
const char *IJK_GLES2_getFragmentShader_yuv420p()
{
return g_shader;
}
IJK_GLES2_Renderer *IJK_GLES2_Renderer_create_base(const char *fragment_shader_source)
{
assert(fragment_shader_source);
IJK_GLES2_Renderer *renderer = (IJK_GLES2_Renderer *)calloc(1, sizeof(IJK_GLES2_Renderer));
if (!renderer)
goto fail;
//编译顶点着色器
renderer->vertex_shader = IJK_GLES2_loadShader(GL_VERTEX_SHADER, IJK_GLES2_getVertexShader_default());
if (!renderer->vertex_shader)
goto fail;
//编译片元着色器
renderer->fragment_shader = IJK_GLES2_loadShader(GL_FRAGMENT_SHADER, fragment_shader_source);
if (!renderer->fragment_shader)
goto fail;
renderer->program = glCreateProgram(); IJK_GLES2_checkError("glCreateProgram");
if (!renderer->program)
goto fail;
glAttachShader(renderer->program, renderer->vertex_shader); IJK_GLES2_checkError("glAttachShader(vertex)");
glAttachShader(renderer->program, renderer->fragment_shader); IJK_GLES2_checkError("glAttachShader(fragment)");
glLinkProgram(renderer->program); IJK_GLES2_checkError("glLinkProgram");
GLint link_status = GL_FALSE;
//查看链接状态
glGetProgramiv(renderer->program, GL_LINK_STATUS, &link_status);
if (!link_status)
goto fail;
//获取通用参数句柄
renderer->av4_position = glGetAttribLocation(renderer->program, "av4_Position"); IJK_GLES2_checkError_TRACE("glGetAttribLocation(av4_Position)");
renderer->av2_texcoord = glGetAttribLocation(renderer->program, "av2_Texcoord"); IJK_GLES2_checkError_TRACE("glGetAttribLocation(av2_Texcoord)");
renderer->um4_mvp = glGetUniformLocation(renderer->program, "um4_ModelViewProjection"); IJK_GLES2_checkError_TRACE("glGetUniformLocation(um4_ModelViewProjection)");
return renderer;
fail:
if (renderer && renderer->program)
IJK_GLES2_printProgramInfo(renderer->program);
IJK_GLES2_Renderer_free(renderer);
return NULL;
}
向OpenGL传递参数
GLboolean IJK_GLES2_Renderer_use(IJK_GLES2_Renderer *renderer)
{
if (!renderer)
return GL_FALSE;
assert(renderer->func_use);
//调用glUseProgram,并向shader传递相关参数
if (!renderer->func_use(renderer))
return GL_FALSE;
IJK_GLES_Matrix modelViewProj;
//设置矩阵参数
IJK_GLES2_loadOrtho(&modelViewProj, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
glUniformMatrix4fv(renderer->um4_mvp, 1, GL_FALSE, modelViewProj.m); IJK_GLES2_checkError_TRACE("glUniformMatrix4fv(um4_mvp)");
IJK_GLES2_Renderer_TexCoords_reset(renderer);
IJK_GLES2_Renderer_TexCoords_reloadVertex(renderer);
IJK_GLES2_Renderer_Vertices_reset(renderer);
IJK_GLES2_Renderer_Vertices_reloadVertex(renderer);
return GL_TRUE;
}
创建yuv纹理
static GLboolean yuv420p_use(IJK_GLES2_Renderer *renderer)
{
ALOGI("use render yuv420p\n");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glUseProgram(renderer->program); IJK_GLES2_checkError_TRACE("glUseProgram");
if (0 == renderer->plane_textures[0])
glGenTextures(3, renderer->plane_textures);
for (int i = 0; i < 3; ++i) {
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, renderer->plane_textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
glUniform1i(renderer->us2_sampler[i], i);
}
glUniformMatrix3fv(renderer->um3_color_conversion, 1, GL_FALSE, IJK_GLES2_getColorMatrix_bt709());
return GL_TRUE;
}
执行绘制
上面创建好了纹理,但没有绑定数据,在绘制的时候还需要绑定纹理数据。
GLboolean IJK_GLES2_Renderer_renderOverlay(IJK_GLES2_Renderer *renderer, SDL_VoutOverlay *overlay)
{
if (!renderer || !renderer->func_uploadTexture)
return GL_FALSE;
glClear(GL_COLOR_BUFFER_BIT); IJK_GLES2_checkError_TRACE("glClear");
GLsizei visible_width = renderer->frame_width;
GLsizei visible_height = renderer->frame_height;
if (overlay) {
visible_width = overlay->w;
visible_height = overlay->h;
//如果宽高发生变化
if (renderer->frame_width != visible_width ||
renderer->frame_height != visible_height ||
renderer->frame_sar_num != overlay->sar_num ||
renderer->frame_sar_den != overlay->sar_den) {
renderer->frame_width = visible_width;
renderer->frame_height = visible_height;
renderer->frame_sar_num = overlay->sar_num;
renderer->frame_sar_den = overlay->sar_den;
renderer->vertices_changed = 1;
}
renderer->last_buffer_width = renderer->func_getBufferWidth(renderer, overlay);
// 绑定纹理数据
if (!renderer->func_uploadTexture(renderer, overlay))
return GL_FALSE;
} else {
// NULL overlay means force reload vertice
renderer->vertices_changed = 1;
}
GLsizei buffer_width = renderer->last_buffer_width;
if (renderer->vertices_changed ||
(buffer_width > 0 &&
buffer_width > visible_width &&
buffer_width != renderer->buffer_width &&
visible_width != renderer->visible_width)){
renderer->vertices_changed = 0;
//重新设置顶点坐标
IJK_GLES2_Renderer_Vertices_apply(renderer);
//向shader传入顶点buffer
IJK_GLES2_Renderer_Vertices_reloadVertex(renderer);
renderer->buffer_width = buffer_width;
renderer->visible_width = visible_width;
GLsizei padding_pixels = buffer_width - visible_width;
GLfloat padding_normalized = ((GLfloat)padding_pixels) / buffer_width;
IJK_GLES2_Renderer_TexCoords_reset(renderer);
IJK_GLES2_Renderer_TexCoords_cropRight(renderer, padding_normalized);
IJK_GLES2_Renderer_TexCoords_reloadVertex(renderer);
}
//执行绘制
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); IJK_GLES2_checkError_TRACE("glDrawArrays");
return GL_TRUE;
}
绑定纹理数据
static GLboolean yuv420p_uploadTexture(IJK_GLES2_Renderer *renderer, SDL_VoutOverlay *overlay)
{
if (!renderer || !overlay)
return GL_FALSE;
int planes[3] = { 0, 1, 2 };
const GLsizei widths[3] = { overlay->pitches[0], overlay->pitches[1], overlay->pitches[2] };
const GLsizei heights[3] = { overlay->h, overlay->h / 2, overlay->h / 2 };
const GLubyte *pixels[3] = { overlay->pixels[0], overlay->pixels[1], overlay->pixels[2] };
switch (overlay->format) {
case SDL_FCC_I420:
break;
case SDL_FCC_YV12://yv12进行转换
planes[1] = 2;
planes[2] = 1;
break;
default:
ALOGE("[yuv420p] unexpected format %x\n", overlay->format);
return GL_FALSE;
}
// 绑定纹理数据
for (int i = 0; i < 3; ++i) {
int plane = planes[i];
glBindTexture(GL_TEXTURE_2D, renderer->plane_textures[i]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_LUMINANCE,
widths[plane],
heights[plane],
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
pixels[plane]);
}
return GL_TRUE;
}
上面就是整个OpenGL的绘制过程,这里做一个简要的总结:
1.为线程创建EGL环境
2.创建顶点着色器,片元着色器,编译OpenGL程序,并获取到需要的引用
3.向OpenGL程序传递参数,这里传递的是顶点数据
4.创建yuv3个纹理,当然只会创建一次
5.将SDL_VoutOverlay三个分量的yuv数据与纹理进行绑定
6.调用glDrawArrays,执行绘制。
以上是个人见解,如果错误或不同见解,欢迎指正和交流。