Skip to content

feat(export): full-timeline video export orchestrator + export_video command#112

Merged
appergb merged 1 commit into
mainfrom
feat-export-pipeline
Jun 23, 2026
Merged

feat(export): full-timeline video export orchestrator + export_video command#112
appergb merged 1 commit into
mainfrom
feat-export-pipeline

Conversation

@Felix201209

Copy link
Copy Markdown
Collaborator

What

A clean, verifiable minimal slice of the export subsystem: a per-frame export orchestrator that connects two already-ready building blocks — the wgpu compositor (opentake-render) and the ffmpeg VideoEncoder (opentake-media) — into a pure-video (no audio) .mp4 export.

src-tauri/src/export.rs walks every frame of the current timeline:
plan.frame(f)Compositor::render_to_rgbaDecodedFrameRgbaFrameVideoEncoder::push_frame, then finish(). Export renders at the full export resolution (export_render_size, not the preview cap).

Scope (first cut)

  • H.264 / .mp4 only. H.265 (.mp4) and ProRes 422 (.mov) are accepted by the request type but rejected with a clear error until their container/preset branches are validated (TODO). The encoder already builds correct args for both; this command just hasn't opted them in.
  • No audio mix, no progress callback / cancellation yet — deliberately deferred to keep the spine minimal and verifiable.
  • Single-threaded serial composite (the Rc-textured compositor requires it; export is one frame at a time anyway).

Design / safety

Files

  • New src-tauri/src/export.rs — orchestrator + export_video #[tauri::command].
  • New src-tauri/tests/export_integration.rs — ffmpeg+GPU gated end-to-end test.
  • src-tauri/src/lib.rs — 2 lines: pub mod export; + register export::export_video in invoke_handler.

Verification (all green on real hardware)

  • cargo fmt --all — clean
  • cargo clippy -p opentake-tauri --tests -- -D warnings — clean
  • cargo test -p opentake-tauri --lib export6 unit tests pass (preset/codec/extension validation, unwired-codec rejection, serde defaults)
  • cargo test -p opentake-tauri --test export_integration1 integration test passes: generates a 6-frame fixture, runs the full plan→composite→encode chain, and ffprobe confirms the output is a real 1280x720 h264 mp4 with exactly 6 frames. Auto-skips when ffmpeg/ffprobe or a GPU adapter are unavailable.

Refs SPEC §2.4 / §8.2.

🤖 Generated with Claude Code

…command

Wire the two ready-made building blocks — the wgpu compositor
(opentake-render) and the ffmpeg VideoEncoder (opentake-media) — into a
connected pure-video export spine. The new src-tauri/src/export.rs walks
every frame of the current timeline, composites each on the GPU at the
full export resolution (export_render_size, not the preview cap), and
pipes the RGBA frames into a streaming H.264 encoder to produce a real
.mp4 on disk.

Scope (first cut): H.264 / .mp4 only; no audio mix; no progress/cancel.
H.265 (.mp4) and ProRes 422 (.mov) are accepted by the request type but
rejected with a clear error until their container/preset branches are
validated (TODO). The manifest/text projection + MediaResolver + GpuContext
logic is a self-contained copy of the preview path so render.rs's
composite_frame body is untouched.

The orchestration is split into a Tauri-free run_export(timeline, manifest,
project_dir, req) so it can be driven directly by an ffmpeg+GPU gated
integration test (tests/export_integration.rs) that builds a 6-frame
timeline, runs the whole plan->composite->encode chain, and asserts the
output mp4's width/height/codec/frame-count via ffprobe. Unit tests cover
preset resolution (codec/extension validation, unwired-codec rejection)
and serde defaults (#[serde(default)] -> H.264/1080p).

lib.rs: register export::export_video; `pub mod export` for the test.

Refs SPEC §2.4 / §8.2.
Copilot AI review requested due to automatic review settings June 23, 2026 13:58
@Felix201209 Felix201209 requested a review from appergb as a code owner June 23, 2026 13:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Felix201209 Felix201209 self-assigned this Jun 23, 2026

@appergb appergb left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审核通过 ✅(自动审核流程 · 主控亲审)

  • 新增 src-tauri/src/export.rs 整条时间线逐帧导出编排:snapshot→resolve_preset→project text/media→build_render_plan→逐帧 compositor.render_to_rgba→encoder.push_frame→finish。H.264/.mp4 限定,H.265/ProRes 明确拒绝并校验扩展名。run_export 与 Tauri 解耦便于测试。
  • 资源/路径解析正确(External/Project 相对 project_dir)、source_frame.max(0)、tolerance=0 精确取帧、render_size 偶数化。
  • 自包含复制 preview 逻辑、未碰 composite_frame → 与 #47/#48/#77/#79/#91 无碰撞(仅动 export.rs 新文件 + lib.rs 加 2 行注册 + 新集成测试)。CI 双绿。
  • 测试齐全:单测 preset/quality/serde 默认 + ffmpeg/GPU 门控集成测试(真导出→ffprobe 校验宽高/编码/帧数,无 GPU/ffmpeg 自动跳过)。
  • follow-up(非阻塞):run_export ~77 行 > 50 规约建议抽出帧循环;MediaResolver/projection 与 preview 路径重复,稳定后 hoist 为 pub(crate);out_path 缺路径边界校验(桌面 saveDialog 场景风险有限);H.265/ProRes/音频/进度取消按设计后续接。

@appergb appergb merged commit 20778b1 into main Jun 23, 2026
2 checks passed
@appergb appergb deleted the feat-export-pipeline branch June 23, 2026 14:23
appergb added a commit that referenced this pull request Jun 23, 2026
feat(export): linear audio mixdown for full-timeline export (refs Phase5/#112)
H-Chris233 pushed a commit to H-Chris233/OpenTake that referenced this pull request Jun 24, 2026
…se5/appergb#112)

Extend the appergb#112 video export spine with a linear audio mixdown, replacing
the dead `push_audio` side-channel with a wired decode → mix → mux path.

- opentake-media: new pure `encode::mix` module — lay each clip's mono f32
  PCM at its frame-derived sample offset, apply a per-sample gain envelope,
  sum overlapping clips, hard-limit to [-1,1]. `mono_f32_to_s16le` for the
  mux wire format. Fully unit-tested, no ffmpeg/domain deps.
- opentake-media encoder: `finish` now mux's a supplied mixed buffer via a
  second ffmpeg pass (`-c:v copy` + AAC/LPCM `-shortest`), restoring the
  video-only file if the mux fails. `mux_args` is pure and unit-tested.
- export.rs orchestrator: decode every audio/video clip's source window with
  the reused `extract_pcm` (mix rate, mono f32), build the `volume_at` gain
  envelope, skip muted tracks and audio-less sources, mix and push to the
  encoder. No-audio timelines still produce the same video-only output.
- export_integration.rs: ffmpeg+GPU-gated test builds a sine-audio fixture,
  exports, and asserts ffprobe reads back an AAC audio stream + cleaned temps;
  video-only test now asserts no audio stream leaks in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants