这个公众号会路线图式的遍历分享音视频技术:音视频基础 → 音视频工具 → 音视频工程示例 → 音视频工业实战。关注一下成本不高,错过干货损失不小 ↓↓↓
随着相机采集越来越多的与滤镜、美颜、特效等前处理流程结合,关注采集预览的性能变得十分重要。采集预览阶段表示打开相机,但是还没开始进行直播推流或者视频录制的阶段,但这时候一般也开始进行滤镜、美颜、特效前处理了。在这个阶段我们可以关注以下性能指标:
- 相机打开成功率,相机成功打开没有发生错误的比例。
- 相机打开速度相关:
- 相机打开平均时长,从相机打开到第一帧采集到的图像被预览模块渲染出来的平均时长。
- 相机打开秒开率,从相机打开到第一帧采集到的图像被预览模块渲染出来的时长小于 1s 的比例。
- 采集预览流畅度相关:
- 预览平均采集帧率,预览阶段相机采集的帧率。
- 预览平均识别帧率,预览阶段相机采集后经过前处理后的帧率。
- 预览平均渲染帧率,预览阶段前处理完成后预览渲染帧率。
此外,我们还需要关注:采集画面质量、采集内存等指标的优化。
1、相机打开成功率优化
1.1、权限检测与引导
手机设备上的应用要打开相机是需要向用户申请权限的,所以优化权限申请的交互及文案提升权限通过率是优化相机打开成功率的一个方案。
这里有几个建议:
- 1)只在确实需要使用相机的场景才向用户申请相机权限;
- 2)提前向用户说明申请相机权限对后续功能的必要性;
- 3)正式申请权限前先测试用户意愿。在有些手机系统的设计中,如果用户拒绝了你的权限申请,那么下一次需要到手机设置中找到对应 App 的隐私权限选项才能打开权限,这里的路径很深。因此,可以在真正申请权限前,弹出选择框让用户选择是否同意授予权限,如果用户同意才正式弹出权限申请窗口来申请权限,避免因为用户此次拒绝了权限申请而加大了后续获取权限的难度和成本。
1.2、错误重试与监测
如果确实遇到相机打开报错,可以重试相机打开流程。
另外,需要统计相机打开错误的细分错误码,这样就能更好的定位相机打开失败的原因进行针对性的优化。
2、相机打开速度优化
2.1、优先使用 CPU 资源
优化相机打开速度,可以从业务层进行处理,优先将 CPU 资源让给相机,相机打开后回调给业务相机首帧已出的事件,这样业务收到该事件后再进行其它初始化,体验会得到很大提升。
2.2、异步初始化非必要组件
通常相机会与特效等 SDK 混合使用,可以子线程异步加载特效组件,首帧可以无特效立即展示出来,等检测特效加载好后再加入效果即可。
2.3、首帧占位体验优化
第一帧展示前可以使用上一次关闭高斯模糊图来占位,这样体验比纯黑色效果好很多,可参考微信朋友圈相机。
3、采集预览流畅度优化
3.1、线程模型优化
把采集和视频特效放在同一个线程,随着特效功能越来越强,计算越来越重,会影响到最终的输出帧率。要优化可以改为多线程的方式,这里有下面几点需要注意:
1)使可并发的任务跑在不同的线程上:
- 采集线程:使用系统相机能力实现图像采集;
- CPU 处理线程:跑一些 AI 模型任务;
- GPU 处理线程:跑一些图像处理任务。
2)使用缓冲区组合生产消费者模型,各个模块可以并行,而且性能兼容性更好:
- 线程 1:采集 → buffer 1 → 线程 2:图像处理 → buffer 2 → 主线程:渲染
这样一来,在采集后如果要继续做其他任务(比如编码、发送网络等)也能比较方便的接入。
3.2、采集与前处理数据交互优化
采集和前处理(AI 模型、图像处理、特效等)模块交互时,可以做下面几项优化:
- 采集到图像处理进行图像下采样。有时候我们采集时需要较高的分辨率,但是在算法处理时则不需要,这时候采集完直接下采样交给后续的图像处理链路,可以降低数据量,优化性能。
- 图像处理链路对齐分辨率,防止多次采样消耗性能。图像处理链路也不光是下采样就完了,因为整个图像处理链路可能涉及不同的节点(比如人脸识别模块、降噪模块、美颜模块),如果这几个节点对应的算法输入分辨率不一致,那就需要一路上做多次图像上采样或下采样,这样就带来了额外的性能开销。如果在数据链路设计之初,各个模块就能对齐分辨率,就节省了反复上下采样的消耗。
- 图像处理链路对齐颜色空间,防止颜色空间转换消耗性能。同样的,不同的节点的算法模型如果使用不同的颜色空间,一路下来就会涉及颜色格式的转换,这样也会带来更多的性能开销。
3.3、减少 CPU 与 GPU 的数据拷贝
GPU 和 CPU 要尽量少做数据拷贝,性能比较差。可以使用系统能力来实现 GPU 和 CPU 的内存共享来做相关的优化。
比如在 iOS 上,使用设置了 kCVPixelBufferIOSurfacePropertiesKey
属性的 CVPixelBufferRef 是可以支持 GPU 和 CPU 共享内存的。从相机采集出来的、从 VideoToolbox 解码出来的 CVPixelBufferRef 都具有这个属性,所以通常来讲使用系统的 API 时,你并不太需要操心这个问题。
如果你要自己创建一个图像来进行渲染,还希望能读出其数据做其他处理,并且希望支持 GPU 和 CPU 共享内存,可以参考下面这篇文章:Rendering to a texture with iOS 5 texture cache api[1]
3.4、不同设备智能选择分辨率和帧率
通常低端机效果较多情况下选择 1080P 30FPS 会比较卡,影响用户体验,如何选择合适的分辨率和帧率则尤为重要。
- 服务器大数据收集每个机型的平均帧率与分辨率,对于不满足帧率阀值则需要调整分辨率或帧率,也可以降低某些特效复杂度来提高帧率;
- 针对于机型纬度种类会非常多,也可以参考使用芯片等其它维度进行设备打分,通过打分方式对于不同设备选择不同参数。
4、采集画面质量优化
画面采集质量,比如清晰度、亮度等指标对于最终视频观看的体验尤为重要,可通过以下几点尝试提高画质:
4.1、对焦优化
可智能选择人脸自动对焦或者手动对焦,防止曝光不合理影响画质。下面的对焦策略,可以参考:
- 手动对焦:
- 用户点击哪里就对焦哪里。
- 自动对焦:
- 基于系统能力在识别场景发生变化后,进行一次中心对焦。比如在 iOS 系统中,可以监听 AVCaptureDeviceSubjectAreaDidChangeNotification 系统通知,检测到场景变化时触发对焦。
- 如果有识别到画面从无人脸到有人脸时,做一次人脸对焦(这里是只做一次人脸对焦,不能一直跟着人脸对焦,这样可以防止用户不想对焦人脸的场景:在有人脸时,点击了其他地方进行手动对焦)。
- 人脸对焦时,一般对焦两眼中间的点位效果比较好。比如在 iOS 系统,可以使用 AVCaptureMetadataOutput 设置 AVMetadataObjectTypeFace,但缺点是仅能获取到人脸框,无法得到精准点,可以选择对焦人脸框中心点。如果能使用其他人脸识别 SDK 来识别到精准点,可以对焦到两眼中心效果会更好,比如常取 43 号点位。
- 手动对焦后,在满足这些条件时会切换到自动对焦:
- 前后摄像头切换。
- 场景发生较大切换。比如,相机位移或晃动较大,外部光线敏感度变化较大等。
- 画面中从无人脸变为有人脸。
4.2、摄像头模糊优化
很多手机在使用中可能会出现镜头被弄脏的情况,这时候采集处理的画面质量自然就比较模糊了,针对这种情况可通过算法检测预览画面是否模糊,并提示用户清洁一下摄像头来解决清晰度的问题。
4.3、画质增强优化
对于采集后的图像还可以通过算法进行画质增强。
音视频知识图谱 2022.09中就介绍了部分图像降噪和增强相关的算法分类,这里就不深入探讨了。
5、采集内存优化
优化相机内存占用大小,有利于减少内存 OOM
问题导致的崩溃。在不同的平台,可以选择恰当的采集数据输出格式来优化内存使用:
1)iOS 采集数据输出格式
输出格式可配置为 BGRA
& YUV
,尽量设置为 YUV
数据格式,可以减少 width * height * 2.5
数据大小。
- 1)例如
720 * 1280
分辨率,单张图片BGRA
占用720 * 1280 * 4 = 3.5M
,YUV
占用720 * 1280 * 1.5 = 1.3M
。 - 2)由于系统底层存储了
CVPixelBufferPool
队列,使用队列的好处是当外层锁住 CVPixelBufferA,底层则直接使用 CVPixelBufferB,有利于提高多线程性能,如果全部都锁住了,那相机将不会再吐数据。实测队列大小为13
,所以YUV
数据格式相对于BGRA
节约共(3.5 M - 1.3M) * 13 = 28.6M
。 -
- 因为渲染纹理需要
BGRA
,所以需要通过 OpenGLES 将YUV
数据转换为 BGRA 纹理即可,开销非常小。另一个好处是很多算法输入也都是YUV
数据格式。
- 因为渲染纹理需要
2)Android 采集数据输出格式
安卓因为支持 Camera1 & Camera2 两种模式,通常根据线上大数据决定当前设备启用哪种模式。
- 1)Camera1 支持输出
YUV
或者SurfaceTexture
,如果需要算法识别则输出YUV
,不需要则直接输出SurfaceTexture
来提高性能。 - 2)如果输出
YUV
数据格式,通常需要每一帧进行旋转,做图像的裁剪、缩放、旋转、尺寸变化时要注意优化性能。可以使用 libyuv 来做常规的图像处理,一些 libyuv 版本甚至做过汇编级别的优化来提升图像处理的性能。
参考资料
[1]
Rendering to a texture with iOS 5 texture cache api: https://allmybrain.com/2011/12/08/rendering-to-a-texture-with-ios-5-texture-cache-api/