资讯动态

旧手机改造行车记录仪:Android Camera2与MediaCodec实战指南

发布时间:2026/8/31 12:21:32 来源:尧图企业网站定制
简介本资源是一个基于Android平台的行车记录仪系统完整实现方案面向移动开发初学者与Android应用实践者解决普通用户利用闲置智能手机替代专用硬件实现行车视频录制与管理的需求。项目采用Java语言开发基于ADT环境构建支持Android 4.5及以上系统具备录像分段、时长自定义、滚动覆盖保存及本地回放等核心功能可直接编译运行。压缩包共1381个文件包含29个Java源文件主逻辑与Activity控制、55个XML布局与配置文件界面与权限定义、260张PNG图标与UI资源、87个编译生成的class文件以及jar库、properties配置和prefs偏好设置等结构完整、模块清晰涵盖从摄像头调用、MediaRecorder封装到文件管理的全链路实现。目前已有128人下载学习适合用于Android多媒体开发实战、课程设计参考或车载类App二次开发基础模板。 大概两年前我把自己那台退役的小米8翻了出来打算做一个真正能用的车载记录仪。起因挺朴素的家里那台三百块的记录仪白天凑合能用一到夜间或者隧道里就糊成一片更糟的是存储卡还莫名坏过一次差点把一段剐蹭的关键视频弄丢。当时我就在想手头这台旧手机摄像头素质不差CMOS底也够大为什么不能把它改造成一台行车记录仪于是就有了这套基于Android手机摄像头实现的记录仪系统。这套东西做下来能解决的问题很具体用旧手机替代记录仪硬件利用手机自带摄像头、定位、传感器完成视频录制、循环存储、时间水印、GPS轨迹和碰撞事件保护。整个工程从 Camera2 采集到 MediaCodec 编码再到文件管理和前后台切换全部自己掌控不依赖任何第三方录像App。如果你是一名 Android 开发者或者手里正好有台闲置手机想低成本搞一个记录仪这篇文章应该能帮你少走不少弯路。1. 为什么自研一套手机行车记录仪方案对比与整体架构1.1 成品记录仪、录屏App、还是自己写先聊一个最基础的问题市面上有便宜到几十块的记录仪也有功能很全的App为什么还要自己写一套成品记录仪的痛点在硬件迭代太慢。很多低端记录仪用的方案是几年前的芯片传感器尺寸小、动态范围差白天光线充足时看不太出来但一到逆光、夜间、隧道口这种大光比场景就露馅。我自己那台就是典型——晚上对面来车开远光画面直接过曝成白茫茫一片视频里根本看不清对方车牌。手机则不同前几年旗舰机的传感器至今依然能打还自带不错的HDR和夜景处理硬件底子比大部分低端记录仪强。至于现成的录像类App我也试过不少。问题在于一是很多App免费版带水印或广告二是分段规则、文件命名、覆盖策略这些关键行为不可控。尤其碰撞事件锁存这种记录仪核心功能大部分通用App做得都很粗糙要么不提供要么藏在设置里默认不开启。对于我这种追求可控性的人来说自己写一套是绕不开的路。1.2 技术栈选型Camera2、MediaCodec、MediaMuxer项目核心选型其实就三条路。采集层我选的是 Camera2 API 而不是 Camera1。Camera1 已经废弃多年对新设备的支持只能说“能用”很多手动控制能力比如曝光补偿、对焦模式、3A区域设置在 Camera1 上要么缺失要么行为不一致。Camera2 虽然上手复杂度高一点但胜在控制力强这对行车场景很重要。编码封装层我选的是 MediaCodec MediaMuxer而不是 MediaRecorder。后面我会专门讲为什么这里先给结论MediaRecorder 是一个封装好的“黑盒”你很难在录制过程中精细控制分段、I帧间隔、码率切换也不方便把视频文件直接按自定义规则命名再交给自己的文件管理逻辑。用 MediaCodec 裸编码配合 MediaMuxer 封装成 MP4你才拥有完整的控制权。附加功能层用到了系统的几个常规组件LocationManager 获取 GPS 轨迹SensorManager 监听加速度计做碰撞检测前台服务 开机广播实现通电自启。这样一套组合下来几乎不需要任何第三方依赖整个工程就能跑起来。1.3 项目模块划分我最终把工程拆成了五个模块每个模块职责单一这样调试起来不用满世界找代码模块职责关键类采集模块相机设备管理、预览、CaptureSession 控制CameraManager、CameraDevice、CaptureRequest编码模块MediaCodec 编码、MediaMuxer 封装、分段输出MediaCodec、MediaMuxer、MediaFormat文件管理模块循环覆盖、事件目录、损坏文件检测File、MediaMetadataRetriever事件检测模块加速度计碰撞判定、GPS 轨迹记录SensorManager、LocationManager应用外壳前台服务、开机广播、设置面板Service、BroadcastReceiver整个项目结构基本可以照抄camera包专门负责相机encoder包专门负责编码输出storage包负责文件策略event包负责传感器和 GPS。工程解压之后入口是一个普通的MainActivity但真正干活的是RecordService界面只是用来预览和调整参数的。2. Camera2 采集层让相机在各种路况下稳定输出2.1 选对后置摄像头与输出尺寸行车记录仪只用到后置摄像头但 Camera2 里后置也可能不只有一个。为了保证选到“最合适”的那颗我做了两层过滤第一层通过CameraCharacteristics.LENS_FACING拿到所有后置摄像头第二层用REQUEST_AVAILABLE_CAPABILITIES判断哪颗支持 FULL 或至少 LEGACY 级别的能力优先选支持 FULL 的。如果不做这个判断某些机器可能会选中广角副摄画面畸变严重甚至压根不支持 1080p30 的输出尺寸。输出尺寸是另一个关键点。我实测下来1080p30 已经足够日常使用没必要上 4K。原因有三一是 4K 在夜间弱光下噪点更明显传感器本身的分辨率并不等于真实解析力二是 4K 编码功耗大手机在挡风玻璃下本来就容易发热再叠加 4K 编码很容易触发热节流三是 4K 视频文件体积大对存储卡读写速度要求也高最后循环覆盖的周期会变得很短。我选尺寸的逻辑是从 StreamConfigurationMap.getOutputSizes(ImageFormat.JPEG) 或者 MediaCodec 支持的列表里挑最接近 1920x1080 且宽高比为 16:9 的组合。注意不要直接用getOutputSizes(SurfaceTexture.class)的最大值因为有些设备最大预览尺寸是 4K但你未必想用 4K 跑全流程匹配到 1080p 就够了。2.2 创建 CaptureSession预览 Surface 与编码 Surface 如何共存这是很多人第一次碰 Camera2 时最容易懵的地方。CaptureSession 不是随便创建一个就能用的你需要在一开始就告诉底层 HAL你将要往哪些“目标 Surface”上输出数据。我用了两个目标一个是SurfaceView的 Surface用来实时预览另一个是 MediaCodec 编码器通过createInputSurface()拿到的 Surface。这里有一个隐藏的坑如果你把编码 Surface 和预览 Surface 同时放进 Session而它们的分辨率不一致某些设备会有额外的 scaling 开销。最稳妥的做法是让两个 Surface 使用相同分辨率预览 Surface 也输出 1080p然后在 UI 层用TextureView.setTransform做等比缩放适配屏幕。别为了省性能把预览设成 720p 而编码设成 1080pHAL 层做分辨率转换的耗时可能比想象中大。创建 Session 的代码核心流程是ListSurface outputs new ArrayList(); outputs.add(previewSurface); outputs.add(encoderSurface); cameraDevice.createCaptureSession(outputs, new CameraCaptureSession.StateCallback() { Override public void onConfigured(NonNull CameraCaptureSession session) { CaptureRequest.Builder builder cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); builder.addTarget(previewSurface); builder.addTarget(encoderSurface); // 设置对焦模式、曝光参数等 session.setRepeatingRequest(builder.build(), null, null); } }, backgroundHandler);注意我用的是TEMPLATE_RECORD而不是TEMPLATE_PREVIEW。这个模板会针对录像场景做一定的自动参数优化比如优先保证稳定的帧率在曝光策略上更偏向视频而不是静态拍照。实测下来在抖动较多的行车环境中TEMPLATE_RECORD的画面稳定性确实比TEMPLATE_PREVIEW好一些。2.3 对焦与曝光策略行车场景的特殊调整行车记录仪的对焦策略和普通录像差别挺大。一开始我用的是CONTINUOUS_VIDEO模式想着让相机自己连续追焦肯定省心。实际跑了几天才发现问题夜间当路灯、对面车灯、路面反光交替出现时连续对焦会频繁“拉风箱”画面一抽一抽的很影响视频可读性。后来我改成固定焦距具体做法是在路面空旷处先把对焦距离拉到一个合适值然后锁定对焦。你可以在启动阶段调一次AF_MODE_AUTO等对焦回调触发FOCUS_MODE_FIXED之后就不再去碰对焦了。这样处理之后画面几乎是稳定的因为行车记录仪绝大部分时间关注的是一段有限距离内的路面变焦只会带来不必要的干扰。曝光方面我保留自动曝光但加了曝光补偿。由于挡风玻璃反射光的影响我把补偿值调到了-0.5 EV到-1.0 EV之间宁可画面略暗也不让天空和车灯过曝。具体值需要按你的车型和贴膜深浅微调。白平衡则直接锁定为WHITE_BALANCE_MODE_DAYLIGHT避免相机在路灯和自然光之间来回切换颜色。2.4 手机横装后的旋转与镜像问题手机装上车载支架时一般是横着放的。但手机传感器是竖屏方向的如果不管它录出来的视频会旋转 90 度播放器里还得手动扭脖子。这个问题需要在编码输入或者最终封装时处理好。我采用的方式是预览层用TextureView.setTransform做 90 度旋转让用户在界面上看到的是横屏画面编码层则无论摄像头方向如何都在 MediaCodec 的输入 Surface 之前做一次旋转。实际上把视频旋转这一步交给 MediaCodec 处理并不方便更常见的做法是在编码配置里直接用MediaFormat.KEY_ROTATION部分设备支持或者用 OpenGL 在输入 Surface 上做旋转。我最终的简化方案是把手机横置时的传感器方向通过CameraCharacteristics.SENSOR_ORIENTATION计算出来旋转信息写入 MP4 的rotation元数据播放器会自动旋转播放。这个方案实现简单而且绝大多数车载播放器、手机播放器都支持读取 rotation 标签。镜像问题在行车记录仪场景一般不需要处理除非你用的是前置摄像头来做车内录像但我们的主场景是后视镜视角不做镜像反而更符合实际情况。3. MediaCodec 分段录像与循环覆盖编码选型及实现细节3.1 为什么 MediaRecorder 不够用开头我已经留了个悬念这里展开说。MediaRecorder 最大的问题是控制粒度过粗。你想要固定 I 帧间隔、精确控制码率、动态切换分辨率这些操作在 MediaRecorder 里基本都做不到。而且 MediaRecorder 一旦 start后续就不能“无缝”地切断当前文件再开启新文件每次 stop 和 start 之间还有明显的准备时间容易丢镜头。循环录像要求的是不中断地连续录像然后删除旧文件MediaCodec MediaMuxer 组合天然适合这种场景MediaCodec 负责把图像帧编码成 H.264MediaMuxer 负责写入 MP4 容器。当一段视频达到设定时长后我只需要结束当前 MediaMuxer立刻新建一个 MediaMuxer 继续写编码器不需要 stop整个切换过程非常平滑。3.2 MediaCodec 配置的关键参数我最终采用的编码参数如下实测在绝大多数设备上都能稳定运行MediaFormat format MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_BIT_RATE, 10_000_000); // 10 Mbps format.setInteger(MediaFormat.KEY_FRAME_RATE, 30); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 每一秒一个关键帧 format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31); codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);这几个参数我逐个说一下依据。码率 10 Mbps 是 1080p30 的一个平衡点码率再低夜景暗部会出明显块状噪声再高对存储卡写速度和手机发热都更不友好。I 帧间隔设为 1 秒是为了回放时快速拖动也能很快定位到关键帧代价是体积会略有增加配合 10 Mbps 的整体码率这个代价可接受。Profile 用 Baseline 而不是 Main/High主要是兼容性考虑Baseline 在车载播放器和普通手机播放器上的兼容性最好省得视频拿到别的设备上放不出来。3.3 编码器 Buffer 轮转的实操流程如果用 Surface 输入模式你不需要自己往编码器塞 YUV 数据流程会简单很多编码器configure之后调用createInputSurface()把返回的 Surface 交给 Camera2 的 Session。相机输出到这片 Surface编码器自动消费图像。在循环里调用dequeueOutputBuffer取出编码好的 H.264 数据。拿到字节后判断是否是BUFFER_FLAG_CODEC_CONFIG如果是就吐给 MediaMuxer 的writeSampleData实际上 MediaMuxer 会自动处理 SPS/PPS你可以原样交给它。正常的视频帧则往 MediaMuxer 里写同时记录presentationTimeUs。其中最容易写错的一点写入 MediaMuxer 的presentationTimeUs必须是单调递增的不能直接拿系统的elapsedRealtime毫秒数当微秒用。而我是在每帧从dequeueOutputBuffer返回时用bufferInfo.presentationTimeUs取到的时间戳来写。实测下来这样最稳不会出现播放时时间轴乱跳的问题。MediaMuxer 使用上有几个坑我吃了不少苦头。第一addTrack之后必须等到start()完成才能写数据第二一个 MediaMuxer 实例只能写一个文件写完必须调用stop()和release()再重新创建第三不要在stop()之前把文件复制走否则可能得到没有 moov 的损坏文件。这些说起来简单但开发过程中真的很折磨人。3.4 如何做分段和循环覆盖分段的触发条件我用的是“视频时长达到阈值”而不是“文件大小达到阈值”。阈值我设成 60 秒。为什么是 60 秒因为 10 Mbps 码率下一分钟视频体积大约是 75 MB既不会让文件太小导致频繁创建新文件也不会让单个文件太大事件锁存时移动文件的粒度也算合适。分段切换的逻辑大致是这样if (currentDurationUs SEGMENT_DURATION_US) { muxer.stop(); muxer.release(); closeCurrentSegment(); createNewMuxer(); currentSegmentStartTimeUs currentFrameTimeUs; }循环覆盖策略单独放到了一个文件管理服务里。每当一个新分段落地后我会扫描录像目录下所有CAM_*.mp4文件把总大小和“保留容量阈值”比较。保留容量我设成总容量的 80%例如 64GB 卡就给录像目录留 50GB 的预算。超过预算就按文件名从旧到新删除注意必须跳过events子目录那里面的都是事件保护文件。文件命名建议统一CAM_YYYYMMDD_HHMMSS.mp4这个格式天然支持排序扫描时按文件名排序就等于按时间排序。不要用System.currentTimeMillis()直接当文件名因为系统时间可能被用户调整会导致排序和覆盖策略错乱。3.5 断电和异常退出后的文件损坏防护行车记录仪最常见的“异常退出”就是汽车断电。如果此时你正在写 MP4而 MP4 的 moov 原子还没写进去文件就读不了。MediaMuxer 在stop()时才会写 moov所以崩溃/断电时最后一个小段大概率是损坏的。我的处理方式是双重保险一方面每次写完一个分段并stop()后用MediaMetadataRetriever打开文件验证一次打不开就立刻删除另一方面每次系统重启、应用启动时扫描整个录像目录对每个文件做一次快速校验发现损坏就清理出目录。这个方法不能保住最后一段视频但可以保证你事后查看时所有视频都能正常播放不至于关键时刻打不开文件。4. 时间水印、GPS轨迹与碰撞锁存记录仪该有的“加分项”4.1 时间水印的两种实现路线时间水印这种看似简单的功能其实也有两套方案。第一种是把时间画在预览画面上再通过 Surface 输入进编码器。简单直接使用 Canvas 在每帧上drawText。但问题也很明显——每帧绘制需要 CPU/GPU 参与1080p 下开销不小容易掉帧。而且如果你直接把文字画在预览 Surface 上用户看到的和录出来的是同一份效果直观。实测下来低端机上 if 处理不好会掉到 25fps。第二种是后处理叠加。思路是编码时不加水印录完之后用 MediaCodec 解码再重新编码缺点是这套方案对 CPU 开销更大不适合实时录制。我最终走的是中间路线把时间水印做在编码纹理的 Surface 上使用 SurfaceView setOnFrameAvailableListener每一秒更新一次时间戳 bitmap然后通过自定义 Renderer 把这层叠加到预览画面上。这种做法不用每帧都去重新 drawText编码器的 Surface 直接消费叠加后的画面CPU 占用低了很多。当然这套东西实现复杂度偏高如果不追求极致省电直接用第一种 Canvas 方案也能接受毕竟 1080p 手机没那么容易掉帧。4.2 GPS 轨迹记录从坐标到可回放的轨迹文件GPS 记录没有太多花活重点在于“记录什么”和“记录多密”。我用 LocationManager 同时请求GPS_PROVIDER和NETWORK_PROVIDER优先用 GPS 的坐标。更新周期设成 5 秒一次太密没有意义因为行车记录仪视频回放时通常按秒级查看太疏则回放轨迹会有明显跳变。数据除了写入数据库还会同时输出一份纯文本轨迹文件。推荐用 KML 格式它可以直接丢进 Google Earth 或许多轨迹回放软件里查看而且文本格式非常容易生成Placemark nameCAM_20250101_120000.mp4/name Point coordinates116.1234,39.5678,0/coordinates /Point /Placemark轨迹文件与视频同名放在同目录这样处理碰撞事件时只要移动视频文件轨迹也会跟着走。4.3 加速度计碰撞检测与事件保护这一块是成品记录仪的核心卖点也是自己实现时最容易被忽视的部分。我用SensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)监听频率设为SENSOR_DELAY_GAME约 50Hz然后计算加速度矢量模长float acc (float) Math.sqrt(x * x y * y z * z);正常行车时加速度模长在 9.8 m/s² 附近波动。我设置的触发条件是连续 3 帧超过 19.6 m/s²即 2g才判定为碰撞。为什么是连续 3 帧因为单帧超阈值很可能是颠簸或传感器噪声3 帧连续超阈值才能排除绝大部分误报。触发之后要做两件事第一把碰撞发生前 30 秒和发生后 30 秒的视频片段移到events目录。这里我直接在文件层操作因为分段是 60 秒一个碰撞大概率会发生在一个段的中间所以逻辑上我会把当前段以及它前一个段都移过去保证覆盖碰撞前后各 30 秒。第二把当前 GPS 坐标和时间戳写入事件日志。后续你可以把这个日志做成一个列表界面方便快速定位每个事件的视频文件。必须提醒一句事件目录要设置独立上限否则时间久了会被保护文件撑爆。我设的是 2GB超过后按时间最远的事件文件先清理。5. 通电自启与息屏保活把手机调教成真正的行车记录仪5.1 开机自启动和前台服务的权限处理一个不需要点屏幕就能自动开始录制的记录仪才算真正可用。所以开机自启必须做。实现方式不复杂注册一个接收ACTION_BOOT_COMPLETED的 BroadcastReceiver在里面启动前台服务。前台服务的必要性不只是为了用户可感知更是因为从 Android 8 开始后台服务存活时间被大幅压缩只有前台服务才能长期运行。你需要在通知栏常驻一条“行车记录仪正在录制”的通知这是 Android 对用户的透明要求也是你自己调试时查看状态最方便的地方。新版本系统对开机启动限制比较多尤其国内 ROM。实测发现只写一个 BOOT_COMPLETED 接收器是不够的你还需要引导用户做两件事一是把应用加入“自启动白名单”二是关闭电池优化限制。代码里可以用PowerManager.isIgnoringBatteryOptimizations检测如果不是豁免状态就跳转系统设置页让用户手动开启。5.2 息屏后摄像头还能不能继续工作这是很多人的疑惑锁屏之后相机还能继续采集数据吗答案是能但需要做两件事保证它不被系统掐断。第一获取一个PARTIAL_WAKE_LOCK。PARTIAL_WAKE_LOCK只保证 CPU 保持唤醒不像SCREEN_BRIGHT_WAKE_LOCK会让屏幕保持常亮。行车记录仪不需要经常开屏幕屏幕一直亮反而会加剧发热、烧屏所以我说不要用FLAG_KEEP_SCREEN_ON。第二把相机设备引用、CaptureSession 引用保存在 Service 里而不是 Activity 里避免 Activity 销毁导致会话丢失。Service 的onStartCommand返回START_STICKY让系统在服务被杀后尝试重建。还有一个容易让人忽略的点如果用户用“最近任务”划掉应用很多手机会顺带把服务干掉。我在 Service 里重写了onTaskRemoved在里面调startService把自己拉起来。这个技巧在多数 ROM 上有效虽然某些激进系统仍然会管住但比什么都不做要稳得多。5.3 充电检测怎么避免停车后耗尽电瓶行车记录仪接的是 ACC 电源理论上停车断电后就不该再录像了。但如果你只是单纯把手机接在车充上车充可能一直有点那就得靠软件判断了。我监听ACTION_POWER_CONNECTED和ACTION_POWER_DISCONNECTED。只有检测到 USB 电源接入时才开始录像拔电后自动停止录像并正常保存当前段。同时如果手机电量低于 20%我会强制结束录像任务防止电池过放。这个策略可以最大程度避免旧手机电池被“饿死”。顺便说一句如果你要真正长期用机械上最好的方案是买一个 ACC 取电器转 USB 口然后把旧手机电池拆掉、直接接电源板供电。但拆电池涉及硬件改造风险自担不在软件讨论范围内。6. 实测踩坑清单从相机断开到高温降级的解决方案6.1 CameraError 断连和重连Camera2 设备在长时间运行后可能出现ERROR_CAMERA_DISCONNECTED尤其当系统相机被其他应用抢占时。这个问题在行车记录仪场景里很致命因为你会发现录着录着画面就停了而且没有任何声音提示。我的处理方式分两层。第一层是监听CameraDevice.StateCallback的onDisconnected和onError收到错误后进入“重连流程”释放当前设备等待 50ms重新打开摄像头。第二层是守护线程每 30 秒检查一次是否仍然在正常录制如果发现编码器输出时间戳长时间没有更新就主动触发重连。实测下来重连成功率在绝大多数手机上接近 100%但重连过程中会有约 1~2 秒的录像缺失。这一点无法完全避免除非设备硬件和驱动足够稳定。日常使用中推荐把最后一段不完整的文件也保存下来方便事后通过文件校验逻辑去重。6.2 编码器初始掉帧和预热我第一次跑通完整流程时发现每次开机后的前几秒视频帧率总是忽高忽低严重时能掉到 12fps。查了一圈发现是编码器冷启动的锅HAL 层和编码器内部状态还没完全就绪如果相机帧立刻涌入部分帧会被丢弃。处理办法是加一个预热阶段编码器start()之后先不要立刻让 CaptureSession 开始录制而是让编码器内部跑几百毫秒空转等到第一个输出 buffer 出现后再把相机输入接到录制会话上。这样做的代价是启动画面会慢半拍但换来了启动后平滑的帧率。6.3 高温降级一段从 1080p 到 720p 的自动切换逻辑挡风玻璃下面的手机夏天简直就是个铁板烧。哪怕只是正午晒 20 分钟机内温度就能到 45℃以上。如果这时候还在以 1080p30 高码率录制很容易触发系统热节流CPU/GPU 降频编码器跟不上帧率画面开始卡顿。我加了一个简单的温度监控逻辑通过BatteryManager读取电池温度当电池温度超过 42℃ 时自动把分辨率和码率降级为 720p 6Mbps。这个降级必须在“分段切换点”执行不能中途硬切——也就是等当前 60 秒分段写完下一个分段用新参数创建编码器。实测下来降级后机身温度能下降 3~5℃视频质量虽然下降但至少录制不中断。你也可以根据自己手机的散热情况调整阈值有的手机 45℃ 还活蹦乱跳有的 40℃ 就已经卡成幻灯片需要实测校准。6.4 厂商 ROM 兼容性麒麟、骁龙、天玑的差异最后说一个绕不开的现实问题不同 GPU/SOC 对 Camera2 和 MediaCodec 的实现是有差异的。我手里测试过的机器包括骁龙 845、骁龙 778G、麒麟 990 和天玑 1100。其中麒麟 990 的 3A 曲线明显偏暖同样的DAYLIGHT白平衡设置下颜色偏红天玑 1100 的编码器对I_FRAME_INTERVAL1支持不完美实测会有跳变部分采样点间隔变成 2 秒。更麻烦的是个别设备的 Camera2 HAL 层有 bug预览 Surface 和编码 Surface 分辨率不一致时会出现画面拉伸。针对兼容性我建议你在工程里保留一张“设备能力表”启动时做一次相机和编码器能力检测。比如用codecInfo.getCapabilitiesForType(...)去查实际支持的码率范围和帧率范围再针对检测结果做参数修正。这个表格不需要做得多复杂关键是避免在特定 ABI 上直接按预设参数初始化导致崩溃。问题现象解决方案相机断开录像停止且无提示onError 重连 心跳检测编码器冷启动掉帧开机前几秒卡顿预热阶段 延迟接入 Session温度过高帧率持续下降温度监控 分辨率降级麒麟 3A 偏色画面偏暖手动白平衡修正天玑 I 帧参数异常关键帧间隔不稳定预检测并回退到默认值开发这套系统的过程中我最深的一个体会是行车记录仪并不是一个“能录像的 App”而是一个需要长时间无人值守稳定运行的设备端系统。它的难点不在于单点功能而在于把所有异常情况都考虑进去让系统在断电、高温、手机碎片化这些极度恶劣的条件下依然能优先保证“该录的视频录到了”。如果你也想动手做一个我的建议是先跑通 Camera2 预览和 MediaCodec 分段输出这两条主链路再逐步往上加 GPS 和碰撞检测。先用模拟器把文件逻辑跑稳再上真机能省很多来回折腾的时间。最后一个小技巧在开发阶段打开adb shell dumpsys media.codec可以实时看到编码器的帧率、丢帧情况和码率排查问题比盲目加日志高效得多。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价