在OpenGL ES中进行YUV420到RGB转换的纹理

时间:2014-02-13 07:16:14

标签: opengl-es gpu rgb yuv egl

我必须使用飞思卡尔iMX53处理器(OpenGL ES 2.0,EGL)上的AMD GPU将YUV420P图像转换并显示为RGB色彩空间。 Linux OS,没有X11。为了实现这一点,我应该能够创建一个包含YUV420P数据的合适图像:这可以是YUV420P / YV12图像类型,也可以是3个简单的8位图像,每个分量对应一个(Y,U,V)。

glTexImage2D被排除,因为它很慢,YUV420P帧是实时视频解码@ 25FPS的结果,而glTexImage2D我们无法保持所需的帧速率。

还有另一种选择:eglCreateImageKHR / glEGLImageTargetTexture2DOES。唯一的问题是这些无法处理任何适合YUV420 / YV12数据的图像格式。

EGLint attribs[] = {
  EGL_WIDTH, 800,
  EGL_HEIGHT, 480,
  EGL_IMAGE_FORMAT_FSL, EGL_FORMAT_YUV_YV12_FSL,
  EGL_NONE
};

EGLint const req_attribs[] = {
  EGL_RED_SIZE, 5,
  EGL_GREEN_SIZE, 6,
  EGL_BLUE_SIZE, 5,
  EGL_ALPHA_SIZE, 0,
  EGL_SAMPLES, 0,
  EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
  EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  EGL_NONE
};

...

display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, NULL, NULL);
eglBindAPI(EGL_OPENGL_ES_API);
eglChooseConfig(display, req_attribs, config, ARRAY_SIZE(config), &num_configs);
ctx = eglCreateContext(display, curr_config, NULL, NULL);
surface = eglCreateWindowSurface(display, curr_config, fb_handle, NULL);

...

EGLImageKHR yuv_img = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NEW_IMAGE_FSL, NULL, attribs); 
eglQueryImageFSL(display, yuv_img, EGL_CLIENTBUFFER_TYPE_FSL, (EGLint *)&ptr);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, yuv_img);

glEGLImageTargetTexture2DOES(...)失败。如果我将'attribs'中的相应行更改为:

EGL_IMAGE_FORMAT_FSL,EGL_FORMAT_RGB_565_FSL,

然后可以将图像分配给OpenGL ES纹理,但不适合保存8位数据(Y / U / V)或YUV420 / YV12数据。搜索网络(包括飞思卡尔社区论坛)我还没有找到任何解决方案。

如何创建以下图像:

  • 快速创建;
  • 最终可以分配给已经存在的缓冲区(给出物理地址或虚拟地址);
  • 可以在片段/顶点着色器程序中用于执行YUV - > RGB转换;

约束是为了避免由于性能原因而导致不必要的memcpy(...)。

1 个答案:

答案 0 :(得分:8)

我已经在i.MX53上实现了几种YUV格式,并且效果非常好。我有一篇关于它的发表文章,虽然它被推广到更多Android平台:

http://software.intel.com/en-us/articles/using-opengl-es-to-accelerate-apps-with-legacy-2d-guis

我怀疑你的问题是你没有绑定到正确的纹理目标。它应该是这样的:

glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, hEglImage[iTextureIndex]);

glBindTexture(GL_TEXTURE_EXTERNAL_OES, hTexture[iIndex]);   

eglImageAttributes应该是以下之一:

EGLint eglImageAttributes[] = {EGL_WIDTH, iTextureWidth, EGL_HEIGHT, iTextureHeight, EGL_IMAGE_FORMAT_FSL, EGL_FORMAT_YUV_YV12_FSL, EGL_NONE};
EGLint eglImageAttributes[] = {EGL_WIDTH, iTextureWidth, EGL_HEIGHT, iTextureHeight, EGL_IMAGE_FORMAT_FSL, EGL_FORMAT_YUV_NV21_FSL, EGL_NONE};
EGLint eglImageAttributes[] = {EGL_WIDTH, iTextureWidth, EGL_HEIGHT, iTextureHeight, EGL_IMAGE_FORMAT_FSL, EGL_FORMAT_YUV_UYVY_FSL, EGL_NONE};

hEglImage[iTextureIndex] = eglCreateImageKHR(eglDisplay, EGL_NO_CONTEXT, EGL_NEW_IMAGE_FSL, NULL, eglImageAttributes);

struct EGLImageInfoFSL EglImageInfo;
eglQueryImageFSL(eglDisplay, hEglImage[iTextureIndex], EGL_CLIENTBUFFER_TYPE_FSL, (EGLint *)&EglImageInfo);

虽然飞思卡尔i.MX53平台的这一特性使视频的YUV到RGB色彩空间转换速度极快,但确实有一些限制:

  1. 它只支持3种YUV格式。
  2. eglCreateImageKHR()必须分配缓冲区。没有办法让它使用现有的缓冲区。飞思卡尔证实,NULL指针不能是其他任何东西,这在技术上违反了Khronos规范。
  3. 飞思卡尔已经在i.MX6平台上解决了这些问题,尽管架构确实不同。希望这会有所帮助。