Android图形渲染框架包括:Gralloc分配缓冲区,BufferQueue作为缓冲队列连接生产者与消费者,可以使用TextureView、SurfaceView和GLSurfaceView组件进行渲染。最终是渲染到Surface上,通过SurfaceFlinger或者HardwareComposer硬件混合渲染器合成到WindowManager提供的屏幕上,由VSync提供帧同步。
Android图形组件
图形组件包括图像生产者与图像消费者,具体数据流链路如下图1所示:
图1—Android图形框架
1、图像生产者
图像生产者可以是生成图形缓冲区以供消费的任何内容。比如:Canvas、视频解码器等。
2、图像消费者
图像消费者最常见的是surfaceFlinger,该系统服务会消费当前可见的Surface,并使用WindowManager提供的信息将它们合成到屏幕。SurfaceFlinger使用OpenGL或Hardware Composer来合成一组Surface。其他OpenGL应用也可以消费图像流,比如相机应用消费预览图像流。非OpenGL应用也可以作为消费者,比如ImageReader。
3、硬件混合渲染器
硬件混合渲染器HWC是显示系统的硬件抽象实现。SurfaceFlinger可以将某些合成工作委托给HWC,以分担OpenGL和CPU的工作量。
4、数据流
关于Android图形管道的描述,如下图2所示。主屏幕、状态栏和系统桌面作为图像生产者,经过BufferQueue传输,由SurfaceFlinger合成。
图2—Android图形管道
图形缓冲区
1、BufferQueue
BufferQueue是Android图形组件的粘合剂。作为缓冲队列,可以调节从生产方到消费方的周期。关于BufferQueue通信过程,如下图所示:
图3—BufferQueue通信过程
BufferQueue是将缓冲区池与队列结合的数据结构,使用Binder在进程间传递数据。生产方接口为IGraphicBufferProducer。BufferQueue有三种不同工作模式:
类同步模式:BufferQueue默认在类同步模式下运行。在该模式下,每个来自生产方的缓冲区都会在消费方消耗,不会丢弃任何缓冲区。如果生产速度快于消费速度,会阻塞等待释放缓冲区。
非阻塞模式:BufferQueue在非阻塞模式下,如果生产速度快于消费速度导致缓冲区满,会抛出异常而不是等待释放缓冲区。此模式也不会丢弃任何缓冲区。这有助于应用程序无法理解图形框架复杂依赖关系导致潜在死锁。
丢弃模式:如果缓冲区已满,会丢弃后面的缓冲区而不是抛出异常。比如,如果需要对纹理视图进行快速渲染绘制,会选择性丢弃缓冲区。
2、Gralloc
Gralloc内存分配器负责图形缓冲区分配,通过两个特定于供应商的 HIDL 接口来进行实现( hardware/interfaces/graphics/allocator/
和 hardware/interfaces/graphics/mapper/
)。
Gralloc 分配器 HAL层 hardware/libhardware/include/hardware/gralloc.h
通过用法标志执行缓冲区分配。用法标志包括以下属性:
- 从软件 (CPU) 访问内存的频率
- 从硬件 (GPU) 访问内存的频率
- 是否将内存用作 OpenGL ES (GLES) 纹理
- 视频编码器是否会使用内存
如果生产方的缓冲区格式指定 RGBA_8888
像素,并且生产方指明将从软件访问缓冲区,则 Gralloc 将按照 R-G-B-A 的顺序为每个像素创建 4 个字节的缓冲区。如果情况相反,生产方指明仅从硬件访问其缓冲区且缓冲区作为 GLES 纹理,Gralloc 可以执行 GLES 驱动程序所需的任何操作(比如 BGRA 排序、非线性布局和替换颜色格式)。允许硬件使用其首选格式可以提高性能。Gralloc 返回的句柄可以通过 Binder 在进程之间进行传递。
3、受保护的缓冲区
Gralloc 使用标记 GRALLOC_USAGE_PROTECTED
允许仅通过受硬件保护的路径显示图形缓冲区。这些叠加平面是显示 DRM 内容的唯一途径(SurfaceFlinger 或 OpenGL ES 驱动程序无法访问受 DRM 保护的缓冲区)。
受 DRM 保护的视频只能在叠加平面上呈现。支持受保护内容的视频播放器必须使用 SurfaceView 实现。在不受保护的硬件上运行的软件无法读取或写入缓冲区;受硬件保护的路径必须显示在硬件混合渲染器HWC叠加层上(也就是说,如果硬件混合渲染器切换到 OpenGL ES 合成,受保护的视频将从屏幕中消失)。
渲染组件
1、SurfaceView
SurfaceView 结合 Surface 和 View。SurfaceView 的 View 组件由 SurfaceFlinger合成,从而可以通过单独的线程渲染,并与应用界面渲染隔离。创建Surface时进行打洞,在attachToWindow回调请求设置透明区域requestTransparentRegion,从而不遮挡父类Window。另外,SurfaceView使用到双缓冲,即前台后台交替。
2、GLSurfaceView
GLSurfaceView结合SurfaceView与GLThread,提供单独渲染线程,管理eglContext上下文,使用OpenGL进行渲染,有两种渲染模式:RENDERMODE_CONTINUOUSLY(连续渲染)和RENDERMODE_WHEN_DIRTY(调用requestRender才进行渲染)。
3、SurfaceTexture
SurfaceTexture 将 Surface 和 GLES 纹理相结合来创建 BufferQueue。当生产方将新的缓冲区放入队列时,onFrameAvailable()
回调会通知应用。然后,应用调用 updateTexImage()
,这会释放先前占用的缓冲区,从队列中获取新缓冲区并执行 EGL 调用,从而使 GLES 可将此缓冲区作为外部纹理使用。Android 7.0 添加了对安全纹理视频播放的支持,以便对受保护的视频内容进行 GPU 后处理。其中,外部 GLES 纹理 (GL_TEXTURE_EXTERNAL_OES
) 与传统 GLES 纹理 (GL_TEXTURE_2D
) 的区别如下:
- 外部纹理直接在从
BufferQueue
接收的数据中渲染纹理多边形。 - 外部纹理渲染程序的配置与传统的 GLES 纹理渲染程序不同。
- 外部纹理不一定可以执行所有传统的 GLES 纹理活动。
外部纹理的主要优势是它们能够直接从 BufferQueue
数据进行渲染。SurfaceTexture
实例在为外部纹理创建 BufferQueue
实例时将使用方用法标志设置为 GRALLOC_USAGE_HW_TEXTURE
,以确保 GLES 可以识别该缓冲区中的数据。
使用到SurfaceTexture的典型案例是Grafika 的连续拍摄,涉及从设备相机录制帧并在屏幕上显示帧。 要录制帧,请使用 MediaCodec 类的 createInputSurface
方法创建 Surface,并将该 Surface 传递给相机。要显示帧,请创建 SurfaceView
的实例并将 Surface 传递给 setPreviewDisplay
。相机的数据传输路径如下图4所示:
图4—Grafika连续拍摄
4、TextureView
TextureView结合View和SurfaceTexture。TextureView 对 SurfaceTexture 进行包装,并负责响应回调以及获取新的缓冲区。TextureView 优点:具有View属性,支持旋转、缩放、移动、Alpha;缺点:缓存一定数量帧数据,延时100ms左右。SurfaceView 优点:性能好、占用内存少、延时低;缺点:不具有View属性。
SurfaceFlinger与WindowManager
1、SurfaceFlinger
SurfaceFlinger 接受两种缓冲区:BufferQueue和SurfaceControl,对它们进行合成,然后发送到屏幕。WindowManager 为 SurfaceFlinger 提供缓冲区和窗口元数据。SurfaceFlinger 可通过两种方式接受缓冲区:通过 BufferQueue 和 SurfaceControl。当应用进入前台时,它会从 WindowManager 请求缓冲区。
在屏幕处于两次刷新之间时,屏幕会向 SurfaceFlinger 发送 VSYNC 信号。VSYNC 信号表明可对屏幕进行刷新而不会产生撕裂。当 SurfaceFlinger 接收到 VSYNC 信号后,SurfaceFlinger 会遍历其层列表,以查找新的缓冲区。如果 SurfaceFlinger 找到新的缓冲区,SurfaceFlinger 会获取缓冲区;否则,SurfaceFlinger 会继续使用上一次获取的那个缓冲区。在收集可见层的所有缓冲区之后,便会询问硬件混合渲染器 (HWC) 应如何进行合成。如果 HWC 将层合成类型标记为客户端合成,则 SurfaceFlinger 将合成这些层。然后,SurfaceFlinger 会将输出缓冲区传递给 HWC。
2、WindowManager
WindowManager 会控制窗口对象,它们是用于容纳视图对象的容器。窗口对象始终由 Surface 对象提供支持。WindowManager 会监督生命周期、输入和聚焦事件、屏幕方向、转换、动画、位置、变形、Z 轴顺序等。
可以到GitHub一起学习音视频:GitHub - xufuji456/FFmpegAndroid: android端基于FFmpeg实现音频剪切、拼接、转码、编解码;视频剪切、水印、截图、转码、编解码、转Gif动图;音视频合成与分离,配音;音视频解码、同步与播放;FFmpeg本地推流、H264与RTMP实时推流直播;FFmpeg滤镜:素描、色彩平衡、hue、lut、模糊、九宫格等;歌词解析与显示