标签: 视频编码

  • 视频编码:录制与直播分流,切段由帧号阈值决定

    结论:视频编码链里「录制」和「直播」是两条分流:loggerd 订阅非直播 EncodeData 并转为媒体文件与 EncodeIdx,直播主题由 WebRTC 消费、不按录制媒体流写盘。切段由帧号阈值决定——每路按 SEGMENT_LENGTH × MAIN_FPS 关闭/重开编码器,段长通常 60 秒、20 FPS,但输入缺帧时不能只按墙上时钟推断切段位置。

    本文基于固定源码快照的静态阅读,未取得完整 LFS/平台环境,也未在相机或 V4L 编码硬件上运行。采集端与落盘端属于相邻专题,这里聚焦编码进程、多相机同步、后端差异与切段/索引。

    进程、相机和消息边界

    encoderd 随 onroad 运行;stream_encoderd 用同一可执行文件加 --stream,在直播或非汽车模式运行;部分硬件另有 --clip 离线导出入口,不属于这两个实时进程。三者的输入都是 camerad 通过 VisionIPC 发布的帧。

    边界 已核对行为
    进程选择 encoderd 随 onroad 运行;stream_encoderd 同可执行文件加 --stream;--clip 是离线导出,不属于实时进程
    相机表 窄路面流发主录制流(硬件 HEVC、PC FFVHUFF)与低码率 qNarrowRoadEncodeData;广角、座舱各有主流
    座舱流 座舱主流的 record 取决于 RecordFront
    输入 启动时探测可用 VisionIPC 流,每个可用流起一个编码线程;连接后从帧缓冲及 VisionIpcBufExtra 读取帧号/时间戳
    输出 发布编码字节、尺寸、Unix 发布时刻、帧 ID、SOF/EOF、编码 ID、段号/段内 ID 与关键帧标记
    时间域 logMonoTime、采集 EOF 和 Unix 发布时间是不同时间域
    录制/直播分流 loggerd 订阅非直播 EncodeData 转媒体文件;直播主题由 WebRTC 消费,不写盘

    实时调度与切段

    sync_encoders() 意图在多编码器起始阶段汇总可用流帧号,把共同起点推到当前帧后两个帧号,每路达到阈值才编码。但父线程在增加 max_waiting 的同时逐个启动子线程,等待数尚未冻结,早启动的线程可能误走单路分支。即使门正常通过,也只同步起始帧 ID,不等于整段硬件时钟同步。

    encoder_thread() 检查 VisionIPC 缓冲自身帧 ID 与附加元数据的帧 ID 是否相等,失配则跳过,首次落后时记日志。每路按 SEGMENT_LENGTH × MAIN_FPS 的帧号阈值关闭/重开编码器。

    缩略图有独立的节拍:同一窄路面帧会分别送主编码器和 qcamera 编码器;缩略图由 JpegEncoder::pushThumbnail() 在 frame_id % 1200 == 100 时生成,使用 JPEG 消息发布,不是每帧都有缩略图。

    loggerd.handle_encoder_msg() 对齐编码器段号与当前日志段,新段创建 writer、等待关键帧后写入媒体,并把 EncodeData.idx 转为 EncodeIdx 写入事件日志。encoderd、loggerd 独立启停,段号对齐依赖首次收到的偏移,不能把编码器内部段号直接当路由段号。

    切段与索引的对应关系需要端到端核对:编码器按帧号阈值分段,loggerd 再按段号把媒体写入文件并把 EncodeData.idx 转为 EncodeIdx;两个进程独立启停,因此「编码器内部段号」与「路由段号」不能默认相同。跨段队列、音频等待与段边界处的关键帧对齐,都需要单独验证,不能只凭媒体文件存在就判定编码链完整。

    编码后端:硬件与 PC 不等价

    V4LEncoder 打开 Qualcomm V4L2 编码设备、提交 NV12 缓冲;后台 dequeue_handler() 接收配置头和编码包,以 EOF 时间戳断言输入/输出配对,关键帧带配置头发布,段结束时等待队列排空及 EOS。这些时序需实板确认。

    后端 行为与限制
    硬件(V4L) 打开 Qualcomm V4L2 编码设备、提交 NV12;以 EOF 时间戳断言输入/输出配对,关键帧带配置头发布,段结束等待队列排空及 EOS
    PC(Ffmpeg) NV12 转 I420、必要时缩放;qcamera 用 H.264,其他流用 FFVHUFF;header 为空,set_bitrate() 和 request_keyframe() 仅记「不支持」
    缩略图 NV12 下采样经 FFmpeg MJPEG 输出 thumbnail;尺寸与源流比例用断言约束

    软件后端不是硬件 HEVC 的等价替代。直播时 encoder_set_bitrate() 与 encoder_request_keyframe() 每帧读取参数,WebRTC 侧通过 LivestreamEncoderBitrate 和 LivestreamRequestKeyframe 交互;PC 后端只记录不支持。把直播插件换成独立编码器时,应明确参数所有权、关键帧请求的确认与超时。

    故障边界与验证

    源码可见的故障路径包括:VisionIPC 尚未可用时重试、帧号失配跳帧、编码失败记录、V4L poll 超时继续等待、V4L 输入/输出时间戳失配断言。一个容易被忽略的边界是:外层 VisionIPC 重连后并未清空已创建的编码器或重做多相机起始同步;若新流尺寸/帧号语义改变,恢复行为需要专门测试,不能从重连循环存在就推断安全恢复。

    故障路径 当前处理 未覆盖的部分
    VisionIPC 不可用 重试 重连后未清编码器、未重做起始同步
    帧号失配 跳过该帧,首次落后时记日志 恢复后帧号语义改变的行为待测
    V4L poll 超时 继续等待 无独立截止语义
    输入/输出时间戳失配 断言 需实板确认

    test_encoder.py 是硬件测试入口,会检查媒体文件、帧数、编码索引及多相机首帧一致性;本次未运行。面向新硬件,至少应验证缓冲所有权、帧号/时间戳、编码包与索引一一对应、关键帧切段、断流重连和录制/直播互不抢占。

    常见问题

    录制和直播走的是同一条链路吗?

    不是完全同一条。loggerd 订阅非直播 EncodeData 并转为媒体文件与 EncodeIdx;直播主题由 WebRTC 消费,不按录制媒体流写盘。两者共享编码器输出,但下游消费与落盘路径不同。

    切段是按墙上时钟还是按帧号?

    按帧号阈值。每路按 SEGMENT_LENGTH × MAIN_FPS 关闭/重开编码器,段长通常 60 秒、20 FPS;输入缺帧时不能只按墙上时钟推断切段位置。

    VisionIPC 重连后就等于安全恢复了吗?

    不能这样推断。源码里外层重连后并未清空已创建的编码器或重做多相机起始同步;若新流尺寸或帧号语义改变,恢复行为需要专门测试。重连循环存在,不等于恢复过程安全。

    PC 上的软件编码能替代硬件编码吗?

    不能当成等价替代。硬件走 Qualcomm V4L2、关键帧带配置头、段结束等待 EOS;PC 后端 NV12 转 I420、header 为空,set_bitrate() 和 request_keyframe() 仅记「不支持」。直播插件替换编码器时要明确参数所有权与关键帧确认。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/VIDEO_ENCODING_DEEP_DIVE.md(取用了编码进程与相机表、实时调度与切段、三类编码后端、故障边界与验证范围)。
    • 项目 02 —— openpilot-research/CAMERA_IMU_DEEP_DIVE.md(仅用于确认采集端 VisionIPC 帧边界,未取用其未公开内容)。

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

    更新记录

    首次发布 2026-10-11 最近核对:2026-10-11

  • 编码配置表:录制、音频与直播的进程快照分层

    结论:loggerd 的相机/编码表是进程静态对象,不是运行时可重建的配置:encoderd 与 loggerd 各自加载同一头文件并构造本进程的静态表,运行中改参数不会重建配置表。RecordFront=false 在这条路径只代表「不写本地媒体文件」,座舱流仍会发布 cabinEncodeData 并可能写派生索引;RecordAudio 至少参与两个独立时刻的读取,二者不在同一事务。直播是独立的 stream_encoderd 进程与主题,录制与直播的保留/远程访问策略必须独立设计。

    静态描述表与进程边界

    EncoderSettings 给出编码参数:主流按输入宽度选 5/10 Mbps、GOP 20/30;qcamera 为 H.264、256 kbps、GOP 15;直播默认 H.264、5 Mbps、GOP 5,默认 B 帧数为 0。EncoderInfo/LogCameraInfo 由 topic、文件名、录制/音频/直播标志、输出尺寸、FPS 和 Cap’n Proto union 读写函数指针组成。

    编码目标 编码与码率 GOP 默认 B 帧
    主流(按输入宽度) 5/10 Mbps 20/30 0
    qcamera H.264、256 kbps 15 0
    直播默认 H.264、5 Mbps 5 0

    cameras_logged 含窄路面主流与 qcamera 两个编码器(共享同一路 VisionIPC 帧),广角与座舱各一个;stream_cameras_logged 是三路独立直播主题,record=false,没有媒体文件名也不配置缩略图。encoderd main() 无参数使用录制表,--stream 使用直播表,两种是同一可执行文件的不同进程模式。

    录制策略不是编码生产开关

    RecordFront 在座舱编码信息的静态初始化期间读一次,用作 record 字段。encoderd 的编码线程没用该字段过滤编码器:座舱流一旦被发现,仍会发布 cabinEncodeData。loggerd 也仍订阅非直播编码主题;record=false 时不创建 dcamera.hevc writer,但通过关键帧门后仍写 cabinEncodeIdx。

    因此源文档的结论是:「关闭座舱录制」在这条路径代表不写本地媒体文件,不是停止座舱图像采集/编码,也不是证明下游没有索引。直播座舱流使用独立 topic 和进程,不能由 RecordFront 的本地文件策略自动推导其权限。

    RecordAudio 的两个读取时刻

    RecordAudio 至少参与两个独立时刻:qcamera 编码信息的 include_audio 在各进程静态初始化时读取;loggerd_thread() 建订阅时再次读取,只有当时为真才订阅原始音频。micd 的 callback() 本身持续发布 rawAudioData,并不以 RecordAudio 阻止采集。

    在正常一致的启动快照下,loggerd 收音频后把它同时作为 rlog 普通事件和 qcamera 音轨输入。但两次 Params 读取不在同一事务,运行中切换也不会刷新已有订阅或配置表:若启动窗口内值变化,静态 include_audio 与实际订阅可能不一致。源文档指出,例如等待音频的 qcamera writer 未得到音频初始化包,会走有界包数的等待/丢包路径,是否在常规 manager 时序下可达需故障注入验证。

    平台格式、直播与时间边界

    项 静态行为
    MAIN_ENCODE_TYPE 依 Hardware::PC() 选择 PC 的 BIG_BOX_LOSSLESS 或设备的 FULL_H_E_V_C;固定 fcamera.hevc 文件名不能单独证明实际编码为 HEVC。
    直播输出尺寸 livestream_width()/height() 对 TIZI 为 1152×720、MICI 为 1280×720,其他设备返回 -1;VideoEncoder 构造器对非正输出尺寸退回输入尺寸。
    STREAM_BITRATE 用 atoi 读取且无配置层范围校验;运行时 LivestreamEncoderBitrate 由上层逐帧读 Params,PC 后端不支持动态调整。
    SEGMENT_LENGTH 通常固定 60 秒;LOGGERD_TEST 存在时改用 atoi(LOGGERD_SEGMENT_LENGTH),是测试配置入口,不应作为生产插件 API。

    直播三个 EncoderInfo 都没有文件名,且 loggerd 订阅时明确排除以 livestream 开头的 EncodeData,WebRTC 另行消费这些主题。录制与直播可以同用相机源,但数据保留/远程访问策略必须独立设计。两个进程各自加载同一头文件并构造本进程的静态对象,因此不能把它们的 Params 快照视为原子一致。

    验证与重构要求

    已有测试检查多路段媒体与索引、座舱文件有无、qcamera 音轨和 rlog 原始音频;源文档指出它们未直接断言 RecordFront 运行中变化时生产端是否继续编码、跨进程 RecordAudio 快照竞争、直播/录制权限隔离或静态表重建。本轮未运行这些测试。

    插件宿主应将「采集许可、编码生产、本地存储、音频复用、上传、远程直播」拆为不同能力,并在一个版本化配置事务中确认生效时刻。隐私开关变更需要可观察的撤销/排空及下游回执,而不是只写 Params;录制与直播要按 topic、相机和车辆状态独立授权。台架验收应覆盖启动竞态、开关热变、进程重启不一致、qcamera 等音频、座舱关闭录制但仍发布 EncodeData、直播单独启停及文件/索引残留。

    常见问题

    关闭座舱录制(RecordFront=false)会停止座舱图像采集吗?

    不会。源文档明确 encoderd 的编码线程没有用该字段过滤编码器,座舱流一旦被发现仍会发布 cabinEncodeData;record=false 只是不创建 dcamera.hevc writer,但通过关键帧门后仍写 cabinEncodeIdx。

    RecordAudio 关掉后麦克风就停止采集了吗?

    不会。源文档指出 micd 的 callback() 本身持续发布 rawAudioData,并不以 RecordAudio 阻止采集;RecordAudio 只影响 qcamera 的 include_audio 与 loggerd 是否订阅原始音频。

    固定文件名 fcamera.hevc 能否说明实际编码是 HEVC?

    不能。MAIN_ENCODE_TYPE 依 Hardware::PC() 选择 PC 的 BIG_BOX_LOSSLESS 或设备的 FULL_H_E_V_C,源文档明确固定文件名不能单独证明实际编码为 HEVC。

    运行中修改录制/音频参数会立即生效吗?

    不会。源文档指出配置在进程静态初始化时读取,运行中切换不会刷新已有订阅或配置表;两次 RecordAudio 读取也不在同一事务,启动窗口内值变化可能造成 include_audio 与实际订阅不一致。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/ENCODER_CONFIG_RECORDING_LIVESTREAM_SNAPSHOT_DEEP_DIVE.md(取用了静态编码配置表、录制/音频/直播的进程快照边界、平台格式与时间边界、重构验证要求)。

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

    更新记录

    首次发布 2026-10-11 最近核对:2026-10-11