Skip to content

Secondary artefacts during render: blocked on ResolvedScenario's construction site and the encode loops #179

Description

@LeadcodeDev

The "secondary artefacts emitted during render" gap (High/M) from the re-scored Remotion differential. Investigated alongside #178 and refused, with the reasoning recorded here.

What is missing

grep -rn "artifacts" crates/rustmotion-core/src/schema/scenario.rs returns nothing. still and captions are independent subcommands invoked separately from the video render, so producing a thumbnail and a subtitle file next to a video takes three invocations that re-render or re-decode the same content.

Three independent blockers

Each is sufficient on its own; all were found by reading the code.

1. ResolvedScenario has exactly one construction site, and it is not where you would add the field.

ResolvedScenario — the type render, still, info and batch actually consume — is built only at crates/rustmotion/src/include.rs:76-82; every path through loader.rs goes through include::resolve_includes. A field added to Scenario in schema/scenario.rs therefore stays invisible to render.rs unless include.rs is touched too. And crates/rustmotion-studio/src/scenario/model.rs:89 (empty_scenario()) would stop compiling regardless.

2. "Do not re-render" is architecturally blocked, which is the whole point of the feature.

Per-frame RGBA buffers only materialise inside the per-format encode loops — h264.rs:244 inside a rayon::par_iter closure, and the same shape in ffmpeg.rs and formats.rs. render_frame_task(...) is called there, converted to YUV and dropped immediately. Nothing hands a finished frame to a general-purpose hook.

The only function able to produce an arbitrary frame outside those loops is render_frame_task_scaled, which is the render. Calling it a second time for a thumbnail reproduces exactly the cost of the separate rustmotion still invocation the feature exists to replace — so a naive implementation would deliver the syntax without the benefit.

3. Captions cannot be reused cleanly as they stand.

cmd_captions shells whisper.cpp (absent from the environment this was investigated in, so unverifiable there) and writes its output with a bare std::fs::write at captions.rs:422 — not atomic, contradicting the discipline PR #145 (helper commands must not destroy files they do not own) and PR #151 (write to a sibling, promote on success) established.

The honest path, for later

Extract the thumbnail from the already-produced video file, with ffmpeg -ss <t> -i output.mp4, after the encode finishes. Zero additional Skia work, and it reuses the sub-process pattern already in assets.rs. It sidesteps blocker 2 entirely.

That does not help until blocker 1 is resolved: the declaration still has to reach render.rs, which means include.rs and the studio's empty_scenario() move together with schema/scenario.rs.

Captions would need captions.rs's write made atomic first — worth doing on its own merits regardless of this gap.

Open questions for whoever picks this up

  • Should a failed artefact fail the video, or be reported and skipped? The render is the expensive part; losing it because a thumbnail could not be written seems wrong, but silently producing fewer outputs than declared is the failure mode this repository keeps eliminating.
  • Where do artefact paths resolve from — the scenario's directory, or the output's? --fix's refusal rules and PR fix(cli): stop the helper commands destroying files they do not own #145's file-ownership guards are the precedent to follow.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions