全栈开发知识库

19.相机预览

相机预览可以使用OpenGL方便的实现相机美颜、滤镜、塑性以及一些特效。

相机的预览实现一般有2种方式:

  • 一种是基于Android原生SurfaceTexture的纯GPU实现方式
  • 一种是通过相机的预览回调接口获取帧的YUV数据,利用CPU算法处理完成之后,传入显存,再利用GPU实现YUV转RGBA进行渲染,即CPU + GPU的实现方式

基于Android原生SurfaceTexture的纯GPU实现方式,相机可以使用SurfaceTexture作为预览载体。
SurfaceTexture可以来自于GLSurfaceView、TextureView或SurfaceView这些独立拥有Surface的封装类,也可以自定义实现。

作为预览载体的SurfaceTexture绑定的纹理需要是OES纹理。

使用OES纹理后,我们不需要在片段着色器中自己做YUV to RGBA的转换,因为OES纹理可以直接接收YUV数据或者直接输出YUV数据。

YUV

YUV和RGBA是两种不同的颜色编码格式,主要区别于在数据组织和应用场景。

  • YUV主要用于视频压缩、传输(例如H264/HEVC),优势为亮度和色度分离适合压缩(人眼对亮度更敏感,色度可降采样)。 YUV为视频压缩设计,牺牲直接显示兼容性换取高压缩率。

  • RGBA主要用于图像显示、图形渲染(如屏幕绘制),每个像素的RGBA值连续存储,可直接兼容显示设备。
    RGBA为显示优化,直接支持像素渲染和透明通道。

OES与YUV的关系

OES纹理(GL_TEXTURE_EXTERNAL_OES)是OpenGL ES的一种扩展纹理类型,主要用于直接操作外部设备(如摄像头、视频解码器)的纹理数据。它与YUV的关系如下:

  • 视频解码后的YUV数据: 移动端视频解码器(如Android的MediaCodec)输出的YUV帧数据,通常通过OES纹理直接传递给GPU,避免CPU到GPU的数据拷贝。

使用OES纹理需要修改片段着色器,在着色器脚本的头部增加扩展纹理的声明:

#extension GL_OES_EGL_image_external : require

并且纹理采样器不再使用sample2D,需要换成samplerExternalOES作为纹理采样器。