Общие сведения об использовании Android-камеры SurfaceTexture и MediaCodec Surface

Я пытаюсь понять использование/поток графической памяти в Android и, в частности, в отношении кодирования кадров с камеры с использованием MediaCodec. Чтобы сделать это, мне нужно понять кучу терминологии/концепций графики, OpenGL и Android, которые мне непонятны. Я прочитал материал по графической архитектуре Android, кучу вопросов SO и кучу источников, но я все еще сбит с толку, прежде всего потому, что кажется, что термины имеют разные значения в разных контекстах.

Я просмотрел CameraToMpegTest с сайта Фаддена здесь. Мой конкретный вопрос заключается в том, как MediaCodec::createInputSurface() работает в сочетании с Camera::setPreviewTexture(). Кажется, создается текстура OpenGL, а затем она используется для создания Android SurfaceTexture, которую затем можно передать setPreviewTexture(). Мои конкретные вопросы:

  1. Что на самом деле делает вызов setPreviewTexture() с точки зрения того, в какой буфер памяти попадают кадры с камеры?
  2. Насколько я понимаю, текстура OpenGL — это кусок памяти, доступный графическому процессору. На Android это должно быть выделено с помощью galloc с правильными флагами использования. В описании SurfaceTexture для Android упоминается, что он позволяет «передавать изображения в заданную текстуру OpenGL»: https://developer.android.com/reference/android/graphics/SurfaceTexture.html#SurfaceTexture(int). Что делает SurfaceTexture поверх текстуры OpenGL?
  3. MediaCodec::createInputSurface() возвращает Android Surface. Насколько я понимаю, Android Surface представляет сторону производителя буферной очереди, поэтому может быть несколько буферов. В справочнике по API упоминается, что "поверхность должна быть с аппаратным ускорением API, например OpenGL ES». Как кадры, снятые камерой, попадают из SurfaceTexture в этот Surface, который поступает на вход кодировщика? Я вижу, что CameraToMpegTest каким-то образом создает EGLSurface, используя этот Surface, но, не зная много об EGL, я не понимаю эту часть.
  4. Может ли кто-нибудь прояснить использование «рендеринга»? Я вижу такие вещи, как «рендеринг на поверхность», «рендеринг на экран» среди других вариантов использования, которые, кажется, могут означать разные вещи.

Изменить: продолжение ответов mstorsjo:

  1. Я еще немного покопался в коде для SurfaceTexture и CameraClient::setPreviewTarget() в CameraService, чтобы попытаться лучше понять внутреннюю работу Camera::setPreviewTexture() и задать еще несколько вопросов. На мой первоначальный вопрос о понимании распределения памяти кажется, что SurfaceTexture создает BufferQueue, а CameraService передает связанный IGraphicBufferProducer реализации HAL платформы платформы. Затем HAL камеры может соответствующим образом установить флаги использования gralloc (например, GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_NEVER | GRALLOC_USAGE_HW_TEXTURE), а также удалить буферы из очереди из этого BufferQueue. Таким образом, буферы, в которые камера захватывает кадры, являются буферами, выделенными gralloc, с некоторыми специальными флагами использования, такими как GRALLOC_USAGE_HW_TEXTURE. Я работаю на платформах ARM с унифицированной архитектурой памяти, поэтому GPU и CPU могут обращаться к одной и той же памяти. Какое влияние окажет флаг GRALLOC_USAGE_HW_TEXTURE на распределение буфера?
  2. Часть OpenGL (ES) SurfaceTexture, по-видимому, в основном реализована как часть GLConsumer, а волшебство, похоже, кроется в updateTexImage(). Выделяются ли дополнительные буферы для текстуры OpenGL (ES) или можно использовать тот же буфер gralloc, который был заполнен камерой? Есть ли какое-то копирование памяти, которое должно произойти здесь, чтобы получить данные о пикселях камеры из буфера gralloc в текстуру OpenGL (ES)? Наверное, я не понимаю, что делает вызов updateTexImage().

person Mike Sweeney    schedule 13.03.2017    source источник


