Просмотр текстуры

Класс TextureView — это объект представления, который объединяет представление с SurfaceTexture.

Рендеринг с помощью OpenGL ES

Объект TextureView является оболочкой для SurfaceTexture, отвечая на обратные вызовы и получая новые буферы. Когда TextureView получает новые буферы, TextureView отправляет запрос на аннулирование представления и выполняет отрисовку, используя содержимое последнего буфера в качестве источника данных, выполняя рендеринг в соответствии с состоянием представления.

OpenGL ES (GLES) может рендерить на TextureView, передавая SurfaceTexture в вызов создания EGL, но это создаёт проблему. При рендеринге GLES на TextureView производители и потребители BufferQueue находятся в одном потоке, что может привести к остановке или сбою вызова обмена буферами. Например, если производитель быстро отправляет несколько буферов подряд из потока пользовательского интерфейса, вызов обмена буферами EGL должен извлечь буфер из очереди BufferQueue. Однако, поскольку потребитель и производитель находятся в одном потоке, доступных буферов не будет, и вызов обмена зависнет или завершится сбоем.

Чтобы предотвратить задержки при замене буферов, BufferQueue всегда должен быть доступен буфер для извлечения из очереди. Для этого BufferQueue сбрасывает содержимое ранее полученного буфера при постановке нового буфера в очередь. Также накладываются ограничения на минимальное и максимальное количество буферов, чтобы предотвратить одновременное использование всех буферов потребителем.

Выбор SurfaceView или TextureView

SurfaceView и TextureView выполняют схожие роли и оба являются элементами иерархии представлений. Однако SurfaceView и TextureView имеют разные реализации. SurfaceView принимает те же параметры, что и другие представления, но содержимое SurfaceView прозрачно при визуализации.

TextureView лучше обрабатывает альфа-канал и поворот, чем SurfaceView, но SurfaceView обладает более высокой производительностью при компоновке элементов пользовательского интерфейса поверх видео. При рендеринге клиентом с помощью SurfaceView, SurfaceView предоставляет клиенту отдельный слой композиции. SurfaceFlinger компонует отдельный слой как аппаратное наложение, если это поддерживается устройством. При рендеринге клиентом с помощью TextureView, инструментарий пользовательского интерфейса компонует содержимое TextureView в иерархию представлений с помощью графического процессора. Обновление содержимого может привести к перерисовке других элементов представлений, например, если другие представления расположены поверх TextureView. После завершения рендеринга представления SurfaceFlinger компонует слой пользовательского интерфейса приложения и все остальные слои, так что каждый видимый пиксель компонуется дважды.

Пример: игровое видео Grafika

Функция Play Video от Grafika включает в себя два видеоплеера: один реализован на основе TextureView, а другой — на основе SurfaceView. В процессе декодирования видео кадры из MediaCodec передаются на поверхность для TextureView и SurfaceView. Главное различие между реализациями заключается в шагах, необходимых для отображения правильного соотношения сторон.

Масштабирование SurfaceView требует специальной реализации FrameLayout. WindowManager должен передавать новое положение окна и новые значения размера в SurfaceFlinger. Масштабирование SurfaceTexture элемента TextureView требует настройки матрицы преобразования с помощью TextureView#setTransform() .

После установки правильного соотношения сторон обе реализации следуют одному и тому же шаблону. Когда SurfaceView/TextureView создаёт поверхность, код приложения включает воспроизведение. Когда пользователь нажимает кнопку воспроизведения , запускается поток декодирования видео, используя поверхность в качестве конечного объекта вывода. После этого код приложения не выполняет никаких действий — компоновка и отображение обрабатываются SurfaceFlinger (для SurfaceView) или TextureView.

Пример: двойное декодирование Grafika

Double Decode от Grafika демонстрирует манипуляцию SurfaceTexture внутри TextureView.

Функция Double Decode в Grafika использует пару объектов TextureView для отображения двух видео, воспроизводимых рядом, имитируя приложение для видеоконференций. При изменении ориентации экрана и перезапуске приложения декодеры MediaCodec не останавливаются, имитируя воспроизведение видеопотока в реальном времени. Для повышения эффективности клиент должен поддерживать поверхность в активном состоянии. Поверхность — это дескриптор интерфейса производителя в BufferQueue объекта SurfaceTexture. Поскольку TextureView управляет SurfaceTexture, клиенту необходимо поддерживать SurfaceTexture в активном состоянии, чтобы поддерживать поверхность в активном состоянии.

Чтобы поддерживать SurfaceTexture в активном состоянии, метод Double Decode Grafika получает ссылки на SurfaceTexture из объектов TextureView и сохраняет их в статическом поле. Затем метод Double Decode Grafika возвращает false из TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() , чтобы предотвратить уничтожение SurfaceTexture. Затем TextureView передаёт SurfaceTexture в onSurfaceTextureDestroyed() , который может сохраняться при изменении конфигурации активности, передаваемой клиентом новому TextureView через setSurfaceTexture() .

Отдельные потоки управляют каждым видеодекодером. Mediaserver отправляет буферы с декодированными данными объектам SurfaceTextures, потребителям BufferQueue. Объекты TextureView выполняют рендеринг и выполняются в потоке пользовательского интерфейса.

Реализация двойного декодирования Grafika с помощью SurfaceView сложнее, чем с помощью TextureView, поскольку объекты SurfaceView разрушают поверхности при изменении ориентации. Кроме того, использование объектов SurfaceView добавляет два слоя, что неидеально из-за ограничений на количество доступных на аппаратном уровне наложений.