23、车载摄像头与视频:Camera2 API集成、行车记录仪、倒车影像、视频流处理
车载摄像头,说白了就是汽车的「眼睛」。在Android Automotive OS里,这块坑特别多。我最早接触车载摄像头项目时,以为跟手机上的Camera2开发差不多,结果一上手就发现完全不是那么回事——分辨率、帧率、延迟、安全优先级,每个维度都跟手机不一样。
今天咱们就把这块彻底讲透。从Camera2 API的集成,到行车记录仪和倒车影像的具体实现,再到视频流处理的核心逻辑,我会把我在项目里踩过的坑、总结的经验,全都摊开来讲。
核心知识点一览:
- Camera2 API在车载环境下的适配要点
- 行车记录仪的前后双录实现
- 倒车影像的低延迟显示方案
- 视频流编码、存储与回放
23.1 Camera2 API 在车载环境下的适配
Android的Camera2 API,在手机上已经非常成熟了。但在车上,有几个关键差异你必须知道。
第一个差异:摄像头数量与方向。 车载系统通常有4-8个摄像头,前后左右甚至环视。每个摄像头的物理方向是固定的,不像手机可以旋转。我做过一个项目,客户要求前置摄像头必须输出竖屏画面,但传感器本身是横屏的。嗯,这里要注意——你不能简单用旋转矩阵,因为车载摄像头通常有固定的安装角度,旋转后视野会丢失。
正确的做法是在配置CameraCharacteristics时,读取SENSOR_ORIENTATION,然后根据实际安装角度做裁剪而非旋转。
// 获取摄像头方向
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
Integer sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
// 车载摄像头通常固定,直接映射到显示方向
// 前视:0度,后视:180度,左视:270度,右视:90度
int displayRotation = getDisplayRotation();
int correction = (sensorOrientation - displayRotation + 360) % 360;
// 注意:这里不要旋转,而是裁剪对应区域
我的经验: 车载摄像头的预览尺寸,别用手机上的1920x1080。我建议用1280x720,因为车载屏幕分辨率通常不高,而且720p的带宽压力小很多,延迟也能降下来。你想想看,倒车影像如果延迟超过100ms,驾驶员会明显感觉「卡」。
第二个差异:CameraManager的权限模型。 在Android Automotive上,摄像头权限不是运行时权限,而是通过android.car.CAR_CAMERA权限来控制。这个权限是系统级的,普通应用拿不到。所以如果你的应用要访问摄像头,必须声明这个权限,并且确保应用是系统应用或具有系统签名。
我曾经遇到一个坑:第三方导航应用想调用前视摄像头做AR导航,结果一直报权限错误。后来发现,Android Automotive对第三方应用的摄像头访问做了严格限制——除非用户明确授权,否则只能访问后视摄像头(倒车场景)。
23.2 行车记录仪:前后双录的实现
行车记录仪的核心需求很简单:同时录制前后两路视频,循环存储,事件触发时锁定片段不被覆盖。
实现上,我习惯用两个独立的CameraCaptureSession,分别绑定前后摄像头。注意,这里不能共用同一个Session,因为前后摄像头的帧率、分辨率可能不同。
// 前摄像头配置
CameraDevice frontCamera = ...;
frontCamera.createCaptureSession(
Arrays.asList(frontSurface, frontImageReader.getSurface()),
new CameraCaptureSession.StateCallback() { ... },
handler
);
// 后摄像头配置(独立Session)
CameraDevice rearCamera = ...;
rearCamera.createCaptureSession(
Arrays.asList(rearSurface, rearImageReader.getSurface()),
new CameraCaptureSession.StateCallback() { ... },
handler
);
循环录制怎么搞? 说白了就是分段存储。我一般把视频切成1分钟一段,存到外置存储的/sdcard/DCIM/Dashcam/目录下。当总大小超过设定阈值(比如32GB),就删除最旧的片段。但要注意——事件触发的片段要加锁,不能删。
事件触发逻辑:
- G-Sensor检测到碰撞 → 锁定当前片段前后各30秒
- 用户手动点击「保存」 → 锁定当前片段
- 锁定的片段移到
/sdcard/DCIM/Dashcam/Protected/目录
这里有个细节:视频文件的命名最好带上时间戳和摄像头ID,方便后续检索。我用的格式是FRONT_20250101_143022.mp4,这样按文件名排序就是时间顺序。
23.3 倒车影像:低延迟显示方案
倒车影像是车载摄像头里最「硬」的需求。为什么?因为延迟必须控制在100ms以内,否则驾驶员会感觉画面滞后,影响倒车安全。
我最早用TextureView来做预览,结果延迟一直在150ms左右,怎么优化都下不去。后来换成SurfaceView,延迟直接降到80ms。原因很简单——TextureView有额外的合成层,而SurfaceView是直接输出到显示硬件。
注意: 倒车影像的优先级必须高于其他所有UI。当车辆挂入R挡时,系统应该立即抢占显示资源。在Android Automotive中,可以通过CarOccupantZoneManager来设置显示优先级,确保倒车画面不被其他应用遮挡。
引导线的绘制也是个技术活。我建议用Overlay的方式,在SurfaceView上层叠加一个自定义View,根据方向盘角度动态绘制引导线。不要直接在视频帧上画线,因为那样需要修改每一帧的像素数据,性能开销太大。
// 引导线叠加层
public class GuidelineOverlay extends View {
@Override
protected void onDraw(Canvas canvas) {
// 根据方向盘角度计算轨迹线
float steeringAngle = getSteeringAngle();
// 绘制两条轨迹线(左轮、右轮)
Path leftPath = calculatePath(steeringAngle, LEFT);
Path rightPath = calculatePath(steeringAngle, RIGHT);
canvas.drawPath(leftPath, paint);
canvas.drawPath(rightPath, paint);
}
}
23.4 视频流处理:编码、存储与回放
视频流处理这块,核心是MediaCodec。我习惯用MediaCodec的异步模式,配合Surface输入,效率最高。
编码参数上,行车记录仪我推荐H.264编码,码率8-12Mbps,帧率30fps。H.265虽然压缩率更高,但解码兼容性在车机上还不够好。我遇到过一些老款车机,H.265的视频播放卡顿甚至花屏。
MediaFormat format = MediaFormat.createVideoFormat("video/avc", 1280, 720);
format.setInteger(MediaFormat.KEY_BIT_RATE, 10_000_000); // 10Mbps
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 关键帧间隔1秒
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
MediaCodec codec = MediaCodec.createEncoderByType("video/avc");
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Surface inputSurface = codec.createInputSurface();
存储格式: 我建议用MP4容器,因为兼容性最好。但要注意,MP4的moov box在文件末尾,如果录制过程中断电,文件会损坏。解决办法是使用MediaMuxer的addTrack方法,并在录制完成后正确写入moov box。或者,你也可以用TS格式,它更抗中断。
回放方面,我推荐用ExoPlayer,它对车载场景做了很多优化,比如无缝切换、自适应码率。而且ExoPlayer支持MediaSource的拼接,可以方便地实现「事件前后片段」的连续播放。
避坑指南: 我曾经在视频回放时遇到音画不同步的问题。后来发现是音频采样率设置不对——车载麦克风的采样率通常是16000Hz,而视频编码器默认用44100Hz。解决办法是统一用48000Hz,这是车机硬件最稳定的采样率。
23.5 实战:倒车影像的完整流程
最后,我把倒车影像的完整流程串起来,给你一个清晰的实现路径。
| 步骤 | 操作 | 关键点 |
|---|---|---|
| 1 | 监听车辆R挡信号 | 通过CarPropertyManager监听GEAR_SELECTION |
| 2 | 打开后视摄像头 | 使用CameraManager.openCamera,指定后视摄像头ID |
| 3 | 创建SurfaceView预览 | 设置SurfaceView为全屏,优先级最高 |
| 4 | 启动预览Session | 配置低延迟参数,关闭自动对焦(固定焦距) |
| 5 | 叠加引导线 | 根据方向盘角度动态绘制 |
| 6 | 退出R挡时关闭 | 释放摄像头资源,恢复之前界面 |
嗯,这里要注意第4步的「关闭自动对焦」。车载后视摄像头通常是广角定焦镜头,自动对焦反而会导致画面频繁抖动。我建议直接设为CONTROL_AF_MODE_OFF,然后手动设置焦距到无穷远。
好了,车载摄像头这块的内容就讲到这里。从Camera2 API的适配,到行车记录仪和倒车影像的具体实现,再到视频流处理的编码存储,每个环节都有不少细节。你在实际项目中遇到问题,可以回头看看这些经验,应该能帮你少走不少弯路。