Как получить доступ к изображению EGL прямо с Android Surface для использования в видеодекодере MediaCodec?

В настоящее время я пишу приложение для Android, в котором мне нужно кэшировать видеокадры, чтобы я мог легко перемещаться вперед и назад с минимальной задержкой или без нее.

Прямо сейчас я разрешаю Android декодировать видеокадр, предоставляя Surface для вызова Configure объекта MediaCodec и вызывая releaseOutputBuffer с флагом рендеринга, установленным на true.

Единственный способ, который я нашел для доступа к декодированным данным поверхности (помимо декодирования возвращенного байтового буфера, формат которого, по-видимому, зависит от устройства), - это вызвать updateTeximage на SurfaceTexture, связанном с поверхностью, прикрепить его к цели GL_TEXTURE_EXTERNAL_OES и отобразить в GL_TEXTURE2D Целевая текстура, которую я создал сам, чтобы кэшировать.

Я хотел бы оптимизировать этот процесс кеширования и иметь возможность декодировать кадры в другом потоке. Используя мой текущий метод, это означает, что мне придется создать еще один контекст EGL для видеодекодера, поделиться контекстом и т. Д.

У меня вопрос: Можно ли получить доступ к EGL-изображению или собственным данным буфера, связанным с Surface, без вызова updateTexImage?

Таким образом я мог кэшировать изображение egl (которое не требует контекста EGL согласно EGL_ANDROID_image_native_buffer). Это также будет кешировать в формате YUV, который будет намного более эффективным с точки зрения хранения, чем необработанные текстуры RGB, которые я кэширую сейчас.


person Nico Cornelis    schedule 12.09.2013    source источник


Ответы (1)


Короткий ответ: нет.

Более длинный ответ: Surface инкапсулирует очередь буферов. (Изменить: система теперь подробно объясняется здесь.) Когда вы вызываете updateTexImage(), если доступен новый фрейм данных, буфер в голове удаляется, и следующий в очереди становится текущим. Вызов updateTexImage() необходим для просмотра следующих друг за другом кадров; нет механизма проверки буферов не в голове.

SurfaceTexture является оболочкой для экземпляра GLConsumer. Этот потребитель требует, чтобы производитель (видеодекодер) сгенерировал данные в формате, который можно использовать в качестве «аппаратной текстуры», то есть чего-то, что реализация GL устройства может понять. Это может быть или не быть YUV. Более того, потребитель не требует, чтобы буфер был доступен «программному обеспечению», что означает, что вы не можете предполагать, что можете получить доступ к данным напрямую - вам нужно использовать GLES. (См. заголовок gralloc для получения полного списка флагов.)

Что было бы неплохо, так это возможность скопировать буфер из заголовка BufferQueue в отдельную структуру данных (BufferArrayList?) Без преобразования формата, но в настоящее время нет такого механизма (Android 4.3). Я не знаю лучшего способа сделать это, чем то, что вы описываете (общие контексты EGL и т. Д.).

Обновление: у моего товарища по офису было предложение: использовать шейдер для рендеринга буфера в две текстуры, одну для Y и для CbCr (в GLES 3 вы можете использовать текстуру RG). Это сохраняет все манипуляции в GLES без расширения до полного RGB. Внутренне он преобразует выход MediaCodec в RGB и дважды обрабатывает его, но это, вероятно, дешевле, чем копировать его в пользовательское пространство и делать это самостоятельно на процессоре.

person fadden    schedule 12.09.2013
comment
Прежде всего, большое спасибо за подробный ответ. Я провел еще несколько экспериментов и попытался получить доступ к буферам напрямую. Я обнаружил, что на моем устройстве формат вывода видео - 0x7fa30c03, который не является стандартным форматом в AOSP. После некоторой ручной переустановки я смог сохранить кеш-фрейм YUV420 для каждого декодированного фрейма. Может есть независимая от устройства функция, которая конвертирует байтовый буфер в обычный yuv? Я пробовал использовать класс YuvImage, но похоже, что формат 0x7fa30c03 не реализован для этого класса. Еще раз спасибо за ответ. - person Nico Cornelis; 13.09.2013
comment
Вы твердо находитесь на территории, не относящейся к портативным устройствам. 0x7fa30c03 - это проприетарный OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka (объявленный в native / include / media / openmax / OMX_IVCommon.h); Наиболее близким к библиотеке декодера является зависящая от поставщика /system/lib/libI420colorconvert.so, используемая библиотекой видеоредактора (android.googlesource.com/platform/frameworks/av / + /). Большая часть YUV-материала во фреймворке предназначена для использования с камерой, которая работает лучше, чем видеодекодер, когда дело касается форматов буферов. - person fadden; 13.09.2013
comment
Еще раз спасибо за быстрый ответ. Думаю, я пока буду придерживаться пути GLES. Я просмотрел код AOSP еще раз и обнаружил, что образ EGL создается с использованием расширения EGL_ANDROID_image_native_buffer. Я не очень хорошо знаком с родными буферами. Может быть, можно кэшировать байтовые буферы, возвращаемые медиакодеком, и обернуть их в структуру, которую можно передать как EGLClientBuffer в eglCreateImage, когда я захочу получить ее из кеша? - person Nico Cornelis; 13.09.2013
comment
(Ответ слегка обновлен.) Одна из особенностей медиа / графических драйверов заключается в том, что они не гарантируют симметричности: то, что он скопировал OMX 0x7fa30c03 в ваш ByteBuffer, не означает, что существует соответствующий формат буфера gralloc. Может быть, он выполнил прямой memcpy, может быть, он изменил выравнивание или шаг. Заполнение буфера может сработать для данного устройства и версии программного обеспечения, но в лучшем случае оно будет хрупким. Это может быть интересно: snorp.net/2011/12/16/ android-direct-texture.html (опять же, без использования общедоступных API). Если вы хотите копнуть глубже, вы можете попробовать заменить GLConsumer. - person fadden; 13.09.2013