Skip to main content

ff_preview/scene/
types.rs

1//! Model-agnostic description of a timeline for the real-time preview runner.
2//!
3//! A [`Scene`] is the primitive seam between an editing engine and
4//! [`SceneRunner`](super::runner::SceneRunner): it carries only the
5//! model's primitivised fields (paths, durations, opacity, blend, animation
6//! tracks), never the editing model itself. Resolving a `Scene` against the
7//! media — probing for duration, audio presence, and frame size — happens in
8//! [`ScenePlayer::open`](super::ScenePlayer::open), so a `Scene` re-derived on
9//! every edit needs no re-probe and behaviour is preserved. An engine derives a
10//! `Scene` from its own editing model; `Scene` is the only input the runner accepts.
11
12use std::path::PathBuf;
13use std::time::Duration;
14
15use ff_filter::{AnimatedValue, RealtimeLayerDescriptor, XfadeTransition};
16
17// ── Scene ─────────────────────────────────────────────────────────────────────
18
19/// A whole timeline's worth of playback work, described without the editing model.
20#[derive(Debug, Clone)]
21pub struct Scene {
22    /// Presentation frame rate. Clamped to at least `1.0` by the runner.
23    pub fps: f64,
24    /// Explicit output canvas `(width, height)`, or `None` to size from the base track.
25    pub canvas: Option<(u32, u32)>,
26    /// Optional timeline-global `lavfi` filtergraph string (e.g.
27    /// `color=s=1920x1080:c=black@0.0,drawtext=text='Title'`) generated and composited
28    /// as the **topmost** video layer, matching the export path. `None` = no overlay.
29    pub lavfi_overlay: Option<String>,
30    /// Video tracks, composited bottom-up: index `0` is the V1 base, `1..` are overlays.
31    pub video_tracks: Vec<SceneVideoTrack>,
32    /// Dedicated audio-only tracks (A1, A2, …).
33    pub audio_tracks: Vec<SceneAudioTrack>,
34}
35
36// ── SceneVideoTrack ─────────────────────────────────────────────────────────────
37
38/// One video track: an ordered list of clip placements along the timeline. The
39/// track's index in [`Scene::video_tracks`] is its compositing order (`0` = base).
40#[derive(Debug, Clone)]
41pub struct SceneVideoTrack {
42    /// Placements in timeline order.
43    pub placements: Vec<ScenePlacement>,
44}
45
46// ── ScenePlacement ──────────────────────────────────────────────────────────────
47
48/// One video clip placed on the timeline.
49///
50/// Fields are pre-resolved model projections (e.g. `in_point` defaulted, `speed`
51/// clamped, `xfade_dur` computed); media-dependent resolution (the effective
52/// clip duration when `out_point` is `None`) is done by the runner at open time.
53#[derive(Debug, Clone)]
54pub struct ScenePlacement {
55    /// Source media path.
56    pub source: PathBuf,
57    /// Global timeline position where this placement starts.
58    pub offset: Duration,
59    /// Source-file PTS at which playback starts (`Clip::in_point`, defaulted to zero).
60    pub in_point: Duration,
61    /// Source-file PTS at which playback ends (`None` = play to EOF).
62    pub out_point: Option<Duration>,
63    /// Playback speed multiplier (`1.0` = normal), clamped to at least `0.01`.
64    pub speed: f64,
65    /// Crossfade duration from the previous placement into this one. Meaningful on
66    /// the V1 base track only; `Duration::ZERO` = hard cut.
67    pub xfade_dur: Duration,
68    /// The `xfade` transition kind for this crossfade (V1 base track only). `None`
69    /// when there is no transition; the runner defaults to `Fade` if a duration is
70    /// set without a kind.
71    pub xfade_kind: Option<XfadeTransition>,
72    /// Per-clip opacity in `[0.0, 1.0]`.
73    pub opacity: f32,
74    /// The dimension-free compositing description (effects, blend, position, and the
75    /// opacity/position animation tracks). The runner realises it per frame via
76    /// [`RealtimeLayer::with_dimensions`](ff_filter::RealtimeLayer::with_dimensions).
77    pub layer: RealtimeLayerDescriptor,
78    /// Audio fade-in duration (`Duration::ZERO` = none).
79    pub fade_in: Duration,
80    /// Audio fade-out duration (`Duration::ZERO` = none).
81    pub fade_out: Duration,
82    /// Resolved audio gain (dB), static or animated. A static value is applied once at
83    /// open; an animated one is evaluated per tick.
84    pub volume: AnimatedValue<f64>,
85}
86
87// ── SceneAudioTrack ─────────────────────────────────────────────────────────────
88
89/// One dedicated audio-only track (A1, A2, …).
90#[derive(Debug, Clone)]
91pub struct SceneAudioTrack {
92    /// Placements in timeline order.
93    pub placements: Vec<SceneAudioPlacement>,
94}
95
96// ── SceneAudioPlacement ─────────────────────────────────────────────────────────
97
98/// One audio-only clip placed on the timeline.
99#[derive(Debug, Clone)]
100pub struct SceneAudioPlacement {
101    /// Source media path.
102    pub source: PathBuf,
103    /// Global timeline position where this placement starts.
104    pub offset: Duration,
105    /// Source-file PTS at which playback starts (defaulted to zero).
106    pub in_point: Duration,
107    /// Source-file PTS at which playback ends (`None` = play to EOF).
108    pub out_point: Option<Duration>,
109    /// Playback speed multiplier (`1.0` = normal), clamped to at least `0.01`,
110    /// applied by resampling.
111    pub speed: f64,
112    /// Audio fade-in duration (`Duration::ZERO` = none).
113    pub fade_in: Duration,
114    /// Audio fade-out duration (`Duration::ZERO` = none).
115    pub fade_out: Duration,
116    /// Resolved audio gain (dB), static or animated. A static value is applied once at
117    /// open; an animated one is evaluated per tick.
118    pub volume: AnimatedValue<f64>,
119}