Single-lib cleanup: the per-crate src/bridge/ and src/ffi.rs layers are gone (oakundo/oakcommon/oaknode/oaktimeline/oakcodec/oakaudio/ oakrender/oaktask/oakplugin/oakstorage); cross-crate calls are plain Rust, CHandle marshalling shrinks to the oakengine boundary, and tests call the Rust APIs directly (pure C-ABI wrapper tests removed where the domain layer already covers the behavior). exporter.h family implemented: oakengine_export_render (CLI contract), oakengine_export_render_with_params (was a stub), last_error and progress callback; synchronous path reuses task_create_export + start_sync. Fixes on the way: oaktask video ticket self-deadlock, audio params dropped on the export path, codec encoder AAC slicing and H.264 time base. Real-mp4 tests cover both entry points, progress and the illegal-argument matrix. Also: oakstorage session maps null project handles to None (version- info path), configstore test double literal 3.14 -> 3.15 (clippy PI lint), oakaudio output callback scratch buffer + env-aware P1 test, cli media round-trip test uses a generated 16-frame clip (no more minute-long debug runs).
140 lines
5.2 KiB
Rust
140 lines
5.2 KiB
Rust
// Oak Video Editor - Non-Linear Video Editor
|
|
// Copyright (C) 2026 Oak Team
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! `PreCacheTask`, mirroring `src/task/src/precache/precachetask.h`.
|
|
//!
|
|
//! Renders a footage node through [`crate::render::RenderTask`] to fill
|
|
//! the playback cache. The footage and its sequence are now
|
|
//! [`crate::nodeops::NodeRef`] domain references (the deleted oaknode C
|
|
//! ABI stubs are gone); video params come from the sequence's parameter
|
|
//! streams and the cache range is the full footage video length.
|
|
//!
|
|
//! **Simplifications over the C++**: the deep project copy / viewer
|
|
//! wiring is not built (the render drives the given footage directly —
|
|
//! the ticket carries the footage filename and the footage node's
|
|
//! identity as the cache key) and the timeline work-area intersection is
|
|
//! replaced by the full footage length. The direct ticket arena's eval
|
|
//! producer does not persist frames to the frame cache yet, so this
|
|
//! render pass warms nothing on disk; the plumbing is in place.
|
|
//!
|
|
//! CPP-PARITY: src/task/src/precache/precachetask.h
|
|
|
|
use oakcommon::videoparams::VideoParams;
|
|
|
|
use crate::error::Result;
|
|
use crate::nodeops::{self, NodeRef};
|
|
use crate::render::{RenderTask, RenderTaskBehavior};
|
|
use crate::task::{Task, TaskBehavior};
|
|
use oakcore_rs::{Rational, TimeRange};
|
|
|
|
/// A pre-cache task: renders frames of a footage node into the playback
|
|
/// cache without any output file.
|
|
pub struct PreCacheTask {
|
|
/// The render base (itself a task).
|
|
pub render: RenderTask,
|
|
/// The footage being cached.
|
|
pub footage: NodeRef,
|
|
/// Frame index within the footage being cached.
|
|
pub index: i32,
|
|
/// The sequence context the footage lives in.
|
|
pub sequence: NodeRef,
|
|
}
|
|
|
|
impl PreCacheTask {
|
|
/// Create a pre-cache task for the given footage at `index` inside
|
|
/// `sequence`. The old `footage: CHandle` / `sequence: CHandle`
|
|
/// signature is replaced by domain [`NodeRef`]s (single-lib
|
|
/// unification).
|
|
pub fn new(footage: NodeRef, index: i32, sequence: NodeRef) -> PreCacheTask {
|
|
// The sequence's video params drive the render timebase (the
|
|
// deleted `oaknode_sequence_get_video_params` stub is replaced by
|
|
// the direct behavior query).
|
|
let video_params: Option<VideoParams> =
|
|
nodeops::sequence_video_params(&sequence.0, sequence.1, 0);
|
|
|
|
// The footage filename labels the task (the deleted
|
|
// `oaknode_footage_filename` stub is replaced by the direct
|
|
// behavior query).
|
|
let filename = nodeops::footage_filename(&footage.0, footage.1);
|
|
let title = format!("Pre-caching {filename}:{index}");
|
|
let base = Task::new(&title, None);
|
|
|
|
// The render target is the footage node itself (pre-cache fills
|
|
// that footage's frame cache).
|
|
let render = RenderTask::new(base, video_params, footage.clone(), Default::default(), None);
|
|
|
|
PreCacheTask {
|
|
render,
|
|
footage,
|
|
index,
|
|
sequence,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TaskBehavior for PreCacheTask {
|
|
fn run(&mut self, task: &mut Task) -> Result<()> {
|
|
// Share the caller's cancellation atom with the inner render base.
|
|
self.render.base.set_cancel_atom(task.get_cancel_atom());
|
|
|
|
// The full footage length is the cache range (simplified: no
|
|
// work-area intersection). The deleted `oaknode_footage_get_video_length`
|
|
// stub is replaced by the direct behavior query.
|
|
let length = nodeops::node_length(&self.footage.0, self.footage.1);
|
|
let range = TimeRange::new(Rational::new(0, 1), length);
|
|
|
|
// No color manager / frame-cache handles exist on the direct
|
|
// ticket path (the arena keys the cache by the footage node
|
|
// identity — see `RenderTask::build_video_ticket`).
|
|
self.render.set_render_inputs(0 /* k_online */, false, range);
|
|
|
|
// Drive the render with `self` as the subclass behavior (the C++
|
|
// virtual dispatch receiver); the render is temporarily moved out to
|
|
// avoid a self-referential borrow and put back before returning.
|
|
let mut render =
|
|
std::mem::replace(&mut self.render, crate::render::RenderTask::placeholder());
|
|
let result = render.render(task, self);
|
|
self.render = render;
|
|
|
|
result
|
|
}
|
|
}
|
|
|
|
impl RenderTaskBehavior for PreCacheTask {
|
|
fn frame_downloaded(&mut self, task: &mut Task, frame: &oakrender::texture::Texture) -> Result<()> {
|
|
// Do nothing: pre-cache just fills the frame cache (the direct
|
|
// ticket arena records the render through the ticket's cache
|
|
// identity; see the module docs).
|
|
let _ = (task, frame);
|
|
Ok(())
|
|
}
|
|
|
|
fn audio_downloaded(
|
|
&mut self,
|
|
task: &mut Task,
|
|
buffer: &oakrender::ticket::AudioSamples,
|
|
) -> Result<()> {
|
|
// Pre-cache doesn't cache any audio.
|
|
let _ = (task, buffer);
|
|
Ok(())
|
|
}
|
|
|
|
fn encode_subtitle(&mut self, task: &mut Task, text: &str) -> Result<()> {
|
|
let _ = (task, text);
|
|
Ok(())
|
|
}
|
|
}
|