SurfaceTexture
是 surface 和 OpenGL ES (GLES) 纹理的组合。SurfaceTexture
实例用于提供输出到 GLES 纹理的 surface。
SurfaceTexture
包含 BufferQueue
的一个实例,应用是该实例的消费者。当生产者将新缓冲区排队时,onFrameAvailable()
回调会通知应用。然后,应用调用 updateTexImage()
,该调用会释放先前持有的缓冲区,从队列中获取新缓冲区,并进行 EGL 调用以使缓冲区作为外部纹理提供给 GLES。
外部 GLES 纹理
外部 GLES 纹理 (GL_TEXTURE_EXTERNAL_OES
) 与传统 GLES 纹理 (GL_TEXTURE_2D
) 在以下方面有所不同
- 外部纹理直接从
BufferQueue
收到的数据渲染纹理多边形。 - 外部纹理渲染器的配置方式与传统 GLES 纹理渲染器不同。
- 外部纹理无法执行所有传统 GLES 纹理活动。
外部纹理的主要优势在于它们能够直接从 BufferQueue
数据进行渲染。当 SurfaceTexture
实例为外部纹理创建 BufferQueue
实例时,会将消费者使用标志设置为 GRALLOC_USAGE_HW_TEXTURE
,以确保缓冲区中的数据可被 GLES 识别。
由于 SurfaceTexture
实例与 EGL 上下文交互,因此应用只能在其方法被调用的线程上,在拥有纹理的 EGL 上下文为当前上下文时调用这些方法。有关详情,请参阅 SurfaceTexture
类文档。
时间戳和转换
SurfaceTexture
实例包括 getTimeStamp()
方法(用于检索时间戳)和 getTransformMatrix()
方法(用于检索转换矩阵)。调用 updateTexImage()
会同时设置时间戳和转换矩阵。BufferQueue
传递的每个缓冲区都包含转换参数和时间戳。
转换参数对于提高效率非常有用。在某些情况下,源数据对于消费者而言可能方向不正确。与其在将数据发送给消费者之前旋转数据,不如以其原始方向发送数据,并使用转换来更正它。当使用数据时,转换矩阵可以与其他转换合并,从而最大限度地减少开销。
时间戳对于时间相关的缓冲区源非常有用。例如,当 setPreviewTexture()
将生产者接口连接到相机的输出时,来自相机的帧可用于创建视频。每个帧都需要具有帧捕获时的呈现时间戳,而不是应用接收帧时的时间戳。相机代码设置缓冲区提供的时间戳,从而产生更一致的时间戳序列。
案例研究:Grafika 的连续捕获
Grafika 的连续捕获 涉及录制设备相机中的帧,并将这些帧显示在屏幕上。要录制帧,请使用 MediaCodec 类的 createInputSurface()
方法创建一个 surface,并将该 surface 传递给相机。要显示帧,请创建 SurfaceView
的一个实例,并将该 surface 传递给 setPreviewDisplay()
。请注意,同时录制和显示帧是一个更复杂的过程。
持续捕获 活动会在录制视频的同时显示来自摄像头的视频。在这种情况下,编码后的视频会被写入内存中的循环缓冲区,可以随时保存到磁盘。
此流程涉及三个缓冲区队列
App
— 应用使用SurfaceTexture
实例接收来自摄像头的帧,并将其转换为外部 GLES 纹理。SurfaceFlinger
— 应用声明一个SurfaceView
实例来显示帧。MediaServer
— 配置一个带有输入界面的MediaCodec
编码器以创建视频。
在下图中,箭头指示来自摄像头的数据传播。BufferQueue
实例以彩色显示(生产者为青色,消费者为绿色)。

图 1. Grafika 的持续捕获活动
编码后的 H.264 视频进入应用进程 RAM 中的循环缓冲区。当用户按下捕获按钮时,MediaMuxer
类会将编码后的视频写入磁盘上的 MP4 文件。
所有 BufferQueue
实例都在应用中的单个 EGL 上下文中处理,而 GLES 操作在 UI 线程上执行。编码数据的处理(管理循环缓冲区并将其写入磁盘)在单独的线程上完成。
SurfaceView
类时,surfaceCreated()
回调会为显示和视频编码器创建 EGLContext
和 EGLSurface
实例。当新帧到达时,SurfaceTexture
执行四项活动- 获取帧。
- 使帧作为 GLES 纹理可用。
- 使用 GLES 命令渲染帧。
- 为每个
EGLSurface
实例转发变换和时间戳。
然后,编码器线程从 MediaCodec
中拉取编码后的输出并将其存储在内存中。
安全纹理视频播放
Android 支持对受保护的视频内容进行 GPU 后处理。这使应用可以将 GPU 用于复杂的非线性视频效果(例如扭曲)、将受保护的视频内容映射到纹理以在通用图形场景中使用(例如,使用 GLES)以及虚拟现实 (VR)。

图 2. 安全纹理视频播放
通过以下两个扩展启用支持
- EGL 扩展 — (
EGL_EXT_protected_content
) 启用受保护的 GL 上下文和表面的创建,它们都可以对受保护的内容进行操作。 - GLES 扩展 — (
GL_EXT_protected_textures
) 启用将纹理标记为受保护,以便可以将它们用作帧缓冲区纹理附件。
即使窗口的表面不排队到 SurfaceFlinger
,Android 也能使 SurfaceTexture
和 ACodec (libstagefright.so
) 发送受保护的内容,并提供受保护的视频表面以在受保护的上下文中使用。这是通过在受保护的上下文中创建的表面上设置受保护的消费者位 (GRALLOC_USAGE_PROTECTED
) 来完成的(由 ACodec 验证)。
安全纹理视频播放为 OpenGL ES 环境中强大的 DRM 实现奠定了基础。如果没有强大的 DRM 实现(例如 Widevine Level 1),许多内容提供商不允许在 OpenGL ES 环境中渲染其高价值内容,从而阻止了重要的 VR 用例,例如在 VR 中观看受 DRM 保护的内容。
AOSP 包含用于安全纹理视频播放的框架代码。驱动程序支持取决于 OEM。设备实现者必须实现 EGL_EXT_protected_content
和 GL_EXT_protected_textures extensions
。当使用您自己的编解码器库(以替换 libstagefright
)时,请注意 /frameworks/av/media/libstagefright/SurfaceUtils.cpp
中的更改,这些更改允许将标记有 GRALLOC_USAGE_PROTECTED
的缓冲区发送到 ANativeWindow
(即使 ANativeWindow
不直接排队到窗口合成器),只要消费者使用位包含 GRALLOC_USAGE_PROTECTED
即可。有关实现扩展的详细文档,请参阅 Khronos 注册表 (EGL_EXT_protected_content
和 GL_EXT_protected_textures
)。