Ответы (1)


  1. Это означает, что камера предоставляет выходные кадры через непрозрачный дескриптор, а не в предоставленный пользователем буфер в адресном пространстве приложения (при использовании setPreviewCallback или setPreviewCallbackWithBuffer). Этот непрозрачный маркер, текстура, может использоваться в рисовании OpenGL.

  2. Почти. В этом случае текстура OpenGL является не физическим фрагментом памяти, а дескриптором переменного фрагмента памяти в контексте EGL. В этом случае пример кода сам по себе не выделяет текстуру и не определяет ее размер, он только создает «имя»/дескриптор текстуры с использованием glGenTextures — это просто целое число. В обычном OpenGL (ES) вы должны использовать функции OpenGL, чтобы выделить фактическое хранилище для текстуры и заполнить его содержимым. В этой настройке SurfaceTexture предоставляет API/абстракцию уровня Android для заполнения текстуры данными (т.е. выделяет для нее хранилище с правильными флагами, предоставляет размер и содержимое), что позволяет вам передавать SurfaceTexture другим классам, которые могут заполнить это с данными (либо Camera, который принимает SurfaceTexture напрямую, либо оберните класс Surface, чтобы иметь возможность использовать его в других контекстах). Это позволяет эффективно заполнять текстуру OpenGL содержимым без необходимости передавать буфер необработанных данных в процесс вашего приложения и загружать его в OpenGL.

  3. (Отвечая на пункты 3 и 4 в обратном порядке.) OpenGL (ES) — это универсальный API для рисования. В обычной/исходной настройке, рассмотрим игру, у вас будет несколько текстур для разных частей игрового контента (фоны, реквизит, актеры и т. д.), а затем с помощью API-интерфейсов OpenGL выведите их на экран. Текстуры могут быть либо более или менее просто скопированы на экран, либо обернуты вокруг 3D-объекта, построенного из треугольников. Это процесс, называемый «рендеринг», когда исходные текстуры и набор треугольников берутся и рисуются. В самых простых случаях вы будете отображать контент прямо на экране. Графический процессор обычно может выполнять такой же рендеринг и в любом другом выходном буфере. В играх принято визуализировать некоторую сцену в текстуру и использовать эту предварительно обработанную текстуру как часть окончательного рендеринга, который фактически отображается на экране.

  4. Контекст EGL создается для передачи вывода с камеры на вход кодировщика. Контекст EGL — это в основном контекст для выполнения рендеринга OpenGL. Целью рендеринга является поверхность из кодировщика. То есть любая графика, нарисованная с помощью OpenGL, попадает во входной буфер кодировщика, а не на экран. Теперь сцена, рисуемая с помощью OpenGL, может быть любой последовательностью вызовов функций OpenGL, отображающих игровую сцену в кодере. (Это то, что делает пример игрового рекордера Android Breakout.) В контексте создается дескриптор текстуры. Вместо заполнения текстуры содержимым путем загрузки изображения с диска, как при обычном рендеринге игровой графики, оно преобразуется в SurfaceTexture, чтобы позволить Camera заполнить его изображением с камеры. Класс SurfaceTexture обеспечивает обратный вызов, давая сигнал, когда Camera обновил содержимое. Когда этот обратный вызов получен, контекст EGL активируется, и один кадр визуализируется в выходной целевой объект контекста EGL (который является входом кодировщика). Сам рендеринг не делает ничего необычного, а копирует входную текстуру как есть прямо в выходной файл.

Все это может показаться довольно окольным, но это дает несколько преимуществ:

  • Фактические необработанные биты кадров камеры никогда не должны обрабатываться непосредственно в коде приложения (и, возможно, вообще никогда не должны обрабатываться в процессе приложения и адресном пространстве). Для низких разрешений это не проблема, но setPreviewCallback API становится узким местом, когда дело доходит до более высоких разрешений.
  • Вы можете настраивать цвета и делать все, что вы можете делать в OpenGL, почти бесплатно с ускорением графического процессора.
person mstorsjo    schedule 27.03.2017
comment
Я добавил несколько дополнений к своему первоначальному вопросу на основе вашего ответа. - person Mike Sweeney; 29.03.2017
comment
Я не могу много комментировать эти дополнительные вопросы - первоначальный вопрос был о том, что эти API делают с точки зрения общедоступного API Android (которую я знаю), в то время как последние вопросы касаются того, как реализована платформа, и детали, относящиеся только к делу. для разработчиков платформы. - person mstorsjo; 29.03.2017
comment
Что касается последнего вопроса, я ожидаю, что SurfaceTexture на самом деле представляет собой набор буферов, а не один, так что производитель (камера) может записать один кадр, в то время как потребитель (контекст GL) может продолжать рендеринг предыдущего кадра. В updateTexImage() я бы ожидал, что он поменяет местами эти буферы. Это только мое впечатление и предположение с точки зрения пользователя, но я не знаю фактической реализации. - person mstorsjo; 29.03.2017