текстура для преобразования YUV420 в RGB в OpenGL ES

Мне нужно преобразовать и отобразить изображения YUV420P в цветовое пространство RGB с помощью графического процессора AMD на процессоре Freescale iMX53 (OpenGL ES 2.0, EGL). ОС Linux, нет 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. Поискав в сети (включая форум сообщества Freescale), я не нашел никакого решения для этого.

Как создать изображение, которое:

  • быстро создавать;
  • в конечном итоге может быть назначен уже существующему буферу (дается физический адрес или виртуальный адрес);
  • может использоваться в программе шейдера фрагментов / вершин для преобразования YUV -> RGB;

Ограничение состоит в том, чтобы избежать ненужных memcpy (...) из соображений производительности.


person tselmeci    schedule 13.02.2014    source источник


Ответы (1)


Я реализовал это на 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);

Хотя эта функция платформы Freescale i.MX53 делает преобразование цветового пространства YUV в RGB для видео чрезвычайно быстрым, у нее есть несколько ограничений:

  1. Он поддерживает только эти 3 формата YUV.
  2. eglCreateImageKHR () должен выделить буферы. Невозможно заставить его использовать существующие буферы. Freescale подтвердил, что указатель NULL не может быть ничем другим, что технически нарушает спецификацию Khronos.

Freescale решил эти проблемы на платформе i.MX6, хотя архитектура действительно отличается. Надеюсь это поможет.

person ClayMontgomery    schedule 13.02.2014
comment
Большое спасибо за вашу помощь, это оказалась действительно ценной информацией. Я новичок в OpenGL (ES) и меня смущает множество новых деталей, которые мне нужно иметь в виду, чтобы решить даже такую ​​простую проблему. Мой YUV420 - ›Конвертер RGB теперь работает, хотя и не слишком эффективно, поскольку использует 3 изображения EGL_FORMAT_YUV_YV12_FSL для хранения компонентов Y, U и V. Мне не удалось правильно получить компоненты Y, U и V из одной текстуры EGL_FORMAT_YUV_YV12_FSL в коде фрагментного шейдера, и этот другой подход просто работает ... - person tselmeci; 14.02.2014
comment
Кстати, мне очень грустно, что на iMX53 я не могу передать адрес существующего буфера eglCreateImageKHR (). Возможно, я подойду к этой проблеме с обратной стороны: я создаю много изображений и передаю их физические адреса в VPU, чтобы поместить в него декодированный кадр. Не уверен, что это сработает (например, для VPU нужна DMA-память) ... - person tselmeci; 14.02.2014
comment
Я так и сделал. Пусть EGL выделяет буферы, а затем использует эти буферы для приема декодированных кадров от VPU. Я обнаружил, что это избавляет от необходимости использовать IPU и работает еще лучше. Загрузка процессора очень низкая. Вы можете изменить плагин Freescale gstreamer isink или модульный тест mxc-vpu-test. - person ClayMontgomery; 14.02.2014
comment
Безуспешно. Адреса буфера кадра VPU для Y, Cb и Cr берутся из eglQueryImageFSL. VPU декодирует кадр, я выполняю memcpy с виртуального адреса, предоставленного eglQueryImageFSL, в буфер кадра видео, и он снова и снова показывает один и тот же мусор. Phy-адреса начинаются с 0x00100000 (1 МБ), возможно, они смещены внутри области памяти GPU? Теперь я просто передаю эти адреса в ВПУ ... - person tselmeci; 17.02.2014
comment
Ладно, разобрался! :) Параметр ядра 'gpu_nommu' должен использоваться, поэтому физические адреса будут доступны для всей системы (а не только для графического процессора, использующего его MMU). - person tselmeci; 17.02.2014
comment
Хорошая точка зрения. У вас также должна быть последняя версия драйвера EGL от Freescale. Мой - libEGL.so 270933 16.11.2011. Вам вообще не понадобится memcpy (). - person ClayMontgomery; 17.02.2014