4. 录制架构设计:音频源、视频源、编码器绑定、Muxer输出流程

4.1 边界与核心问题

录制架构是多媒体系统的“写端”——将音视频实时输入封装为持久化文件。核心边界是从 传感器驱动层到 文件抽象层,需解决以下矛盾:

  • 实时性:编码与写入不能阻塞采集循环
  • 同步性:音频/视频时间戳需保持统一时间基
  • 容错性:源断开、编码器过载、磁盘满时须优雅降级
  • 格式兼容:Muxer需支持 MP4、WebM、TS 等多容器

4.2 分层与组件职责

整个录制管道划分为 4 个边界清晰的层级:

层级组件职责
源层CameraSource / AudioSource驱动摄像头与麦克风,输出原始帧(NV12/YUV + PCM)
编码层VideoEncoder / AudioEncoder接收原始帧,输出编码后 ES(H.264/H.265/AAC)
绑定层TrackBinder / CodecProxy管理编码器生命周期,处理 MediaCodec 回调,输出复用时间戳
输出层MediaMuxer / FsWriter接收编码轨道数据,封装为容器文件
关键设计约束:编码层与源层之间不允许直接共享 Buffer 指针(跨进程边界),必须通过 Surface/ 共享内存传递,避免 ANR 与内存抖动。

4.3 数据流与控制流

主数据流(顺时针):

  1. CameraSource 通过 Surface 投递 YUV 帧 → VideoEncoder
  2. AudioSource 通过 ByteBuffer 投递 PCM → AudioEncoder
  3. 编码器输出 BufferInfo(含 PTS/标志)→ TrackBinder
  4. TrackBinder 按 PTS 排序 → MediaMuxer 的 writeSampleData()
  5. MediaMuxer 写入文件(MP4 moov box 需在 stop 时更新)

控制流(逆时针):

  • App 层调用 startRecording() → 启动所有源与编码器
  • TrackBinder 监听编码器 onOutputBufferAvailable,背压控制:当 Muxer 写入慢时暂停给编码器注入帧
  • 所有源完成 EOS 后 → 触发 stopRecording() → 编码器 drain → Muxer stop → 文件 finalize

4.4 信任链路与同步策略

录制系统的正确性依赖跨组件的时间基对齐:

  • 时钟源:统一使用 System.nanoTime() 生成 PTS,避免各组件使用不同单调钟
  • 音频驱动 PTS:AudioSource 以 AudioRecord.getTimestamp() 为准,回传纳秒
  • 视频驱动 PTS:CameraSource 以帧回调时间戳 + 帧间隔推算,输入 Surface 时由编码器自动分配
  • Muxer 信任:MediaMuxer 要求所有轨道的 PTS 从 0 开始连续递增;TrackBinder 负责偏移校准
常见的信任断链:当使用第三方编码器(如 HW encoder 复用)时,编码器可能自行修改 PTS 或丢帧,导致 Muxer 输出的视频加速/慢放。必须在测试阶段记录编码器输出 PTS 序列,检查单调递增性。

4.5 选型权衡:软编码 vs 硬编码 vs 混合

维度硬编码(MediaCodec)软编码(FFmpeg / x264)混合方案
功耗低(专用 DSP)高(CPU 满载)中(视频硬编 + 音频软编)
兼容性不同厂商 bitstream 差异完全可预测需处理两者同步
编码质量中(部分厂商弱)高(可调参数多)折中
延迟低(管线优化)高(CPU 排队)可控
推荐策略:Android 10+ 优先使用 MediaCodec 硬编码 + MediaMuxer 原生 API;仅在需要定制编码参数(如 CBR vs VBR 精确码率)或 RTMP 直播场景时引入 FFmpeg 软编码。

4.6 风险点与缓解措施

  1. 编解码器重启延迟:横竖屏切换导致 Camera 重启,编码器需 stop() + release() 后重建 → 预创建备用编码器池
  2. Muxer 写入瓶颈:SD 卡写入慢导致背压 → 使用双缓冲队列 + 超时降级(丢帧而不是丢文件)
  3. 时间戳溢出:长时间录制 PTS 超过 Long.MAX_VALUE → 定期归一化 PTS 基线
  4. 跨进程 CameraSource 死锁:Camera HAL 报错导致回调不触发 → 设置 watchdog 超时自动 stop

4.7 落地步骤(架构实施路线)

  1. Step 1: 实现 AudioSource 与 CameraSource 适配器,分别输出 Frame 对象(含 buf + pts + 标志位)
  2. Step 2: 创建 EncoderManager,封装 MediaCodec 的 configure/start/stop/release,支持动态切换编码器类型
  3. Step 3: 实现 TrackBinder 线程,维护音频/视频两条 ConcurrentLinkedQueue,按 PTS 归并后喂给 Muxer
  4. Step 4: 集成 MediaMuxer,添加文件命名策略与临时文件原子重命名
  5. Step 5: 添加背压监控指标(队列积压大小),在 onStateChanged 回调中触发降级逻辑

4.8 验收标准

  • 录制 60 秒 1080p30 + 立体声 AAC,输出 MP4 文件音画同步误差 < 40ms
  • 连续录制 1 小时无崩溃、无漏帧(对比原始帧计数)
  • 切换摄像头(前后)可连续录制,新片段 PTS 从 0 开始
  • 磁盘满时回调 onError(ERROR_FILE_LIMIT),不生成损坏文件
  • 横竖屏旋转 50 次后录制依然正常启动
录制管道架构:源 → 编码器 → 绑定 → Muxer CameraSource AudioSource VideoEncoder AudioEncoder TrackBinder MediaMuxer MP4 / WebM 关键说明: • 编码器输出 BufferInfo 中的 PTS 由 TrackBinder 统一校准为 0 基准 • 背压控制:当 Muxer writeSampleData 返回 ERROR 时,TrackBinder 暂停从编码器取数据 控制流(start/stop)