修复前摄偏蓝

本来以为差不多就行了,虽然前面依靠 AI 的帮助也稀里糊涂做了很多,但是感觉已经接近尾声了,没想到还是有些小问题。

到这里前后摄都已经能实时预览和保存照片了,但最后还有一点很明显:前摄的画面和拍出来的照片都偏蓝,和真实颜色有色差。
色差

这张图片在拍摄时我在回复消息,不小心上镜,其实当时我穿的是浅绿色的衣服。

这次的问题不在白平衡参数,而在更基础的像素格式。检查板级摄像头配置:

device/board/spacemit/musepaper2/camera/vdi_impl/v4l2/svivi_cam1.json
device/board/spacemit/musepaper2/camera/vdi_impl/v4l2/svivi_cam2.json

可以确认 svivi_cam2 / gc08a8_spm 输出的是 NV12。NV12 和 NV21 的亮度 Y 平面相同,但色度平面的排列刚好相反:

NV12: Y + UV
NV21: Y + VU

隐藏预览帧保存路径原来按 NV21 创建 PixelMap,于是 U、V 被交换。画面不是完全不可用,但颜色会明显偏冷,前摄上尤其容易看出来。

最终在 applications/standard/camera/common/src/main/ets/default/camera/CameraService.ts 中保留帧自身的像素格式,并让 MUSEPaper2 的 NV12 帧按 NV12 解码:
return
随后创建 PixelMap 时使用这一格式,而不是再把所有 YUV 数据一律当成 NV21。

重新构建并安装 Camera HAP 后,前摄保存出新的照片,之前明显的蓝色偏色已经消失,前摄预览和最终 JPEG 的颜色也保持一致。

这个问题看起来很像 ISP 或白平衡没有调好,最后却只是两个字母的顺序错了。嵌入式里有些问题就是这样,现象很大,根因很小。

编译和部署验证

这部分延续上一篇的思路,不只是改代码,还要真的编译、推送、重启、验证。

HDI 和 pipeline:

ninja -C /home/dfq/repo/OpenHarmony/oh61/out/musepaper2 \
  -f camera_only.ninja \
  spacemit_products/spacemit_products/libcamera_host_vdi_impl_1.0.z.so

Camera Framework:

ninja -C /home/dfq/repo/OpenHarmony/oh61/out/musepaper2 \
  -f camera_framework_only.ninja \
  multimedia/camera_framework/libcamera_framework.z.so

ninja -C /home/dfq/repo/OpenHarmony/oh61/out/musepaper2 \
  -f camera_service_only.ninja \
  multimedia/camera_framework/libcamera_service.z.so

Camera HAP:

cd /home/dfq/repo/OpenHarmony/oh61/applications/standard/camera
OHOS_BASE_SDK_HOME=/home/dfq/repo/OpenHarmony/oh61/prebuilts/ohos-sdk/linux \
  node /home/dfq/repo/OpenHarmony/oh61/prebuilts/tool/command-line-tools/6.x/hvigor/bin/hvigorw.js \
  assembleApp --no-daemon --no-incremental

推送到设备:

hdc -t YLMPK100081280004 target mount

hdc -t YLMPK100081280004 file send \
  out/musepaper2/spacemit_products/spacemit_products/libcamera_host_vdi_impl_1.0.z.so \
  /vendor/lib64/libcamera_host_vdi_impl_1.0.z.so

hdc -t YLMPK100081280004 file send \
  out/musepaper2/hdf/drivers_peripheral_camera/libperipheral_camera_pipeline_core.z.so \
  /vendor/lib64/libperipheral_camera_pipeline_core.z.so

hdc -t YLMPK100081280004 file send \
  out/musepaper2/multimedia/camera_framework/libcamera_framework.z.so \
  /system/lib64/platformsdk/libcamera_framework.z.so

hdc -t YLMPK100081280004 file send \
  out/musepaper2/multimedia/camera_framework/libcamera_service.z.so \
  /system/lib64/libcamera_service.z.so

hdc -t YLMPK100081280004 install -r \
  applications/standard/camera/product/phone/build/default/outputs/default/phone-default-signed.hap

单项构建和 HDC 热替换适合缩短调试周期,但它们只能证明修改方向正确。为了确认改动不是只活在当前设备上,我最后又从工程根目录进行了完整产品构建:

cd /home/dfq/repo/OpenHarmony/oh61
./build.sh --product-name musepaper2 --ccache --no-prebuilt-sdk
./build/gen_zip.sh musepaper2

全量构建完成了全部 4289/4289 个 Ninja 步骤,生成的完整烧录包为:

out/musepaper2/packages/phone/images/openharmony-spacemit-musepaper2.zip
SHA-256: 6f7b5a685757e33153915f164c2d4327f846dcbd2f7ad4c52d261a5c2f5f1633

而且不能只看“编译成功”四个字,还要确认关键文件真的进入最终镜像目录:

out/musepaper2/packages/phone/system/app/com.ohos.camera/Camera.hap
out/musepaper2/packages/phone/vendor/etc/wifi/wpa_supplicant.conf
out/musepaper2/packages/phone/vendor/bin/wifi_fix.sh
out/musepaper2/packages/phone/vendor/lib64/libcamera_host_vdi_impl_1.0.z.so

这里 Wi-Fi 配置还有一个容易漏掉的点。在:vendor/spacemit/musepaper2/wifi_config/BUILD.gn 中必须明确写入:

install_images = [ "vendor" ]

否则源码里虽然有 wpa_supplicant.conf,最终 vendor 镜像里却不一定有它。USB 侧的防自动休眠配置、Wi-Fi 的 RTL8852BS 模块和启动脚本、摄像头 VDI/HCS/Camera HAP 都已经纳入板级 GN 构建关系。这样换一块新设备重新编译并烧录完整包时,依赖的是源码和镜像,而不是之前通过 HDC 临时推送留下的文件。

最终验证结果

拍照后日志:

photoOutput captureStartWithInfo: {"captureId":2,"time":-1}
waitForNextImageSaved timeout
savePreviewFrameAsPhoto
savePixelMapAsImage saved photoUri:
file://media/Photo/2/IMG_946889474_001/IMG_200013_164934.jpg

前摄:

photoOutput captureStartWithInfo: {"captureId":4,"time":-1}
waitForNextImageSaved timeout
savePreviewFrameAsPhoto
savePixelMapAsImage saved photoUri:
file://media/Photo/3/IMG_946889508_002/IMG_200013_165008.jpg

也就是说,最终结果是:

后摄:可预览、可拍照、可保存到相册
前摄:可切换、可实时预览、可拍照、可保存到相册、无明显蓝色偏色
页面:底栏不再压到系统导航栏
照片:真实摄像头帧,不含 Camera UI,不是黑图
整包:Camera 的源码修改均进入最终构建产物

总结

这一次摄像头适配看起来只是一个外设驱动问题,但是实际做下来跨了很多层:

HCS 能力配置
Metadata 格式枚举
V4L2 设备匹配
Buffer 格式转换
Pipeline stream 分发
Camera Framework 输出
Camera App profile 选择
前后摄 UI 切换
相册保存逻辑
NV12/NV21 色彩格式
产品镜像打包

这也是我做完之后最大的感受:在 OpenHarmony 这种系统里,“一个外设能用”并不是某一个文件改对了就结束了,而是上层应用、系统服务、HDI、驱动和板级配置一起对齐。

做到这里,摄像头终于从“能看到一点东西”变成了“像一个相机应用了”。

虽然中间有很多地方看起来像在黑暗里摸索,但是摸着摸着,确实摸到了开关。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