10、车载摄像头与视频:CarCameraManager、行车记录仪功能、360环视系统、视频流编码与存储

车载摄像头,说白了就是汽车的「眼睛」。

我刚开始接触车载视频开发时,觉得不就是调个摄像头嘛,跟手机开发差不多。后来才发现,这里面的坑多得很。从硬件抽象层到应用层,从编码格式到存储策略,每一步都有讲究。

今天咱们就聊聊车载摄像头那些事。我会结合自己在项目中的实际经验,把 CarCameraManager、行车记录仪、360 环视、视频编码与存储这几个核心知识点串起来。

10.1 CarCameraManager:车载摄像头的统一管家

Android Automotive 提供了一个专门的 API——CarCameraManager。它负责管理车上的所有摄像头。

你想想看,一辆车可能有前视、后视、侧视、内视摄像头。如果没有统一管理,每个应用各自去调硬件,那不乱套了?

核心能力:

  • 枚举可用摄像头(前后左右、环视、DMS 等)
  • 管理摄像头权限与安全策略
  • 提供视频流回调(Surface / ImageReader)
  • 支持多路摄像头同时访问(比如 360 环视需要 4 路)

我习惯这样获取 CarCameraManager 实例:

Car car = Car.createCar(context);
CarCameraManager cameraManager = (CarCameraManager) car.getCarManager(Car.CAMERA_SERVICE);

拿到实例后,可以查询摄像头列表:

List<CarCamera> cameras = cameraManager.getCameras();
for (CarCamera cam : cameras) {
    Log.d("Camera", "ID: " + cam.getId() + ", 朝向: " + cam.getFacing());
}

嗯,这里要注意:车载摄像头的 ID 不是随便取的。它遵循一套命名规范,比如 camera_0_frontcamera_1_rear。我在项目中遇到过,有些第三方摄像头厂商不按规范来,结果应用层解析 ID 时直接崩了。后来我们加了一层映射表才搞定。

10.2 行车记录仪功能:循环录制与事件触发

行车记录仪,说白了就是一直录、循环录、出事时别丢帧。

我参与过一个前装行车记录仪项目,客户要求:

  • 1080p 30fps 持续录制
  • 每 5 分钟一个文件
  • 循环覆盖最早的文件
  • 碰撞事件触发时,锁定当前片段(不覆盖)

实现思路其实不复杂:

MediaRecorder recorder = new MediaRecorder();
recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
recorder.setVideoSize(1920, 1080);
recorder.setVideoFrameRate(30);
recorder.setOutputFile("/sdcard/DCIM/Camera/record_001.mp4");
recorder.prepare();
recorder.start();

但实际坑在哪?我告诉你——文件切换时的丢帧问题

当 5 分钟到了,你要停止当前录制、启动下一个录制。这个切换过程如果超过 500ms,中间的视频就丢了。我曾经调试这个问题调了整整两天,最后发现是 MediaRecorder 的 stop()start() 之间没有做双缓冲。

我的经验:用两个 MediaRecorder 实例交替工作。一个在录,另一个在准备。切换时几乎无感。

碰撞事件触发,我一般用加速度传感器或 CAN 总线信号。检测到碰撞后,把当前文件重命名,加个 _locked 后缀,然后移到一个单独的目录。这样循环删除时就不会删它了。

10.3 360 环视系统:四路摄像头拼接

360 环视,就是车周围一圈摄像头,合成一个鸟瞰图。

这个功能在高端车上已经很常见了。但你知道吗?它的技术难点不在摄像头本身,而在图像拼接与畸变校正

我画了一张图,帮你理解整个流程:

360 环视系统数据流 前视摄像头 后视摄像头 左视摄像头 右视摄像头 畸变校正(鱼眼镜头 -> 透视图像) 投影变换(透视 -> 鸟瞰) 图像拼接与融合 -> 输出鸟瞰图

流程很清晰:四路原始图像 -> 畸变校正 -> 投影变换 -> 拼接融合。

畸变校正是最吃算力的。鱼眼镜头视角大,但边缘畸变严重。我建议用 OpenCV 的 cv::fisheye::undistortImage(),或者用 GPU 加速的着色器方案。

注意:拼接时会有重叠区域。处理不好就会出现「鬼影」——同一个物体在两个画面里各出现一次。我一般用加权融合算法,重叠区域按距离中心的比例混合像素值。

另外,360 环视对延迟要求很高。从摄像头采集到屏幕显示,最好控制在 100ms 以内。否则驾驶员打方向盘时,画面跟不上,体验极差。

10.4 视频流编码与存储:H.264 vs H.265

视频编码,说白了就是压缩。不压缩的话,1080p 30fps 的原始数据流,一秒就要 1.5Gbps。车机那点存储空间,几分钟就满了。

我常用的编码方案:

编码格式 压缩比 硬件支持 适用场景
H.264 约 100:1 几乎所有车机 SoC 行车记录仪、实时预览
H.265 约 200:1 较新芯片(如高通 SA8155) 长时间录制、4K 视频
MJPEG 广泛 单帧抓拍、低延迟预览

我个人习惯用 H.264 做实时预览,H.265 做存储录制。为什么?H.264 编码延迟低,适合实时显示;H.265 压缩率高,省存储空间。

存储方面,我建议用环形缓冲区。说白了就是固定大小的存储池,新数据覆盖最旧的数据。代码逻辑大概这样:

// 伪代码:环形存储管理器
class RingStorageManager {
    private static final int MAX_FILES = 100;
    private static final long MAX_SIZE_BYTES = 32L * 1024 * 1024 * 1024; // 32GB
    
    public void onNewFileCreated(File file) {
        while (getTotalSize() > MAX_SIZE_BYTES) {
            File oldest = findOldestUnlockedFile();
            if (oldest != null) {
                oldest.delete();
            } else {
                break; // 所有文件都被锁定,停止删除
            }
        }
    }
}

这里有个细节:文件系统选择。车机一般用 ext4 或 f2fs。我遇到过用 FAT32 的,单个文件不能超过 4GB,录 4K 视频几分钟就超了。后来全换成 exFAT 才解决。

避坑指南:我曾经在存储满时没有做优雅处理,结果 MediaRecorder 直接抛异常崩溃了。后来我加了一个存储空间监听器,低于 500MB 时提前警告,低于 100MB 时强制停止录制并保存当前文件。

10.5 实战经验:多路视频的同步问题

最后聊一个很多人忽略的问题——多路视频同步

360 环视需要四路摄像头同时采集,但硬件触发时间可能有微小差异。如果不同步,拼接出来的画面会出现错位。

我当时的解决方案:

  • 硬件层面:所有摄像头共用同一个 VSYNC 信号
  • 软件层面:每帧数据打上硬件时间戳,拼接时按时间戳对齐
  • 缓冲策略:维护一个 3 帧的滑动窗口,等待所有摄像头都到达同一时间戳后再处理

嗯,这个方案不算完美,但足够实用。如果你有更好的办法,欢迎交流。


车载摄像头这块,说难不难,说简单也不简单。关键是理解硬件特性、掌握编码原理、注意工程细节。希望今天的内容对你有帮助。

公众号:蓝海资料掘金营,微信deep3321