Rerun 视频数据可视化指南:AssetVideo 原语与 VideoFrameReference 的配合使用
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
AssetVideo是 Rerun 中用于承载视频二进制数据(Video binary)的核心原语(Archetype),负责将视频文件内容及其媒体类型写入数据存储;而要让视频真正"动起来",还需要为每一帧额外记录一个VideoFrameReference。本文以 Rerun 仓库内的 AssetVideo 参考文档 为主线,结合类型定义、SDK 生成代码与官方示例,完整讲解该原语的字段模型、媒体类型识别机制、与帧引用原语的协作方式,并给出 Python / Rust / C++ 三种语言可直接运行的实战代码。读完你将掌握"如何把一个 MP4 视频资产 + 逐帧时间戳正确写入 Rerun,并在 2D/3D 视图中回放"的完整技术方案。
AssetVideo 是什么:一个承载视频二进制的数据原语
按官方文档的定义,AssetVideo是A video binary(一个视频二进制资产)。它本身不做解码、不负责播放,只负责把视频文件的原始字节与媒体类型作为组件(Component)写入 Rerun 数据存储,供渲染端按需解码。文档明确了两条核心约束:
- 目前只支持 MP4 容器(
Only MP4 containers are currently supported),仓库类型定义中的TODO(#7354): Only mp4 is supported for now.(见 asset_video.def.rs)也印证了这一点; - 要显示视频,必须同时为每一帧记录一个
VideoFrameReference(见 video_frame_reference.md)。也就是说AssetVideo只提供"视频内容",帧的呈现时机完全由VideoFrameReference决定。
该原语由 re_type_definitions 中的类型定义统一声明,再由re_types_builder代码生成器产出 Rust / Python / C++ 三种语言的绑定。生成后的 Rust 结构体位于 crates/store/re_sdk_types/src/archetypes/asset_video.rs,其字段模型与文档完全一致。
字段模型:blob + media_type
AssetVideo共两个字段,在类型系统中被区分为"必需"与"推荐"两级:
| 字段 | 级别 | 组件类型 | 说明 |
|---|---|---|---|
blob | 必需(Required) | Blob | 视频文件的原始字节内容 |
media_type | 推荐(Recommended) | MediaType | 媒体类型(MIME),目前仅支持video/mp4 |
生成代码中以REQUIRED_COMPONENTS/RECOMMENDED_COMPONENTS两个静态数组精确登记了这两级组件(见 asset_video.rs),并提供了new(blob)、with_blob(...)、with_media_type(...)等构造/链式方法。其中media_type的可选语义在源码注释里写得很清楚:
Supported values:
video/mp4. If omitted, the viewer will try to guess from the data blob. If it cannot guess, it won't be able to render the asset.
即:media_type 可以省略,省略后 Viewer 会在渲染期尝试从数据本身猜测;若猜不出则渲染失败。
媒体类型识别:三级回退机制
AssetVideo的媒体类型判定在 asset_video_ext.rs 的from_file_path/from_file_contents中实现,实际存在三级回退:
- 从文件扩展名猜测:
from_file_path先调用MediaType::guess_from_path(filepath); - 从文件内容(魔数)猜测:
from_file_contents内部再通过MediaType::or_guess_from_data(media_type, &contents)校验/修正; - 渲染期兜底猜测:若前两步都无法确定,Viewer 渲染时会再次尝试从数据猜测,仍失败则报错。
对应的 MP4 媒体类型常量定义在 media_type_ext.rs:pub const MP4: &'static str = "video/mp4";。
让视频"动"起来:VideoFrameReference 帧引用机制
AssetVideo本身是静态的二进制资产,真正驱动视频播放的是VideoFrameReference。该原语的定义位于 video_frame_reference.def.rs,参考文档见 video_frame_reference.md,其字段模型为:
| 字段 | 级别 | 组件类型 | 说明 |
|---|---|---|---|
timestamp | 必需 | VideoTimestamp | 引用离该时间戳最近的视频帧 |
video_reference | 可选 | EntityPath | 指向某个含AssetVideo/VideoStream的实体;缺省时假设视频就在同一实体下 |
opacity | 可选 | Opacity | 视频不透明度,用于多层媒体叠加,默认 1.0(完全不透明) |
draw_order | 可选 | DrawOrder | 2D 绘制顺序,值越大越靠上层,默认-15.0 |
视频时间戳的语义细节
timestamp字段的类型是VideoTimestamp,其编码说明见 video_timestamp.md:底层 Arrow 数据类型为Int64,单位为纳秒。类型定义中对其语义有三点重要约束:
- 取"最近"帧而非"最新"帧:渲染端会引用离该时间戳最近的一帧,这样对精度不高的时间戳类型更宽容(能容忍取整误差);
- 时间戳相对于视频起点:0 永远对应视频第一帧;
- 与 PTS 的关系:多数情况下等价于显示时间戳(PTS),但当视频存在 B 帧(双向预测帧)时,首帧 PTS 可能带有偏移。
video_reference 的 latest-at 语义
video_reference是可选的实体路径引用,用于关联到存放AssetVideo(或流式VideoStream)的实体。文档特别提醒:
- 若缺省,则假定视频就位于当前
VideoFrameReference所在实体; - 该引用始终指向数据存储(data store),因此对引用视频的 blueprint 覆盖(override)会被忽略;
- 对于一系列连续的帧引用,建议只在序列开头指定一次
video_reference,后续帧靠 Rerun 的 latest-at 查询语义继续命中该视频实体。
VideoStream是与之并列的流式视频原语,仓库中同样提供了 video_stream_query_and_mux.py 与 video_stream_synthetic.py 两个示例可供参考,本文聚焦AssetVideo。
完整可运行的代码示例
Python:自动确定帧(video_auto_frames)
完整源码见 docs/snippets/all/archetypes/video_auto_frames.py。它先以static=True记录视频资产,再调用read_frame_timestamps_nanos()自动读出全部帧的纳秒时间戳,最后用send_columns以列式(columnar)方式一次性写入所有帧引用:
"""Log a video asset using automatically determined frame references.""" import sys import rerun as rr if len(sys.argv) < 2: # TODO(#7354): Only mp4 is supported for now. print(f"Usage: {sys.argv[0]} <path_to_video.[mp4]>") sys.exit(1) rr.init("rerun_example_asset_video_auto_frames", spawn=True) # Log video asset which is referred to by frame references. video_asset = rr.AssetVideo(path=sys.argv[1]) rr.log("video", video_asset, static=True) # Send automatically determined video frame timestamps. frame_timestamps_ns = video_asset.read_frame_timestamps_nanos() rr.send_columns( "video", # Note timeline values don't have to be the same as the video timestamps. indexes=[rr.TimeColumn("video_time", duration=1e-9 * frame_timestamps_ns)], columns=rr.VideoFrameReference.columns_nanos(frame_timestamps_ns), )运行方式:python video_auto_frames.py /path/to/video.mp4。其中rr.TimeColumn("video_time", ...)创建一条名为video_time的时间轴,其取值(纳秒→秒)不必与视频帧时间戳一致——视频帧时间戳决定"显示哪一帧",时间轴值决定"何时显示"。rr.VideoFrameReference.columns_nanos(...)则把一维纳秒数组直接包装为帧引用组件列,与send_columns的列式写入无缝配合,性能优于逐行log。
Python:手动帧引用(video_manual_frames)
完整源码见 docs/snippets/all/archetypes/video_manual_frames.py。它只记录两个帧引用,让同一视频分别冻结在 1 秒与 2 秒处,并发送一个并排双视图 blueprint 展示效果:
"""Manual use of individual video frame references.""" import sys import rerun as rr import rerun.blueprint as rrb if len(sys.argv) < 2: # TODO(#7354): Only mp4 is supported for now. print(f"Usage: {sys.argv[0]} <path_to_video.[mp4]>") sys.exit(1) rr.init("rerun_example_asset_video_manual_frames", spawn=True) # Log video asset which is referred to by frame references. rr.log("video_asset", rr.AssetVideo(path=sys.argv[1]), static=True) # Create two entities, showing the same video frozen at different times. rr.log( "frame_1s", rr.VideoFrameReference(seconds=1.0, video_reference="video_asset"), ) rr.log( "frame_2s", rr.VideoFrameReference(seconds=2.0, video_reference="video_asset"), ) # Send blueprint that shows two 2D views next to each other. rr.send_blueprint( rrb.Horizontal( rrb.Spatial2DView(origin="frame_1s"), rrb.Spatial2DView(origin="frame_2s"), ) )这里可以看到video_reference="video_asset"的典型用法:视频资产记录在video_asset实体,而两个帧引用位于不同的子实体,通过显式路径跨实体关联。rr.VideoFrameReference(seconds=1.0, ...)直接以秒为单位构造时间戳,SDK 内部换算为纳秒。
Rust:自动确定帧
完整源码见 docs/snippets/all/archetypes/video_auto_frames.rs(仓库中_args为文档构建系统注入的命令行参数占位符,实际运行时应替换为std::env::args().collect::<Vec<_>>()):
//! Log a video asset using automatically determined frame references. use rerun::external::anyhow; fn main() -> anyhow::Result<()> { let args = _args; let Some(path) = args.get(1) else { // TODO(#7354): Only mp4 is supported for now. anyhow::bail!("Usage: {} <path_to_video.[mp4]>", args[0]); }; let rec = rerun::RecordingStreamBuilder::new( "rerun_example_asset_video_auto_frames", ) .spawn()?; // Log video asset which is referred to by frame references. let video_asset = rerun::AssetVideo::from_file_path(path)?; rec.log_static("video", &video_asset)?; // Send automatically determined video frame timestamps. let frame_timestamps_nanos = video_asset.read_frame_timestamps_nanos()?; let video_timestamps_nanos = frame_timestamps_nanos .iter() .copied() .map(rerun::components::VideoTimestamp::from_nanos) .collect::<Vec<_>>(); let time_column = rerun::TimeColumn::new_duration_nanos( "video_time", // Note timeline values don't have to be the same as the video timestamps. frame_timestamps_nanos, ); rec.send_columns( "video", [time_column], rerun::VideoFrameReference::update_fields() .with_many_timestamp(video_timestamps_nanos) .columns_of_unit_batches()?, )?; Ok(()) }Rust 侧的关键 API:AssetVideo::from_file_path(path)读取文件并自动猜测媒体类型;video_asset.read_frame_timestamps_nanos()返回单调递增的纳秒时间戳向量;VideoFrameReference::update_fields().with_many_timestamp(...)批量填充帧时间戳;columns_of_unit_batches()把组件数据切分为单位长度的子批次,供send_columns使用。
C++:自动确定帧
完整源码见 docs/snippets/all/archetypes/video_auto_frames.cpp,接口与 Rust 高度对应:
// Log a video asset using automatically determined frame references. #include <rerun.hpp> #include <iostream> using namespace std::chrono_literals; int main(int argc, char* argv[]) { if (argc < 2) { // TODO(#7354): Only mp4 is supported for now. std::cerr << "Usage: " << argv[0] << " <path_to_video.[mp4]>" << std::endl; return 1; } const auto path = argv[1]; const auto rec = rerun::RecordingStream("rerun_example_asset_video_auto_frames"); rec.spawn().exit_on_failure(); // Log video asset which is referred to by frame references. auto video_asset = rerun::AssetVideo::from_file(path).value_or_throw(); rec.log_static("video", video_asset); // Send automatically determined video frame timestamps. std::vector<std::chrono::nanoseconds> frame_timestamps_ns = video_asset.read_frame_timestamps_nanos().value_or_throw(); // Note timeline values don't have to be the same as the video timestamps. auto time_column = rerun::TimeColumn::from_durations( "video_time", rerun::borrow(frame_timestamps_ns) ); std::vector<rerun::components::VideoTimestamp> video_timestamps( frame_timestamps_ns.size() ); for (size_t i = 0; i < frame_timestamps_ns.size(); i++) { video_timestamps[i] = rerun::components::VideoTimestamp(frame_timestamps_ns[i]); } rec.send_columns( "video", time_column, rerun::VideoFrameReference() .with_many_timestamp(rerun::borrow(video_timestamps)) .columns() ); }C++ 侧使用std::chrono::nanoseconds承载帧时间戳,TimeColumn::from_durations直接以chrono时长构造时间轴,value_or_throw()统一处理错误传播。
底层原理:从类型定义到最终渲染
单一事实来源与代码生成
AssetVideo的所有语义都先在 asset_video.def.rs 中声明(该文件并非可执行代码,而是被re_types_builder解析的 Rerun 类型定义 DSL)。文件头标注了#[docs(category = "Video")]、#[rerun(state = "stable")]、#[rerun(visualizer_none)](无专属可视化器,因为渲染逻辑在视频帧可视化器中)等元信息,随后生成 Rust 实现、Python 绑定(rr.AssetVideo)与 C++ 绑定(rerun::AssetVideo)。这也是为什么文档文件头部注明"DO NOT EDIT! This file was auto-generated"——所有语言侧的文档与代码都源自同一份类型定义,保证多语言行为一致。
read_frame_timestamps_nanos 的实现路径
自动确定帧功能的核心在 asset_video_ext.rs 的read_frame_timestamps_nanos(仅在有videofeature 时编译)。其内部流程为:
- 从
blob组件取回原始字节(Blob::serialized_blob_as_slice); - 依次从
media_type组件或数据魔数确定媒体类型; - 调用
re_video::VideoDataDescription::load_from_bytes(blob_bytes, media_type, "AssetVideo")解析视频元数据; - 通过
frame_timestamps_nanos()迭代器收集全部帧的 PTS,返回单调递增的纳秒数组;若视频无时间刻度(timescale)则返回VideoLoadError::NoTimescale。
可见"自动确定帧"本质上是 SDK 侧对 MP4 容器做了一次轻量解析,把帧时间戳批量提取出来,而不是逐帧解码。
渲染端的 latest-at 视频查询
在 Viewer 端,视频帧由 crates/views/re_view_spatial/src/visualizers/video/video_frame_reference.rs 中的VideoFrameReferenceVisualizer负责绘制。该文件内部定义了latest_at_query_video_from_datastore(第 306 行起)与latest_at_query_video_stream_from_datastore(第 342 行起)两个查询函数,均通过ctx.recording_engine().cache().latest_at(...)从数据存储中按时间轴做 latest-at 查询——这正对应文档中"帧引用序列只需在开头指定一次video_reference,后续依赖 latest-at 语义保持引用激活"的设计。当VideoFrameReference的video_reference指向VideoStream实体时,渲染端还会结合 Viewer 当前激活的时间轴进行流式取帧。
可在哪些视图中查看
按官方文档,AssetVideo(连同VideoFrameReference)支持在以下视图中显示:
- Spatial2DView:视频帧以平面纹理形式呈现;
- Spatial3DView:仅当视频记录在某个投影(projection)之下时才可显示(例如作为相机图像面片);
- DataframeView:以表格形式查看/查询视频资产及帧引用数据。
使用限制与注意事项
- 容器格式:当前仅支持 MP4 容器(
video/mp4),代码中TODO(#7354)明确标注这是阶段性限制;更细的编解码器(codec)支持矩阵以官方视频参考文档为准,本文档不展开。 - 必须有帧引用才能显示:只记录
AssetVideo而不同时记录VideoFrameReference,Viewer 无法将视频呈现为动态画面。 - 时间戳语义:帧时间戳以纳秒为单位、相对于视频起点(0 = 第一帧),且渲染端取"最近帧"而非"最新帧"。
- media_type 建议显式给出:虽然可省略并交由猜测机制兜底,但显式提供
video/mp4可以避免渲染期猜测失败。 - 静态记录:示例中视频资产均使用
log_static/static=True记录,表示该资产不随时间变化,属于推荐做法。
小结
AssetVideo是 Rerun 视频工作流的"内容层",VideoFrameReference是"时间层",二者缺一不可:前者以blob+media_type两个组件承载 MP4 二进制与媒体类型,后者以纳秒级VideoTimestamp逐帧引用内容层并决定呈现时机。本文给出的三种语言示例(Python 自动帧、Python 手动帧、Rust、C++)覆盖了"自动提取帧时间戳批量写入"与"手动指定帧引用"两种典型模式,可直接作为机器人多模态数据、传感器回放等场景中视频可视化的起点。进一步深入可阅读 VideoFrameReference 参考文档 与 re_video 解码实现。
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考