鸿蒙相机开发避坑:Camera Kit 预览帧率抖动与 YUV 转码性能优化
最近在做一个鸿蒙上的证件照拍摄工具,核心功能是用 Camera Kit 实时预览,并支持人脸框检测和自动裁剪。功能不难,但实际跑起来后发现:预览画面在移动端有明显的帧率抖动,用 Frame Profiler 一抓,每帧的耗时忽高忽低,低的时候 8ms,高的时候能飙到 40ms。折腾了两天,最后定位到两个问题——Surface 消费端阻塞和YUV 转码用错了姿势。
一、问题一:预览帧率抖动,根因在消费端
鸿蒙的相机预览数据通过 Surface 输出。我们最初的实现是在 onFrameAvailable 回调里直接做图像处理(人脸检测),处理完才返回。这就导致相机输出端被业务逻辑阻塞,帧间隔变得不稳定。
优化方案是双缓冲 + 异步处理:
相机 Surface 只负责接收帧,收到后立刻拷贝到一块自己管理的 PixelMap 缓冲区;
把 PixelMap 丢给一个独立的任务队列(用 @ohos.taskpool)做人脸检测;
检测结果通过回调更新 UI 上的取景框。
typescript
import { taskpool } from '@kit.ArkTS';
// 把人脸检测封装成 taskpool 并发任务,避免阻塞相机回调
@Concurrent
function detectFace(pixelMap: image.PixelMap): Promise<FaceResult> {
// 检测逻辑
return doDetect(pixelMap);
}
function onFrameAvailable(pixelMap: image.PixelMap) {
// 只做拷贝和派发,立即返回
const cloned = pixelMap.cloneSync();
taskpool.execute(detectFace, cloned).then((result: FaceResult) => {
updateOverlay(result);
});
pixelMap.release();
}
改完之后帧间隔稳定在 16ms 左右,抖动基本消失。
二、问题二:YUV 转 RGB 慢得离谱
证件照需要输出 JPEG,相机默认给的是 YUV_420_SP 格式,转 RGBA 时我们一开始用逐像素手写公式,1080P 一张图要 60ms 以上。
后来换成 image.createPixelMap 配合 PixelMap.scale 和 image.createImagePacker 的原生能力,让系统底层走 NEON 指令集做色彩空间转换,单帧转码从 60ms 降到 9ms。关键点是:不要自己写像素循环,交给系统的图像处理管线。
typescript
// 用系统能力做 YUV→RGBA,走底层 SIMD 优化
const yuvBuffer = ...; // 相机回调的 ArrayBuffer
const opts: image.InitializationOptions = {
size: { width: 1080, height: 1920 },
pixelFormat: image.PixelMapFormat.RGBA_8888,
editable: true
};
const pixelMap = await image.createPixelMap(yuvBuffer, opts);
三、总结
鸿蒙相机开发的性能坑,很多不在相机本身,而在消费者端和数据格式转换上。两条经验:
相机回调里只做“搬运”,重活全部异步化,否则一定阻塞出帧;
图像格式转换优先用系统 image 模块的 API,底层有 NEON/SIMD 优化,手写循环在移动端是下策。
希望对做鸿蒙多媒体开发的同学有帮助,尤其做扫码、拍照类应用的,这几个坑基本都会踩一遍。
- 点赞
- 收藏
- 关注作者
评论(0)