AI 参与说明(Agent:Codex):本文由 Codex 根据 ITU-T、ISO、AOMedia、WebM Project、MP4 Registration Authority、IETF、MCAP、Foxglove、Apple、OpenCV、GenRobot 与 FFmpeg 的一手资料辅助调研、撰写和校验,并用 FFmpeg 8.0 与 Python
mcap1.4.0 实测文中的示例。资料核验于 2026-08-28;关于 GenRobot 的部分以公开文档、公开代码和可访问界面为范围,不构成其产品安全、隐私或稳定性的完整审计。播放器、硬件解码器和 FFmpeg build 的支持范围可能不同,交付前仍需在目标设备上验证。
先给结论
H.264 和 MP4 不在同一层:
- H.264,也称 AVC,是视频编码标准,规定如何表示和解码压缩后的视频;它不负责音频。H.264/AVC 这套标准在 ITU-T 与 ISO/IEC 体系中分别编号为 H.264 与 ISO/IEC 14496-10,各版发布时间可能不同。
- MP4 是容器格式,负责把已经编码的视频、音频、字幕、时间戳和元数据组织成文件。当前 ISO/IEC 14496-12:2026 定义 ISO Base Media File Format(ISOBMFF),ISO/IEC 14496-14:2020 则定义由它派生的 MP4 file format。
因此,一个 .mp4 文件可以装 H.264 视频,但 .mp4 并不等于 H.264;H.264 也可以出现在 Matroska(.mkv)、MPEG-TS 等其他容器里。ISO 还用 ISO/IEC 14496-15:2024 专门规定 AVC 等 NAL unit structured video 在 ISOBMFF 中的存储方式。
flowchart TB
A[原始视频帧 Raw video frames] -->|H.264 encoder| B[H.264 video stream]
C[原始音频 Raw audio] -->|AAC encoder| D[AAC audio stream]
B --> E[MP4 muxer]
D --> E
F[Subtitles 与 metadata] --> E
E --> G[example.mp4]
日常说“用 H.264 codec”通常能被理解,但严格说,H.264 是编码标准与 bitstream format;具体 encoder 或 decoder 才是实现,例如本文通过 FFmpeg 的 libx264 wrapper 调用 x264 encoder。
名称与标准谱系
视频领域的名称常把标准组织、标准编号和市场名称混在一起。最重要的对应关系如下:
| 常用名称 | 同一标准的其他名称 | 谱系与说明 |
|---|---|---|
| H.264 | AVC(Advanced Video Coding)、MPEG-4 Part 10、ISO/IEC 14496-10 | ITU-T 与 ISO/IEC MPEG 的联合标准 |
| H.265 | HEVC(High Efficiency Video Coding)、MPEG-H Part 2、ISO/IEC 23008-2 | H.264 的后继联合标准 |
| H.266 | VVC(Versatile Video Coding)、MPEG-I Part 3、ISO/IEC 23090-3 | H.265 的后继联合标准 |
| MPEG-4 Visual | MPEG-4 Part 2、ISO/IEC 14496-2 | 较早的 MPEG 视频标准,不是 H.264 |
| VP8、VP9 | WebM video codecs | 由 Google/WebM Project 发布的两代规范 |
| AV1 | AOMedia Video 1 | Alliance for Open Media 制定的开放视频标准 |
因此,H.26x、MPEG 与 AOM 不是三个互不相交的“文件格式清单”:H 是 ITU-T H-series Recommendation 的编号前缀,并不是 HEVC 的缩写;MPEG 的 Part 号用于标识同一标准套件中的组成部分,也不是 codec 的版本号。H.264、H.265、H.266 同时拥有 ITU-T 名称和 MPEG Part 名称;MPEG-4 Part 2 则是另一套较早的 bitstream,不能因“MPEG-4”几个字就与 H.264 混为一谈。VP8/VP9 所属的 WebM Project 与制定 AV1 的 AOMedia 也是不同项目,虽有产业参与者和技术路线的延续,AV1 的正式名称仍不是“VP10”。这些对应关系可在 ITU-T video coding 页面、JVET 页面 与 AOMedia AV1 specification 中核对。
这些名字可以逐层读:H.265 是 ITU-T Recommendation 编号,HEVC 是 High Efficiency Video Coding 的缩写,ISO/IEC 则把同一标准编为 MPEG-H Part 2。这里 MPEG 源自 Moving Picture Experts Group;MPEG-4、MPEG-H、MPEG-I 是标准套件名称,不是按 4、5、6 顺序排列的 codec 版本。x264、x265 只是 encoder 项目名,名称中的 x 也不代表另一套标准。
标准、encoder、wrapper 与硬件不是一回事
以 H.264 和 H.265 为例,各层名称应这样读:
| 层次 | H.264/AVC 示例 | H.265/HEVC 示例 |
|---|---|---|
| 标准与 bitstream | H.264 / AVC | H.265 / HEVC |
| software encoder | x264 | x265 |
| FFmpeg wrapper | libx264 | libx265 |
| hardware encoder | h264_videotoolbox、h264_nvenc、h264_qsv | hevc_videotoolbox、hevc_nvenc、hevc_qsv |
| MP4 sample entry / codec string | avc1、avc3 | hvc1、hev1 |
| container | MP4、Matroska、MPEG-TS 等 | MP4、Matroska、MPEG-TS 等 |
x264、x265 是 CPU software encoder;FFmpeg 的 libx264、libx265 是调用相应 library 的 wrapper,不是新编码标准。VideoToolbox、NVENC、QSV 则调用 Apple、NVIDIA、Intel 的硬件能力,是否可用取决于芯片、驱动和 FFmpeg build;它们与 software encoder 即使输出同一种 bitstream,在速度、画质、延迟和可调参数上也不必相同。FFmpeg 官方将 libx265 明确定义为 x265 H.265/HEVC encoder wrapper。
H.264 与 H.265 怎么选
HEVC 的设计目标是在相近主观质量下比 AVC 更节省传输或存储码率,但实际结果受素材、encoder、preset、rate control 和延迟约束影响,不能套用一个固定百分比。
直观地说,H.264 主要围绕 16×16 macroblock 组织画面,而 HEVC 改用最大可达 64×64、还能递归细分的 Coding Tree Unit,并增加更灵活的 prediction、transform 与 filtering 工具。编码器因此更容易用大块表示平坦区域、用小块表示复杂边缘;代价是编码时需要搜索更多可能性。RFC 7798 的 HEVC 概览 给出了这些结构差异。
| 维度 | H.264 / AVC | H.265 / HEVC |
|---|---|---|
| Compression efficiency | 成熟、易获得稳定结果 | 通常更省码率,仍应以目标素材实测 |
| 编解码复杂度 | 软件与旧硬件负担通常较低 | 软件编码通常更重;有硬件支持时结论会变化 |
| Compatibility | 老设备、浏览器与实时链路通常更稳妥 | 新设备和 4K 播放较常见,但 Web 与目标平台须逐项验证 |
| Typical use cases | 通用分发、直播、视频会议、兼容性优先 | UHD/4K、长视频存储、带宽或容量更敏感的交付 |
| Profile / Level | 常见 Baseline、Main、High 与 Level | 常见 Main、Main 10,以及 Tier / Level |
Profile、Level、Tier 都是能力与约束信号,不是“低中高画质 preset”。codec 名称也不能单独证明 bit depth 或 HDR:HEVC Main 可以是 8-bit,Main 10 才允许更高 bit depth;HDR 还依赖 transfer characteristics、colour primaries、matrix coefficients、静态或动态 metadata,以及完整的显示链路。交付前应以 ffprobe 检查 profile、pix_fmt 和 color metadata,再到目标终端实测。ITU-T 的 HEVC 项目说明 与 ISO/IEC 23008-2 给出了标准定位。
Container 里面有什么
容器可以理解为带时间轴的“盒子”。一个 MP4 文件通常包含若干 track,例如:
- 一个 H.264 video track;
- 一个 AAC audio track,也可能有多语言 audio track;
- 可选的 subtitles、章节、封面、拍摄时间、旋转信息等 metadata;
- 用于同步播放的 timing、索引和结构信息。
ISOBMFF 标准明确包含 timed media 的 timing、structure 与 media information;MP4 Registration Authority 也分别登记了 video、audio、subtitles、text 和 metadata 等 sample entry code。MP4RA codec registry 中既有 H.264/AVC 的 avc1,也有 AV1 的 av01,说明 MP4 family 并不绑定单一视频编码。
需要注意,avc1 是大小写敏感的 sample entry 或 codec string 前缀,不是 H.264 的同义词。avc1 / avc3、hvc1 / hev1、av01 描述 MP4 family 内的 sample entry 或 codec string,不是 encoder,也不是 container。IETF 的 RFC 6381 给出了 video/mp4; codecs="avc1.640028" 的例子,其中还携带 High Profile、Level 4.0 等信息。
按 MP4RA codec registry 的定义,hvc1 要求 HEVC parameter sets 只放在 sample entry;hev1 允许它们位于 sample entry,也允许随 media samples 携带。两者是 HEVC 在 ISOBMFF 中的不同承载约束,不是两个视频 codec;只改 FourCC/tag 并不能自动重排 parameter sets。MP4RA 的登记也只说明 code point 有规范定义,不能据此推断所有播放器、浏览器或 MP4 profile 都支持它。
MCAP:机器人数据记录容器,而不是 H.264 或 MP4 的替代品
MCAP 是一个用于记录带时间戳的 pub/sub messages 的模块化 container file format,message 可以使用任意 serialization format。它的首要目标是把 camera、IMU、pose、LiDAR 和诊断等多个 topic 保留在同一条时间线上,而不是定义一个通用的音视频播放文件。MCAP Format Specification 将文件定义为 <Magic><Header><Data section>[<Summary section>][<Summary Offset section>]<Footer><Magic>;Data section 的核心 records 是 Schema、Channel 和 Message,可选 Chunk、Summary 与 index 用于 compression、快速汇总和 seek。
| 层次 | 它解决的问题 | 典型单位 |
|---|---|---|
| H.264 | 如何压缩和解码 video | NAL units / video bitstream |
| MP4 | 如何组织可播放的 audio、video 与 timed tracks | track / sample |
| MCAP | 如何按时间记录并检索多个 pub/sub data streams | Schema / Channel / Message |
因此,.mcap 可以包含 H.264,但不是因为 MCAP 自己变成了 video codec 或 MP4。MCAP 的 Channel.message_encoding 说明的是 message 的 serialization,例如 protobuf、cdr 或 json;Schema.encoding 又独立说明 schema 的 encoding。媒体 codec 应由具体 schema 或应用约定描述,而不能把 h264 当作 message serialization。这个区分可在 MCAP registry 中核对。
H.264 在 MCAP 里的一种具体承载方式
Foxglove CompressedVideo 是一个具体而非通用的约定:它把一个 compressed video frame 放进 message 的 data,以 format 标识 h264、h265、vp9 或 av1。若 format 是 h264,data 必须是 Annex B 格式,且一条 message 要包含恰好解出一帧所需的 NAL units;IDR message 还必须带有 SPS。Foxglove 对这种 message 不支持含 B frames 的 video stream,因为 B frames 需要 lookahead。
flowchart TB
A[Raw camera frames] --> B[H.264 encoder]
B --> C[Foxglove CompressedVideo message<br/>format: h264; data: Annex B NAL units]
D[IMU, pose, LiDAR and diagnostics messages] --> E[MCAP writer]
C --> E
E --> F[recording.mcap]
上图只展示 Foxglove schema 的一种视频管线。这个 schema 可使用 ROS 1、ROS 2、JSON、Protobuf、FlatBuffers 或 OMG IDL 等受支持的 message encoding 实现;具体 MCAP 文件是否能被某个 viewer 正确解码,仍取决于它的 schema、codec、bitstream 约束与该 viewer 的 platform support。
这也解释了转换边界:将 .mcap 改名为 .mp4,或直接把它当作 ordinary -c copy remuxing 输入,都不是有效的 MCAP-to-MP4 转换。正确流程是先用 MCAP-aware reader 解析目标 channel、schema、message payload 与 timestamp,再由应用把已提取的 video elementary stream 映射到目标媒体容器;如果 payload 已经是目标接受的 H.264 bitstream,后半段才可能是无重新编码的 remux。这样做不能自动保留或表达其他 sensor topic 的语义和时间同步关系。
最小可复现实例:写入并读取一条带 schema 的 MCAP message
下面的 Python 示例使用 mcap==1.4.0 写入一个 JSON message,再读取它。它不是视频示例,目的是把 Schema、Channel、Message 三层关系与 H.264/MP4 的 codec/container 层次分开。安装方式与 API 以 MCAP Python library 的官方示例 为准。
python -m pip install "mcap==1.4.0"
python write_read_mcap.py
# write_read_mcap.py
import json
from mcap.reader import make_reader
from mcap.writer import Writer
path = "sample.mcap"
with open(path, "wb") as stream:
writer = Writer(stream)
writer.start()
schema_id = writer.register_schema(
name="sample",
encoding="jsonschema",
data=b'{"type":"object"}',
)
channel_id = writer.register_channel(
schema_id=schema_id,
topic="sample_topic",
message_encoding="json",
)
writer.add_message(
channel_id=channel_id,
log_time=42,
publish_time=42,
data=json.dumps({"sample": "test"}).encode(),
)
writer.finish()
with open(path, "rb") as stream:
reader = make_reader(stream)
for schema, channel, message in reader.iter_messages(topics=["sample_topic"]):
print(f"{channel.topic} ({schema.name}): {message.data.decode()}")
在 macOS、Python 3.14.0、mcap 1.4.0 上,预期输出是:
sample_topic (sample): {"sample": "test"}
实际检查已有 recording 时,可使用 MCAP CLI 的 mcap info recording.mcap 查看 topic、messages、duration、compression 与 chunks,或用 mcap doctor recording.mcap 检查文件结构。支持 indexed reading 的文件可以借助 Summary 与 Chunk Index 为本地或 remote seek 减少读取的数据量;但 Summary、message index 都是 optional,不能假定每个 .mcap 都具备它们。
GenRobot 的采集案例:H.264、MCAP 与 Ego
GenRobot 的 Open Data 页面 把用户给出的 monitor.genrobot.click 链接标为 Task Visualization。该公开页面当前提供 Open local file、topic 选择、播放时间线,以及图像、图表和 3D 面板;从这些可见功能可以确认,它是与其公开数据相关的 MCAP 可视化入口。
但不要把该网页等同于完整的 Matrix Studio。官方手册将 Matrix Studio 描述为运行在 Linux、Docker 和 GPU 环境中的数据处理系统,涵盖 Data Viz、sensor calibration 和 Ground Truth Production;其默认启动方式是本机 localhost,也可显式开启局域网 HTTPS 访问。Matrix Studio User Manual 还说明,它可从本地数据目录读取 MCAP、显示 topic 汇总和时间统计。最新 DAS Ego 手册给出的可视化域名是 monitor.genrobot.com,而公开数据页链接的是 .click 域名;目前没有一手资料证明两个域名是完全等价的部署,因此应分别按实际页面和版本验证。
Ego 在这个采集场景中是什么意思
这里的 Ego 来自 egocentric,意为“以采集者自身视角和初始姿态为中心”,不是一种视频 codec、MCAP profile,也不能泛化为所有机器人系统里的“机器人自我”。在 GenRobot 的命名中,DAS Ego 是 DAS 产品线中的第一人称、头戴式、多模态数据采集设备,也是 ego-centric data collection 的入口。DAS Ego Manual v2.0 说明它由六个广角 RGB camera、IMU 与 audio 等组成,并应佩戴在眉毛附近以获得第一人称视角。
对一份 DAS Ego + Finger merged package,官方当前数据说明使用以下设备编号;文档也明确提示该命名未来可能调整:
| MCAP 前缀或 topic | 当前含义 | 为什么需要和 H.264 区分 |
|---|---|---|
robot0 | DAS Ego | 是头戴式第一人称采集设备,不是 video stream 本身 |
robot1、robot2 | 左、右 DAS Finger | 代表与 Ego 对齐的两台手部采集设备 |
/robot0/sensor/camera0..5/compressed | 六路 RGB camera stream,30 Hz | GenRobot 对 DAS Ego 的主题说明将其列为 H.264 encoder;H.264 只描述这些 payload 的 video encoding |
/robot0/sensor/imu、/robot0/vio/eef_pose | 200 Hz IMU 与 30 Hz 的 6D pose | 这些不是视频,正是 MCAP 同步记录的价值 |
| calibration、audio、handtracking、validity topics | camera 内外参、audio、手部关键点和数据有效性等 | 它们不能被塞进普通 MP4 track 后仍保留相同的机器人数据语义 |
这些 topic 的具体 schema、频率与坐标说明见 DAS Ego Data Introduction 和 DAS Ego-Finger Data Introduction。其中 DAS Ego 在首次采集时建立 world coordinate frame;/robot0/vio/eef_pose 记录其在该 world frame 中的 pose,/robot0/vio/relative_eef_pose 则以 time 0 的 DAS Ego pose 作为原点记录相对变化。把 Ego、左右 Finger 放进同一套时间与坐标参照,正是一个 video file 单独难以表达的部分。
因此,在这个具体系统中可以把一份 recording 理解为:H.264 用于压缩各路 camera payload,MCAP 用于把这些 payload 与 IMU、pose、calibration、audio 和其他 message 按 timestamp 关联;Ego 则说明这些 data 的第一人称采集参考系与设备角色。它们不是同一层的替代名词。
为什么 .mcap 不能直接“转成一个原样 MP4”
GenRobot 公开的 das-datakit 是面向其 topic 和 Protobuf schema 的解析与转换工具,不应视为任意 MCAP 的通用语义 decoder。其公开实现会针对已知 topic 解析相机 payload;mcap_decoder.py 先解码相机帧,再以 mp4v 写出 camera0_output.mp4。图像 topic parser 与 转换示例 表明,这条路径属于 decode 后的 video export,而不是把原始 H.264 packet 无损 -c copy remux 到 MP4。
这给出一个实用判断:若目标只是查看或训练某个 GenRobot dataset,应先使用其匹配版本的 schema 与 SDK;若目标是制作播放视频,应明确接受解码再编码,或者先证明某个 camera channel 的 payload、timestamp 与 bitstream 能满足目标 MP4 muxer 的要求。两种流程都不会自动把 IMU、pose 和 calibration 的机器人语义变成普通视频 metadata。
数据与隐私边界
Open local file 表明公开 viewer 支持从浏览器选择本地文件,但单靠这个 UI 不能证明任何录制数据都不会离开设备。DAS Ego 官方手册既提醒采集数据可能包含敏感信息、需取得录制许可,也提供把录制数据上传至配置 object storage 的模式;Matrix Studio 也支持网络访问模式。处理含人像、audio、地点或商业场景的 recording 时,应先在受控的本地或自托管环境中运行,并以实际网络流量、所用版本和组织数据政策确认边界,而不是从文件选择控件推断隐私保证。
ARKit、世界位姿与采集坐标系
本节只解释 iOS ARKit 的 ARFrame 与 ARCamera 语义,帮助阅读带有 world、pose 或 relative pose 的第一人称采集数据。它不证明 GenRobot、DAS Ego 或任何名为 vio 的 topic 使用了 ARKit,也不证明其字段与 ARFrame.camera.transform 一一对应;产品归因必须以设备 SDK、schema 和坐标约定为准。
ARFrame.camera.transform 表示什么
ARKit 在每个 ARFrame 中提供 camera;ARCamera transform 的官方定义是“camera 在 world coordinate space 中的位置与方向”。world 与 camera coordinate system 都遵循 right-handed convention;camera local coordinate system 不随 UI device orientation 改变。记录 image 与 pose 时,应以 ARFrame timestamp 的 capture time 配对,而不是把 delegate callback 的到达时间当作 image capture time。Apple 还单独提供 ARCamera viewMatrix(for:),并明确将它定义为从 world space 到 camera space 的 transform。
为避免把方向写反,下面采用 homogeneous column-vector 的记号:T_A←B 表示把 B coordinate space 中的点变换到 A coordinate space。依据上述 API 定义,可把某时刻的 camera pose 记为:
T_W←C(t) = frame.camera.transform
p_W = T_W←C(t) · p_C
T_C0←C1 = inverse(T_W←C(t0)) · T_W←C(t1)
第三行表示“time t1 的 camera pose,用 time t0 的 camera coordinate system 表达”。调换两个矩阵的顺序会得到相反方向的 transform;只相减 translation 也会丢失 rotation。这里的 T_W←C 是为了说明矩阵方向而采用的工程记号,不是 ARKit 的字段名或某个 GenRobot message 的既定 serialization。若渲染或投影路径需要 world-to-camera matrix,应使用 ARKit 明确提供的 viewMatrix(for:),不要把 transform 按名字猜成同一方向。
world coordinate system、origin 与相对位姿
Understanding World Tracking 说明 ARKit 的 y-axis 向上,x/z 的实际朝向受 session configuration 影响。worldAlignment 不是视觉 codec 参数,而是坐标系与真实环境如何对齐的约定:
worldAlignment | Apple 定义的轴与 origin | 采集数据解读 |
|---|---|---|
.gravity | y-axis 与 gravity 平行,origin 是 session 首次运行时的 device position | 适合以重力为上方向的局部 trajectory,但不是地理坐标 |
.gravityAndHeading | y-axis 与 gravity 平行,x/z 与 compass heading 对齐;origin 仍是初始 device position | 水平朝向有 heading 参照,但 position origin 仍是局部的 |
.camera | scene coordinate system 锁定 camera orientation,且 ARKit 不进行 device motion tracking | 不能把它当成稳定的 room/world trajectory |
这些含义见 Apple 的 ARConfiguration WorldAlignment、.gravity、.gravityAndHeading 与 .camera 文档。ARSession.setWorldOrigin(relativeTransform:) 会以相对当前 world coordinate space 的 4×4 transform 改变 world coordinate space 的 basis。因此,日志一旦 rebase,rebase 前后的 world pose 不能在没有该相对 transform 的情况下直接比较。
resetTracking 的语义更强:在同类 configuration 上运行 session 时,它会把 device position tracking 强制回到初始状态;Apple 还说明 reset 会移除已有 anchors。resetTracking 文档 因而意味着一条 trajectory 要记录 session start、reset 和 rebase 边界。仅把某一帧称为“world origin”而不记录这些事件,无法让离线使用者可靠拼接多段 pose。
VIO、relocalization 与采集边界
Apple 将 world tracking 描述为 visual-inertial odometry(VIO):把 device motion sensor 与 camera image 的 computer vision analysis 结合,利用跨 video frames 的 feature 变化估计 device position 与 motion;ARWorldTrackingConfiguration 提供 6DOF tracking。它是理解“视觉 + IMU + pose”同步记录的一种通用参照,但不是证明某一采集设备采用 Apple 实现的证据。
ARKit 也明确给出边界:低纹理或昏暗场景、过快或剧烈运动都会降低 tracking quality;平面与 anchor 的 estimate 可能随观测继续变化。session interruption 后,ARCamera.TrackingState.Reason.relocalizing 表示系统在尝试恢复先前的 world state;在此期间 hit testing 与 anchor placement 更不准确。若重定位成功,先前 world coordinate system 与 anchor positions 会恢复;若设备不在原来附近,状态可能持续不结束,见 Apple 的 relocalizing 文档。
所以,在 GenRobot/DAS Ego 一类 recording 中,pose 的最低可解释信息应包括:每帧 timestamp、reference frame、matrix direction、单位、相对 pose 的 reference timestamp,以及 reset/rebase/tracking-quality event。若数据只标为 vio 或 world,不能仅据名称推定 ARKit、全局地理坐标或跨 session 的绝对一致性;还需要该产品的 schema 和 calibration 文档。
iPhone LiDAR、ARKit scene depth 与采集记录
LiDAR(Light Detection and Ranging)可理解为主动测距传感器;在 ARKit 的公开 API 层面,它最重要的输出是物体距离,而不是一颗 RGB camera 的 image。scene depth 是 ARKit 在支持 LiDAR Scanner 的设备与 world-tracking configuration 中提供的后置深度能力,不是“任何 iPhone/iPad 都有的深度图”,也不是一个 world pose 字段。不要仅按设备类别或 topic 名称猜测支持情况;Apple 要求对所用 configuration 在运行时检查 supportsFrameSemantics(_:)。
| 图像或深度来源 | ARKit 的对应字段 | 不能混淆的语义 |
|---|---|---|
| 后置 color/RGB camera | ARFrame.capturedImage | camera 捕获的 image pixel buffer,不是距离图 |
| 后置 LiDAR Scanner | ARFrame.sceneDepth、smoothedSceneDepth | 到 real-world object 的后置 camera distance,以 ARDepthData 暴露;不是 ARCamera.transform |
| 前置 TrueDepth camera | ARFrame.capturedDepthData | 仅用于 face-based experience 的 ARFaceTrackingConfiguration,类型为 AVDepthData,不是后置 LiDAR 的 sceneDepth |
flowchart TB
A[Rear RGB camera] --> B[ARFrame.capturedImage]
C[Rear LiDAR Scanner] --> D[ARFrame.sceneDepth<br/>ARDepthData]
E[Camera image and IMU] --> F[VIO world tracking]
F --> G[ARFrame.camera.transform<br/>world pose]
H[Front TrueDepth camera] --> I[ARFrame.capturedDepthData<br/>AVDepthData, face tracking only]
J[Optics, aperture and focus distance] --> K[Depth of Field<br/>sharpness range]
B --> L[Recording contract<br/>timestamp, intrinsics and calibration]
D --> L
G --> L
这是概念与 API 输出的对应图,不是对 Apple 未公开内部 sensor/ISP pipeline 的断言。后置 LiDAR 的 ARDepthData、VIO 导出的 world pose、前置 TrueDepth 的 AVDepthData 与镜头光学景深分别回答不同的问题;把它们放在同一条 recording 中时,关联它们的是 timestamp、camera model 与 coordinate transform,而不是相同的字段名称。
前置 TrueDepth 的 depth sensor 与 color camera frame rate 可以不同,因此 capturedDepthData 可能为空,且应配对它专用的 capturedDepthDataTimestamp。与之不同,Apple 将 .sceneDepth 定义为以 LiDAR Scanner 提供 device 到 camera view 中 real-world object 的 distance;启用后,ARFrame 才会填充对应的 ARDepthData。下面是 Apple 推荐的 capability check 方式;需要同时使用两种 depth 时,检查与配置的语义集合必须一致:
import ARKit
func startWorldTrackingWithSceneDepth(session: ARSession) {
let semantics: ARConfiguration.FrameSemantics = [
.sceneDepth,
.smoothedSceneDepth
]
guard ARWorldTrackingConfiguration.supportsFrameSemantics(semantics) else {
return // 当前 device/configuration 没有后置 LiDAR scene depth
}
let configuration = ARWorldTrackingConfiguration()
configuration.frameSemantics = semantics
session.run(configuration)
}
这里调用的是具体的 ARWorldTrackingConfiguration,而非 ARConfiguration 基类;语义与 availability 见 Apple 的 .sceneDepth、supportsFrameSemantics(_:) 和 官方 point-cloud sample。这也是“并非所有 iPhone/iPad 均有 LiDAR scene depth”的可执行判断,而不是靠型号清单做推测。
ARDepthData.depthMap 是 CVPixelBuffer,表示从 camera plane 到 environment 的估计 distance,单位为 meter;每个 depth pixel 对应 capturedImage 可见 scene 中的一个 region。confidenceMap 则为每个 depth component 给出 ARConfidenceLevel。记录或重建时,应把 depthMap、可选的 confidenceMap、所启用的是 sceneDepth 还是 smoothedSceneDepth 一并保存并过滤,而不能把所有 pixel 当成同等精度的量测。后者是跨时间 average,旨在减小 frame-to-frame delta,适合稳定显示,却不是另一颗独立的深度传感器。ARDepthData 和 Apple sample 还提示:depth 值是 low-resolution;插值或 upsampling 可改善显示,不会凭空增加原始 LiDAR measurement。
深度图要变为 3D data,至少要关联同一 ARFrame 的 timestamp、ARCamera.intrinsics 与 ARCamera.transform。Apple 的 point-cloud sample 正是以 depth、capturedImage 与 camera intrinsics 组合出 point cloud。离线管线应把 RGB buffer 和 depth buffer 视为可能不同的 image plane:记录两者的 size 与采用的 pixel mapping,并把 K 映射到 depth-pixel coordinate system 后再反投影;不能无条件把 depth pixel (u, v) 当成原 image 的同一坐标。随后才可用同帧 T_W←C 将 camera-space point 变到 ARKit world space。
这与 AVFoundation 的 AVDepthData 还要再分开:它是 compatible camera device 输出的 per-pixel distance 或 disparity container。Apple 明确说明这类 depth map 可带有与同时捕获的 YUV image 相匹配的 nonrectilinear geometry;若用于 3D computer vision,应使用 cameraCalibrationData 做 rectification,而不是仅为合成背景虚化直接拿来反投影。因此,记录 depth 时还要写清是 ARKit 的 ARDepthData 还是 AVFoundation 的 AVDepthData,以及对应的 calibration/rectification state。
LiDAR depth 与 world tracking 也应分层解读:Apple 将前者列为 ARWorldTrackingConfiguration 的可选 frame semantic,而将 world tracking 描述为 camera image 与 motion sensor 的 visual-inertial odometry(VIO)。Apple 没有在该 API 合同中声称 sceneDepth 是 camera.transform 的来源,或启用它会保证更准确的 VIO pose。因此 LiDAR 不能单独建立 world origin、替代 pose,或证明其他采集设备采用 ARKit;它只是与 image、intrinsics、timestamp、confidence 和已定义 pose coordinate system 配套时,才可用来解释 scene geometry。见 Apple 的 Understanding World Tracking。
景深、scene depth 与深度图不是同一个概念
中文“景深”通常指 Depth of Field(DoF):对焦平面前后、视觉上仍可接受为清晰的距离范围。它是光学成像属性,不是一张逐像素的距离测量表。较大的光圈(较小 f-number)、较长焦距或更近的拍摄距离通常会使景深变浅;反之则使清晰范围变大。Canon 对景深的说明 说明其受 aperture、focal length、focus position 和 shooting distance 等共同影响。在第一人称采集里,浅景深、motion blur 或失焦可能减少可跟踪的 image feature,但并不能直接给出物体距离。
iPhone 的 Portrait Effect 容易让这两个概念在视觉上混在一起。Apple 的 isPortraitEffectActive 定义是:效果开启时会 blur background,模拟浅景深;它属于计算摄影效果,而不是镜头物理 aperture 真的改变了可接受清晰范围。Apple 的 photo depth capture API 可在 compatible 后置 dual camera 或前置 TrueDepth camera 上交付 depth data,用于 Portrait mode 等按前景/背景差异处理的效果;AVPortraitEffectsMatte 则是面向人物前景的 segmentation matte,并不等于一张完整、可用于 3D reconstruction 的连续距离图。因此,仅有一帧背景已模糊的 RGB image,不能当作 LiDAR depth map 或可靠的 3D measurement 使用。
scene depth 或 depth map 则是另一类 data:每个 depth pixel 表示对应 scene region 到 camera plane 的距离。以 ARKit 为例,ARDepthData.depthMap 的值单位是 meter,并有可选 confidenceMap 表示该 depth 值的可信度;使用 rear-camera scene depth 需要在 world-tracking configuration 中启用 .sceneDepth,且只在支持 LiDAR 的设备和 configuration 上可用。ARDepthData 与 ARFrame sceneDepth 是这两个 API 的正式定义。smoothedSceneDepth 会跨多帧平均以降低闪烁,但不应被误读为同一时刻的原始 LiDAR reading。Apple 的 scene-depth 示例 对此作了区分。
| 名称 | 它回答的问题 | 采集记录中的正确处理 |
|---|---|---|
Depth of Field / 景深 | 哪个距离范围在图像中看起来清晰 | 作为 camera exposure/focus quality 的因素;不要把它作为 3D distance 或 MCAP depth topic 解读 |
后置 LiDAR sceneDepth / ARDepthData | 每个 image region 离 rear camera 多远 | 与 image timestamp、camera intrinsics、pixel coordinate system、confidence 和 frame semantic 一起保存与使用 |
AVDepthData | compatible photo 或 TrueDepth path 的 per-pixel distance/disparity | 记录 calibration data 与 rectification state;不能只因它能驱动 Portrait Effect 就视为已可 3D 反投影 |
| world pose | camera 或 device 在某个 reference frame 中的位置和方向 | 与同一时刻的 camera/depth data 通过明确的 coordinate transform 对齐 |
当 image plane 已由 K、畸变模型和对应 resolution 定义时,pinhole/rectified 场景可用以下关系把一个 depth pixel 还原到 world:
p_C = z · inverse(K) · [u, v, 1]^T
p_W = T_W←C(t) · [p_C, 1]^T
其中 z 是该 pixel 的 depth,K 是与该 image plane 匹配的 intrinsics,T_W←C(t) 是该 timestamp 的 camera-to-world pose。这个关系不适用于把 fisheye 或其他 distorted pixels 直接套入 pinhole K 的情形;必须先使用匹配的 distortion model,或使用相应的 undistort/rectification result。
系统 GDC:不是 pose,也不是自动补全深度
GDC 常指 Geometric Distortion Correction。它对 image geometry 做重映射,用于减轻宽视场镜头边缘的 fisheye/barrel 等几何畸变。Apple 在 AVFoundation 中直接使用这一缩写:支持 GDC 的 capture device 可开启 isGeometricDistortionCorrectionEnabled,官方指出这会在 frame outer edge 减轻 fisheye effect,代价是损失一小部分 horizontal field of view,随后把 corrected image upscale 回原 image size。Apple:GDC capability Apple:GDC enabled 因而,“输出仍是同一 resolution”不表示 optical geometry 或可用 field of view 没有变化;Apple 还单独公开了 corrected video field of view。
从数据工程角度,GDC、offline undistortion 和 stereo rectification 应这样分开:
| 操作 | 目的 | 需要与输出 image 配套保存的几何信息 |
|---|---|---|
| capture-side GDC / ISP GDC | 在实时 camera pipeline 中输出校正后的 frame | corrected output 的 intrinsics、field of view、resolution,以及 GDC configuration/version;不能继续沿用 raw-image calibration |
| offline undistortion | 对已录制的 distorted image 按 calibration 做 remap | 原始 K、D、所选 newCameraMatrix、output resolution 和 valid-pixel ROI |
| stereo rectification | 把左右 image 重投影到共同理想 image plane,使对应点可沿对齐的 epipolar line 匹配 | 左右 R/P、baseline、rectified maps 和 encoded resolution |
OpenCV 将 calibration 的 camera matrix 写为 K = [[fx, 0, cx], [0, fy, cy], [0, 0, 1]],并区分 radial 与 tangential distortion;它的 undistort 可理解为预先计算 initUndistortRectifyMap 再执行 remap。OpenCV Camera Calibration 这说明去畸变既会重新采样 pixel,也会依照 chosen new camera model 改变有效 field of view、crop/black border 的取舍。将 corrected/cropped/scaled image 与 raw-image 的 K/D 混用,会导致系统性的 reprojection error;这是由该几何模型得出的工程推论,不是某家产品对其内部 pipeline 的承诺。
flowchart TB
A[Camera sensor and lens<br/>distorted pixel geometry] --> B{Capture-side GDC enabled?}
B -->|No| C[Raw image<br/>use matching K and D]
B -->|Yes| D[Corrected image<br/>use matching output intrinsics]
C --> E[H.264 encoder]
D --> E
F[IMU and camera timestamps] --> G[VIO or world tracking]
H[Camera-to-device extrinsics] --> G
E --> I[MCAP recording]
G --> I
J[Calibration and GDC metadata] --> I
该图是通用采集链路,不是对 DAS Ego 内部顺序的断言。GDC 不会单独估计 world pose,也不会凭空生成 reliable scene depth;它只是让后续 feature tracking、stereo matching、hand projection 或 rendering 使用更一致的 image geometry。rolling shutter、motion blur、camera/IMU time offset 和 VIO drift 仍是独立问题。
GenRobot 公开数据能证实什么,不能证实什么
截至 2026-08-28,GenRobot 的公开文档、das-datakit 和可读 das-ego-stack source 中未发现设备端 GDC/ISP GDC 的明确声明;未公开的 VIO/QC Docker image 不能据此反推其内部没有此阶段。公开证据支持的最近概念是 calibration 与 rectification:CameraCalibration schema 包含 distortion_model、D、raw (distorted) image 的 K、stereo rectification 的 R、processed/rectified image 的 P,以及 camera-to-base pose T_b_c。StereoCalibration 还记录左右 R/P、baseline 与 encoded resolution 上的 rectified maps;DAS Ego data documentation 将各 camera 的 camera_info 标为这类 calibration data。DAS Ego Data Introduction
这证明其数据模型能够表达 raw/rectified geometry,却不能证明校正发生在 ISP、录制前,还是离线处理。公开的 hand-frame export script 对 Double Sphere model 将 pinhole hand point 前向投影到 distorted pixel 以做 overlay,也不能被泛化为所有 topic 已经去畸变。外部使用者应以实际 camera_info、image resolution、K/D/R/P、distortion_model、timestamp 和 pose direction 为一组契约来处理,不要仅凭 GDC、camera0 或 vio 这样的名称推断内部处理阶段。
读取第一人称 recording 时的最小检查清单
- 图像是 raw/distorted、GDC-corrected,还是 stereo-rectified;对应的
K/D/R/P是哪一组? T_b_c、eef_pose或camera pose的 matrix direction 是什么,采用何种 axis convention、unit 和 quaternion order?- image、IMU、depth、pose 的 timestamp 是否来自同一 clock,是否记录 exposure/capture time、latency 和 offset?
- depth 来源是后置 LiDAR
ARDepthData、前置 TrueDepth/AVFoundationAVDepthData,还是 stereo;是否保存sceneDepth/smoothedSceneDepth、confidence、calibration 和 rectification state? - calibration 是否与当前 camera serial number、resolution、crop/resize、GDC mode 和固件版本一致?
- trajectory 是否跨 session、reset、rebase 或 relocalization;若是,是否提供每段的 world-frame transform?
影响画质、体积与兼容性的参数
| 参数 | 它描述什么 | 初学者容易误解的地方 |
|---|---|---|
| Resolution | 每帧的像素尺寸,例如 1920×1080 | 分辨率更高不保证画质更好,还取决于 bitrate、编码器和源素材 |
| Frame rate | 每秒显示多少帧,例如 24、30 或 60 fps | 更高通常更流畅,也会增加编码与解码压力 |
| Bitrate | 单位时间的数据量,常用 kbit/s 或 Mbit/s | 同一编码下,提高 bitrate 通常能保留更多细节,但文件也更大 |
| Profile | 一组编码工具与 bitstream 限制,例如 Baseline、Main、High | 它是解码能力与互操作边界,不是画质档位 |
| Level | 对画面尺寸、处理速率、buffer、bitrate 等资源设限,例如 Level 4.0 | 它也不是画质档位;目标设备必须能处理所用 Profile 与 Level |
H.264 当前规范把 Profile 与 Level 分别列在 Annex A,并列出 Level 对 frame rate、decoded picture buffer 等能力的限制。ITU-T H.264 (06/2026) 目录 可直接定位这些条款。
粗略估算文件体积时,可以使用:
文件体积(byte)≈ 总 bitrate(bit/s)× 时长(s)÷ 8
这里的总 bitrate 要包含视频、音频和少量容器开销;Variable Bitrate(VBR)素材只能用平均 bitrate 估算。
Lossy、lossless、remuxing 与 transcoding
- Lossy compression 会丢弃部分信息以缩小体积。面向播放与分发的 H.264 通常这样使用;再次有损编码可能继续损失画质。
- Lossless compression 能从压缩数据还原原始信息。FFmpeg 的
libx264wrapper 确实支持 lossless mode,但这不是普通 H.264 分发文件的默认做法,体积与兼容性也要另行评估。FFmpeg libx264 文档 - Remuxing 只更换或重组容器,不解码、不过滤、不重新编码 elementary stream。FFmpeg 的
-c copy就是 streamcopy,因此速度快且没有重新编码造成的画质损失;不过目标容器必须接受被复制的 stream。 - Transcoding 会先解码,再用目标编码器重新编码。它适合缩放、滤镜、修改编码或解决播放兼容性,计算成本更高,而且多数情况下有损。FFmpeg 对 streamcopy 与 transcoding 的说明
所以,把 H.264/AAC 的 MKV remux 成 MP4 时,视频内容可以保持不变;把它重新编码成另一组 H.264 参数时,即使输出仍是 .mp4,也属于 transcoding。
为什么同是 MP4,有的设备仍然打不开
播放器必须同时满足多层条件:能识别 MP4 容器、能解码具体 video codec 及其 Profile/Level、能解码 audio codec,还要能处理所选 subtitle 与 metadata 格式。文件扩展名或 video/mp4 MIME type 本身都不能完整说明这些内部信息。
| 文件示例 | 可以得出的结论 | 还需验证什么 |
|---|---|---|
| MP4 + H.264 High Profile + AAC-LC | 常见的交付组合 | 目标设备支持的 Level、pixel format、分辨率与 frame rate |
| MP4 + AV1 + AAC | av01 已有正式登记 | 目标播放器是否有 AV1 decoder;登记不等于终端支持 |
| MKV + H.264 + AAC | H.264 video stream 并未因为容器不同而改变身份 | 目标播放器是否支持 Matroska container |
| MP4 + H.264 + 某种 subtitle | 视频本身可能可播放 | muxer 与播放器是否接受该 subtitle sample entry |
遇到“文件扩展名正确但打不开”时,先查看内部 stream,而不是继续修改扩展名。
用 FFmpeg 看清并验证
以下命令已在 macOS、FFmpeg 8.0 上实测。前置条件是系统里同时有 ffmpeg、ffprobe,而且当前 build 启用了 libx264、libx265 与 AAC encoder;可以用 ffmpeg -hide_banner -encoders 检查。FFmpeg 官方说明这些 wrapper 需要在构建时启用相应 library。FFmpeg codec documentation
1. 生成可复现的测试素材
下面生成 1 秒、320×180、30 fps 的 H.264 + AAC Matroska 文件:
ffmpeg -f lavfi -i 'testsrc2=size=320x180:rate=30' \
-f lavfi -i 'sine=frequency=1000:sample_rate=48000' \
-t 1 -c:v libx264 -pix_fmt yuv420p -c:a aac input.mkv
2. 用 ffprobe 查看 container 与 stream
ffprobe -v error \
-show_entries 'format=format_name,duration,size:stream=index,codec_type,codec_name,profile,width,height,r_frame_rate,bit_rate' \
-of json input.mkv
预期能看到 format_name 近似 matroska,webm,video stream 的 codec_name 是 h264、profile 是 High、尺寸为 320×180、r_frame_rate 为 30/1,另有一个 aac audio stream。短素材或某些容器可能不提供 stream-level bit_rate,字段缺失不等于没有数据。ffprobe 官方文档 说明 -show_entries 用于筛选字段,-of json 用于 JSON 输出。
3. Remux 成 MP4,不重新编码
ffmpeg -i input.mkv \
-map 0:v:0 -map 0:a:0 \
-c copy remuxed.mp4
再次对 remuxed.mp4 执行上面的 ffprobe 命令,预期 container 变成 MOV/MP4 family,而 video 与 audio 的 codec_name 仍是 h264 和 aac。这表示 encoded packets 被复制到了新容器;两个文件的二进制内容和 container metadata 不会相同,但没有发生重新编码。若输入含 MP4 不接受的 codec、subtitle 或必要信息不足,命令可能失败,这时不能靠 -c copy 强行兼容。
4. Transcode 成一组明确的 H.264/AAC 参数
ffmpeg -i input.mkv \
-map 0:v:0 -map 0:a:0 \
-c:v libx264 -crf 23 -preset medium -pix_fmt yuv420p \
-c:a aac -b:a 128k transcoded.mp4
这里 -crf 23 选择 libx264 的 constant-quality mode,-preset medium 选择 encoding preset,-pix_fmt yuv420p 明确输出 pixel format,audio 则重新编码为 128 kbit/s AAC。输出仍是 H.264 + AAC 的 MP4,但 video 与 audio 都已重新编码,体积、bitrate 和画质可能改变;FFmpeg 对 crf、preset 与 Profile 等选项的当前定义见 libx264 wrapper 文档。
实际处理自己的文件时,应先 ffprobe,再决定能否 remux;只有 codec、分辨率、frame rate、pixel format 或兼容性确实需要变化时,才 transcode。
5. 生成并识别最小 HEVC/MP4 样本
下面用 FFmpeg 的 libx265 wrapper 编码一秒测试画面,并用 hvc1 sample entry 写入 MP4:
ffmpeg -v error -f lavfi -i 'testsrc2=size=320x180:rate=30' \
-t 1 -c:v libx265 -preset medium -crf 28 \
-x265-params log-level=error -tag:v hvc1 -an hevc.mp4
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,codec_long_name,profile,codec_tag_string,pix_fmt \
-of default=noprint_wrappers=1 hevc.mp4
本机预期输出包含 codec_name=hevc、profile=Main、codec_tag_string=hvc1 和 pix_fmt=yuv420p。这里的 CRF 28 只为生成演示文件;x265 与 x264 的 CRF 标尺、编码工具和结果不同,不能把它与前例的 x264 CRF 23 直接比较。
参考资料
资料与链接核验日期:2026-08-28。
- ITU-T Recommendation H.264 (06/2026): Advanced video coding for generic audiovisual services
- ISO/IEC 14496-10:2025: Advanced video coding
- ISO/IEC 14496-12:2026: ISO base media file format
- ISO/IEC 14496-14:2020: MP4 file format
- ISO/IEC 14496-15:2024: Carriage of NAL unit structured video in ISOBMFF
- ISO/IEC 14496-2:2004: Visual
- ISO/IEC 23008-2:2025: High efficiency video coding
- ISO/IEC 23090-3:2024: Versatile video coding
- ITU-T Recommendation H.265 (01/2026): High efficiency video coding
- ITU-T Recommendation H.266: Versatile video coding
- ITU-T: Joint Collaborative Team on Video Coding
- ITU-T: Joint Video Experts Team
- AOMedia: AV1 specification
- WebM Project: VP8 and VP9 documentation
- MP4 Registration Authority: codec sample entry codes
- RFC 6381: The
CodecsandProfilesParameters for Bucket Media Types - VideoLAN: x264
- x265 documentation: Introduction
- NVIDIA Video Codec SDK
- FFmpeg documentation
- ffprobe documentation
- FFmpeg codecs documentation
- FFmpeg formats documentation
- MCAP Format Specification
- MCAP Format Registry
- MCAP CLI
- MCAP Python library: Basic Reading and Writing
- Foxglove
CompressedVideoschema - GenRobot Open Data: Task Visualization
- GenRobot: Matrix Studio User Manual
- GenRobot: DAS Ego Manual v2.0
- GenRobot: DAS Ego Data Introduction
- GenRobot: DAS Ego-Finger Data Introduction
- GenRobot
das-datakit - GenRobot
das-datakitimage topic parser - GenRobot
das-datakitMCAP-to-MP4 example - Apple: Understanding World Tracking
- Apple:
ARCamera.transform - Apple:
ARFrame.sceneDepth - Apple:
ARFrame.capturedDepthData - Apple:
ARDepthData - Apple: Displaying a point cloud using scene depth
- Apple:
AVDepthData - Apple: Capturing photos with depth
- Apple: Portrait Effect state
- Apple: Geometric distortion correction capability
- Apple: Geometric distortion correction state
- Canon: Depth of field
- OpenCV: Camera Calibration
- GenRobot
CameraCalibrationschema - GenRobot
StereoCalibrationschema - GenRobot Double Sphere hand projection example