Класс TextureView – это объект представления, который объединяет представление с SurfaceTexture.
Рендеринг с помощью OpenGL ES
Объект TextureView упаковывает SurfaceTexture, реагируя на обратные вызовы и получая новые буферы. Когда объект TextureView получает новые буферы, TextureView
отправляет запрос на аннулирование представления и отрисовывает изображение, используя содержимое новейшего
буфера в качестве источника данных, в соответствии с текущим состоянием представления.
OpenGL ES (GLES) может выполнять рендеринг на объекте TextureView, передавая SurfaceTexture в вызов создания EGL, но это создает проблему. Когда GLES выполняет рендеринг на TextureView, поставщики и потребители BufferQueue находятся в одном потоке, что может привести к задержке или сбою вызова замены буфера. Например, если производитель отправляет несколько буферов в быстрой последовательности из потока UI, вызов замены буфера EGL должен удалить буфер из BufferQueue. Однако, поскольку потребитель и производитель находятся в одном потоке, буферов не будет, и вызов swap зависнет или завершится неудачно.
Чтобы предотвратить остановку замены буфера, BufferQueue всегда нужен доступный буфер. для вывода из очереди. Для этого BufferQueue удаляет содержимое ранее полученного буфера, когда в очередь добавляется новый буфер. Он также накладывает ограничения на минимальное и максимальное количество буферов, чтобы потребитель не мог использовать все буферы одновременно.
Выбор SurfaceView или TextureView
SurfaceView и TextureView выполняют схожие роли и являются элементами иерархии View. Однако SurfaceView и TextureView реализованы по-разному. Объект SurfaceView принимает те же параметры, что и другие представления, но
SurfaceView его содержимое при отрисовке прозрачно.
TextureView лучше обрабатывает альфа-канал и поворот, чем SurfaceView, но SurfaceView эффективнее при компоновке элементов интерфейса, наложенных на видео. Когда клиент выполняет отрисовку с объектом SurfaceView, этот объект SurfaceView предоставляет
клиенту отдельный слой композиции. SurfaceFlinger создает отдельный слой в виде аппаратного оверлея, если устройство поддерживает эту функцию. Когда клиент выполняет отрисовку с помощью TextureView, набор инструментов для создания интерфейса компонует контент объекта TextureView в иерархию View с помощью графического процессора. Обновление контента может привести к перерисовке других элементов представления, например если они расположены поверх TextureView. После завершения отрисовки представления SurfaceFlinger объединяет слой интерфейса приложения и все остальные слои, так что каждый видимый пиксель объединяется дважды.
Пример использования: видеореклама Grafika в Google Play
Grafika's Play Video включает два видеопроигрывателя: один реализован с помощью TextureView, а другой – с помощью SurfaceView. При декодировании видео кадры из MediaCodec отправляются на поверхность для TextureView и SurfaceView. Основное различие между этими реализациями заключается в том, как задается правильное соотношение сторон.
Для масштабирования SurfaceView требуется специальная реализация FrameLayout.
WindowManager должен отправить новые значения положения и размера окна в SurfaceFlinger. Чтобы масштабировать объект SurfaceTexture TextureView, необходимо настроить матрицу преобразования с помощью TextureView#setTransform().
После того как соотношение сторон будет задано правильно, обе реализации будут следовать одной и той же схеме. Когда SurfaceView или TextureView создает поверхность, код приложения включает воспроизведение. Когда пользователь нажимает Воспроизвести, запускается поток декодирования видео, а поверхность используется в качестве целевого объекта вывода. После этого код приложения ничего не делает – композицией и показом занимается SurfaceFlinger (для SurfaceView) или TextureView.
Пример использования: двойное декодирование от Grafika
В Grafika Double Decode показано, как управлять SurfaceTexture в TextureView.
В Grafika Double Decode используются два объекта 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().
Каждый декодер видео работает в отдельном потоке. Медиасервер отправляет буферы с декодированными выходными данными в SurfaceTextures, потребителям BufferQueue. Объекты TextureView выполняют рендеринг и запускаются в потоке UI.
Реализовать двойное декодирование Grafika с помощью SurfaceView сложнее, чем с помощью TextureView, поскольку объекты SurfaceView уничтожают поверхности при изменении ориентации. Кроме того, использование объектов SurfaceView добавляет два слоя, что нежелательно из-за ограничений на количество наложений, доступных на оборудовании.