Класс 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 создают поверхность, код приложения включает воспроизведение. Когда пользователь нажимает кнопку воспроизведения , запускается поток декодирования видео, в котором поверхность выступает в качестве целевого объекта вывода. После этого код приложения ничего не делает — композиция и отображение обрабатываются SurfaceFlinger (для SurfaceView ) или TextureView .
Пример из практики: технология Double Decode от 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() .
Для каждого видеодекодера используются отдельные потоки. Медиасервер отправляет буферы с декодированным результатом объектам SurfaceTextures, которые являются потребителями BufferQueue. Объекты TextureView выполняют рендеринг и работают в потоке пользовательского интерфейса.
Реализация двойного декодирования Grafika с использованием SurfaceView сложнее, чем с TextureView , поскольку объекты SurfaceView разрушают поверхности при изменении ориентации. Кроме того, использование объектов SurfaceView добавляет два слоя, что не идеально из-за ограничений на количество доступных на аппаратном обеспечении наложений.