1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use re_chunk::TimePoint;
use re_log_types::{TimeType, TimelineName};
use re_video::{Mp4TranscodeOptions, TimeWindow};
/// Configuration for [`crate::load_mp4_from_bytes`].
#[derive(Clone, Debug)]
pub struct Mp4Config {
/// What kind of chunks to produce.
pub mode: Mode,
/// Name of the timeline used for stream-mode samples and for the
/// `VideoFrameReference` index chunk in asset mode.
///
/// Defaults to `"video"`.
pub timeline_name: TimelineName,
/// How to interpret the timeline values. Applies to the stream-mode sample
/// timeline and to the asset-mode `VideoFrameReference` index timeline.
///
/// [`TimeType::DurationNs`] interprets the PTS as a duration since the start
/// of the video (the mp4 default); [`TimeType::TimestampNs`] as wall-clock
/// nanoseconds since the Unix epoch. The emitted values are identical either
/// way — only the timeline's declared type differs. Pair `TimestampNs` with
/// a downstream retag step that supplies the real wall-clock times.
pub timeline_type: TimeType,
}
impl Default for Mp4Config {
fn default() -> Self {
Self {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default(),
time_window: None,
},
timeline_name: "video".into(),
timeline_type: TimeType::DurationNs,
}
}
}
/// Output mode for [`crate::load_mp4_from_bytes`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Mode {
/// Emit an `AssetVideo` blob chunk plus a `VideoFrameReference` index chunk.
///
/// `timepoint` is placed on the `AssetVideo` blob chunk. Public callers
/// generally pass `TimePoint::default()` (static); the file importer passes
/// its enriched timepoint with a zero-duration cell on the `video`
/// timeline plus any `created_at` / `modified_at` cells.
Asset { timepoint: TimePoint },
/// Emit a static `VideoStream(codec=…)` chunk, per-sample (or per-GOP)
/// `VideoSample` chunks at PTS, and a trailing dedicated `IsKeyframe` chunk
/// holding one sparse `true` row per keyframe.
///
/// The timeline used for the samples is named [`Mp4Config::timeline_name`]
/// and typed [`Mp4Config::timeline_type`].
///
/// A source containing h264/h265 B-frames — or any source for which [`Mp4TranscodeOptions`]
/// requests a transform (a different output codec, a GOP size) — is transcoded
/// with ffmpeg into an equivalent B-frame-free stream before emission, because
/// the `VideoStream` archetype cannot yet model differing DTS/PTS. Transcoding
/// requires an `ffmpeg` executable.
// TODO(#10090): emit B-frames directly once `VideoStream` can model DTS != PTS.
Stream {
/// Should the samples be grouped into one Rerun chunk per GOP?
///
/// If `true`, groups samples into one Rerun chunk per GOP (keyframe through the sample just
/// before the next keyframe). Otherwise, emits one Rerun chunk per sample.
chunk_by_gop: bool,
/// How to transcode the stream (output codec, GOP size, GPU acceleration).
///
/// The default is a no-op: a B-frame-free source is read directly without
/// invoking ffmpeg.
transcode: Mp4TranscodeOptions,
/// Exclusive time window of the source video to read. `None` reads the whole file.
///
/// Uses ffmpeg if the window doesn't exactly line up with a GOP start.
time_window: Option<TimeWindow>,
},
}