Класс TextureView — это объект представления, который объединяет представление с SurfaceTexture.
Рендеринг с помощью OpenGL ES
Объект TextViewView оборачивает SurfaceTexture, отвечая на обратные вызовы и получая новые буферы. Когда TextView получает новые буферы, он выдает запрос на аннулирование представления и рисует, используя содержимое новейшего буфера в качестве источника данных, рендеринг происходит там, где и как бы то ни было, состояние представления указывает на это.
OpenGL ES (GLES) может выполнять рендеринг в TextView, передавая SurfaceTexture в вызов создания EGL, но это создает проблему. Когда GLES выполняет рендеринг в TextView, производители и потребители BufferQueue находятся в одном потоке, что может привести к остановке или сбою вызова замены буфера. Например, если производитель отправляет несколько буферов подряд из потока пользовательского интерфейса, вызову замены буфера EGL необходимо исключить буфер из очереди BufferQueue. Однако, поскольку потребитель и производитель находятся в одном потоке, доступных буферов не будет, и вызов подкачки зависнет или завершится сбоем.
Чтобы гарантировать, что замена буфера не остановится, BufferQueue всегда нужен буфер, доступный для удаления из очереди. Чтобы реализовать это, BufferQueue отбрасывает содержимое ранее полученного буфера, когда новый буфер ставится в очередь, и накладывает ограничения на минимальное и максимальное количество буферов, чтобы предотвратить использование потребителем всех буферов одновременно.
Выбор SurfaceView или TextureView
SurfaceView и TextureView выполняют схожие роли и являются членами иерархии представлений. Однако SurfaceView и TextureView имеют разные реализации. SurfaceView принимает те же параметры, что и другие представления, но содержимое SurfaceView прозрачно при визуализации.
TextView имеет лучшую обработку альфа-канала и поворота, чем SurfaceView, но SurfaceView имеет преимущества в производительности при компоновке элементов пользовательского интерфейса, наложенных на видео. Когда клиент выполняет рендеринг с помощью SurfaceView, SurfaceView предоставляет клиенту отдельный слой композиции. SurfaceFlinger создает отдельный слой как аппаратное наложение, если оно поддерживается устройством. Когда клиент выполняет рендеринг с помощью TextView, набор инструментов пользовательского интерфейса объединяет содержимое TextureView в иерархию представлений с помощью графического процессора. Обновления содержимого могут привести к перерисовке других элементов представления, например, если другие представления расположены поверх TextViewView. После завершения рендеринга представления SurfaceFlinger объединяет слой пользовательского интерфейса приложения и все остальные слои, так что каждый видимый пиксель объединяется дважды.
Практический пример: Play Video от Grafika
Play Video Grafika включает в себя пару видеоплееров, один из которых реализован с помощью TextureView, а другой — с помощью SurfaceView. Часть действия, связанная с декодированием видео, отправляет кадры из MediaCodec на поверхность как для TextureView, так и для SurfaceView. Самая большая разница между реализациями — это шаги, необходимые для представления правильного соотношения сторон.
Для масштабирования SurfaceView требуется специальная реализация FrameLayout. WindowManager необходимо отправить новое положение окна и новые значения размера в SurfaceFlinger. Масштабирование SurfaceTexture в TextView требует настройки матрицы преобразования с помощью TextureView#setTransform()
.
После представления правильного соотношения сторон обе реализации следуют одному и тому же шаблону. Когда SurfaceView/TextureView создает поверхность, код приложения включает воспроизведение. Когда пользователь нажимает кнопку воспроизведения , он запускает поток декодирования видео, в котором поверхность является целью вывода. После этого код приложения ничего не делает — компоновка и отображение обрабатываются SurfaceFlinger (для SurfaceView) или TextureView.
Практический пример: двойное декодирование Grafika
Двойное декодирование Grafika демонстрирует манипулирование SurfaceTexture внутри TextureView.
Двойное декодирование Grafika использует пару объектов TextViewView для отображения двух видео, воспроизводимых рядом, имитируя приложение для видеоконференций. При изменении ориентации экрана и возобновлении активности декодеры MediaCodec не останавливаются, имитируя воспроизведение видеопотока в реальном времени. Чтобы повысить эффективность, клиент должен поддерживать поверхность в рабочем состоянии. Поверхность является дескриптором интерфейса производителя в BufferQueue SurfaceTexture. Поскольку объект TextView управляет SurfaceTexture, клиенту необходимо поддерживать работоспособность SurfaceTexture, чтобы поверхность оставалась активной.
Чтобы сохранить SurfaceTexture в рабочем состоянии, двойное декодирование Grafika получает ссылки на SurfaceTextures из объектов TextureView и сохраняет их в статическом поле. Затем двойное декодирование Grafika возвращает false
из TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed()
, чтобы предотвратить разрушение SurfaceTexture. Затем TextureView передает SurfaceTexture в onSurfaceTextureDestroyed()
, который может поддерживаться при изменении конфигурации активности, которую клиент передает в новый TextureView через setSurfaceTexture()
.
Отдельные потоки управляют каждым видеодекодером. Mediaserver отправляет буферы с декодированными выводами SurfaceTextures, потребителям BufferQueue. Объекты TextureView выполняют рендеринг и выполняются в потоке пользовательского интерфейса.
Реализация двойного декодирования Grafika с помощью SurfaceView сложнее, чем реализация с помощью TextureView, поскольку объекты SurfaceView разрушают поверхности во время изменения ориентации. Кроме того, использование объектов SurfaceView добавляет два слоя, что не идеально из-за ограничений на количество наложений, доступных на оборудовании.