ТекстураВью

Класс 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 компонует слой пользовательского интерфейса приложения и все остальные слои, так что каждый видимый пиксель комбинируется дважды.

Практический пример: Play Video от Grafika

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

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

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

Практический пример: двойное декодирование Grafika

Двойное декодирование Grafika демонстрирует манипулирование SurfaceTexture внутри TextureView.

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

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

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

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