标签: loggerd

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

    结论: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

  • loggerd 测试:合成 VisionIPC、qlog 抽样与隐私门

    结论:loggerd 的测试用合成输入覆盖记录器主链,而不是真实相机链路:它自建名为 camerad 的 VisionIPC server,三路各 40 个 320×240 NV12 缓冲,以 20 Hz 等价循环发送,并随机发布 cereal 服务消息。这决定了它的证据边界——它能校验 rlog/qlog 数量与顺序、段文件集合与书签保留,却不能证明真实相机时序、AV 同步、失帧恢复或编码器 buffer 所有权。另外两处易被高估的断言是:test_rlog 没有在循环后断言发送列表已空,test_record_audio 的 ffprobe 未检查退出码。

    输入夹具:合成 VisionIPC 与随机 cereal 服务

    CEREAL_SERVICES 取 Event schema 中同时位于服务表、允许记录且名称不含 encode 的主题。_publish_random_messages() 启动 loggerd,对每个服务等读者;每轮给所有服务发新 Event,随机发 200~1000 轮,每 100 轮及结束时等读者更新,随后停止进程。源文档提醒,这个等待反映消息队列读者进度,不等同于磁盘同步完成。

    _publish_camera_and_audio_messages() 自建 VisionIPC server,三路各 40 个 320×240 NV12 缓冲;设置 LOGGERD_TEST 与段长,启动 loggerd、encoderd 后以 20 Hz 等价循环编号发送三路缓冲/CameraState、每轮 800 个 int16 零音频样本。它不启动真实 camerad,不测传感器寄存器、曝光、DMA 或时钟同步。源文档也说明该测试文件既有可在 PC 上运行的逻辑,也会调用本机编译产物、FFmpeg、VisionIPC 与日志文件。

    源文档特别指出:视频载荷来自 np.empty,未显式初始化为有意义像素;VisionIpcServer.send() 的时间戳参数是 uint64_t,夹具传入 n/fps 浮点秒值,因此不能把测试输出当真实纳秒级相机时间契约。

    夹具/检查 实际内容
    _get_latest_log_dir() 按目录 mtime 取最新日志段;依赖当前测试 Prefix/LOG_ROOT 下的目录列表。
    _gen_bootlog() 调本地 ./bootlog 二进制并在 5 秒 Timeout 内从输出解析文件路径;要求构建产物。
    _check_init_data() 只检查第一条消息类型为 initData;没有检查所有字段。
    _check_sentinel() 用第二条及末条消息核对 route/segment 起止标记。

    各测试的证据范围

    测试 断言与限制
    test_init_data_values() 写入构建身份等 Params,检查 initData 映射、版本、内核字段;AccessToken 虽在参数列表中但记录值必须为空;只覆盖该组参数,不证明其他敏感字段已净化。
    test_bootlog() 写 /tmp/launch_log,要求 bootlog 含两条消息、wall time 约 5 秒内、launchLog 相等;两次 bootlog 的 UID 不同且 BootCount 从 0 到 1;写入的 /tmp/launch_log 未在测试中删除。
    test_qlog() 从有/无 decimation 的服务各随机挑子集,只核对 qlog 中每主题的数量是否符合公式;不核对选中消息具体载荷或跨段抽样相位。
    test_rlog() 随机 5~10 种服务,逐条从相应服务的发送列表头部弹出并比较序列化字节;没有在循环后断言各发送列表已空。
    test_rotation() 随机 2~3 段、4~5 秒/段,要求每段文件名集合恰为 rlog、qlog、qcamera、三路 HEVC;不读取这六个文件的码流或每段内容。
    书签保留两组用例 对有/无 userBookmark 的段核对 xattr;不验证跨进程 xattr 负缓存、上传/删除器的后续保留决策。
    test_record_front() 真/假两种启动前 Params 值,仅比较最新段 dcamera.hevc 是否存在;不核对动态切换、内容、索引或隐私擦除。
    test_record_audio() 真/假两种启动前 Params 值,检查 qcamera 是否有音轨及 rlog 是否出现原始音频;ffprobe 子进程未检查退出码。

    test_qlog/test_rlog 使用随机服务及消息数量且未固定 seed,所以一次通过不能覆盖所有主题/抽样组合。源文档也澄清:test_rlog 的逐条比较确实检查了已读取消息的载荷,不应把缺少尾部断言误写成完全没有内容校验。

    test_rotation() 只要求每段文件名集合恰为六个文件,不读取码流内容,因此它验证的是段切分与命名,而不是每段编码的正确性。

    隐私与录音门的边界

    test_init_data_values() 只覆盖该组参数,源文档明确不证明其他敏感字段已净化。test_record_front() 只比较最新段 dcamera.hevc 是否存在。test_record_audio() 存在一个易漏点:subprocess.run(ffprobe..., capture_output=True) 未检查退出码,命令失败而 stdout 为空时,关闭录音用例的「无音轨」条件仍可能通过。源文档据此要求在插件化时区分「明确无音轨」和「探针失败」。

    书签保留的两组用例只核对 xattr,源文档指出它们不验证跨进程 xattr 负缓存、上传/删除器的后续保留决策;test_bootlog() 写入的 /tmp/launch_log 也未在测试中删除。这些残留与边界提醒说明,测试覆盖的是「可见输入 + 源码断言」,不是端到端的保留与清理保证。

    插件化验证要求

    将记录功能拆成插件时,应保留独立契约:服务注册/抽样、rlog 顺序和完整性、媒体包/索引对应、音频/座舱录制许可、书签后的保留与上传、异常关机后的文件可读性。对每个测试输入记录种子,断言发送与接收双向数量相等;FFprobe 等外部探针须检查退出码。

    真实相机插件还需带纳秒时间戳的帧级对齐、NV12 形状/stride/有效位、坏帧与断流注入。源文档最后限定:全部结论限定于源码断言与可见输入,未运行构建、FFmpeg、进程或媒体生成,因此无法确认各测试在本归档环境的实际通过情况,本 suite 也不能替代实机时序和隐私审计。

    常见问题

    loggerd 测试用的是真实相机吗?

    不是。源文档明确夹具自建名为 camerad 的 VisionIPC server,三路各 40 个 320×240 NV12 缓冲,不启动真实 camerad,也不测传感器寄存器、曝光、DMA 或时钟同步。

    为什么测试里的相机时间戳不能当真实契约?

    VisionIpcServer.send() 的时间戳参数是 uint64_t,而夹具传入 n/fps 浮点秒值,源文档明确不能把测试输出当真实纳秒级相机时间契约,具体转换效果需用构建后的绑定验证。

    test_rlog 通过是否说明日志完整?

    不足以保证完整。它逐条比较从各服务发送列表头部弹出的序列化字节,但没有在循环后断言发送列表已空,因此若日志只保留每主题的正确前缀,尾部缺失不会由这条断言本身发现。

    test_record_audio 判定「无音轨」可信吗?

    需要警惕。源文档指出 subprocess.run(ffprobe..., capture_output=True) 未检查退出码,命令失败而 stdout 为空时,关闭录音用例的「无音轨」条件仍可能通过,插件化时应区分「明确无音轨」和「探针失败」。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/LOGGERD_TEST_SYNTHETIC_VIPC_QLOG_RLOG_PRIVACY_GATES_DEEP_DIVE.md(取用了合成 VisionIPC 与随机 cereal 夹具、各测试证据范围表、隐私与录音门边界、插件化验证要求)。

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

    更新记录

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

  • cereal 消息契约:schema、服务表、valid 与落盘各管什么

    结论:cereal 的消息契约必须拆成四层看:schema 定义、services.py 服务表配置、运行消息有效性、loggerd 落盘结果。union 分支存在不表示进程正在发布,也不表示 loggerd 会记录;logMonoTime 是消息封装时间,valid 也只表示运行有效位,不承诺车辆总线帧通过车型解析或 panda 安全检查。把「字段存在」当作「数据已发布或已记录」是这份契约最常见的误读。

    四层契约各管一段

    schema 层,Event 有 logMonoTime、valid 与 union,carState、carControl、carParams 指向 opendbc 的 car.capnp,customReserved* 保留固定 field ID。服务表层,SERVICE_LIST 声明服务名、期望频率、should_log、qlog decimation 和队列容量,build_header() 生成对应的 C++ 表——频率是配置与健康检查依据,不等于真实发布速率。

    消息构建层,new_message() 默认 valid=False,把 logMonoTime 设为单调时钟纳秒并初始化主题,发布者通常再赋有效位。日志订阅层,loggerd_thread() 订阅 should_log 服务、非直播 EncodeData 和可选原始音频;普通消息按配置写 rlog,按计数抽样写 qlog。因此收到消息、valid=True、内容可信和时戳新鲜是四个不同判断。

    层 源码与含义 不能推出的结论
    schema Event 有 logMonoTime/valid/union;车型消息指向 car.capnp union 分支存在不表示进程正在发布或 loggerd 会记录
    服务表 SERVICE_LIST 声明频率、should_log、decimation、队列容量 频率是配置依据,不等于真实发布速率;should_log=False 也可能有专门编码路径
    消息构建 new_message() 默认 valid=False,设置单调时间 收到、有效、可信、时戳新鲜是不同判断
    日志订阅 loggerd_thread() 按 should_log 与计数抽样写盘 qlog 不是每个主题的完整回放数据

    关键主题与字段语义

    CAN 接入侧,Event 的 can 是从车接收帧,sendcan 是主机候选发送帧;CarState.canValid/canTimeout/canErrorCounter 表示车型解析健康,不能只取上次解码值。services.py 为 can 设大队列与 qlog 高抽样率,因此完整 CAN 分析应使用 rlog。执行决策侧,enabled、latActive、longActive 不等价,Actuators 的扭矩/转角/曲率属互斥横向命令语义,纵向 accel 为 m/s²。

    模型到规划侧,modelV2 的 qlog 抽样未配置,不应拿 qlog 复原所有推理帧;LateralManeuverPlan 是调试覆盖曲率,不是一般模型输出。使能与监督侧,enabled 与 active 分开,警报文本/声音、人机监督策略与注意力状态在独立消息中。摄像头与编码侧,帧标识、曝光时间戳、编码索引/时间戳和视频字节是不同层,图像主数据经 VisionIPC,EncodeData 由 loggerd 分离写媒体文件。

    链路 载荷定义 核心字段与边界
    CAN 接入 Event.can/sendcan、CarState canValid/canTimeout/canErrorCounter 表示解析健康
    执行决策 CarControl、CarParams enabled/latActive/longActive 不等价;safetyConfigs/dashcamOnly/passive 决定执行边界
    模型到规划 ModelDataV2、LongitudinalPlan、LateralManeuverPlan 模型含帧 ID、EOF 时间、耗时、轨迹;modelV2 qlog 抽样未配置
    使能与监督 SelfdriveState、DriverMonitoringState enabled 与 active 分开;监督策略独立消息
    panda 安全状态 PandaState controlsAllowed、模式/参数、RX 无效、TX 阻断、心跳丢失分列
    摄像头与编码 FrameData、EncodeIndex、EncodeData 帧标识、时间戳与视频字节分层;EncodeData 分离写媒体文件

    需要单独记住:Event 的 logMonoTime 是消息封装时间;相机 timestampEof、模型 timestampEof、GNSS Unix 时间和 IMU 自有时间字段并不自动相等。传感器/定位插件必须显式声明时钟域、单位、采样时点和迟到处理。

    Route 与 Segment 文件如何落盘

    元数据由 logger_build_init_data() 记录版本、设备、内核、Git 信息、参数和部分硬件日志,参数值只有不带 DONT_LOG 标志才写入。命名/开段阶段,路由名由递增计数与随机后缀组成,段目录为 <route>--<part>;开段创建 rlog.lock、rlog.zst、qlog.zst,写 initData 与 START sentinel,结束段/路由写 END sentinel 并移除锁。

    压缩阶段,rlog.zst、qlog.zst 是 zstd 压缩的连续 Cap’n Proto Event 字节流,qlog 只在抽样命中时附写。切段时,rotate_if_needed() 与 handle_encoder_msg() 等已出现的编码器进入新段后切段,相机/编码器异常时按时间回退切段;新段编码数据须等关键帧,先于关键帧的帧被丢弃。媒体文件按相机表写 fcamera.hevc、ecamera.hevc、可选 dcamera.hevc 与 qcamera.ts;RecordAudio 打开时 rawAudioData 还会写入 rlog(decimation=None,不进 qlog),这涉及额外的数据保留与隐私审查。

    抽样率是理解落盘差异的关键:can 的 qlog 抽样率为 2053、sendcan 为 139、carState 为 10、modelV2 为 None。编码索引虽通常 should_log=False,但在 write_encode_data() 中专门转写且始终进 qlog。不要用 qlog 作为控制/安全故障分析的唯一依据。

    对插件化与硬件的最低要求

    1. 插件 schema 需要版本、字段号与兼容策略;Cap’n Proto union 的固定编号不能任意复用,customReserved* 约束也不能未经全链路兼容评估就改控制主契约。
    2. 发布权限应按主题分级:观测只读、规划候选、carControl 和 sendcan 分开授权;valid、新鲜度、总线校验与硬件许可各自独立。
    3. 存储插件需保留元数据、段边界、单调时钟、媒体索引、丢帧及部分文件状态;为取证保留 rlog,不可用 qlog 抽样代替。

    常见问题

    消息字段存在,是否说明该数据一定被记录?

    不代表。四层契约是分开的:schema 里 union 分支存在,不表示受管进程正在发布,也不表示 loggerd 会记录;服务表里的频率是配置与健康检查依据,不等于真实发布速率;只有 loggerd_thread() 按 should_log 与抽样配置实际写盘的内容才在日志里。

    为什么不能用 qlog 复原所有推理帧?

    因为 modelV2 的 qlog 抽样配置为 None,carState 抽样率为 10,can 为 2053,各主题抽样并不一致,qlog 只在抽样命中时附写。源文档明确 qlog 不是每个主题的完整回放数据,控制/安全故障分析不要以它作为唯一依据。

    Event 的 logMonoTime 就是传感器采样时间吗?

    不是。logMonoTime 是消息封装时间;相机 timestampEof、模型 timestampEof、GNSS Unix 时间和 IMU 自有时间字段并不自动相等。传感器/定位插件必须显式声明时钟域、单位、采样时点和迟到处理。

    valid=True 是否表示这一帧数据可信?

    不等于。valid 只是运行有效位,源文档明确它「不承诺车辆总线帧通过车型解析或 panda 安全检查」。车端数据是否可信还要看 CarState 的解析健康字段、时间戳新鲜度以及 PandaState 中的许可与故障分列。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/MESSAGE_LOG_CONTRACTS_DEEP_DIVE.md(取用了四层契约、关键主题字段语义、Route/Segment 落盘流程与 qlog 抽样率)。
    • 项目 02 —— openpilot-research/LOGGERD_SERVICE_SAMPLING_POLL_CONTRACT_DEEP_DIVE.md(仅用于确认 loggerd 逐服务订阅与计数抽样相位的边界)。

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

    更新记录

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