refactor: workspace layout — crates/, app at root, legacy C++ removed
Single mechanical restructure commit: - root Cargo.toml = oakapp bin + workspace; one cargo build produces oakapp, oak-cli, oak-worker, liboakengine.dylib - app/rust/src -> src/ (app at repo root, no rust/ nesting) - src/<mod>/rust -> crates/oak<mod>; src/oakcore-rs -> crates/oakcore; src/bindings/oakotio -> crates/oakotio; src/engine/rust -> crates/oakengine (keeps cdylib+staticlib+rlib) - public C headers include/<mod>/ -> crates/oakengine/include/<mod>/ - OFX SDK headers vendored into crates/oakplugin/ofx/ (HostSupport gone) - legacy deleted: old src/ C++ modules, engine/, core/, ffmpeg_bridge/, app/ (Qt), cli/worker C++, root CMakeLists, third_party/KDDockWidgets submodule, otio-install, all build-* output (~40GB) - oakstorage kept but excluded from the workspace (skeleton w/ todos); gpui excluded (own workspace) - verified: cargo build green, cargo test --workspace 1845/0 (with the documented OCIO_RS_* env override for the homebrew OCIO)
This commit is contained in:
@@ -0,0 +1,576 @@
|
||||
// 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/>.
|
||||
|
||||
//! `engine/include/oakengine/audio.h` over the oakaudio module.
|
||||
//!
|
||||
//! The engine API is static (the singleton is implicit); the oakaudio C
|
||||
//! ABI passes the manager handle explicitly, so every family call goes
|
||||
//! through [`manager()`] (a borrowed handle; empty when no instance
|
||||
//! exists — engine semantics then report `paNoDevice`/error as
|
||||
//! documented). The borrowed `OakAudioParams*` handles are read through
|
||||
//! the liboakcore `oakcore_audioparams_*` accessors.
|
||||
|
||||
use std::ffi::{c_char, c_double, c_int, c_void};
|
||||
|
||||
use crate::bridge::audio as a;
|
||||
use crate::error::Error;
|
||||
use crate::handle::{
|
||||
box_handle, free_box, guard, guard_i64, guard_void, unbox, CHandle, OakEngineAudioProcessor,
|
||||
};
|
||||
|
||||
/// paNoDevice — no audio device selected.
|
||||
const PA_NO_DEVICE: i64 = -1;
|
||||
|
||||
/// Borrowed handle of the AudioManager singleton (empty when none).
|
||||
fn manager() -> CHandle {
|
||||
unsafe { a::oakaudio_manager_instance() }
|
||||
}
|
||||
|
||||
/// Borrowed handle of the AudioManager singleton for other facade
|
||||
/// families (empty ctx == NULL when none).
|
||||
pub(crate) fn audio_manager_handle_raw() -> CHandle {
|
||||
manager()
|
||||
}
|
||||
|
||||
/// `oakengine_audio_create_instance` — create the singleton (no-op when
|
||||
/// it already exists).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_create_instance() -> c_int {
|
||||
guard(|| Error::from_module(unsafe { a::oakaudio_manager_create_instance() }))
|
||||
}
|
||||
|
||||
/// `oakengine_audio_destroy_instance` — destroy the singleton (no-op when
|
||||
/// none exists).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_destroy_instance() -> c_int {
|
||||
guard_void(|| unsafe {
|
||||
a::oakaudio_manager_destroy_instance();
|
||||
});
|
||||
crate::error::OAKENGINE_OK
|
||||
}
|
||||
/// `oakengine_audio_manager_handle` — borrowed token of the singleton
|
||||
/// (NULL when none); only for event-subscription use, never freed.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_manager_handle() -> *mut c_void {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
m.ctx
|
||||
}
|
||||
}
|
||||
|
||||
/// `oakengine_audio_get_output_device` — paNoDevice when none/no instance.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_get_output_device() -> i64 {
|
||||
guard_i64(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Ok(PA_NO_DEVICE);
|
||||
}
|
||||
Ok(i64::from(unsafe { a::oakaudio_manager_get_output_device(m) }))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_set_output_device`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_set_output_device(device: i64) -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_set_output_device(m, device as c_int) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_get_input_device` — paNoDevice when none/no instance.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_get_input_device() -> i64 {
|
||||
guard_i64(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Ok(PA_NO_DEVICE);
|
||||
}
|
||||
Ok(i64::from(unsafe { a::oakaudio_manager_get_input_device(m) }))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_set_input_device`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_set_input_device(device: i64) -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_set_input_device(m, device as c_int) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_hard_reset` — re-initialize PortAudio and refresh the
|
||||
/// device lists.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_hard_reset() -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_hard_reset(m) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_clear_buffered_output`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_clear_buffered_output() -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_clear_buffered_output(m) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_push_to_output` — queue interleaved samples described
|
||||
/// by the borrowed `OakAudioParams*` handle.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_push_to_output(
|
||||
params: *const c_void,
|
||||
samples: *const c_char,
|
||||
samples_size: i64,
|
||||
error_buf: *mut c_char,
|
||||
error_buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let m = manager();
|
||||
if m.is_null() || params.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
let rate = a::oakcore_audioparams_sample_rate(params);
|
||||
let layout = a::oakcore_audioparams_channel_layout(params);
|
||||
let format = a::oakcore_audioparams_format(params);
|
||||
Error::from_module(a::oakaudio_manager_push_to_output(
|
||||
m,
|
||||
rate,
|
||||
layout,
|
||||
format,
|
||||
samples,
|
||||
samples_size,
|
||||
error_buf,
|
||||
error_buf_size,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_stop_recording`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_stop_recording() -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_stop_recording(m) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_stop_output`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_stop_output() -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_stop_output(m) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_reset_output_clock`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_reset_output_clock() -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_reset_output_clock(m) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_set_output_notify_interval`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_set_output_notify_interval(bytes: i64) -> c_int {
|
||||
guard(|| {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
Error::from_module(unsafe { a::oakaudio_manager_set_output_notify_interval(m, bytes) })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_start_recording` — takes ownership of `params`
|
||||
/// (the handle is destroyed when the recording ends).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_start_recording(
|
||||
params: *mut c_void,
|
||||
error_buf: *mut c_char,
|
||||
error_buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let m = manager();
|
||||
if m.is_null() {
|
||||
return Err(Error::Failed("no AudioManager instance".into()));
|
||||
}
|
||||
if params.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let rc = a::oakaudio_manager_start_recording(
|
||||
m,
|
||||
params.cast::<a::EncodingParams>(),
|
||||
error_buf,
|
||||
error_buf_size,
|
||||
);
|
||||
Error::from_module(rc)
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Audio synchronization
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_audio_estimate_envelope_offset` — estimate the sample offset
|
||||
/// between two RMS envelopes.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_estimate_envelope_offset(
|
||||
reference: *const c_double,
|
||||
reference_len: c_int,
|
||||
candidate: *const c_double,
|
||||
candidate_len: c_int,
|
||||
reference_valid: *const u8,
|
||||
_reference_valid_len: c_int,
|
||||
candidate_valid: *const u8,
|
||||
_candidate_valid_len: c_int,
|
||||
window_samples: u64,
|
||||
max_offset_windows: i64,
|
||||
out: *mut OakAudioWaveformOffset,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if reference.is_null() || candidate.is_null() || out.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let mut result = a::OffsetResult {
|
||||
offset_samples: 0,
|
||||
confidence: 0.0,
|
||||
valid: 0,
|
||||
};
|
||||
Error::from_module(a::oakaudio_sync_estimate_envelope_offset(
|
||||
reference,
|
||||
reference_len,
|
||||
candidate,
|
||||
candidate_len,
|
||||
reference_valid,
|
||||
candidate_valid,
|
||||
window_samples,
|
||||
max_offset_windows,
|
||||
&mut result,
|
||||
))?;
|
||||
(*out).offset_samples = result.offset_samples;
|
||||
(*out).confidence = result.confidence;
|
||||
(*out).valid = result.valid;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_estimate_stretch_and_offset` — rate + offset
|
||||
/// correlation.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_estimate_stretch_and_offset(
|
||||
reference: *const c_double,
|
||||
reference_len: c_int,
|
||||
candidate: *const c_double,
|
||||
candidate_len: c_int,
|
||||
reference_valid: *const u8,
|
||||
_reference_valid_len: c_int,
|
||||
candidate_valid: *const u8,
|
||||
_candidate_valid_len: c_int,
|
||||
window_samples: u64,
|
||||
max_offset_windows: i64,
|
||||
min_rate: c_double,
|
||||
max_rate: c_double,
|
||||
rate_step: c_double,
|
||||
out: *mut OakAudioWaveformStretchOffset,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if reference.is_null() || candidate.is_null() || out.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let mut result = a::StretchOffsetResult {
|
||||
rate: 0.0,
|
||||
offset_samples: 0,
|
||||
confidence: 0.0,
|
||||
valid: 0,
|
||||
};
|
||||
Error::from_module(a::oakaudio_sync_estimate_stretch_and_offset(
|
||||
reference,
|
||||
reference_len,
|
||||
candidate,
|
||||
candidate_len,
|
||||
reference_valid,
|
||||
candidate_valid,
|
||||
window_samples,
|
||||
max_offset_windows,
|
||||
min_rate,
|
||||
max_rate,
|
||||
rate_step,
|
||||
&mut result,
|
||||
))?;
|
||||
(*out).rate = result.rate;
|
||||
(*out).offset_samples = result.offset_samples;
|
||||
(*out).confidence = result.confidence;
|
||||
(*out).valid = result.valid;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_sync_place_by_source_time` — timeline placement from
|
||||
/// source timecodes.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_sync_place_by_source_time(
|
||||
reference: *const OakAudioSyncSourceClip,
|
||||
candidate: *const OakAudioSyncSourceClip,
|
||||
reference_timeline_in_num: i64,
|
||||
reference_timeline_in_den: i64,
|
||||
out: *mut OakAudioSyncPlacement,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if reference.is_null() || candidate.is_null() || out.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let mut num: i64 = 0;
|
||||
let mut den: i64 = 0;
|
||||
let mut valid: c_int = 0;
|
||||
Error::from_module(a::oakaudio_sync_place_by_source_time(
|
||||
reference.cast::<a::SourceClip>(),
|
||||
candidate.cast::<a::SourceClip>(),
|
||||
reference_timeline_in_num,
|
||||
reference_timeline_in_den,
|
||||
&mut num,
|
||||
&mut den,
|
||||
&mut valid,
|
||||
))?;
|
||||
(*out).timeline_in_num = num;
|
||||
(*out).timeline_in_den = den;
|
||||
(*out).valid = valid;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_sync_place_by_waveform_offset` — timeline placement
|
||||
/// from a waveform offset.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_sync_place_by_waveform_offset(
|
||||
reference_timeline_in_num: i64,
|
||||
reference_timeline_in_den: i64,
|
||||
candidate_offset_samples: i64,
|
||||
sample_rate: c_int,
|
||||
out: *mut OakAudioSyncPlacement,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if out.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let mut num: i64 = 0;
|
||||
let mut den: i64 = 0;
|
||||
let mut valid: c_int = 0;
|
||||
Error::from_module(a::oakaudio_sync_place_by_waveform_offset(
|
||||
reference_timeline_in_num,
|
||||
reference_timeline_in_den,
|
||||
candidate_offset_samples,
|
||||
sample_rate,
|
||||
&mut num,
|
||||
&mut den,
|
||||
&mut valid,
|
||||
))?;
|
||||
(*out).timeline_in_num = num;
|
||||
(*out).timeline_in_den = den;
|
||||
(*out).valid = valid;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Audio processor
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_audio_processor_create`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_audio_processor_create() -> *mut OakEngineAudioProcessor {
|
||||
crate::handle::guard_ptr(|| {
|
||||
let p = unsafe { a::oakaudio_processor_init() };
|
||||
if p.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_handle::<OakEngineAudioProcessor>(p))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_processor_free` — NULL no-op.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_processor_free(p: *mut OakEngineAudioProcessor) {
|
||||
guard_void(|| unsafe {
|
||||
free_box(p);
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_processor_open` — open the conversion graph; `from`/
|
||||
/// `to` are borrowed `OakAudioParams*` handles (read via oakcore).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_processor_open(
|
||||
p: *mut OakEngineAudioProcessor,
|
||||
from: *const c_void,
|
||||
to: *const c_void,
|
||||
tempo: c_double,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let handle = unbox(p)?;
|
||||
if from.is_null() || to.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let in_rate = a::oakcore_audioparams_sample_rate(from);
|
||||
let in_layout = a::oakcore_audioparams_channel_layout(from);
|
||||
let in_format = a::oakcore_audioparams_format(from);
|
||||
let out_rate = a::oakcore_audioparams_sample_rate(to);
|
||||
let out_layout = a::oakcore_audioparams_channel_layout(to);
|
||||
let out_format = a::oakcore_audioparams_format(to);
|
||||
Error::from_module(a::oakaudio_processor_open(
|
||||
handle,
|
||||
in_rate,
|
||||
in_layout,
|
||||
in_format,
|
||||
out_rate,
|
||||
out_layout,
|
||||
out_format,
|
||||
tempo,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_processor_close` — NULL/not-open no-op.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_processor_close(p: *mut OakEngineAudioProcessor) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if p.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
let handle = unbox(p)?;
|
||||
Error::from_module(a::oakaudio_processor_close(handle))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_processor_is_open` — 1 when open, 0 when NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_processor_is_open(p: *mut OakEngineAudioProcessor) -> c_int {
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
if p.is_null() {
|
||||
return Ok(0);
|
||||
}
|
||||
let handle = unbox(p)?;
|
||||
Ok(a::oakaudio_processor_is_open(handle))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_audio_processor_convert` — **not backed**: the oakaudio
|
||||
/// module's processor converts planar→planar, while the engine contract
|
||||
/// is planar→packed with an owned output buffer. Returns
|
||||
/// `OAKENGINE_E_FAILED` until the module exposes a packed-output
|
||||
/// converter.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_processor_convert(
|
||||
_p: *mut OakEngineAudioProcessor,
|
||||
_in: *mut *mut f32,
|
||||
_nb_in_samples: c_int,
|
||||
_out_data: *mut *const c_void,
|
||||
_out_size: *mut c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_audio_processor_output_params` — **not backed**: the
|
||||
/// oakaudio module has no output-params getter. Returns NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_processor_output_params(
|
||||
_p: *mut OakEngineAudioProcessor,
|
||||
) -> *mut c_void {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
/// `engine/include/oakengine/audio.h` — envelope-offset result.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OakAudioWaveformOffset {
|
||||
/// Offset in samples.
|
||||
pub offset_samples: i64,
|
||||
/// Correlation confidence.
|
||||
pub confidence: c_double,
|
||||
/// 1 when usable.
|
||||
pub valid: c_int,
|
||||
}
|
||||
|
||||
/// `engine/include/oakengine/audio.h` — rate+offset result.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OakAudioWaveformStretchOffset {
|
||||
/// Playback rate.
|
||||
pub rate: c_double,
|
||||
/// Offset in samples.
|
||||
pub offset_samples: i64,
|
||||
/// Correlation confidence.
|
||||
pub confidence: c_double,
|
||||
/// 1 when usable.
|
||||
pub valid: c_int,
|
||||
}
|
||||
|
||||
/// `engine/include/oakengine/audio.h` — source-clip description.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OakAudioSyncSourceClip {
|
||||
/// Source start time num.
|
||||
pub source_start_time_num: i64,
|
||||
/// Source start time den.
|
||||
pub source_start_time_den: i64,
|
||||
/// Media in num.
|
||||
pub media_in_num: i64,
|
||||
/// Media in den.
|
||||
pub media_in_den: i64,
|
||||
/// 1 when source start time is meaningful.
|
||||
pub has_source_start_time: c_int,
|
||||
}
|
||||
|
||||
/// `engine/include/oakengine/audio.h` — timeline placement result.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OakAudioSyncPlacement {
|
||||
/// Timeline in-point num.
|
||||
pub timeline_in_num: i64,
|
||||
/// Timeline in-point den.
|
||||
pub timeline_in_den: i64,
|
||||
/// 1 when usable.
|
||||
pub valid: c_int,
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
// 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/>.
|
||||
|
||||
//! oakaudio C ABI bridge: direct Rust calls into the `oakaudio` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oakaudio`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oakaudio C ABI imports, mirroring the oakaudio crate's exports
|
||||
//! (`src/audio/rust/src/ffi.rs`; headers `include/audio/*.h`), plus the
|
||||
//! liboakcore `oakcore_audioparams_*` readers used to convert the
|
||||
//! engine's borrowed `OakAudioParams*` handles.
|
||||
|
||||
use std::ffi::{c_char, c_double, c_int, c_void};
|
||||
|
||||
use crate::handle::CHandle;
|
||||
|
||||
/// `oakaudio_offsetresult` C ABI POD. Single-lib unification: aliases
|
||||
/// the oakaudio crate's struct (identical layout).
|
||||
pub type OffsetResult = oakaudio::ffi::sync::OffsetResult;
|
||||
|
||||
|
||||
/// `oakaudio_stretchoffsetresult` C ABI POD. Single-lib unification: aliases
|
||||
/// the oakaudio crate's struct (identical layout).
|
||||
pub type StretchOffsetResult = oakaudio::ffi::sync::StretchOffsetResult;
|
||||
|
||||
|
||||
/// `oakaudio_sourceclip` C ABI POD. Single-lib unification: aliases
|
||||
/// the oakaudio crate's struct (identical layout).
|
||||
pub type SourceClip = oakaudio::ffi::sync::SourceClip;
|
||||
|
||||
|
||||
|
||||
/// `oakaudio` recording-params POD — single-lib unification: aliases the
|
||||
/// oakaudio crate's type (itself the oakcodec `oakcodec_encoding_params`).
|
||||
pub type EncodingParams = oakaudio::bridge::codec::EncodingParams;
|
||||
|
||||
extern "C" {
|
||||
pub fn oakcore_audioparams_create(sample_rate: c_int, channel_layout: u64, format: c_int) -> *mut c_void;
|
||||
pub fn oakcore_audioparams_free(params: *mut c_void);
|
||||
pub fn oakcore_audioparams_sample_rate(params: *const c_void) -> c_int;
|
||||
pub fn oakcore_audioparams_channel_layout(params: *const c_void) -> u64;
|
||||
pub fn oakcore_audioparams_format(params: *const c_void) -> c_int;
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_create_instance() -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_create_instance() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_destroy_instance() {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_destroy_instance() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_instance() -> CHandle {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_instance() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_free(_self: *mut CHandle) {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_free(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_set_output_notify_interval(_self: CHandle, bytes: i64) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_set_output_notify_interval(_self, bytes) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_push_to_output(
|
||||
_self: CHandle,
|
||||
rate: c_int,
|
||||
layout: u64,
|
||||
format: c_int,
|
||||
samples: *const c_char,
|
||||
samples_size: i64,
|
||||
error_buf: *mut c_char,
|
||||
error_buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_push_to_output(_self, rate, layout, format, samples, samples_size, error_buf, error_buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_clear_buffered_output(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_clear_buffered_output(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_stop_output(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_stop_output(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_reset_output_clock(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_reset_output_clock(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_get_output_device(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_get_output_device(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_set_output_device(_self: CHandle, device: c_int) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_set_output_device(_self, device) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_get_input_device(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_get_input_device(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_set_input_device(_self: CHandle, device: c_int) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_set_input_device(_self, device) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_hard_reset(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_hard_reset(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
/// `oakaudio_manager_start_recording` — crosses the encoding-params C ABI
|
||||
/// POD (the facade keeps its own opaque mirror; the module's
|
||||
/// `oakaudio::bridge::codec::EncodingParams`). Kept as a link-time
|
||||
/// `extern "C"` declaration against the frozen module C ABI.
|
||||
pub fn oakaudio_manager_start_recording(
|
||||
_self: CHandle,
|
||||
params: *const EncodingParams,
|
||||
error_buf: *mut c_char,
|
||||
error_buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_start_recording(_self, params, error_buf, error_buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_stop_recording(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_stop_recording(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_manager_seconds(_self: CHandle, out: *mut c_double) -> c_int {
|
||||
unsafe { oakaudio::ffi::manager::oakaudio_manager_seconds(_self, out) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_processor_init() -> CHandle {
|
||||
unsafe { oakaudio::ffi::processor::oakaudio_processor_init() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_processor_free(_self: *mut CHandle) {
|
||||
unsafe { oakaudio::ffi::processor::oakaudio_processor_free(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_processor_open(
|
||||
_self: CHandle,
|
||||
in_rate: c_int,
|
||||
in_layout: u64,
|
||||
in_format: c_int,
|
||||
out_rate: c_int,
|
||||
out_layout: u64,
|
||||
out_format: c_int,
|
||||
speed: c_double,
|
||||
) -> c_int {
|
||||
unsafe { oakaudio::ffi::processor::oakaudio_processor_open(_self, in_rate, in_layout, in_format, out_rate, out_layout, out_format, speed) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_processor_close(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::processor::oakaudio_processor_close(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_processor_is_open(_self: CHandle) -> c_int {
|
||||
unsafe { oakaudio::ffi::processor::oakaudio_processor_is_open(_self) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_sync_estimate_envelope_offset(
|
||||
reference: *const c_double,
|
||||
reference_len: c_int,
|
||||
candidate: *const c_double,
|
||||
candidate_len: c_int,
|
||||
reference_valid: *const u8,
|
||||
candidate_valid: *const u8,
|
||||
window_samples: u64,
|
||||
max_offset_windows: i64,
|
||||
out: *mut OffsetResult,
|
||||
) -> c_int {
|
||||
unsafe { oakaudio::ffi::sync::oakaudio_sync_estimate_envelope_offset(reference, reference_len, candidate, candidate_len, reference_valid, candidate_valid, window_samples, max_offset_windows, out) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_sync_estimate_stretch_and_offset(
|
||||
reference: *const c_double,
|
||||
reference_len: c_int,
|
||||
candidate: *const c_double,
|
||||
candidate_len: c_int,
|
||||
reference_valid: *const u8,
|
||||
candidate_valid: *const u8,
|
||||
window_samples: u64,
|
||||
max_offset_windows: i64,
|
||||
min_rate: c_double,
|
||||
max_rate: c_double,
|
||||
rate_step: c_double,
|
||||
out: *mut StretchOffsetResult,
|
||||
) -> c_int {
|
||||
unsafe { oakaudio::ffi::sync::oakaudio_sync_estimate_stretch_and_offset(reference, reference_len, candidate, candidate_len, reference_valid, candidate_valid, window_samples, max_offset_windows, min_rate, max_rate, rate_step, out) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_sync_place_by_source_time(
|
||||
reference: *const SourceClip,
|
||||
candidate: *const SourceClip,
|
||||
reference_timeline_in_num: i64,
|
||||
reference_timeline_in_den: i64,
|
||||
out_num: *mut i64,
|
||||
out_den: *mut i64,
|
||||
out_valid: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakaudio::ffi::sync::oakaudio_sync_place_by_source_time(reference, candidate, reference_timeline_in_num, reference_timeline_in_den, out_num, out_den, out_valid) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakaudio` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakaudio_sync_place_by_waveform_offset(
|
||||
reference_timeline_in_num: i64,
|
||||
reference_timeline_in_den: i64,
|
||||
candidate_offset_samples: i64,
|
||||
sample_rate: c_int,
|
||||
out_num: *mut i64,
|
||||
out_den: *mut i64,
|
||||
out_valid: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakaudio::ffi::sync::oakaudio_sync_place_by_waveform_offset(reference_timeline_in_num, reference_timeline_in_den, candidate_offset_samples, sample_rate, out_num, out_den, out_valid) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
// 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/>.
|
||||
|
||||
//! oakcodec C ABI bridge: direct Rust calls into the `oakcodec` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oakcodec`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oakcodec C ABI imports, mirroring the oakcodec crate's exports
|
||||
//! (`src/codec/rust/src/ffi/{format,encoder}.rs`; headers
|
||||
//! `include/codec/{format,encoder}.h`). Also carries the
|
||||
//! `oakcodec_encoding_params` POD the facade's encoding-params handle
|
||||
//! wraps, and the oakaudio recording-params pointer pass-through.
|
||||
|
||||
use std::ffi::{c_char, c_int};
|
||||
|
||||
use crate::handle::CHandle;
|
||||
|
||||
/// `include/codec/encoder.h` — the encoding-params POD, a complete mirror
|
||||
/// `olive::EncodingParams` — single-lib unification: aliases the oakcodec
|
||||
/// crate's POD (`oakcodec_encoding_params`, identical `#[repr(C)]` layout;
|
||||
/// the engine's `OakEngineEncodingParams` handle is a heap box over exactly
|
||||
/// this struct, so every engine getter/setter reads/writes a field and
|
||||
/// `encoder_init`/recording can consume the pointer directly).
|
||||
pub type EncodingParamsPOD = oakcodec::ffi::encoder::oakcodec_encoding_params;
|
||||
|
||||
|
||||
/// Zeroed encoding-params POD (all fields 0 / NUL). The codec crate's
|
||||
/// struct has no zeroed constructor; this facade helper provides it.
|
||||
pub fn zeroed_encoding_params() -> EncodingParamsPOD {
|
||||
// All-C fields: all-zero is a valid value.
|
||||
unsafe { std::mem::zeroed() }
|
||||
}
|
||||
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_count() -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_count() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_name(format: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_name(format, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_extension(
|
||||
format: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_extension(format, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_video_codec_count(format: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_video_codec_count(format) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_video_codec_at(format: c_int, index: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_video_codec_at(format, index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_audio_codec_count(format: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_audio_codec_count(format) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_audio_codec_at(format: c_int, index: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_audio_codec_at(format, index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_subtitle_codec_count(format: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_subtitle_codec_count(format) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_format_subtitle_codec_at(format: c_int, index: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_subtitle_codec_at(format, index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_codec_name(codec: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_codec_name(codec, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_codec_is_still_image(codec: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_codec_is_still_image(codec) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_codec_is_lossless(codec: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_codec_is_lossless(codec) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_pix_fmt_count(format: c_int, codec: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_pix_fmt_count(format, codec) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_pix_fmt_at(
|
||||
format: c_int,
|
||||
codec: c_int,
|
||||
index: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_pix_fmt_at(format, codec, index, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_pix_fmt_index(codec: c_int, pix_fmt: *const c_char) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_pix_fmt_index(codec, pix_fmt) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_sample_format_count(format: c_int, codec: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_sample_format_count(format, codec) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_sample_format_at(format: c_int, codec: c_int, index: c_int) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_sample_format_at(format, codec, index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_filename_contains_digit_placeholder(filename: *const c_char) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_filename_contains_digit_placeholder(filename) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_image_sequence_digit_count(filename: *const c_char) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_image_sequence_digit_count(filename) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_filename_remove_digit_placeholder(
|
||||
filename: *const c_char,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcodec::ffi::format::oakcodec_encoding_filename_remove_digit_placeholder(filename, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoding_generate_matrix(
|
||||
method: c_int,
|
||||
src_width: c_int,
|
||||
src_height: c_int,
|
||||
dst_width: c_int,
|
||||
dst_height: c_int,
|
||||
out_matrix: *mut f64,
|
||||
) -> c_int {
|
||||
unsafe { oakcodec::ffi::encoder::oakcodec_encoding_generate_matrix(method, src_width, src_height, dst_width, dst_height, out_matrix) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
/// `oakcodec_encoder_init` — crosses the encoding-params C ABI POD
|
||||
/// (`oakcodec_encoding_params`; the facade keeps its own POD mirror).
|
||||
/// Kept as a link-time `extern "C"` declaration against the frozen module
|
||||
/// C ABI.
|
||||
pub fn oakcodec_encoder_init(params: *const EncodingParamsPOD) -> CHandle {
|
||||
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_init(params) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcodec` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcodec_encoder_free(encoder: *mut CHandle) {
|
||||
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_free(encoder) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,739 @@
|
||||
// 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/>.
|
||||
|
||||
//! oakcommon C ABI bridge: direct Rust calls into the `oakcommon` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oakcommon`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oakcommon C ABI imports, mirroring the oakcommon crate's exports
|
||||
//! (`src/common/rust/src/ffi.rs`; headers `include/common/*.h`). Only the
|
||||
//! families the facade wraps: config, videoparams, colortransform, xml
|
||||
//! reader/writer and the decibel helpers.
|
||||
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
|
||||
use crate::handle::CHandle;
|
||||
|
||||
/// `include/common/config.h` — error handler callback.
|
||||
pub type ConfigErrorHandler =
|
||||
Option<unsafe extern "C" fn(title: *const c_char, message: *const c_char, userdata: *mut c_void)>;
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_load() -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_load() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_save() -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_save() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_reset_defaults() -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_reset_defaults() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_set(group: *const c_char, key: *const c_char, value: *const c_char) {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_set(group, key, value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_get(
|
||||
group: *const c_char,
|
||||
key: *const c_char,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_get(group, key, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_get_int(group: *const c_char, key: *const c_char, fallback: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_get_int(group, key, fallback) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_get_int64(
|
||||
group: *const c_char,
|
||||
key: *const c_char,
|
||||
fallback: i64,
|
||||
) -> i64 {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_get_int64(group, key, fallback) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_get_double(group: *const c_char, key: *const c_char, fallback: f64) -> f64 {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_get_double(group, key, fallback) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_get_bool(group: *const c_char, key: *const c_char, fallback: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_get_bool(group, key, fallback) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_set_int(group: *const c_char, key: *const c_char, value: c_int) {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_set_int(group, key, value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_set_int64(group: *const c_char, key: *const c_char, value: i64) {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_set_int64(group, key, value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_set_double(group: *const c_char, key: *const c_char, value: f64) {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_set_double(group, key, value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_set_bool(group: *const c_char, key: *const c_char, value: c_int) {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_set_bool(group, key, value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_entry_type(group: *const c_char, key: *const c_char) -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_entry_type(group, key) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_config_set_error_handler(handler: ConfigErrorHandler, userdata: *mut c_void) -> c_int {
|
||||
unsafe { oakcommon::ffi::config::oakcommon_config_set_error_handler(handler, userdata) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_init() -> CHandle {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_init() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_init_basic(
|
||||
width: c_int,
|
||||
height: c_int,
|
||||
pixel_format: c_int,
|
||||
nb_channels: c_int,
|
||||
pixel_aspect_num: c_int,
|
||||
pixel_aspect_den: c_int,
|
||||
interlacing: c_int,
|
||||
divider: c_int,
|
||||
) -> CHandle {
|
||||
unsafe {
|
||||
oakcommon::ffi::videoparams::oakcommon_videoparams_init_basic(
|
||||
width, height, pixel_format, nb_channels, pixel_aspect_num, pixel_aspect_den,
|
||||
interlacing, divider,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_init_with_time_base(
|
||||
width: c_int,
|
||||
height: c_int,
|
||||
time_base_num: c_int,
|
||||
time_base_den: c_int,
|
||||
pixel_format: c_int,
|
||||
nb_channels: c_int,
|
||||
pixel_aspect_num: c_int,
|
||||
pixel_aspect_den: c_int,
|
||||
interlacing: c_int,
|
||||
divider: c_int,
|
||||
) -> CHandle {
|
||||
unsafe {
|
||||
oakcommon::ffi::videoparams::oakcommon_videoparams_init_with_time_base(
|
||||
width, height, time_base_num, time_base_den, pixel_format, nb_channels,
|
||||
pixel_aspect_num, pixel_aspect_den, interlacing, divider,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_free(params: *mut CHandle) {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_free(params) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_width(params: CHandle, width: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_width(params, width) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_width(params: CHandle, width: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_width(params, width) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_height(params: CHandle, height: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_height(params, height) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_height(params: CHandle, height: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_height(params, height) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_time_base(
|
||||
params: CHandle,
|
||||
numerator: *mut c_int,
|
||||
denominator: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_time_base(params, numerator, denominator) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_time_base(
|
||||
params: CHandle,
|
||||
numerator: c_int,
|
||||
denominator: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_time_base(params, numerator, denominator) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_frame_rate(
|
||||
params: CHandle,
|
||||
numerator: *mut c_int,
|
||||
denominator: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_frame_rate(params, numerator, denominator) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_frame_rate(
|
||||
params: CHandle,
|
||||
numerator: c_int,
|
||||
denominator: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_frame_rate(params, numerator, denominator) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_pixel_aspect_ratio(
|
||||
params: CHandle,
|
||||
numerator: *mut c_int,
|
||||
denominator: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_pixel_aspect_ratio(params, numerator, denominator) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_pixel_aspect_ratio(
|
||||
params: CHandle,
|
||||
numerator: c_int,
|
||||
denominator: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_pixel_aspect_ratio(params, numerator, denominator) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_format(params: CHandle, format: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_format(params, format) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_format(params: CHandle, format: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_format(params, format) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_interlacing(params: CHandle, interlacing: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_interlacing(params, interlacing) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_interlacing(params: CHandle, interlacing: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_interlacing(params, interlacing) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_divider(params: CHandle, divider: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_divider(params, divider) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_divider(params: CHandle, divider: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_divider(params, divider) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_video_type(params: CHandle, type_: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_video_type(params, type_) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_video_type(params: CHandle, type_: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_video_type(params, type_) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_premultiplied_alpha(
|
||||
params: CHandle,
|
||||
premultiplied: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_premultiplied_alpha(params, premultiplied) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_premultiplied_alpha(params: CHandle, premultiplied: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_premultiplied_alpha(params, premultiplied) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_color_range(params: CHandle, color_range: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_color_range(params, color_range) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_set_color_range(params: CHandle, color_range: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_color_range(params, color_range) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_is_valid(params: CHandle, valid: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_is_valid(params, valid) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_effective_width(params: CHandle, width: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_effective_width(params, width) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_effective_height(params: CHandle, height: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_effective_height(params, height) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_bytes_per_pixel(params: CHandle, bytes: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_bytes_per_pixel(params, bytes) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_equals(a: CHandle, b: CHandle, out_equal: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_equals(a, b, out_equal) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_format_is_float(pixel_format: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_format_is_float(pixel_format) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_format_name(
|
||||
pixel_format: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_format_name(pixel_format, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_frame_rate_to_string(
|
||||
numerator: c_int,
|
||||
denominator: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_frame_rate_to_string(numerator, denominator, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_name_for_divider(
|
||||
divider: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_name_for_divider(divider, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_scaled_dimension(dimension: c_int, divider: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_scaled_dimension(dimension, divider) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_generate_auto_divider(width: i64, height: i64) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_generate_auto_divider(width, height) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_divider_for_target_resolution(
|
||||
src_width: c_int,
|
||||
src_height: c_int,
|
||||
target_width: c_int,
|
||||
target_height: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_divider_for_target_resolution(src_width, src_height, target_width, target_height) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_bytes_per_channel_for_format(pixel_format: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_bytes_per_channel_for_format(pixel_format) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_bytes_per_pixel_for_format(
|
||||
pixel_format: c_int,
|
||||
channels: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_bytes_per_pixel_for_format(pixel_format, channels) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_static_get_bytes_per_pixel(
|
||||
pixel_format: c_int,
|
||||
channels: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_static_get_bytes_per_pixel(pixel_format, channels) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_buffer_size(params: CHandle, size: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_buffer_size(params, size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_videoparams_get_time_in_timebase_units(
|
||||
params: CHandle,
|
||||
time_num: c_int,
|
||||
time_den: c_int,
|
||||
timestamp: *mut i64,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_time_in_timebase_units(params, time_num, time_den, timestamp) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_init_output(output: *const c_char) -> CHandle {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_init_output(output) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_init_display(
|
||||
display: *const c_char,
|
||||
view: *const c_char,
|
||||
look: *const c_char,
|
||||
) -> CHandle {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_init_display(display, view, look) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_free(transform: *mut CHandle) {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_free(transform) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_is_display(transform: CHandle) -> c_int {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_is_display(transform) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_get_display(
|
||||
transform: CHandle,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_display(transform, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_get_output(
|
||||
transform: CHandle,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_output(transform, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_get_view(
|
||||
transform: CHandle,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_view(transform, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_colortransform_get_look(
|
||||
transform: CHandle,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_look(transform, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_init(data: *const c_char) -> CHandle {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_init(data) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_free(reader: *mut CHandle) {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_free(reader) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_read_next_start_element(reader: CHandle, found: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_read_next_start_element(reader, found) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_name(reader: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_name(reader, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_read_element_text(
|
||||
reader: CHandle,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_read_element_text(reader, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_skip_current_element(reader: CHandle) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_skip_current_element(reader) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_count(reader, count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_attribute_name(
|
||||
reader: CHandle,
|
||||
index: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_name(reader, index, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_attribute_value(
|
||||
reader: CHandle,
|
||||
index: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_value(reader, index, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_reader_has_error(reader: CHandle, has_error: *mut c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_has_error(reader, has_error) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_init() -> CHandle {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_init() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_free(writer: *mut CHandle) {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_free(writer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_write_start_element(writer: CHandle, name: *const c_char) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_start_element(writer, name) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_write_attribute(
|
||||
writer: CHandle,
|
||||
name: *const c_char,
|
||||
value: *const c_char,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_attribute(writer, name, value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_write_characters(writer: CHandle, text: *const c_char) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_characters(writer, text) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_write_text_element(
|
||||
writer: CHandle,
|
||||
name: *const c_char,
|
||||
text: *const c_char,
|
||||
) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_text_element(writer, name, text) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_end_element(writer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_write_end_document(writer: CHandle) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_end_document(writer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_xml_writer_output(writer: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_output(writer, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_decibel_from_linear(linear: f64, out_db: *mut f64) -> c_int {
|
||||
unsafe { oakcommon::ffi::misc::oakcommon_decibel_from_linear(linear, out_db) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_decibel_to_linear(db: f64, out_linear: *mut f64) -> c_int {
|
||||
unsafe { oakcommon::ffi::misc::oakcommon_decibel_to_linear(db, out_linear) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_decibel_from_logarithmic(logarithmic: f64, out_db: *mut f64) -> c_int {
|
||||
unsafe { oakcommon::ffi::misc::oakcommon_decibel_from_logarithmic(logarithmic, out_db) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_decibel_to_logarithmic(db: f64, out_logarithmic: *mut f64) -> c_int {
|
||||
unsafe { oakcommon::ffi::misc::oakcommon_decibel_to_logarithmic(db, out_logarithmic) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_decibel_linear_to_logarithmic(linear: f64, out_logarithmic: *mut f64) -> c_int {
|
||||
unsafe { oakcommon::ffi::misc::oakcommon_decibel_linear_to_logarithmic(linear, out_logarithmic) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakcommon` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakcommon_decibel_logarithmic_to_linear(logarithmic: f64, out_linear: *mut f64) -> c_int {
|
||||
unsafe { oakcommon::ffi::misc::oakcommon_decibel_logarithmic_to_linear(logarithmic, out_linear) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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/>.
|
||||
|
||||
//! C ABI imports from the oak modules, one submodule per module crate.
|
||||
//!
|
||||
//! The facade consumes the module C ABIs (`include/<mod>/*.h`) purely as
|
||||
//! `extern "C"` imports — it never links the module crates at build time.
|
||||
//! At the final app link the symbols resolve against the module shared
|
||||
//! libraries; `cargo test` resolves them against the module crates' rlibs
|
||||
//! (dev-dependencies, see Cargo.toml).
|
||||
//!
|
||||
//! **Signatures are declared from the module crates' actual `#[no_mangle]`
|
||||
//! exports** (their `src/*/ffi*` modules), not from memory of the include
|
||||
//! headers — module bridges in sibling crates have drifted from the real
|
||||
//! ABI before. Every handle crosses the boundary as [`crate::handle::CHandle`]
|
||||
//! (structurally identical to every `Oak<Mod><Type>` value handle).
|
||||
//!
|
||||
//! Only functions the module crates actually implement are declared here;
|
||||
//! engine functions whose backing is still C++-only are facade stubs (see
|
||||
//! the area modules) and never reach this module.
|
||||
|
||||
pub mod audio;
|
||||
pub mod codec;
|
||||
pub mod common;
|
||||
pub mod undo;
|
||||
pub mod plugin;
|
||||
pub mod render;
|
||||
pub mod node;
|
||||
pub mod timeline;
|
||||
pub mod task;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
// 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/>.
|
||||
|
||||
//! oakplugin C ABI bridge: direct Rust calls into the `oakplugin` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oakplugin`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oakplugin C ABI imports, mirroring the oakplugin crate's exports
|
||||
//! (`src/plugin/rust/src/ffi.rs`; headers `include/plugin/*.h`).
|
||||
|
||||
use std::ffi::{c_char, c_int};
|
||||
|
||||
/// Direct call into the `oakplugin` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakplugin_host_scan(bundle_dirs: *const *const c_char, dir_count: c_int) -> c_int {
|
||||
unsafe { oakplugin::ffi::oakplugin_host_scan(bundle_dirs, dir_count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakplugin` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakplugin_host_init() -> c_int {
|
||||
unsafe { oakplugin::ffi::oakplugin_host_init() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakplugin` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakplugin_host_plugin_count() -> c_int {
|
||||
unsafe { oakplugin::ffi::oakplugin_host_plugin_count() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakplugin` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakplugin_host_plugin_id_at(index: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oakplugin::ffi::oakplugin_host_plugin_id_at(index, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakplugin` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakplugin_host_plugin_label(plugin_id: *const c_char, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oakplugin::ffi::oakplugin_host_plugin_label(plugin_id, buf, buf_size) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
// 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/>.
|
||||
|
||||
//! oakrender C ABI bridge: direct Rust calls into the `oakrender` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oakrender`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oakrender C ABI imports, mirroring the oakrender crate's exports
|
||||
//! (`src/render/rust/src/ffi.rs`; headers `include/render/*.h`).
|
||||
|
||||
use std::ffi::{c_char, c_double, c_int, c_void};
|
||||
|
||||
use crate::handle::CHandle;
|
||||
|
||||
/// `include/render/ticket.h` — video render ticket params. Single-lib
|
||||
/// unification: aliases the oakrender crate's POD (same `repr(C)`
|
||||
/// layout; all handle fields are the shared [`CHandle`]).
|
||||
pub type OakVideoTicketParams = oakrender::ffi::OakVideoTicketParams;
|
||||
|
||||
|
||||
/// `include/render/renderer.h` — frame video-params POD returned by
|
||||
/// `oakrender_codec_frame_get_params`. Single-lib unification: aliases
|
||||
/// the oakrender crate's POD.
|
||||
pub type OakRenderVideoParams = oakrender::ffi::OakRenderVideoParams;
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_display_renderer_create_dynamic(backend_id: *const c_char) -> CHandle {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_create_dynamic(backend_id) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_display_renderer_create_opengl() -> CHandle {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_create_opengl() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_display_renderer_init(renderer: CHandle, gl_context: *mut c_void) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_init(renderer, gl_context) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_display_renderer_destroy(renderer: *mut CHandle) {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_destroy(renderer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_display_renderer_is_open_gl(renderer: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_is_open_gl(renderer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_display_renderer_is_vulkan(renderer: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_is_vulkan(renderer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_manager_set_aggressive_gc(enabled: c_int) -> c_int {
|
||||
unsafe { oakrender::ffi::ticket::oakrender_manager_set_aggressive_gc(enabled) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_set_cacher_multicam(multicam_or_null: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::manager::oakrender_set_cacher_multicam(multicam_or_null) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_set_display_color_processor(p_or_null: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::manager::oakrender_set_display_color_processor(p_or_null) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_ticket_render_frame(
|
||||
params: *const OakVideoTicketParams,
|
||||
cb: Option<unsafe extern "C" fn(CHandle, *mut c_void)>,
|
||||
userdata: *mut c_void,
|
||||
) -> CHandle {
|
||||
unsafe { oakrender::ffi::ticket::oakrender_ticket_render_frame(params, cb, userdata) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_ticket_render_audio(
|
||||
output_node: CHandle,
|
||||
in_num: i64,
|
||||
in_den: i64,
|
||||
out_num: i64,
|
||||
out_den: i64,
|
||||
params: *const c_void,
|
||||
mode: c_int,
|
||||
cb: Option<unsafe extern "C" fn(CHandle, *mut c_void)>,
|
||||
userdata: *mut c_void,
|
||||
) -> CHandle {
|
||||
unsafe {
|
||||
oakrender::ffi::ticket::oakrender_ticket_render_audio(
|
||||
output_node, in_num, in_den, out_num, out_den, params as *const CHandle, mode, cb,
|
||||
userdata,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_ticket_wait(ticket: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::ticket::oakrender_ticket_wait(ticket) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_ticket_cancel(ticket: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::ticket::oakrender_ticket_cancel(ticket) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_ticket_get_frame(ticket: CHandle, out: *mut CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::ticket::oakrender_ticket_get_frame(ticket, out) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_ticket_get_samples(ticket: CHandle, out: *mut *mut c_void) -> c_int {
|
||||
unsafe { oakrender::ffi::ticket::oakrender_ticket_get_samples(ticket, out) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_ticket_free(ticket: *mut CHandle) {
|
||||
unsafe { oakrender::ffi::ticket::oakrender_ticket_free(ticket) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_create() -> CHandle {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_create() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_retain(frame: CHandle) -> CHandle {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_retain(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_free(frame: *mut CHandle) {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_free(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_width(frame: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_width(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_height(frame: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_height(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_linesize_bytes(frame: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_linesize_bytes(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_data(frame: CHandle) -> *mut c_void {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_data(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_const_data(frame: CHandle) -> *const c_void {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_const_data(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_is_allocated(frame: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_is_allocated(frame) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_codec_frame_get_params(frame: CHandle, out: *mut OakRenderVideoParams) -> c_int {
|
||||
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_get_params(frame, out) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_color_processor_create(
|
||||
src_space: *const c_char,
|
||||
dst_transform: *const c_char,
|
||||
direction: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oakrender::ffi::color::oakrender_color_processor_create(src_space, dst_transform, direction) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_color_processor_free(processor: *mut CHandle) {
|
||||
unsafe { oakrender::ffi::color::oakrender_color_processor_free(processor) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_color_processor_is_valid(processor: CHandle) -> c_int {
|
||||
unsafe { oakrender::ffi::color::oakrender_color_processor_is_valid(processor) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_color_processor_create_transform(
|
||||
manager: CHandle,
|
||||
input: *const c_char,
|
||||
dest: CHandle,
|
||||
direction: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oakrender::ffi::color::oakrender_color_processor_create_transform(manager, input, dest, direction) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_color_processor_convert(
|
||||
processor: CHandle,
|
||||
ir: c_double,
|
||||
ig: c_double,
|
||||
ib: c_double,
|
||||
ia: c_double,
|
||||
out_r: *mut c_double,
|
||||
out_g: *mut c_double,
|
||||
out_b: *mut c_double,
|
||||
out_a: *mut c_double,
|
||||
) -> c_int {
|
||||
unsafe { oakrender::ffi::color::oakrender_color_processor_convert(processor, ir, ig, ib, ia, out_r, out_g, out_b, out_a) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_color_manager_set_up_default_config() -> c_int {
|
||||
unsafe { oakrender::ffi::color::oakrender_color_manager_set_up_default_config() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_color_manager_get_config(buf: *mut c_char, n: c_int) -> c_int {
|
||||
unsafe { oakrender::ffi::color::oakrender_color_manager_get_config(buf, n) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_lut_is_supported_extension(extension: *const c_char) -> c_int {
|
||||
unsafe { oakrender::ffi::color::oakrender_lut_is_supported_extension(extension) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_lut_supported_extensions_count() -> c_int {
|
||||
unsafe { oakrender::ffi::color::oakrender_lut_supported_extensions_count() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakrender` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakrender_lut_supported_extension_at(i: c_int, buf: *mut c_char, n: c_int) -> c_int {
|
||||
unsafe { oakrender::ffi::color::oakrender_lut_supported_extension_at(i, buf, n) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
// 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/>.
|
||||
|
||||
//! oaktask C ABI bridge: direct Rust calls into the `oaktask` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oaktask`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oaktask C ABI imports, mirroring the oaktask crate's exports
|
||||
//! (`src/task/rust/src/ffi/{manager,task,project}.rs`; headers
|
||||
//! `include/task/*.h`).
|
||||
//!
|
||||
//! Every handle crosses as [`crate::handle::CHandle`]. `oaktask_create_export`
|
||||
//! takes the encoding-params POD ([`crate::bridge::codec::EncodingParamsPOD`],
|
||||
//! field-identical to the task crate's `OakCodecEncodingParams`). String
|
||||
//! getters report the size **including** the NUL; the facade converts with
|
||||
//! [`crate::handle::string_result`].
|
||||
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
|
||||
use crate::handle::CHandle;
|
||||
|
||||
/// `oaktask_event_fn` callback (`include/task/task.h`): `event_id` is an
|
||||
/// `OakTaskEvent` (0=started, 1=progress, 2=finished), `value` 0..1 (or
|
||||
/// start-ms / success flag), `userdata` the subscription token.
|
||||
pub type OakTaskEventFn = unsafe extern "C" fn(event_id: c_int, value: f64, userdata: *mut c_void);
|
||||
|
||||
/// `oaktask_otio_import_confirm_fn` (`include/task/project.h`).
|
||||
pub type OakTaskOtioImportConfirmFn =
|
||||
unsafe extern "C" fn(sequence_names: *const *const c_char, count: c_int, userdata: *mut c_void) -> c_int;
|
||||
|
||||
/// `oaktask_image_sequence_confirm_fn` (`include/task/project.h`).
|
||||
pub type OakTaskImageSequenceConfirmFn =
|
||||
unsafe extern "C" fn(filename: *const c_char, userdata: *mut c_void) -> c_int;
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_manager_init() -> c_int {
|
||||
unsafe { oaktask::ffi::manager::oaktask_manager_init() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_manager_shutdown() {
|
||||
unsafe { oaktask::ffi::manager::oaktask_manager_shutdown() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_register_codec_submitter() -> c_int {
|
||||
unsafe { oaktask::ffi::manager::oaktask_register_codec_submitter() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_manager_count() -> c_int {
|
||||
unsafe { oaktask::ffi::manager::oaktask_manager_count() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_manager_at(i: c_int) -> CHandle {
|
||||
unsafe { oaktask::ffi::manager::oaktask_manager_at(i) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_manager_delete_finished() {
|
||||
unsafe { oaktask::ffi::manager::oaktask_manager_delete_finished() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_free(t: *mut CHandle) {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_free(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_start_sync(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_start_sync(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_start(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_start(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_cancel(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_cancel(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_wait(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_wait(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_is_finished(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_is_finished(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_succeeded(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_succeeded(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_title(t: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_title(t, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_error(t: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_error(t, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_task_subscribe(t: CHandle, cb: Option<OakTaskEventFn>, userdata: *mut c_void) -> i64 {
|
||||
unsafe { oaktask::ffi::task::oaktask_task_subscribe(t, cb, userdata) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_debug_alive_count() -> c_int {
|
||||
unsafe { oaktask::ffi::task::oaktask_debug_alive_count() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_create_project_load(filename: *const c_char) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_create_project_load(filename) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_load_take_project(t: CHandle) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_load_take_project(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_create_project_save(project: CHandle, filename_or_null: *const c_char, use_compression: c_int) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_create_project_save(project, filename_or_null, use_compression) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_create_project_import(folder: CHandle, project: CHandle, urls: *const *const c_char, url_count: c_int) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_create_project_import(folder, project, urls, url_count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_import_take_command(t: CHandle) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_import_take_command(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_import_footage_count(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::project::oaktask_import_footage_count(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_import_footage_at(t: CHandle, index: c_int) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_import_footage_at(t, index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_import_invalid_count(t: CHandle) -> c_int {
|
||||
unsafe { oaktask::ffi::project::oaktask_import_invalid_count(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_import_invalid_at(t: CHandle, index: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe { oaktask::ffi::project::oaktask_import_invalid_at(t, index, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_create_project_load_otio(filename: *const c_char) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_create_project_load_otio(filename) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_load_otio_take_project(t: CHandle) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_load_otio_take_project(t) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_create_project_save_otio(project: CHandle, filename: *const c_char) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_create_project_save_otio(project, filename) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_load_otio_set_confirm_cb(cb: Option<OakTaskOtioImportConfirmFn>, userdata: *mut c_void) {
|
||||
unsafe { oaktask::ffi::project::oaktask_load_otio_set_confirm_cb(cb, userdata) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_create_precache(footage: CHandle, index: c_int, sequence: CHandle) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_create_precache(footage, index, sequence) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
/// `oaktask_create_export` — direct call into the `oaktask` crate
|
||||
/// (single-lib unification; the encoding-params POD is the shared
|
||||
/// oakcodec type).
|
||||
pub fn oaktask_create_export(
|
||||
viewer: CHandle,
|
||||
color_manager: CHandle,
|
||||
params: *const crate::bridge::codec::EncodingParamsPOD,
|
||||
) -> CHandle {
|
||||
unsafe { oaktask::ffi::project::oaktask_create_export(viewer, color_manager, params) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktask` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktask_import_set_image_sequence_confirm_cb(cb: Option<OakTaskImageSequenceConfirmFn>, userdata: *mut c_void) {
|
||||
unsafe { oaktask::ffi::project::oaktask_import_set_image_sequence_confirm_cb(cb, userdata) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
// 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/>.
|
||||
|
||||
//! oaktimeline C ABI bridge: direct Rust calls into the `oaktimeline` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oaktimeline`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oaktimeline C ABI imports, mirroring the oaktimeline crate's exports
|
||||
//! (`src/timeline/rust/src/ffi.rs`; headers `include/timeline/*.h`).
|
||||
//!
|
||||
//! Every handle crosses as [`crate::handle::CHandle`]. The marker/workarea
|
||||
//! time quantities cross as `c_int` num/den pairs; the edit commands take
|
||||
//! `i64` rationals. String getters report the size **including** the NUL;
|
||||
//! the facade converts with [`crate::handle::string_result`].
|
||||
|
||||
use std::ffi::{c_char, c_int};
|
||||
|
||||
use crate::handle::CHandle;
|
||||
|
||||
// `include/timeline/marker.h` exports (complete inventory):
|
||||
// oaktimeline_marker_list_create / free / of / add / count / at /
|
||||
// add_command / remove_at_command / set_time_command /
|
||||
// set_props_command / list_load / list_save.
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_list_create() -> CHandle {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_create() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_list_of(owner: CHandle) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_of(owner) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_list_free(list: *mut CHandle) {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_free(list) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_add(
|
||||
list: CHandle,
|
||||
in_num: c_int,
|
||||
in_den: c_int,
|
||||
out_num: c_int,
|
||||
out_den: c_int,
|
||||
name: *const c_char,
|
||||
color: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_add(list, in_num, in_den, out_num, out_den, name, color) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_count(list: CHandle, out_count: *mut c_int) -> c_int {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_count(list, out_count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_at(
|
||||
list: CHandle,
|
||||
index: c_int,
|
||||
in_num: *mut c_int,
|
||||
in_den: *mut c_int,
|
||||
out_num: *mut c_int,
|
||||
out_den: *mut c_int,
|
||||
color: *mut c_int,
|
||||
name_buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_at(list, index, in_num, in_den, out_num, out_den, color, name_buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_add_command(
|
||||
list: CHandle,
|
||||
in_num: c_int,
|
||||
in_den: c_int,
|
||||
out_num: c_int,
|
||||
out_den: c_int,
|
||||
name: *const c_char,
|
||||
color: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_add_command(list, in_num, in_den, out_num, out_den, name, color) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_remove_at_command(list: CHandle, index: c_int) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_remove_at_command(list, index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_set_time_command(
|
||||
list: CHandle,
|
||||
index: c_int,
|
||||
in_num: c_int,
|
||||
in_den: c_int,
|
||||
out_num: c_int,
|
||||
out_den: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_set_time_command(list, index, in_num, in_den, out_num, out_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_set_props_command(
|
||||
list: CHandle,
|
||||
index: c_int,
|
||||
color: c_int,
|
||||
name: *const c_char,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_set_props_command(list, index, color, name) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_list_load(list: CHandle, reader: CHandle) -> c_int {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_load(list, reader) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_marker_list_save(list: CHandle, writer: CHandle) -> c_int {
|
||||
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_save(list, writer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_create() -> CHandle {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_create() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_of(owner: CHandle) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_of(owner) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_free(w: *mut CHandle) {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_free(w) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_set_enabled(w: CHandle, enabled: c_int) -> c_int {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_enabled(w, enabled) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_get(
|
||||
w: CHandle,
|
||||
in_num: *mut c_int,
|
||||
in_den: *mut c_int,
|
||||
out_num: *mut c_int,
|
||||
out_den: *mut c_int,
|
||||
enabled: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_get(w, in_num, in_den, out_num, out_den, enabled) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_set_range(
|
||||
w: CHandle,
|
||||
in_num: c_int,
|
||||
in_den: c_int,
|
||||
out_num: c_int,
|
||||
out_den: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_range(w, in_num, in_den, out_num, out_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_set_range_command(
|
||||
w: CHandle,
|
||||
in_num: c_int,
|
||||
in_den: c_int,
|
||||
out_num: c_int,
|
||||
out_den: c_int,
|
||||
old_in_num: c_int,
|
||||
old_in_den: c_int,
|
||||
old_out_num: c_int,
|
||||
old_out_den: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_range_command(w, in_num, in_den, out_num, out_den, old_in_num, old_in_den, old_out_num, old_out_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_set_enabled_command(w: CHandle, enabled: c_int) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_enabled_command(w, enabled) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_reset(
|
||||
in_num: *mut c_int,
|
||||
in_den: *mut c_int,
|
||||
out_num: *mut c_int,
|
||||
out_den: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_reset(in_num, in_den, out_num, out_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_load(w: CHandle, reader: CHandle) -> c_int {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_load(w, reader) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_workarea_save(w: CHandle, writer: CHandle) -> c_int {
|
||||
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_save(w, writer) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_add_track_command(list: CHandle) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_add_track_command(list) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_remove_track_command(track: CHandle) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_remove_track_command(track) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_place_block_command(
|
||||
list: CHandle,
|
||||
track_index: c_int,
|
||||
block: CHandle,
|
||||
in_num: i64,
|
||||
in_den: i64,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_place_block_command(list, track_index, block, in_num, in_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_replace_block_with_gap_command(track: CHandle, block: CHandle) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_replace_block_with_gap_command(track, block) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_trim_command(
|
||||
track: CHandle,
|
||||
block: CHandle,
|
||||
new_length_num: i64,
|
||||
new_length_den: i64,
|
||||
mode: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_trim_command(track, block, new_length_num, new_length_den, mode) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_split_command(blocks: *const CHandle, count: c_int, point_num: i64, point_den: i64) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_split_command(blocks, count, point_num, point_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_split_preserving_links_command(
|
||||
blocks: *const CHandle,
|
||||
count: c_int,
|
||||
point_nums: *const i64,
|
||||
point_dens: *const i64,
|
||||
time_count: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_split_preserving_links_command(blocks, count, point_nums, point_dens, time_count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_ripple_delete_gaps_command(
|
||||
sequence: CHandle,
|
||||
in_nums: *const i64,
|
||||
in_dens: *const i64,
|
||||
out_nums: *const i64,
|
||||
out_dens: *const i64,
|
||||
tracks: *const CHandle,
|
||||
range_count: c_int,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_ripple_delete_gaps_command(sequence, in_nums, in_dens, out_nums, out_dens, tracks, range_count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_slide_command(
|
||||
track: CHandle,
|
||||
blocks: *const CHandle,
|
||||
block_count: c_int,
|
||||
in_adjacent: CHandle,
|
||||
out_adjacent: CHandle,
|
||||
movement_num: i64,
|
||||
movement_den: i64,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_slide_command(track, blocks, block_count, in_adjacent, out_adjacent, movement_num, movement_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_ripple_remove_area_command(
|
||||
track: CHandle,
|
||||
in_num: i64,
|
||||
in_den: i64,
|
||||
out_num: i64,
|
||||
out_den: i64,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_ripple_remove_area_command(track, in_num, in_den, out_num, out_den) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oaktimeline` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oaktimeline_insert_gaps_command(
|
||||
list: CHandle,
|
||||
point_num: i64,
|
||||
point_den: i64,
|
||||
length_num: i64,
|
||||
length_den: i64,
|
||||
) -> CHandle {
|
||||
unsafe { oaktimeline::ffi::edit::oaktimeline_insert_gaps_command(list, point_num, point_den, length_num, length_den) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
// 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/>.
|
||||
|
||||
//! oakundo C ABI bridge: direct Rust calls into the `oakundo` crate.
|
||||
//!
|
||||
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
|
||||
//! call below is a compile-time Rust call into `oakundo`'s `ffi` (the
|
||||
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
|
||||
//! internal callers bypass them). Handles cross as the shared
|
||||
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
|
||||
//! declaration (resolved at link time against the sibling crate in the
|
||||
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
|
||||
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! oakundo C ABI imports, mirroring the oakundo crate's exports
|
||||
//! (`src/undo/rust/src/ffi.rs`; headers `include/undo/*.h`).
|
||||
|
||||
use std::ffi::{c_char, c_int};
|
||||
|
||||
use crate::handle::CHandle;
|
||||
|
||||
|
||||
/// `include/undo/undocommand.h` — callback table backing a
|
||||
/// caller-defined undo command. Single-lib unification: aliases the
|
||||
/// oakundo crate's vtable POD.
|
||||
pub type OakUndoCommandVtable = oakundo::undocommand::OakUndoCommandVtable;
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_init(
|
||||
vtable: *const OakUndoCommandVtable,
|
||||
userdata: *mut std::ffi::c_void,
|
||||
) -> CHandle {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_init(vtable, userdata) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_init_multi() -> CHandle {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_init_multi() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_multi_add_child(multi: CHandle, child: CHandle) -> c_int {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_multi_add_child(multi, child) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_multi_child_count(multi: CHandle, out_count: *mut c_int) -> c_int {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_multi_child_count(multi, out_count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_multi_child(
|
||||
multi: CHandle,
|
||||
index: c_int,
|
||||
out_child: *mut CHandle,
|
||||
) -> c_int {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_multi_child(multi, index, out_child) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_redo_now(command: CHandle) -> c_int {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_redo_now(command) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_undo_now(command: CHandle) -> c_int {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_undo_now(command) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_command_free(command: *mut CHandle) {
|
||||
unsafe { oakundo::ffi::command::oakundo_command_free(command) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_init() -> CHandle {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_init() }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_free(stack: *mut CHandle) {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_free(stack) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_push(stack: CHandle, command: CHandle, name: *const c_char) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_push(stack, command, name) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_push_pre_executed(
|
||||
stack: CHandle,
|
||||
command: CHandle,
|
||||
name: *const c_char,
|
||||
) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_push_pre_executed(stack, command, name) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_undo(stack: CHandle) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_undo(stack) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_redo(stack: CHandle) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_redo(stack) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_jump(stack: CHandle, index: i64) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_jump(stack, index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_clear(stack: CHandle) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_clear(stack) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_can_undo(stack: CHandle, out_value: *mut c_int) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_can_undo(stack, out_value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_can_redo(stack: CHandle, out_value: *mut c_int) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_can_redo(stack, out_value) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_count(stack: CHandle, out_count: *mut i64) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_count(stack, out_count) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_index(stack: CHandle, out_index: *mut i64) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_index(stack, out_index) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_command_text(
|
||||
stack: CHandle,
|
||||
row: i64,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_command_text(stack, row, buf, buf_size) }
|
||||
}
|
||||
|
||||
/// Direct call into the `oakundo` crate (single-lib unification; the
|
||||
/// `#[no_mangle]` export stays for the external C ABI).
|
||||
pub fn oakundo_undostack_command_is_done(
|
||||
stack: CHandle,
|
||||
row: i64,
|
||||
out_value: *mut c_int,
|
||||
) -> c_int {
|
||||
unsafe { oakundo::ffi::undostack::oakundo_undostack_command_is_done(stack, row, out_value) }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,611 @@
|
||||
// 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/>.
|
||||
|
||||
//! `engine/include/oakengine/config.h` and
|
||||
//! `engine/include/oakengine/videoparams.h` over the oakcommon module.
|
||||
//!
|
||||
//! The engine config family uses flat keys; the oakcommon store is
|
||||
//! `(group, key)` — the facade passes group = NULL. Engine semantics that
|
||||
//! differ from the module are honored here (a missing key reads as an
|
||||
//! empty string / 0, not a module error).
|
||||
//!
|
||||
//! The engine videoparams family is mostly **facade-local static data**
|
||||
//! (the standard frame-rate / pixel-aspect / divider tables from
|
||||
//! `engine/render/videoparams.cpp`) plus POD↔handle conversion over the
|
||||
//! oakcommon `OakVideoParams` handle; see the mapping notes per function.
|
||||
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use crate::bridge::common as c;
|
||||
use crate::error::Error;
|
||||
use crate::handle::{
|
||||
box_handle, free_box, guard, guard_int, guard_void, string_result, OakEngineClipboard,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// config.h
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Facade copy of the registered config error handler (the module keeps
|
||||
/// its own copy for load/save errors; this one backs
|
||||
/// `oakengine_config_report_error`). The userdata pointer is stored as
|
||||
/// `usize` so the static stays Send/Sync.
|
||||
static ERROR_FN: OnceLock<Mutex<Option<(Option<ConfigErrorFn>, usize)>>> = OnceLock::new();
|
||||
|
||||
/// `engine/include/oakengine/config.h` error callback.
|
||||
pub type ConfigErrorFn = unsafe extern "C" fn(title: *const c_char, message: *const c_char, userdata: *mut c_void);
|
||||
|
||||
fn error_fn_slot() -> &'static Mutex<Option<(Option<ConfigErrorFn>, usize)>> {
|
||||
ERROR_FN.get_or_init(|| Mutex::new(None))
|
||||
}
|
||||
|
||||
/// `oakengine_config_load` — load configuration from disk.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_config_load() -> c_int {
|
||||
guard(|| Error::from_module(unsafe { c::oakcommon_config_load() }))
|
||||
}
|
||||
|
||||
/// `oakengine_config_save` — save configuration to disk.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_config_save() -> c_int {
|
||||
guard(|| Error::from_module(unsafe { c::oakcommon_config_save() }))
|
||||
}
|
||||
|
||||
/// `oakengine_config_get_string` — read a string value (buf/size).
|
||||
/// Returns the string length, 0 when the key is missing or empty.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_config_get_string(
|
||||
key: *const c_char,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
if key.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let rc = c::oakcommon_config_get(std::ptr::null(), key, buf, buf_size);
|
||||
// Engine contract: a missing key reads as an empty string.
|
||||
if rc == -10004 {
|
||||
Ok(0)
|
||||
} else if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_config_set_string` — write a string value.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_config_set_string(
|
||||
key: *const c_char,
|
||||
value: *const c_char,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if key.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let value = if value.is_null() { empty_cstr() } else { value };
|
||||
c::oakcommon_config_set(std::ptr::null(), key, value);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_config_get_int` — read an integer value (fallback when the
|
||||
/// key is missing or not convertible).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_config_get_int(key: *const c_char, default_value: i64) -> i64 {
|
||||
crate::handle::guard_i64(|| unsafe {
|
||||
if key.is_null() {
|
||||
return Ok(default_value);
|
||||
}
|
||||
Ok(c::oakcommon_config_get_int64(std::ptr::null(), key, default_value))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_config_set_int` — write an integer value.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_config_set_int(key: *const c_char, value: i64) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if key.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
c::oakcommon_config_set_int64(std::ptr::null(), key, value);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_config_set_error_handler` — register the error callback
|
||||
/// (NULL clears it). Forwards to oakcommon and keeps a facade copy for
|
||||
/// `oakengine_config_report_error`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_config_set_error_handler(
|
||||
fn_: Option<ConfigErrorFn>,
|
||||
userdata: *mut c_void,
|
||||
) -> c_int {
|
||||
guard(|| {
|
||||
let mut slot = error_fn_slot().lock().unwrap_or_else(|e| e.into_inner());
|
||||
*slot = Some((fn_, userdata as usize));
|
||||
let rc = unsafe { c::oakcommon_config_set_error_handler(fn_, userdata) };
|
||||
if rc != 0 {
|
||||
return Err(Error::Module(rc));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_config_report_error` — report an error through the
|
||||
/// registered handler (logged and discarded when none is set).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_config_report_error(title: *const c_char, message: *const c_char) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let slot = error_fn_slot().lock().unwrap_or_else(|e| e.into_inner());
|
||||
if let Some((Some(fn_), userdata)) = *slot {
|
||||
let title = if title.is_null() { empty_cstr() } else { title };
|
||||
let message = if message.is_null() { empty_cstr() } else { message };
|
||||
fn_(title, message, userdata as *mut c_void);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Static empty C string used where the engine treats NULL as "".
|
||||
static EMPTY_CSTR: std::ffi::c_char = 0;
|
||||
|
||||
/// Pointer to the static empty C string.
|
||||
pub(crate) fn empty_cstr() -> *const c_char {
|
||||
&EMPTY_CSTR as *const c_char
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// videoparams.h — static tables (ported from engine/render/videoparams.cpp)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Standard frame rates as num/den
|
||||
/// (`VideoParams::k_supported_frame_rates`).
|
||||
const SUPPORTED_FRAME_RATES: &[(c_int, c_int)] = &[
|
||||
(10, 1),
|
||||
(15, 1),
|
||||
(24000, 1001),
|
||||
(24, 1),
|
||||
(25, 1),
|
||||
(30000, 1001),
|
||||
(30, 1),
|
||||
(48000, 1001),
|
||||
(48, 1),
|
||||
(50, 1),
|
||||
(60000, 1001),
|
||||
(60, 1),
|
||||
];
|
||||
|
||||
/// Standard pixel aspect ratios as num/den
|
||||
/// (`VideoParams::k_standard_pixel_aspects`).
|
||||
const STANDARD_PIXEL_ASPECTS: &[(c_int, c_int)] = &[
|
||||
(1, 1),
|
||||
(8, 9),
|
||||
(32, 27),
|
||||
(16, 15),
|
||||
(64, 45),
|
||||
(4, 3),
|
||||
];
|
||||
|
||||
/// Supported preview dividers (`VideoParams::k_supported_dividers`).
|
||||
const SUPPORTED_DIVIDERS: &[c_int] = &[1, 2, 3, 4, 6, 8, 12, 16];
|
||||
|
||||
/// Engine-internal video channel count (RGBA).
|
||||
const INTERNAL_CHANNEL_COUNT: c_int = 4;
|
||||
|
||||
/// `oakengine_video_params_supported_frame_rate_count`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_supported_frame_rate_count() -> c_int {
|
||||
guard_int(|| Ok(SUPPORTED_FRAME_RATES.len() as c_int))
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_supported_frame_rate_at` — num/den at `index`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_supported_frame_rate_at(
|
||||
index: c_int,
|
||||
num: *mut c_int,
|
||||
den: *mut c_int,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if num.is_null() || den.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
match SUPPORTED_FRAME_RATES.get(index as usize) {
|
||||
Some((n, d)) => {
|
||||
*num = *n;
|
||||
*den = *d;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::Invalid),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_frame_rate_to_string` — label of a frame rate.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_frame_rate_to_string(
|
||||
num: c_int,
|
||||
den: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let rc = c::oakcommon_videoparams_frame_rate_to_string(num, den, buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_standard_pixel_aspect_count`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_standard_pixel_aspect_count() -> c_int {
|
||||
guard_int(|| Ok(STANDARD_PIXEL_ASPECTS.len() as c_int))
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_standard_pixel_aspect_at` — num/den at `index`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_standard_pixel_aspect_at(
|
||||
index: c_int,
|
||||
num: *mut c_int,
|
||||
den: *mut c_int,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if num.is_null() || den.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
match STANDARD_PIXEL_ASPECTS.get(index as usize) {
|
||||
Some((n, d)) => {
|
||||
*num = *n;
|
||||
*den = *d;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::Invalid),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_standard_pixel_aspect_name` — display name of
|
||||
/// the `index`-th standard pixel aspect. Built from the table the way the
|
||||
/// C++ `VideoParams::standard_pixel_aspect_list()` populates the combo:
|
||||
/// square = "Square", others = "num:den".
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_standard_pixel_aspect_name(
|
||||
index: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
match STANDARD_PIXEL_ASPECTS.get(index as usize) {
|
||||
Some((1, 1)) => Ok(crate::handle::write_string("Square", buf, buf_size)),
|
||||
Some((n, d)) => Ok(crate::handle::write_string(
|
||||
&format!("{n}:{d}"),
|
||||
buf,
|
||||
buf_size,
|
||||
)),
|
||||
None => Err(Error::Invalid),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_format_pixel_aspect_ratio_string` — format a
|
||||
/// printf-style template with the pixel aspect ratio.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_format_pixel_aspect_ratio_string(
|
||||
format: *const c_char,
|
||||
num: c_int,
|
||||
den: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
if format.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let template = crate::handle::read_cstr(format);
|
||||
// The engine formats a single "%1" placeholder with num/den.
|
||||
let rendered = if template.contains("%1") {
|
||||
template.replace("%1", &format!("{num}:{den}"))
|
||||
} else {
|
||||
template
|
||||
};
|
||||
Ok(crate::handle::write_string(&rendered, buf, buf_size))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_supported_divider_count`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_supported_divider_count() -> c_int {
|
||||
SUPPORTED_DIVIDERS.len() as c_int
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_supported_divider_at` — divider at `index`
|
||||
/// (-1 when out of range).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_supported_divider_at(index: c_int) -> c_int {
|
||||
guard_int(|| {
|
||||
Ok(match SUPPORTED_DIVIDERS.get(index as usize) {
|
||||
Some(d) => *d,
|
||||
None => -1,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_divider_name` — display name of a divider
|
||||
/// (`VideoParams::get_name_for_divider`, ported).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_divider_name(
|
||||
divider: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
if divider <= 0 {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let rc = c::oakcommon_videoparams_get_name_for_divider(divider, buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_format_is_float` — 1 when the format is float.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_format_is_float(format: c_int) -> c_int {
|
||||
guard_int(|| Ok(unsafe { c::oakcommon_videoparams_format_is_float(format) }))
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_pixel_format_name` — display name of a format.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_pixel_format_name(
|
||||
format: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let rc = c::oakcommon_videoparams_get_format_name(format, buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_effective_size` — divider-scaled dimensions.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_effective_size(
|
||||
width: c_int,
|
||||
height: c_int,
|
||||
divider: c_int,
|
||||
out_width: *mut c_int,
|
||||
out_height: *mut c_int,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if width <= 0 || height <= 0 || divider <= 0 {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !out_width.is_null() {
|
||||
*out_width = c::oakcommon_videoparams_get_scaled_dimension(width, divider);
|
||||
}
|
||||
if !out_height.is_null() {
|
||||
*out_height = c::oakcommon_videoparams_get_scaled_dimension(height, divider);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_make` — fill an `oak_video_params` POD.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_make(
|
||||
p: *mut OakVideoParamsPod,
|
||||
width: c_int,
|
||||
height: c_int,
|
||||
time_base_num: c_int,
|
||||
time_base_den: c_int,
|
||||
format: c_int,
|
||||
pixel_aspect_num: c_int,
|
||||
pixel_aspect_den: c_int,
|
||||
interlacing: c_int,
|
||||
color_range: c_int,
|
||||
divider: c_int,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if p.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
(*p).width = width;
|
||||
(*p).height = height;
|
||||
(*p).time_base_num = time_base_num;
|
||||
(*p).time_base_den = time_base_den;
|
||||
(*p).format = format;
|
||||
(*p).pixel_aspect_num = pixel_aspect_num;
|
||||
(*p).pixel_aspect_den = pixel_aspect_den;
|
||||
(*p).interlacing = interlacing;
|
||||
(*p).color_range = color_range;
|
||||
(*p).divider = divider;
|
||||
(*p).video_type = 0;
|
||||
(*p).premultiplied_alpha = 0;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_create` — create an engine-side VideoParams
|
||||
/// from a POD (returns an opaque engine pointer; free with
|
||||
/// `oakengine_video_params_free`).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_create(pod: *const OakVideoParamsPod) -> *mut c_void {
|
||||
crate::handle::guard_ptr(|| unsafe {
|
||||
if pod.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let params = c::oakcommon_videoparams_init();
|
||||
if params.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let mut rc = c::oakcommon_videoparams_set_width(params, (*pod).width);
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_height(params, (*pod).height);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_time_base(params, (*pod).time_base_num, (*pod).time_base_den);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_format(params, (*pod).format);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_pixel_aspect_ratio(
|
||||
params,
|
||||
(*pod).pixel_aspect_num,
|
||||
(*pod).pixel_aspect_den,
|
||||
);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_interlacing(params, (*pod).interlacing);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_color_range(params, (*pod).color_range);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_divider(params, (*pod).divider);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_video_type(params, (*pod).video_type);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = c::oakcommon_videoparams_set_premultiplied_alpha(params, (*pod).premultiplied_alpha);
|
||||
}
|
||||
if rc != 0 {
|
||||
let mut p = params;
|
||||
c::oakcommon_videoparams_free(&mut p);
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_handle::<OakEngineClipboard>(params).cast())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_free` — free a params object.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_free(params: *mut c_void) {
|
||||
guard_void(|| unsafe {
|
||||
free_box(params.cast::<OakEngineClipboard>());
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_equal` — 1 when all user-facing fields match.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_equal(
|
||||
a: *const OakVideoParamsPod,
|
||||
b: *const OakVideoParamsPod,
|
||||
) -> c_int {
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
if a.is_null() || b.is_null() {
|
||||
return Ok(0);
|
||||
}
|
||||
Ok(compare_pod(&*a, &*b))
|
||||
})
|
||||
}
|
||||
|
||||
fn compare_pod(a: &OakVideoParamsPod, b: &OakVideoParamsPod) -> c_int {
|
||||
let same = a.width == b.width
|
||||
&& a.height == b.height
|
||||
&& a.time_base_num == b.time_base_num
|
||||
&& a.time_base_den == b.time_base_den
|
||||
&& a.format == b.format
|
||||
&& a.pixel_aspect_num == b.pixel_aspect_num
|
||||
&& a.pixel_aspect_den == b.pixel_aspect_den
|
||||
&& a.interlacing == b.interlacing
|
||||
&& a.color_range == b.color_range
|
||||
&& a.divider == b.divider
|
||||
&& a.video_type == b.video_type
|
||||
&& a.premultiplied_alpha == b.premultiplied_alpha;
|
||||
if same {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_is_valid` — 1 when the POD describes a usable
|
||||
/// video stream.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_video_params_is_valid(p: *const OakVideoParamsPod) -> c_int {
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
if p.is_null() {
|
||||
return Ok(0);
|
||||
}
|
||||
let pod = &*p;
|
||||
let valid = pod.width > 0
|
||||
&& pod.height > 0
|
||||
&& pod.pixel_aspect_num > 0
|
||||
&& pod.pixel_aspect_den > 0
|
||||
&& pod.format >= 0
|
||||
&& pod.time_base_den > 0;
|
||||
Ok(if valid { 1 } else { 0 })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_bytes_per_pixel` — bytes per pixel of
|
||||
/// `format` with `channels` channels.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_bytes_per_pixel(format: c_int, channels: c_int) -> c_int {
|
||||
guard_int(|| Ok(unsafe { c::oakcommon_videoparams_static_get_bytes_per_pixel(format, channels) }))
|
||||
}
|
||||
|
||||
/// `oakengine_video_params_internal_channel_count` — RGBA.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_video_params_internal_channel_count() -> c_int {
|
||||
guard_int(|| Ok(INTERNAL_CHANNEL_COUNT))
|
||||
}
|
||||
|
||||
/// `engine/include/oakengine/videoparams.h` — POD mirror of VideoParams'
|
||||
/// user-facing fields. Rust mirror of `oak_video_params`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OakVideoParamsPod {
|
||||
/// Width.
|
||||
pub width: c_int,
|
||||
/// Height.
|
||||
pub height: c_int,
|
||||
/// Frame duration numerator (e.g. 1001/30000 s).
|
||||
pub time_base_num: c_int,
|
||||
/// Frame duration denominator.
|
||||
pub time_base_den: c_int,
|
||||
/// PixelFormat::Format value.
|
||||
pub format: c_int,
|
||||
/// Pixel aspect numerator.
|
||||
pub pixel_aspect_num: c_int,
|
||||
/// Pixel aspect denominator.
|
||||
pub pixel_aspect_den: c_int,
|
||||
/// Interlacing value.
|
||||
pub interlacing: c_int,
|
||||
/// ColorRange value.
|
||||
pub color_range: c_int,
|
||||
/// Preview resolution divider (1 = full).
|
||||
pub divider: c_int,
|
||||
/// VideoParams::Type value.
|
||||
pub video_type: c_int,
|
||||
/// 0/1 premultiplied alpha.
|
||||
pub premultiplied_alpha: c_int,
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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/>.
|
||||
|
||||
//! Deferred `oakengine_*` families and the reasons.
|
||||
//!
|
||||
//! This module exists purely as documentation: the areas below are in the
|
||||
//! facade's scope (module-backed or assembly-layer) but are **not wrapped
|
||||
//! yet**. Nothing here is exported.
|
||||
//!
|
||||
//! ## Genuinely facade-only areas (out of scope, per M9 §4)
|
||||
//!
|
||||
//! viewer/playback/preview/display/gizmo/app/events/exporter/disk/proxy/
|
||||
//! serializer — the liboakengine assembly layer. No files for them in this
|
||||
//! crate.
|
||||
//!
|
||||
//! worker and ipc were in this list too until the render-worker port
|
||||
//! landed: [`worker`] (`engine/include/oakengine/worker.h`) and the
|
||||
//! shared-memory frame-slot transport (`engine/include/oakengine/ipc.h`,
|
||||
//! the shm/framepool half) now live in this crate — see `src/worker.rs`
|
||||
//! and `src/ipc.rs`.
|
||||
//!
|
||||
//! The node/timeline/task families were deferred while the oaknode crate
|
||||
//! was a `todo!()` skeleton and oaktimeline's test-stub mocks collided
|
||||
//! with the real oakundo crate in one test binary. Both blockers are
|
||||
//! cleared: oaknode now implements the module C ABI, and the facade links
|
||||
//! oaknode/oaktimeline/oaktask WITHOUT their `test-stubs` features (see
|
||||
//! README.md "Testing"), so the real exports resolve against the
|
||||
//! dev-dependency rlibs. The families now live in [`node`]
|
||||
//! (`engine/include/oakengine/{node,project,footage}.h`), [`timeline`]
|
||||
//! (`engine/include/oakengine/timeline.h`) and [`task`]
|
||||
//! (`engine/include/oakengine/task.h`).
|
||||
//!
|
||||
//! ## Partial coverage within wrapped families (documented stubs)
|
||||
//!
|
||||
//! The wrapped families still carry documented stubs where the module
|
||||
//! crates lack the C ABI surface — each stub returns its header's
|
||||
//! documented failure value:
|
||||
//!
|
||||
//! - **codec** (encoding.h, 81/85 wrapped): the preset path/count/name,
|
||||
//! preset load/save and the sequence-bound export/last-used entry
|
||||
//! points (`oakengine_encoding_preset_*`,
|
||||
//! `oakengine_encoding_params_load_file/save_file`,
|
||||
//! `oakengine_export_render_with_params`,
|
||||
//! `oakengine_encoding_params_get/set_last_used`) are stubs — the
|
||||
//! oakcodec crate has no preset API and those entry points need the
|
||||
//! exporter/sequence families.
|
||||
//! - **render color** (color.h, 19/31 wrapped): the color-manager list
|
||||
//! queries (colorspace/display/view/look/compliant/luma), the
|
||||
//! standalone config handle and `color_processor_id` /
|
||||
//! `transform_job_set_processor` are stubs — the oakrender crate
|
||||
//! exposes only `color_manager_get_config`/`set_up_default_config` and
|
||||
//! the processor create/convert surface.
|
||||
//! - **render lut** (lut.h, 0/5 wrapped): the directory/file library is
|
||||
//! facade-level over FileFunctions; the crate only enumerates supported
|
||||
//! LUT extensions.
|
||||
//! - **render audio buffer** (renderer.h): the buffer accessors are
|
||||
//! stubs because the crate's `ticket_get_samples` path is
|
||||
//! unimplemented.
|
||||
//! - **node** (node.h+project.h+footage.h, 226/327 wrapped): gizmo
|
||||
//! accessors, plugin messages, the QBrush getter, input properties,
|
||||
//! thumbnail/waveform caches, shape/subtitle blocks, keyframe
|
||||
//! enumeration (count/at/easing/remove/batch/handles-on-track — the
|
||||
//! oaknode keyframe C ABI is handle-only), input flags/array/data-type
|
||||
//! introspection, category/flags metadata, effect-input lookup,
|
||||
//! exclusive dependencies, `node_get_data`, transform-time, dependency
|
||||
//! copy, project color reference space / alongside cache path, footage
|
||||
//! audio-stream info, colorspace candidates, custom proxy params,
|
||||
//! source start time, stream-enabled, proxy generate. Each stub body
|
||||
//! carries the one-line reason.
|
||||
//! - **timeline** (timeline.h, 126/139 wrapped): the ripple-tracks
|
||||
//! command, default transitions, move-track/move-clip, standalone
|
||||
//! marker creation, auto-cache accessors, clip cache invalidation and
|
||||
//! the multicam find/switch helpers are stubs — the oaktimeline/oaknode
|
||||
//! module surfaces for them do not exist (see the stub bodies).
|
||||
//! - **task** (task.h, 27/27 wrapped): `oakengine_task_create_proxy` is
|
||||
//! stubbed (the oaktask crate exposes no proxy-task C creator);
|
||||
//! `oakengine_task_start_time`/`is_cancelled` are facade-approximated
|
||||
//! (the module has no getters).
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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/>.
|
||||
|
||||
//! Facade error codes, mirroring `engine/include/oakengine/init.h`.
|
||||
//!
|
||||
//! The facade is module 00 of the project-wide -MMCCCC scheme
|
||||
//! (see `include/common/error.h`): its own codes are `-(0*10000 + CCCC)`,
|
||||
//! i.e. -1..-6. Codes returned by a wrapped module call pass through
|
||||
//! **untranslated** — the numeric module prefix preserves provenance
|
||||
//! (e.g. -20004 is oakundo's NOT_FOUND, -30001 oaknode's INVALID) and the
|
||||
//! facade never rewrites them.
|
||||
|
||||
/// Success.
|
||||
pub const OAKENGINE_OK: i32 = 0;
|
||||
/// Empty handle or invalid argument.
|
||||
pub const OAKENGINE_E_INVALID: i32 = -1;
|
||||
/// Call not valid in the current state.
|
||||
pub const OAKENGINE_E_STATE: i32 = -2;
|
||||
/// The underlying operation failed.
|
||||
pub const OAKENGINE_E_FAILED: i32 = -3;
|
||||
/// Index out of range / entry not found.
|
||||
pub const OAKENGINE_E_NOT_FOUND: i32 = -4;
|
||||
/// Allocation failed (reserved; mirrors the -MMCCCC reserved list).
|
||||
pub const OAKENGINE_E_NOMEM: i32 = -5;
|
||||
/// The operation was cancelled (reserved; mirrors the -MMCCCC reserved
|
||||
/// list).
|
||||
pub const OAKENGINE_E_CANCELLED: i32 = -6;
|
||||
|
||||
/// Crate-internal result type.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Crate-internal error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Empty handle or invalid argument.
|
||||
Invalid,
|
||||
/// Wrong state.
|
||||
State,
|
||||
/// The underlying operation failed (context string is log-only).
|
||||
Failed(String),
|
||||
/// Not found.
|
||||
NotFound,
|
||||
/// Out of memory.
|
||||
NoMem,
|
||||
/// Cancelled.
|
||||
Cancelled,
|
||||
/// A module error code that must pass through untranslated.
|
||||
Module(i32),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Map to the public error code. Module codes pass through verbatim.
|
||||
pub fn code(&self) -> i32 {
|
||||
match self {
|
||||
Error::Invalid => OAKENGINE_E_INVALID,
|
||||
Error::State => OAKENGINE_E_STATE,
|
||||
Error::Failed(_) => OAKENGINE_E_FAILED,
|
||||
Error::NotFound => OAKENGINE_E_NOT_FOUND,
|
||||
Error::NoMem => OAKENGINE_E_NOMEM,
|
||||
Error::Cancelled => OAKENGINE_E_CANCELLED,
|
||||
Error::Module(code) => *code,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a module return code. `0` (OK) never becomes an error; any
|
||||
/// negative code is kept as a pass-through [`Error::Module`].
|
||||
pub fn from_module(code: i32) -> Result<()> {
|
||||
if code == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Module(code))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
// 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/>.
|
||||
|
||||
//! Facade scaffolding: engine opaque pointers as thin newtype wrappers
|
||||
//! around module [`CHandle`] values.
|
||||
//!
|
||||
//! Every `OakEngine*` opaque type from `engine/include/oakengine/*.h`
|
||||
//! is a `#[repr(C)]` struct holding one [`CHandle`] (the module C ABI's
|
||||
//! `{ctx, addref, release, abi_version}` value handle, see
|
||||
//! `include/common/handle.h`). The C caller only ever sees an opaque
|
||||
//! pointer, so the field layout is ours to choose; the wrappers exist so
|
||||
//! the exported `oakengine_*` signatures match the frozen headers
|
||||
//! verbatim.
|
||||
//!
|
||||
//! A box is created by [`box_handle`] and freed by [`free_box`]: freeing
|
||||
//! calls the handle's `release` (for a module-borrowed handle that only
|
||||
//! releases the handle shell, never the graph-owned object) and then
|
||||
//! deallocates the box. Consuming exports (`oakengine_*_free`,
|
||||
//! `oakengine_undo_push`, ...) call [`free_box`].
|
||||
//!
|
||||
//! String output follows the engine's buf/size convention (see
|
||||
//! [`write_string`]): the return value is the required length including
|
||||
//! the terminating NUL; negative values are error codes.
|
||||
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
/// The shared ABI value-handle type (single-lib unification, see
|
||||
/// `docs/zh/plans/riir/single-lib.md`): one canonical
|
||||
/// `{ctx, addref, release, abi_version}` type in `oakcore-rs`, re-exported
|
||||
/// by every module crate, so the facade can pass a handle straight into a
|
||||
/// module's `pub` Rust functions without an `extern "C"` declaration.
|
||||
/// `Clone + Copy + Send + Sync` come from the shared type.
|
||||
pub use oakcore_rs::handle::CHandle;
|
||||
|
||||
/// Engine opaque handle types, one per `typedef struct OakEngine*` in
|
||||
/// `engine/include/oakengine/*.h`. All are thin newtype wrappers around a
|
||||
/// [`CHandle`] value with a uniform extraction surface ([`EngineBox`]).
|
||||
macro_rules! engine_handle {
|
||||
($($name:ident),* $(,)?) => {
|
||||
$(
|
||||
/// Opaque engine handle: thin newtype wrapper around a module
|
||||
/// [`CHandle`] value.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct $name {
|
||||
/// The wrapped module handle.
|
||||
pub handle: CHandle,
|
||||
}
|
||||
|
||||
impl EngineBox for $name {
|
||||
fn boxed_new(handle: CHandle) -> Self {
|
||||
$name { handle }
|
||||
}
|
||||
fn handle(&self) -> CHandle {
|
||||
self.handle
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
engine_handle! {
|
||||
OakEngineAudioBuffer,
|
||||
OakEngineAudioProcessor,
|
||||
OakEngineBlock,
|
||||
OakEngineClip,
|
||||
OakEngineClipboard,
|
||||
OakEngineColorConfig,
|
||||
OakEngineColorManager,
|
||||
OakEngineColorProcessor,
|
||||
OakEngineEncodingParams,
|
||||
OakEngineFootage,
|
||||
OakEngineFrame,
|
||||
OakEngineFrameCache,
|
||||
OakEngineKeyframe,
|
||||
OakEngineMarker,
|
||||
OakEngineMarkerList,
|
||||
OakEngineNode,
|
||||
OakEngineNodeDragger,
|
||||
OakEnginePlayback,
|
||||
OakEnginePlaybackCache,
|
||||
OakEnginePreviewRequest,
|
||||
OakEngineProject,
|
||||
OakEngineRenderer,
|
||||
OakEngineSequence,
|
||||
OakEngineTask,
|
||||
OakEngineThumbnailCache,
|
||||
OakEngineTrack,
|
||||
OakEngineTrackList,
|
||||
OakEngineTraverseDb,
|
||||
OakEngineWaveformCache,
|
||||
OakEngineWorkarea,
|
||||
}
|
||||
|
||||
/// Uniform construction/extraction surface of the engine opaque types.
|
||||
pub trait EngineBox: Sized {
|
||||
/// Build the wrapper from a module handle.
|
||||
fn boxed_new(handle: CHandle) -> Self;
|
||||
/// Extract the wrapped module handle (copy).
|
||||
fn handle(&self) -> CHandle;
|
||||
}
|
||||
|
||||
/// Allocate a heap box for a module handle and return its raw pointer.
|
||||
/// The box must later be released with [`free_box`].
|
||||
pub fn box_handle<T: EngineBox>(handle: CHandle) -> *mut T {
|
||||
Box::into_raw(Box::new(T::boxed_new(handle)))
|
||||
}
|
||||
|
||||
/// Dereference an engine opaque pointer and copy out its module handle.
|
||||
/// Returns [`Error::Invalid`] for a NULL pointer or an empty handle.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must point to a live box created by [`box_handle`] (or be
|
||||
/// NULL).
|
||||
pub unsafe fn unbox<T: EngineBox>(ptr: *const T) -> Result<CHandle> {
|
||||
unsafe {
|
||||
if ptr.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let h = (*ptr).handle();
|
||||
if h.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
Ok(h)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free a box created by [`box_handle`]: release the module handle (via
|
||||
/// its `release` function pointer) and deallocate the box. NULL and
|
||||
/// empty handles are no-ops. After the call `ptr` is dangling; the
|
||||
/// caller must not use it again.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a pointer previously returned by [`box_handle`] (or
|
||||
/// NULL) and must not be freed twice.
|
||||
pub unsafe fn free_box<T: EngineBox>(ptr: *mut T) {
|
||||
unsafe {
|
||||
if ptr.is_null() {
|
||||
return;
|
||||
}
|
||||
let handle = (*ptr).handle();
|
||||
if let Some(release) = handle.release {
|
||||
release(handle.ctx);
|
||||
}
|
||||
drop(Box::from_raw(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic-catching FFI wrapper for `i32`-returning exports.
|
||||
pub fn guard<F: FnOnce() -> Result<()>>(f: F) -> c_int {
|
||||
match catch_unwind(AssertUnwindSafe(f)) {
|
||||
Ok(Ok(())) => crate::error::OAKENGINE_OK,
|
||||
Ok(Err(e)) => e.code(),
|
||||
Err(_) => crate::error::OAKENGINE_E_FAILED,
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic-catching FFI wrapper for pointer-returning exports.
|
||||
pub fn guard_ptr<T, F: FnOnce() -> Result<*mut T>>(f: F) -> *mut T {
|
||||
match catch_unwind(AssertUnwindSafe(f)) {
|
||||
Ok(Ok(p)) => p,
|
||||
_ => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic-catching FFI wrapper for `int64_t`-returning exports
|
||||
/// (`OAKENGINE_E_INVALID` sentinel on error, matching the engine's
|
||||
/// "no application core exists" convention).
|
||||
pub fn guard_i64<F: FnOnce() -> Result<i64>>(f: F) -> i64 {
|
||||
match catch_unwind(AssertUnwindSafe(f)) {
|
||||
Ok(Ok(v)) => v,
|
||||
Ok(Err(_)) => crate::error::OAKENGINE_E_INVALID as i64,
|
||||
Err(_) => crate::error::OAKENGINE_E_FAILED as i64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic-catching FFI wrapper for void exports.
|
||||
pub fn guard_void<F: FnOnce()>(f: F) {
|
||||
let _ = catch_unwind(AssertUnwindSafe(f));
|
||||
}
|
||||
|
||||
/// Panic-catching FFI wrapper for exports whose return value IS the
|
||||
/// result (a count, a 1/0 flag, a required string length): the closure
|
||||
/// returns the positive payload, errors are returned as negative codes.
|
||||
pub fn guard_int<F: FnOnce() -> Result<c_int>>(f: F) -> c_int {
|
||||
match catch_unwind(AssertUnwindSafe(f)) {
|
||||
Ok(Ok(v)) => v,
|
||||
Ok(Err(e)) => e.code(),
|
||||
Err(_) => crate::error::OAKENGINE_E_FAILED,
|
||||
}
|
||||
}
|
||||
|
||||
/// Write `s` into `buf` following the engine buf/size convention and
|
||||
/// return the string length **excluding** the terminating NUL (the engine
|
||||
/// headers' "would-be length"; module getters report len+1 and are
|
||||
/// converted with [`string_result`]). A NULL `buf` or `buf_size <= 0`
|
||||
/// only reports the length. `s` is truncated to `buf_size - 1` bytes when
|
||||
/// it does not fit.
|
||||
///
|
||||
/// # Safety
|
||||
/// `buf` must point to `buf_size` writable bytes when non-NULL and
|
||||
/// `buf_size > 0`.
|
||||
pub unsafe fn write_string(s: &str, buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
unsafe {
|
||||
if !buf.is_null() && buf_size > 0 {
|
||||
let copy_len = s.len().min((buf_size as usize).saturating_sub(1));
|
||||
std::ptr::copy_nonoverlapping(s.as_ptr(), buf as *mut u8, copy_len);
|
||||
*buf.add(copy_len) = 0;
|
||||
}
|
||||
}
|
||||
s.len() as c_int
|
||||
}
|
||||
|
||||
/// Read a NUL-terminated C string; NULL yields an empty string.
|
||||
///
|
||||
/// # Safety
|
||||
/// `s` must be a valid NUL-terminated string, or NULL.
|
||||
pub unsafe fn read_cstr(s: *const c_char) -> String {
|
||||
unsafe {
|
||||
if s.is_null() {
|
||||
String::new()
|
||||
} else {
|
||||
std::ffi::CStr::from_ptr(s).to_string_lossy().into_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a module two-stage getter result to the engine convention.
|
||||
/// Module getters report the required buffer size **including** the
|
||||
/// terminating NUL; the engine headers' buf/size convention reports the
|
||||
/// string **length** (excluding the NUL, mirroring the C++ capi
|
||||
/// `write_string`). Negative codes pass through untranslated.
|
||||
pub fn string_result(module_ret: c_int) -> c_int {
|
||||
if module_ret > 0 {
|
||||
module_ret - 1
|
||||
} else {
|
||||
module_ret
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
// 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/>.
|
||||
|
||||
//! # oakengine — the `liboakengine` facade (Rust)
|
||||
//!
|
||||
//! Re-exports the frozen `oakengine_*` C ABI
|
||||
//! (`engine/include/oakengine/*.h`) verbatim on top of the module C ABIs
|
||||
//! (`include/<mod>/*.h`, implemented by the oakundo/oaknode/oaktimeline/
|
||||
//! oakcodec/oakaudio/oakrender/oaktask/oakcommon/oakplugin crates). It is
|
||||
//! the M9 §4 assembly layer: every module call crosses the module C ABI as
|
||||
//! an `extern "C"` import (see [`bridge`]); the facade itself owns only
|
||||
//! cross-cutting state (the process-wide undo stack and the open undo
|
||||
//! group, see [`undo`]).
|
||||
//!
|
||||
//! ## Handle mapping
|
||||
//!
|
||||
//! The engine headers' opaque pointers (`OakEngineNode*`, `OakEngineTrack*`,
|
||||
//! ...) become thin newtype wrappers around module [`handle::CHandle`]
|
||||
//! values (see [`handle`]). Each exported function keeps the exact
|
||||
//! signature from the engine header; inside, it unboxes the module handle,
|
||||
//! calls the module C ABI and boxes the result.
|
||||
//!
|
||||
//! ## FFI discipline
|
||||
//!
|
||||
//! Every export goes through a `catch_unwind` guard ([`handle::guard*`]),
|
||||
//! `free` functions are NULL no-ops, strings use the two-stage buf/size
|
||||
//! convention ([`handle::write_string`]), and module error codes pass
|
||||
//! through untranslated ([`error`], facade module 00 → -1..-6).
|
||||
//!
|
||||
//! ## Testing
|
||||
//!
|
||||
//! The module crates are real dependencies (see Cargo.toml) and
|
||||
//! [`linkage`] anchors them into every link of this crate, so the module
|
||||
//! C ABIs are embedded in the `liboakengine` cdylib next to the facade's
|
||||
//! own exports. `cargo test` links the same crates' rlibs (plus the
|
||||
//! `test-stubs` feature union declared in the dev-dependencies, which
|
||||
//! compiles the oakcommon/oakplugin in-crate mocks); `tests/linkage.rs`
|
||||
//! additionally references every crate for the integration-test binaries
|
||||
//! and `test_link` (below) covers the unit-test binary. Where a wrapped
|
||||
//! family needs module behavior the crates do not implement yet, the
|
||||
//! engine function is a documented stub and its test carries `#[ignore]`
|
||||
//! with a reason (see README.md).
|
||||
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod audio;
|
||||
pub mod bridge;
|
||||
pub mod codec;
|
||||
pub mod common;
|
||||
pub mod deferred;
|
||||
pub mod error;
|
||||
pub mod handle;
|
||||
pub mod ipc;
|
||||
#[cfg(not(test))]
|
||||
pub mod linkage;
|
||||
pub mod node;
|
||||
pub mod plugin;
|
||||
pub mod render;
|
||||
pub mod task;
|
||||
pub mod timeline;
|
||||
pub mod undo;
|
||||
pub mod worker;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_link {
|
||||
// The lib's own unit-test binary must link the module crates' rlibs to
|
||||
// satisfy the facade's `extern "C"` imports that the unit tests compile
|
||||
// in — e.g. the worker session's oakrender display renderer (src/worker.rs).
|
||||
// The integration tests do the same through tests/common/mod.rs
|
||||
// `force_link()`; this covers the `cargo test` unit-test binary.
|
||||
#![allow(dead_code)]
|
||||
fn force_link() -> usize {
|
||||
let fns: [usize; 4] = [
|
||||
oakrender::ffi::renderer::oakrender_display_renderer_create_opengl
|
||||
as *const () as usize,
|
||||
oaknode::ffi::project::oaknode_project_init as *const () as usize,
|
||||
oaktimeline::ffi::marker::oaktimeline_marker_list_create as *const () as usize,
|
||||
oaktask::ffi::manager::oaktask_manager_init as *const () as usize,
|
||||
];
|
||||
fns.iter().sum()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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/>.
|
||||
|
||||
//! Linkage anchors — force the module crates' rlibs into every link.
|
||||
//!
|
||||
//! The facade talks to the modules exclusively through `extern "C"`
|
||||
//! imports (src/bridge/), so rustc would otherwise consider the module
|
||||
//! crates unused and prune their rlibs from the link. This module
|
||||
//! references one `#[no_mangle]` export of every module crate (and
|
||||
//! `oakcore-rs`) from a `#[used]` static, which (a) marks each crate as
|
||||
//! used so its rlib reaches the linker and (b) keeps the anchor alive so
|
||||
//! the referenced object files are pulled. For the `liboakengine` cdylib
|
||||
//! this is what actually embeds the module C ABIs (oakundo_*,
|
||||
//! oakcommon_*, ...) into the dylib next to the facade's own oakengine_*
|
||||
//! exports.
|
||||
//!
|
||||
//! The per-crate symbol mirrors the test-force-link in
|
||||
//! tests/common/mod.rs (same paths, same `as usize` cast idiom), so the
|
||||
//! crate/module paths are proven against the current module layouts.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
/// Pull every module crate into the link. Mirrors
|
||||
/// `tests/common/mod.rs::force_link`; the oakcommon XML/undo anchors are
|
||||
/// repeated because oaknode's serializer resolves those C ABI symbols at
|
||||
/// runtime via dlsym(RTLD_DEFAULT) and they must be present in the dylib
|
||||
/// for that lookup to succeed.
|
||||
fn force_link() -> usize {
|
||||
let fns: [usize; 13] = [
|
||||
// oakcore-rs (pure value types; referenced so its rlib is linked).
|
||||
oakcore_rs::Rational::new(1, 2).numerator() as usize,
|
||||
// One exported C ABI symbol per module crate.
|
||||
oakundo::ffi::undostack::oakundo_undostack_init as usize,
|
||||
oakcommon::ffi::config::oakcommon_config_get_int as usize,
|
||||
oaktimeline::ffi::marker::oaktimeline_marker_list_create as usize,
|
||||
oakcodec::ffi::format::oakcodec_encoding_format_count as usize,
|
||||
oakaudio::ffi::waveform::oakaudio_waveform_length as usize,
|
||||
oakrender::ffi::cache::oakrender_cache_indicator_height as usize,
|
||||
oaktask::ffi::manager::oaktask_manager_init as usize,
|
||||
oakplugin::ffi::oakplugin_host_plugin_count as usize,
|
||||
oaknode::ffi::project::oaknode_project_init as usize,
|
||||
// oaknode's dlsym(RTLD_DEFAULT) targets (see tests/common/mod.rs).
|
||||
oakcommon::ffi::xmlutils::oakcommon_xml_writer_init as usize,
|
||||
oakcommon::ffi::xmlutils::oakcommon_xml_reader_init as usize,
|
||||
oakundo::ffi::command::oakundo_command_init as usize,
|
||||
];
|
||||
fns.iter().sum()
|
||||
}
|
||||
|
||||
/// Keeps [`force_link`] (and through it every referenced export) alive in
|
||||
/// the cdylib/staticlib even though nothing calls it directly.
|
||||
#[used]
|
||||
static FORCE_LINK_ANCHOR: fn() -> usize = force_link;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
// 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/>.
|
||||
|
||||
//! `engine/include/oakengine/plugin.h` over the oakplugin module.
|
||||
//!
|
||||
//! The active-viewer provider and progress-reporter factory are pure
|
||||
//! facade state (module 00 analogues of the C++ capi's statics): the UI
|
||||
//! registers C callbacks here, and the plugin host consumes them once the
|
||||
//! module exposes the corresponding registration points
|
||||
//! (`oakplugin_*_set_*_provider`). Until then the callbacks are stored
|
||||
//! and reported as registered.
|
||||
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use crate::bridge::plugin as p;
|
||||
use crate::error::Error;
|
||||
use crate::handle::guard;
|
||||
|
||||
/// `oakengine_plugin_active_viewer_fn` — returns the active viewer node.
|
||||
pub type ActiveViewerFn =
|
||||
unsafe extern "C" fn(userdata: *mut c_void) -> *mut crate::handle::OakEngineNode;
|
||||
|
||||
/// `oakengine_plugin_reporter_create_fn` — creates a UI progress reporter.
|
||||
pub type ReporterCreateFn =
|
||||
unsafe extern "C" fn(message: *const c_char, title: *const c_char, userdata: *mut c_void) -> *mut c_void;
|
||||
/// `oakengine_plugin_reporter_destroy_fn` — destroys a reporter.
|
||||
pub type ReporterDestroyFn = unsafe extern "C" fn(reporter: *mut c_void, userdata: *mut c_void);
|
||||
/// `oakengine_plugin_reporter_is_cancelled_fn` — 1 when cancelled.
|
||||
pub type ReporterIsCancelledFn = unsafe extern "C" fn(reporter: *mut c_void, userdata: *mut c_void) -> c_int;
|
||||
/// `oakengine_plugin_reporter_set_progress_fn` — progress update.
|
||||
pub type ReporterSetProgressFn =
|
||||
unsafe extern "C" fn(reporter: *mut c_void, progress: f64, userdata: *mut c_void);
|
||||
|
||||
struct ProviderState {
|
||||
active_viewer: Option<(Option<ActiveViewerFn>, usize)>,
|
||||
reporter: Option<(
|
||||
Option<ReporterCreateFn>,
|
||||
Option<ReporterDestroyFn>,
|
||||
Option<ReporterIsCancelledFn>,
|
||||
Option<ReporterSetProgressFn>,
|
||||
usize,
|
||||
)>,
|
||||
}
|
||||
|
||||
fn state() -> &'static Mutex<ProviderState> {
|
||||
static STATE: OnceLock<Mutex<ProviderState>> = OnceLock::new();
|
||||
STATE.get_or_init(|| {
|
||||
Mutex::new(ProviderState {
|
||||
active_viewer: None,
|
||||
reporter: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_plugin_set_active_viewer_provider` — register the active
|
||||
/// viewer callback (NULL clears it).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_plugin_set_active_viewer_provider(
|
||||
fn_: Option<ActiveViewerFn>,
|
||||
userdata: *mut c_void,
|
||||
) -> c_int {
|
||||
guard(|| {
|
||||
let mut s = state().lock().unwrap_or_else(|e| e.into_inner());
|
||||
s.active_viewer = Some((fn_, userdata as usize));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_plugin_set_progress_reporter_factory` — register the
|
||||
/// progress-reporter factory callbacks (NULL clears them).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_plugin_set_progress_reporter_factory(
|
||||
create: Option<ReporterCreateFn>,
|
||||
destroy: Option<ReporterDestroyFn>,
|
||||
is_cancelled: Option<ReporterIsCancelledFn>,
|
||||
set_progress: Option<ReporterSetProgressFn>,
|
||||
userdata: *mut c_void,
|
||||
) -> c_int {
|
||||
guard(|| {
|
||||
let mut s = state().lock().unwrap_or_else(|e| e.into_inner());
|
||||
s.reporter = Some((create, destroy, is_cancelled, set_progress, userdata as usize));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_plugin_load_plugins` — scan the plugin bundle directory
|
||||
/// `path` (oakplugin_host_scan).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_plugin_load_plugins(path: *const c_char) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if path.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let dirs: [*const c_char; 1] = [path];
|
||||
Error::from_module(p::oakplugin_host_scan(dirs.as_ptr(), 1))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_plugin_node_push_button_clicked` — not yet backed: the
|
||||
/// oakplugin crate exposes no push-button API (the OFX button-param
|
||||
/// trigger is C++-only). Returns `OAKENGINE_E_FAILED`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_plugin_node_push_button_clicked(
|
||||
_node: *mut crate::handle::OakEngineNode,
|
||||
_button_id: *const c_char,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
@@ -0,0 +1,958 @@
|
||||
// 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/>.
|
||||
|
||||
//! `engine/include/oakengine/{renderer,color,lut}.h` over the oakrender
|
||||
//! module.
|
||||
//!
|
||||
//! - The **renderer** is a facade-owned box binding a sequence handle to
|
||||
//! an output geometry; each render call submits an oakrender ticket
|
||||
//! (`OakVideoTicketParams`), waits for it and returns the produced
|
||||
//! frame (`OakCodecFrame` wrapped in `OakEngineFrame`). Audio rendering
|
||||
//! submits the ticket but the crate's samples path is unimplemented, so
|
||||
//! it fails with the reason in `last_error`.
|
||||
//! - The **frame accessors** read the wrapped `OakCodecFrame`
|
||||
//! (`channel_count` has no crate accessor and reports 0).
|
||||
//! - The **color processor** family maps onto
|
||||
//! `oakrender_color_processor_*`; the engine's `oak_color_transform`
|
||||
//! POD is converted into an oakcommon colortransform handle for
|
||||
//! `create_transform`. The color-manager list queries, standalone
|
||||
//! config handle and LUT directory/file library have no crate backing
|
||||
//! and are documented stubs (see `deferred.rs`).
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::{c_char, c_double, c_int, c_void};
|
||||
|
||||
use crate::bridge::render as r;
|
||||
use crate::bridge::render::{OakRenderVideoParams, OakVideoTicketParams};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::handle::{
|
||||
box_handle, free_box, guard, guard_int, guard_ptr, guard_void, string_result, unbox, CHandle,
|
||||
EngineBox, OakEngineAudioBuffer, OakEngineColorProcessor, OakEngineFrame, OakEngineNode,
|
||||
OakEngineRenderer, OakEngineSequence,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Render manager / cacher
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_render_manager_set_aggressive_garbage_collection`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_render_manager_set_aggressive_garbage_collection(
|
||||
aggressive: c_int,
|
||||
) -> c_int {
|
||||
guard(|| Error::from_module(unsafe { r::oakrender_manager_set_aggressive_gc(aggressive) }))
|
||||
}
|
||||
|
||||
/// `oakengine_render_manager_requested_backend` — **not backed** (the
|
||||
/// oakrender crate exposes the current backend, not the requested one).
|
||||
/// Returns 0 (k_open_gl).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_render_manager_requested_backend() -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_render_manager_backend_to_string` — **not backed** (the
|
||||
/// crate enumerates backend ids, not enum→string). Returns
|
||||
/// OAKENGINE_E_FAILED.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_render_manager_backend_to_string(
|
||||
_backend: c_int,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_render_cache_set_display_color_processor` — NULL clears.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_render_cache_set_display_color_processor(
|
||||
processor: *mut c_void,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let proc = if processor.is_null() {
|
||||
CHandle::null()
|
||||
} else {
|
||||
unbox(processor.cast::<OakEngineColorProcessor>())?
|
||||
};
|
||||
Error::from_module(r::oakrender_set_display_color_processor(proc))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_render_cache_set_multicam_node` — NULL clears.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_render_cache_set_multicam_node(node: *mut OakEngineNode) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let n = if node.is_null() {
|
||||
CHandle::null()
|
||||
} else {
|
||||
unbox(node)?
|
||||
};
|
||||
Error::from_module(r::oakrender_set_cacher_multicam(n))
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Renderer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Facade-side renderer box: the bound sequence + output geometry.
|
||||
struct RendererBox {
|
||||
/// Unboxed sequence node handle (borrowed).
|
||||
seq: CHandle,
|
||||
/// Output width.
|
||||
width: c_int,
|
||||
/// Output height.
|
||||
height: c_int,
|
||||
/// Output pixel format (`PixelFormat::Format`).
|
||||
pixel_format: c_int,
|
||||
/// Frame-rate numerator.
|
||||
frame_rate_num: c_int,
|
||||
/// Frame-rate denominator.
|
||||
frame_rate_den: c_int,
|
||||
/// Render mode (0 offline / 1 online).
|
||||
mode: c_int,
|
||||
/// Last failure reason.
|
||||
last_error: String,
|
||||
}
|
||||
|
||||
/// Borrow the renderer box (NULL → Invalid).
|
||||
unsafe fn renderer(ptr: *const OakEngineRenderer) -> Result<&'static RendererBox> {
|
||||
unsafe {
|
||||
if ptr.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
Ok(&*(ptr as *const RendererBox))
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow the renderer box mutably.
|
||||
unsafe fn renderer_mut(ptr: *mut OakEngineRenderer) -> Result<&'static mut RendererBox> {
|
||||
unsafe {
|
||||
if ptr.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
Ok(&mut *(ptr as *mut RendererBox))
|
||||
}
|
||||
}
|
||||
|
||||
/// Build an oakcommon video-params handle for the renderer's geometry.
|
||||
unsafe fn make_video_params(b: &RendererBox) -> Result<CHandle> {
|
||||
unsafe {
|
||||
let params = crate::bridge::common::oakcommon_videoparams_init();
|
||||
if params.is_null() {
|
||||
return Err(Error::Failed("video params allocation failed".into()));
|
||||
}
|
||||
let mut rc = crate::bridge::common::oakcommon_videoparams_set_width(params, b.width);
|
||||
if rc == 0 {
|
||||
rc = crate::bridge::common::oakcommon_videoparams_set_height(params, b.height);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = crate::bridge::common::oakcommon_videoparams_set_format(params, b.pixel_format);
|
||||
}
|
||||
if rc == 0 {
|
||||
rc = crate::bridge::common::oakcommon_videoparams_set_time_base(
|
||||
params,
|
||||
b.frame_rate_den,
|
||||
b.frame_rate_num,
|
||||
);
|
||||
}
|
||||
if rc != 0 {
|
||||
let mut p = params;
|
||||
crate::bridge::common::oakcommon_videoparams_free(&mut p);
|
||||
return Err(Error::Failed("video params setup failed".into()));
|
||||
}
|
||||
Ok(params)
|
||||
}
|
||||
}
|
||||
|
||||
/// `oakengine_renderer_create` — NULL for invalid arguments.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_renderer_create(
|
||||
seq: *mut OakEngineSequence,
|
||||
width: c_int,
|
||||
height: c_int,
|
||||
pixel_format: c_int,
|
||||
frame_rate_num: c_int,
|
||||
frame_rate_den: c_int,
|
||||
output_colorspace: *const c_char,
|
||||
) -> *mut OakEngineRenderer {
|
||||
guard_ptr(|| unsafe {
|
||||
if seq.is_null() || width <= 0 || height <= 0 || frame_rate_num <= 0 || frame_rate_den <= 0 {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
// Validate the pixel format against the oakcommon format enum.
|
||||
if crate::bridge::common::oakcommon_videoparams_get_format_name(
|
||||
pixel_format,
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
) < 0
|
||||
{
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let _ = crate::handle::read_cstr(output_colorspace); // resolved by the module at render time
|
||||
let seq_handle = unbox(seq)?;
|
||||
let boxed = Box::new(RendererBox {
|
||||
seq: seq_handle,
|
||||
width,
|
||||
height,
|
||||
pixel_format,
|
||||
frame_rate_num,
|
||||
frame_rate_den,
|
||||
mode: 0,
|
||||
last_error: String::new(),
|
||||
});
|
||||
Ok(Box::into_raw(boxed) as *mut OakEngineRenderer)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_renderer_free` — NULL no-op.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_renderer_free(self_: *mut OakEngineRenderer) {
|
||||
guard_void(|| unsafe {
|
||||
if self_.is_null() {
|
||||
return;
|
||||
}
|
||||
drop(Box::from_raw(self_ as *mut RendererBox));
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_renderer_set_mode` — 0/1 only.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_renderer_set_mode(
|
||||
self_: *mut OakEngineRenderer,
|
||||
mode: c_int,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let b = renderer_mut(self_)?;
|
||||
if mode != 0 && mode != 1 {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
b.mode = mode;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_renderer_last_error` (buf/size).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_renderer_last_error(
|
||||
self_: *const OakEngineRenderer,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let b = renderer(self_)?;
|
||||
Ok(crate::handle::write_string(&b.last_error, buf, buf_size))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_renderer_render_frame` — synchronous frame render.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_renderer_render_frame(
|
||||
self_: *mut OakEngineRenderer,
|
||||
timestamp: i64,
|
||||
) -> *mut OakEngineFrame {
|
||||
guard_ptr(|| unsafe {
|
||||
let b = renderer_mut(self_)?;
|
||||
let video_params = make_video_params(b)?;
|
||||
let params = OakVideoTicketParams {
|
||||
output_node: b.seq,
|
||||
video_params,
|
||||
audio_params: std::ptr::null(),
|
||||
time_num: timestamp * i64::from(b.frame_rate_den),
|
||||
time_den: i64::from(b.frame_rate_num),
|
||||
color_manager: CHandle::null(),
|
||||
mode: b.mode,
|
||||
force_width: 0,
|
||||
force_height: 0,
|
||||
force_matrix: [0.0; 16],
|
||||
has_force_matrix: 0,
|
||||
force_format: -1,
|
||||
force_channel_count: 0,
|
||||
force_color_output: CHandle::null(),
|
||||
force_color_transform: CHandle::null(),
|
||||
cache: CHandle::null(),
|
||||
};
|
||||
let ticket = r::oakrender_ticket_render_frame(¶ms, None, std::ptr::null_mut());
|
||||
let mut vp = video_params;
|
||||
crate::bridge::common::oakcommon_videoparams_free(&mut vp);
|
||||
if ticket.is_null() {
|
||||
b.last_error = "render ticket submission failed".into();
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let wait_rc = r::oakrender_ticket_wait(ticket);
|
||||
let mut frame = CHandle::null();
|
||||
let get_rc = r::oakrender_ticket_get_frame(ticket, &mut frame);
|
||||
let mut t = ticket;
|
||||
r::oakrender_ticket_free(&mut t);
|
||||
if wait_rc != 0 || get_rc != 0 || frame.is_null() {
|
||||
b.last_error = "render failed or timed out".into();
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
b.last_error.clear();
|
||||
Ok(box_handle::<OakEngineFrame>(frame))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_renderer_render_audio` — synchronous audio render. The
|
||||
/// oakrender crate's samples path is unimplemented, so this submits the
|
||||
/// ticket and reports the failure reason.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_renderer_render_audio(
|
||||
self_: *mut OakEngineRenderer,
|
||||
start_timestamp: i64,
|
||||
length_timestamp: i64,
|
||||
) -> *mut OakEngineAudioBuffer {
|
||||
guard_ptr(|| unsafe {
|
||||
let b = renderer_mut(self_)?;
|
||||
let start_num = start_timestamp * i64::from(b.frame_rate_den);
|
||||
let end_num = (start_timestamp + length_timestamp) * i64::from(b.frame_rate_den);
|
||||
let den = i64::from(b.frame_rate_num);
|
||||
let ticket = r::oakrender_ticket_render_audio(
|
||||
b.seq,
|
||||
start_num,
|
||||
den,
|
||||
end_num,
|
||||
den,
|
||||
std::ptr::null(),
|
||||
b.mode,
|
||||
None,
|
||||
std::ptr::null_mut(),
|
||||
);
|
||||
if ticket.is_null() {
|
||||
b.last_error = "audio render ticket submission failed".into();
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let wait_rc = r::oakrender_ticket_wait(ticket);
|
||||
let mut samples: *mut c_void = std::ptr::null_mut();
|
||||
let get_rc = r::oakrender_ticket_get_samples(ticket, &mut samples);
|
||||
let mut t = ticket;
|
||||
r::oakrender_ticket_free(&mut t);
|
||||
let _ = (wait_rc, get_rc, samples);
|
||||
b.last_error = "audio rendering is not implemented by the render module".into();
|
||||
Ok(std::ptr::null_mut())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_renderer_cancel` — cancel the in-flight render call.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_renderer_cancel(self_: *mut OakEngineRenderer) {
|
||||
guard_void(|| unsafe {
|
||||
if let Ok(b) = renderer_mut(self_) {
|
||||
let _ = b; // the crate tracks in-flight tickets internally
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// OakEngineFrame accessors (wraps the module's OakCodecFrame)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Borrow the frame's module handle; `None` for NULL/empty (the engine
|
||||
/// contract: NULL is a no-op yielding zero results).
|
||||
unsafe fn frame_handle(ptr: *const OakEngineFrame) -> Option<CHandle> {
|
||||
unsafe {
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
let h = (*ptr).handle();
|
||||
if h.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `oakengine_frame_width`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_frame_width(self_: *const OakEngineFrame) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
Ok(match frame_handle(self_) {
|
||||
Some(f) => r::oakrender_codec_frame_width(f),
|
||||
None => 0,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_frame_height`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_frame_height(self_: *const OakEngineFrame) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
Ok(match frame_handle(self_) {
|
||||
Some(f) => r::oakrender_codec_frame_height(f),
|
||||
None => 0,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_frame_format` — the frame's params POD format
|
||||
/// (`PixelFormat::Format`).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_frame_format(self_: *const OakEngineFrame) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let Some(f) = frame_handle(self_) else {
|
||||
return Ok(0);
|
||||
};
|
||||
let mut params = OakRenderVideoParams {
|
||||
width: 0,
|
||||
height: 0,
|
||||
time_base_num: 0,
|
||||
time_base_den: 0,
|
||||
format: 0,
|
||||
pixel_aspect_num: 0,
|
||||
pixel_aspect_den: 0,
|
||||
interlacing: 0,
|
||||
color_range: 0,
|
||||
divider: 0,
|
||||
video_type: 0,
|
||||
premultiplied_alpha: 0,
|
||||
};
|
||||
Error::from_module(r::oakrender_codec_frame_get_params(f, &mut params))?;
|
||||
Ok(params.format)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_frame_channel_count` — **not backed** (the oakrender crate
|
||||
/// exposes no frame channel count). Returns 0.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_frame_channel_count(self_: *const OakEngineFrame) -> c_int {
|
||||
let _ = self_;
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_frame_linesize_bytes`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_frame_linesize_bytes(self_: *const OakEngineFrame) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
Ok(match frame_handle(self_) {
|
||||
Some(f) => r::oakrender_codec_frame_linesize_bytes(f),
|
||||
None => 0,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_frame_data` — borrowed pixel data.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_frame_data(self_: *const OakEngineFrame) -> *const c_void {
|
||||
guard_ptr(|| unsafe {
|
||||
Ok(match frame_handle(self_) {
|
||||
Some(f) => r::oakrender_codec_frame_const_data(f) as *mut c_void,
|
||||
None => std::ptr::null_mut(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_frame_free` — NULL no-op.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_frame_free(self_: *mut OakEngineFrame) {
|
||||
guard_void(|| unsafe {
|
||||
if self_.is_null() {
|
||||
return;
|
||||
}
|
||||
let handle = (*self_).handle();
|
||||
let mut h = handle;
|
||||
r::oakrender_codec_frame_free(&mut h);
|
||||
drop(Box::from_raw(self_));
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// OakEngineAudioBuffer accessors (no crate backing — always empty)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_audio_sample_rate` — 0 (no crate samples accessor).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_sample_rate(_self_: *const OakEngineAudioBuffer) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_audio_channel_count` — 0.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_channel_count(_self_: *const OakEngineAudioBuffer) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_audio_sample_count` — 0.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_sample_count(_self_: *const OakEngineAudioBuffer) -> i64 {
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_audio_data` — NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_data(
|
||||
_self_: *const OakEngineAudioBuffer,
|
||||
_channel: c_int,
|
||||
) -> *const f32 {
|
||||
std::ptr::null()
|
||||
}
|
||||
|
||||
/// `oakengine_audio_free` — NULL no-op.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_audio_free(_self_: *mut OakEngineAudioBuffer) {}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color management
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Thread-local reason of the last failed color call.
|
||||
thread_local! {
|
||||
static LAST_COLOR_ERROR: RefCell<String> = const { RefCell::new(String::new()) };
|
||||
}
|
||||
|
||||
/// `oakengine_color_last_error` (buf/size).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_last_error(buf: *mut c_char, buf_size: c_int) -> c_int {
|
||||
crate::handle::guard_int(|| {
|
||||
Ok(LAST_COLOR_ERROR.with(|e| {
|
||||
// SAFETY: `buf` is the caller's buf/size buffer.
|
||||
unsafe { crate::handle::write_string(&e.borrow(), buf, buf_size) }
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_from_project` — **not backed** (needs the
|
||||
/// deferred oaknode project family). Returns NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_from_project(
|
||||
_project: *mut crate::handle::OakEngineProject,
|
||||
) -> *mut crate::handle::OakEngineColorManager {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_get_config_filename` (buf/size).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_get_config_filename(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let rc = r::oakrender_color_manager_get_config(buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_set_config_filename` — **not backed** (the
|
||||
/// crate only reads the config). Returns OAKENGINE_E_FAILED.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_set_config_filename(
|
||||
_mgr: *mut crate::handle::OakEngineColorManager,
|
||||
_filename: *const c_char,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
macro_rules! color_manager_stub {
|
||||
($($name:ident),* $(,)?) => {
|
||||
$(
|
||||
/// Documented stub: the oakrender crate does not implement the
|
||||
/// color-manager list queries (see `deferred.rs`).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $name() -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
color_manager_stub! {
|
||||
oakengine_color_manager_colorspace_count,
|
||||
oakengine_color_manager_display_count,
|
||||
oakengine_color_manager_look_count,
|
||||
}
|
||||
|
||||
macro_rules! color_manager_stub_arg {
|
||||
($($name:ident),* $(,)?) => {
|
||||
$(
|
||||
/// Documented stub: the oakrender crate does not implement the
|
||||
/// color-manager list queries (see `deferred.rs`).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $name(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_index: c_int,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
color_manager_stub_arg! {
|
||||
oakengine_color_manager_colorspace_at,
|
||||
oakengine_color_manager_display_at,
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_view_count` — **not backed**. -1.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_view_count(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_display: *const c_char,
|
||||
) -> c_int {
|
||||
-1
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_view_at` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_view_at(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_display: *const c_char,
|
||||
_index: c_int,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_look_at` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_look_at(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_index: c_int,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_default_display` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_default_display(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_default_view` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_default_view(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_display: *const c_char,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_default_input_color_space` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_default_input_color_space(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_set_default_input_color_space` — **not
|
||||
/// backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_set_default_input_color_space(
|
||||
_mgr: *mut crate::handle::OakEngineColorManager,
|
||||
_colorspace: *const c_char,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_reference_color_space` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_reference_color_space(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_default_luma_coefs` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_default_luma_coefs(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_rgb: *mut c_double,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_compliant_color_space` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_compliant_color_space(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_name: *const c_char,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_manager_compliant_transform` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_manager_compliant_transform(
|
||||
_mgr: *const crate::handle::OakEngineColorManager,
|
||||
_in: *const OakColorTransformPod,
|
||||
_force_display: c_int,
|
||||
_out_is_display: *mut c_int,
|
||||
_out_output: *mut c_char,
|
||||
_output_size: c_int,
|
||||
_out_view: *mut c_char,
|
||||
_view_size: c_int,
|
||||
_out_look: *mut c_char,
|
||||
_look_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color config handle (not backed)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_color_config_load_default` — **not backed**. NULL.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_color_config_load_default() -> *mut crate::handle::OakEngineColorConfig {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
/// `oakengine_color_config_load_file` — **not backed**. NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_config_load_file(
|
||||
_filename: *const c_char,
|
||||
) -> *mut crate::handle::OakEngineColorConfig {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
/// `oakengine_color_config_free` — NULL no-op.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_config_free(
|
||||
_config: *mut crate::handle::OakEngineColorConfig,
|
||||
) {
|
||||
}
|
||||
|
||||
/// `oakengine_color_config_colorspace_count` — **not backed**. 0.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_config_colorspace_count(
|
||||
_config: *const crate::handle::OakEngineColorConfig,
|
||||
) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_color_config_colorspace_at` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_config_colorspace_at(
|
||||
_config: *const crate::handle::OakEngineColorConfig,
|
||||
_index: c_int,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color processor
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `engine/include/oakengine/color.h` — `oak_color_transform` POD mirror.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OakColorTransformPod {
|
||||
/// 0: `output` is a colorspace; 1: display/view/look.
|
||||
pub is_display: c_int,
|
||||
/// Colorspace name, or display device when is_display.
|
||||
pub output: *const c_char,
|
||||
/// Display view (is_display only).
|
||||
pub view: *const c_char,
|
||||
/// Display look (is_display only).
|
||||
pub look: *const c_char,
|
||||
}
|
||||
|
||||
/// `oakengine_color_processor_create` — convert the `oak_color_transform`
|
||||
/// POD into an oakcommon colortransform handle and hand it to
|
||||
/// `oakrender_color_processor_create_transform`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_processor_create(
|
||||
mgr: *const crate::handle::OakEngineColorManager,
|
||||
input: *const c_char,
|
||||
dest: *const OakColorTransformPod,
|
||||
direction: c_int,
|
||||
) -> *mut OakEngineColorProcessor {
|
||||
guard_ptr(|| unsafe {
|
||||
if input.is_null() || dest.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let empty = crate::common::empty_cstr();
|
||||
let ct = if (*dest).is_display != 0 {
|
||||
crate::bridge::common::oakcommon_colortransform_init_display(
|
||||
if (*dest).output.is_null() { empty } else { (*dest).output },
|
||||
if (*dest).view.is_null() { empty } else { (*dest).view },
|
||||
if (*dest).look.is_null() { empty } else { (*dest).look },
|
||||
)
|
||||
} else {
|
||||
crate::bridge::common::oakcommon_colortransform_init_output(
|
||||
if (*dest).output.is_null() { empty } else { (*dest).output },
|
||||
)
|
||||
};
|
||||
if ct.is_null() {
|
||||
LAST_COLOR_ERROR.with(|e| *e.borrow_mut() = "invalid color transform".into());
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let mgr_handle = if mgr.is_null() {
|
||||
CHandle::null()
|
||||
} else {
|
||||
unbox(mgr.cast::<crate::handle::OakEngineColorManager>())?
|
||||
};
|
||||
let proc = r::oakrender_color_processor_create_transform(
|
||||
mgr_handle,
|
||||
input,
|
||||
ct,
|
||||
direction,
|
||||
);
|
||||
let mut ct_handle = ct;
|
||||
crate::bridge::common::oakcommon_colortransform_free(&mut ct_handle);
|
||||
if proc.is_null() {
|
||||
LAST_COLOR_ERROR.with(|e| *e.borrow_mut() = "could not create color processor".into());
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
LAST_COLOR_ERROR.with(|e| e.borrow_mut().clear());
|
||||
Ok(box_handle::<OakEngineColorProcessor>(proc))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_color_processor_free` — NULL no-op.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_processor_free(proc: *mut OakEngineColorProcessor) {
|
||||
guard_void(|| unsafe {
|
||||
free_box(proc);
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_color_processor_is_valid` (1/0).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_processor_is_valid(
|
||||
proc: *const OakEngineColorProcessor,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
if proc.is_null() {
|
||||
return Ok(0);
|
||||
}
|
||||
let p = unbox(proc)?;
|
||||
Ok(r::oakrender_color_processor_is_valid(p))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_color_processor_convert_color` — single RGBA color.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_processor_convert_color(
|
||||
proc: *const OakEngineColorProcessor,
|
||||
in_rgba: *const c_double,
|
||||
out_rgba: *mut c_double,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if in_rgba.is_null() || out_rgba.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let p = unbox(proc)?;
|
||||
let rc = r::oakrender_color_processor_convert(
|
||||
p,
|
||||
*in_rgba,
|
||||
*in_rgba.add(1),
|
||||
*in_rgba.add(2),
|
||||
*in_rgba.add(3),
|
||||
out_rgba,
|
||||
out_rgba.add(1),
|
||||
out_rgba.add(2),
|
||||
out_rgba.add(3),
|
||||
);
|
||||
Error::from_module(rc)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_color_processor_id` — **not backed** (the crate exposes no
|
||||
/// processor cache id). Returns OAKENGINE_E_FAILED.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_processor_id(
|
||||
_proc: *const OakEngineColorProcessor,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_color_transform_job_set_processor` — **not backed** (the
|
||||
/// job is a C++ type). Returns OAKENGINE_E_INVALID for a NULL job.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_color_transform_job_set_processor(
|
||||
job: *mut c_void,
|
||||
_proc: *const OakEngineColorProcessor,
|
||||
) -> c_int {
|
||||
if job.is_null() {
|
||||
crate::error::OAKENGINE_E_INVALID
|
||||
} else {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LUT library (not backed)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_lut_directory_count` — **not backed** (the LUT directory/
|
||||
/// file library is facade-level over FileFunctions; the crate only
|
||||
/// enumerates supported extensions). Returns 0.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_lut_directory_count() -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_lut_directory_at` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_lut_directory_at(
|
||||
_index: c_int,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_lut_file_count` — **not backed**. Returns 0.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_lut_file_count() -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// `oakengine_lut_file_at` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_lut_file_at(
|
||||
_index: c_int,
|
||||
_buf: *mut c_char,
|
||||
_buf_size: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
|
||||
/// `oakengine_lut_set_directories` — **not backed**.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_lut_set_directories(
|
||||
_dirs: *const *const c_char,
|
||||
_count: c_int,
|
||||
) -> c_int {
|
||||
crate::error::OAKENGINE_E_FAILED
|
||||
}
|
||||
@@ -0,0 +1,822 @@
|
||||
// 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/>.
|
||||
|
||||
//! `engine/include/oakengine/task.h` — the engine background-task system
|
||||
//! (the C++ `olive::Task` / `olive::TaskManager`) over the oaktask module.
|
||||
//!
|
||||
//! Task ownership follows the header: `oakengine_task_create_*` returns an
|
||||
//! OWNED task; `oakengine_task_manager_add` hands it to the manager (which
|
||||
//! deletes it when done, so the handle becomes borrowed);
|
||||
//! `oakengine_task_free` deletes a task that never reached the manager.
|
||||
//! A task run with `oakengine_task_start_sync` stays owned by the caller.
|
||||
//!
|
||||
//! The facade owns the global task manager (module-00 analogue of the C++
|
||||
//! app-startup `TaskManager`): it is initialized lazily on the first
|
||||
//! manager-family call, mirroring the undo family's process-wide stack.
|
||||
//!
|
||||
//! The oaktask module exposes no getters for the C++ `Task::get_start_time`
|
||||
//! / `Task::is_cancelled` / `ProjectSaveTask::get_project`; those three are
|
||||
//! answered from facade-side state recorded at creation/cancel
|
||||
//! ([`TaskMeta`], see the per-export notes).
|
||||
//!
|
||||
//! String output follows the engine buf/size convention: the return value
|
||||
//! is the would-be length **excluding** the NUL. The module reports the
|
||||
//! size **including** the NUL, converted with
|
||||
//! [`crate::handle::string_result`]; module error codes (-80001..) pass
|
||||
//! through untranslated.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::bridge::codec::{EncodingParamsPOD, zeroed_encoding_params};
|
||||
use crate::bridge::node as n;
|
||||
use crate::bridge::task as t;
|
||||
use crate::codec::OakEngineEncodingParams;
|
||||
use crate::common::OakVideoParamsPod;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::handle::{
|
||||
box_handle, free_box, guard, guard_i64, guard_int, guard_ptr, string_result, unbox, CHandle,
|
||||
OakEngineClipboard, OakEngineNode, OakEngineProject, OakEngineSequence, OakEngineTask,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Facade-side task state
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Facade-side sidecars for tasks created through this module, keyed by the
|
||||
/// module task handle's `ctx` (the stable identity of the underlying task;
|
||||
/// see [`crate::handle::CHandle`]). Entries are dropped by
|
||||
/// [`oakengine_task_free`]; a task handed to the manager keeps its entry
|
||||
/// until free — the header forbids touching a borrowed handle after the
|
||||
/// task is removed, so an entry left behind by a manager-run task is an
|
||||
/// intentional, documented process-lifetime leak.
|
||||
#[derive(Clone)]
|
||||
struct TaskMeta {
|
||||
/// Epoch-millisecond creation stamp, returned by
|
||||
/// [`oakengine_task_start_time`] once the task has been started through
|
||||
/// the facade (the module has no start-time getter; the C++ reports the
|
||||
/// real `Task::get_start_time`).
|
||||
created_at_ms: u64,
|
||||
/// Whether the task was started through the facade
|
||||
/// (`oakengine_task_start_sync` / `oakengine_task_manager_add` /
|
||||
/// `oakengine_cli_task_dialog_run`).
|
||||
started: bool,
|
||||
/// Facade-initiated cancel flag (the module has no `is_cancelled`
|
||||
/// getter; only cancels made through this facade are visible).
|
||||
cancelled: bool,
|
||||
/// The project a save task writes (addref'd at creation, released at
|
||||
/// free) — the module has no save-project getter.
|
||||
save_project: Option<CHandle>,
|
||||
/// The encoding-params box an export task owns, dropped at free
|
||||
/// (mirrors the C++ `FacadeExportTask` destructor; stored as `usize` so
|
||||
/// the map stays `Send`).
|
||||
export_params: Option<usize>,
|
||||
/// The color manager an export task owns, released at free.
|
||||
export_color_manager: Option<CHandle>,
|
||||
}
|
||||
|
||||
impl TaskMeta {
|
||||
fn new() -> Self {
|
||||
TaskMeta {
|
||||
created_at_ms: now_millis(),
|
||||
started: false,
|
||||
cancelled: false,
|
||||
save_project: None,
|
||||
export_params: None,
|
||||
export_color_manager: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Epoch milliseconds (0 when the clock is before the epoch; never in
|
||||
/// practice).
|
||||
fn now_millis() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_millis() as u64)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
static META: OnceLock<Mutex<HashMap<usize, TaskMeta>>> = OnceLock::new();
|
||||
|
||||
fn meta_lock() -> std::sync::MutexGuard<'static, HashMap<usize, TaskMeta>> {
|
||||
META.get_or_init(|| Mutex::new(HashMap::new()))
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
}
|
||||
|
||||
fn meta_insert(key: usize, meta: TaskMeta) {
|
||||
meta_lock().insert(key, meta);
|
||||
}
|
||||
|
||||
fn meta_get(key: usize) -> Option<TaskMeta> {
|
||||
meta_lock().get(&key).cloned()
|
||||
}
|
||||
|
||||
fn meta_set_started(key: usize) {
|
||||
if let Some(m) = meta_lock().get_mut(&key) {
|
||||
m.started = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn meta_set_cancelled(key: usize) {
|
||||
if let Some(m) = meta_lock().get_mut(&key) {
|
||||
m.cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Release every facade-side sidecar of a task (called by
|
||||
/// [`oakengine_task_free`]): the addref'd save project, the owned
|
||||
/// encoding-params box and the derived color manager of an export task.
|
||||
fn drop_task_meta(key: usize) {
|
||||
if let Some(meta) = meta_lock().remove(&key) {
|
||||
if let Some(mut project) = meta.save_project {
|
||||
unsafe { n::oaknode_project_free(&mut project) };
|
||||
}
|
||||
if let Some(ptr) = meta.export_params {
|
||||
unsafe {
|
||||
crate::codec::oakengine_encoding_params_destroy(ptr as *mut OakEngineEncodingParams)
|
||||
};
|
||||
}
|
||||
if let Some(mut manager) = meta.export_color_manager {
|
||||
unsafe { n::oaknode_colormanager_free(&mut manager) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Box an owned module task handle as an engine handle, registering its
|
||||
/// facade sidecars. NULL/empty handles stay NULL.
|
||||
fn box_task(h: CHandle) -> *mut OakEngineTask {
|
||||
if h.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
meta_insert(h.ctx as usize, TaskMeta::new());
|
||||
box_handle::<OakEngineTask>(h)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global task manager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Lazily initialize the global task manager on first facade use
|
||||
/// (module-00 analogue of the C++ app-startup `TaskManager` creation; the
|
||||
/// same pattern as the undo family's `global_stack`). The manager lives for
|
||||
/// the process. `oaktask_manager_init` only fails when already initialized,
|
||||
/// which the `OnceLock` prevents, so this always succeeds.
|
||||
fn manager_ensure() -> Result<()> {
|
||||
static INIT: OnceLock<()> = OnceLock::new();
|
||||
let _ = INIT.get_or_init(|| unsafe {
|
||||
t::oaktask_manager_init();
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stable opaque token for `oakengine_task_manager_handle`: a boxed
|
||||
/// [`CHandle`] whose `ctx` is the address of a facade static (never
|
||||
/// dereferenced). The oaktask module exposes no manager handle, so the
|
||||
/// token exists purely to give the (out-of-scope, per README)
|
||||
/// `OAKENGINE_EVENT_TASK_MANAGER_*` subscription an ABI-ready handle. The
|
||||
/// box is leaked for the process, like the C++ `TaskManager::instance()`.
|
||||
fn manager_token() -> *mut c_void {
|
||||
static TOKEN: OnceLock<usize> = OnceLock::new();
|
||||
// Stored as `usize` so the `OnceLock` stays `Sync`.
|
||||
let boxed = TOKEN.get_or_init(|| {
|
||||
box_handle::<OakEngineTask>(CHandle {
|
||||
ctx: &MANAGER_TOKEN as *const u8 as *mut c_void,
|
||||
addref: None,
|
||||
release: None,
|
||||
abi_version: 0,
|
||||
}) as usize
|
||||
});
|
||||
*boxed as *mut OakEngineTask as *mut c_void
|
||||
}
|
||||
|
||||
static MANAGER_TOKEN: u8 = 0;
|
||||
|
||||
/// `oakengine_task_manager_handle` — borrowed token of the global task
|
||||
/// manager (NULL never: the facade initializes the manager lazily on first
|
||||
/// use, see [`manager_ensure`]; the C++ engine creates it at app startup).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_task_manager_handle() -> *mut c_void {
|
||||
guard_ptr(|| {
|
||||
manager_ensure()?;
|
||||
Ok(manager_token())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_manager_count` — number of tasks known to the manager
|
||||
/// (running plus failed-but-kept). The manager is created on first use, so
|
||||
/// the header's "no manager exists" state is unreachable (0 when empty).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_task_manager_count() -> c_int {
|
||||
guard_int(|| {
|
||||
manager_ensure()?;
|
||||
Ok(unsafe { t::oaktask_manager_count() })
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_manager_first` — borrowed handle of the manager's first
|
||||
/// task (NULL when the queue is empty).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_task_manager_first() -> *mut OakEngineTask {
|
||||
guard_ptr(|| {
|
||||
manager_ensure()?;
|
||||
let h = unsafe { t::oaktask_manager_at(0) };
|
||||
if h.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_handle::<OakEngineTask>(h))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_manager_add` — hand `task` to the manager queue
|
||||
/// (transfers ownership; the manager deletes the task when done). The
|
||||
/// module's `oaktask_task_start` performs the transfer; a task already
|
||||
/// running on the manager reports the module's `OAKTASK_E_STATE`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_manager_add(task: *mut OakEngineTask) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
manager_ensure()?;
|
||||
Error::from_module(t::oaktask_task_start(h))?;
|
||||
meta_set_started(h.ctx as usize);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_manager_cancel` — ask the manager to cancel `task`. The
|
||||
/// module's `oaktask_task_cancel` signals the running task's cancellation
|
||||
/// atom; the "failed-but-kept task is removed and deleted" half is managed
|
||||
/// by the module's own bookkeeping (`oaktask_manager_delete_finished`) and
|
||||
/// has no engine export, so it is not mirrored here.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_manager_cancel(task: *mut OakEngineTask) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
manager_ensure()?;
|
||||
Error::from_module(t::oaktask_task_cancel(h))?;
|
||||
meta_set_cancelled(h.ctx as usize);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task accessors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_task_title` (buf/size; E_INVALID for NULL).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_title(
|
||||
task: *mut OakEngineTask,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_task_title(h, buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_error` (buf/size; E_INVALID for NULL). Meaningful after
|
||||
/// a failed run.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_error(
|
||||
task: *mut OakEngineTask,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_task_error(h, buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_start_time` — start timestamp in epoch milliseconds.
|
||||
///
|
||||
/// The module has no start-time getter, so the facade reports the
|
||||
/// **creation** stamp (see [`TaskMeta`]) once the task has been started
|
||||
/// through the facade; 0 before then (matching "0 when the task never
|
||||
/// started"). Deviation from the C++ `Task::get_start_time`, which records
|
||||
/// the actual start instant.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_start_time(task: *mut OakEngineTask) -> i64 {
|
||||
guard_i64(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
Ok(match meta_get(h.ctx as usize) {
|
||||
Some(m) if m.started => m.created_at_ms as i64,
|
||||
_ => 0,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_is_cancelled` — 1 when the task was asked to cancel.
|
||||
///
|
||||
/// The module has no `is_cancelled` getter, so only cancels issued through
|
||||
/// [`oakengine_task_cancel`] / [`oakengine_task_manager_cancel`] on this
|
||||
/// facade are visible (0 otherwise). Deviation from the C++
|
||||
/// `Task::is_cancelled`, which reflects the task's own cancellation atom.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_is_cancelled(task: *mut OakEngineTask) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
Ok(if meta_get(h.ctx as usize).map(|m| m.cancelled).unwrap_or(false) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_cancel` — signal the task to cancel as soon as possible
|
||||
/// (module `Task::cancel`, the `Task::Cancel` analogue).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_cancel(task: *mut OakEngineTask) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
Error::from_module(t::oaktask_task_cancel(h))?;
|
||||
meta_set_cancelled(h.ctx as usize);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_start_sync` — run on the calling thread; 1 = succeeded,
|
||||
/// 0 = failed or cancelled, E_INVALID for NULL. Ownership stays with the
|
||||
/// caller.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_start_sync(task: *mut OakEngineTask) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_task_start_sync(h);
|
||||
meta_set_started(h.ctx as usize);
|
||||
Ok(rc)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_free` — delete a task that was never added to the
|
||||
/// manager (releases the module task handle, which drops an owned task).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_free(task: *mut OakEngineTask) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if task.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let h = (*task).handle;
|
||||
if h.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
drop_task_meta(h.ctx as usize);
|
||||
free_box::<OakEngineTask>(task);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_cli_task_dialog_run` — run `task` through the engine's CLI
|
||||
/// modal progress dialog; 1 on success, 0 on failure/cancellation.
|
||||
///
|
||||
/// The C++ `CLITaskDialog` renders a terminal progress dialog around a
|
||||
/// synchronous run; the facade ports the observable behavior (sync run,
|
||||
/// 1/0 result) with the dialog chrome itself stubbed. `parent` is unused.
|
||||
/// The capi returns 0 (not E_INVALID) for a NULL task, so this mirrors it.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_cli_task_dialog_run(
|
||||
task: *mut OakEngineTask,
|
||||
_parent_or_null: *mut c_void,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
if task.is_null() {
|
||||
return Ok(0);
|
||||
}
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_task_start_sync(h);
|
||||
meta_set_started(h.ctx as usize);
|
||||
Ok(rc)
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task creators (all return OWNED tasks, NULL on invalid input)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_task_create_project_load` — task that loads an OVE project
|
||||
/// from `filename`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_create_project_load(
|
||||
filename: *const c_char,
|
||||
) -> *mut OakEngineTask {
|
||||
guard_ptr(|| unsafe {
|
||||
if filename.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_task(t::oaktask_create_project_load(filename)))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_create_project_load_otio` — task that loads an
|
||||
/// OpenTimelineIO project. The module always supports OTIO (the interchange
|
||||
/// format is inferred from the filename extension), so valid input never
|
||||
/// yields the header's "built without OTIO support" NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_create_project_load_otio(
|
||||
filename: *const c_char,
|
||||
) -> *mut OakEngineTask {
|
||||
guard_ptr(|| unsafe {
|
||||
if filename.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_task(t::oaktask_create_project_load_otio(filename)))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_create_project_save` — task that saves `project`.
|
||||
///
|
||||
/// `use_compression` selects the compressed `.ove` writer; `override_filename`
|
||||
/// may be NULL to save to the project's own filename. `layout` (an opaque
|
||||
/// `SerializedLayoutInfo *` in the engine) is **ignored**: the module's
|
||||
/// `ProjectSaveTask` has no layout slot, so a non-NULL layout is accepted
|
||||
/// but not copied into the file.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_create_project_save(
|
||||
project: *mut OakEngineProject,
|
||||
use_compression: c_int,
|
||||
override_filename: *const c_char,
|
||||
_layout: *const c_void,
|
||||
) -> *mut OakEngineTask {
|
||||
guard_ptr(|| unsafe {
|
||||
let ph = unbox(project)?;
|
||||
let h = t::oaktask_create_project_save(ph, override_filename, use_compression);
|
||||
if h.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
// Keep the project borrowed for the task's lifetime so
|
||||
// `oakengine_task_save_get_project` can answer from facade state
|
||||
// (the module has no save-project getter).
|
||||
let mut meta = TaskMeta::new();
|
||||
meta.save_project = Some(ph.addref());
|
||||
meta_insert(h.ctx as usize, meta);
|
||||
Ok(box_handle::<OakEngineTask>(h))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_create_project_save_otio` — task that saves `project` in
|
||||
/// OpenTimelineIO format.
|
||||
///
|
||||
/// The engine header passes only the project, but the module's creator
|
||||
/// requires the output filename; the facade derives it from the project's
|
||||
/// own filename (the OTIO save of the current project file) and returns
|
||||
/// NULL when the project has no filename. The module always supports OTIO,
|
||||
/// so a valid input never yields the "built without OTIO support" NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_create_project_save_otio(
|
||||
project: *mut OakEngineProject,
|
||||
) -> *mut OakEngineTask {
|
||||
guard_ptr(|| unsafe {
|
||||
let ph = unbox(project)?;
|
||||
let filename = project_filename_of(ph)?;
|
||||
if filename.is_empty() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let c_filename =
|
||||
std::ffi::CString::new(filename).map_err(|_| Error::Failed("invalid filename".into()))?;
|
||||
Ok(box_task(t::oaktask_create_project_save_otio(ph, c_filename.as_ptr())))
|
||||
})
|
||||
}
|
||||
|
||||
/// Two-stage read of the project's filename (empty when unset).
|
||||
fn project_filename_of(project: CHandle) -> Result<String> {
|
||||
let needed = unsafe { n::oaknode_project_filename(project, std::ptr::null_mut(), 0) };
|
||||
if needed <= 0 {
|
||||
return Ok(String::new());
|
||||
}
|
||||
let mut buf = vec![0 as c_char; needed as usize];
|
||||
let rc = unsafe { n::oaknode_project_filename(project, buf.as_mut_ptr(), needed) };
|
||||
if rc < 0 {
|
||||
return Err(Error::Module(rc));
|
||||
}
|
||||
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
|
||||
Ok(String::from_utf8_lossy(unsafe {
|
||||
std::slice::from_raw_parts(buf.as_ptr() as *const u8, len)
|
||||
})
|
||||
.into_owned())
|
||||
}
|
||||
|
||||
/// `oakengine_task_create_project_import` — task that imports `url_count`
|
||||
/// media files into `folder` (a folder node of the target project).
|
||||
///
|
||||
/// The URL array is copied by the module during the call. The engine header
|
||||
/// passes only the folder; the module creator needs the owning project,
|
||||
/// derived here via `oaknode_node_get_project`. Unlike the capi (which
|
||||
/// rejects `url_count <= 0`), a zero-count task IS created — the header's
|
||||
/// `oakengine_task_import_file_count` documents 0 as "nothing to import,
|
||||
/// free instead of run". `url_count < 0`, a NULL URL inside the array, or a
|
||||
/// folder with no project yield NULL.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_create_project_import(
|
||||
folder: *mut OakEngineNode,
|
||||
urls: *const *const c_char,
|
||||
url_count: c_int,
|
||||
) -> *mut OakEngineTask {
|
||||
guard_ptr(|| unsafe {
|
||||
let fh = unbox(folder)?;
|
||||
if url_count < 0 || (urls.is_null() && url_count > 0) {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let mut project = CHandle::null();
|
||||
Error::from_module(n::oaknode_node_get_project(fh, &mut project))?;
|
||||
if project.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let h = t::oaktask_create_project_import(fh, project, urls, url_count);
|
||||
// Release the transient borrowed project handle (the import task
|
||||
// keeps its own copy).
|
||||
n::oaknode_project_free(&mut project);
|
||||
Ok(box_task(h))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_create_proxy` — **not backed** (stub, always NULL).
|
||||
///
|
||||
/// The oaktask crate's `ProxyTask` is driven by a codec task request and no
|
||||
/// proxy-task creator exists on the module C ABI (`oaktask_create_precache`
|
||||
/// is a different task). The engine's `FacadeProxyTask` would need
|
||||
/// `oakengine_footage_proxy_generate`, which lives in the deferred exporter
|
||||
/// family (see `deferred.rs`). Returns NULL per the creators' "NULL on
|
||||
/// invalid input" contract.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_create_proxy(
|
||||
_footage: *mut OakEngineNode,
|
||||
) -> *mut OakEngineTask {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
/// `oakengine_task_create_export` — task that renders an export of
|
||||
/// `sequence` with `params`.
|
||||
///
|
||||
/// Takes ownership of `params` (destroyed with the task, mirroring the C++
|
||||
/// `FacadeExportTask` destructor; the module copies the POD it needs at
|
||||
/// creation, so the retained box is a lifetime guarantee for C callers).
|
||||
/// The color manager is derived from the sequence's owning project
|
||||
/// (`oaknode_colormanager_init`), mirroring how the C++ exporter obtains
|
||||
/// its manager; a sequence without a project exports with an empty manager.
|
||||
/// The module creator requires a POD pointer, so the facade's opaque params
|
||||
/// handle is copied out through the public `oakengine_encoding_params_*`
|
||||
/// getters ([`export_params_pod`]) — its backing `ParamsBox` (POD + option
|
||||
/// map) is private to `codec.rs` and cannot be read here.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_create_export(
|
||||
sequence: *mut OakEngineSequence,
|
||||
params: *mut OakEngineEncodingParams,
|
||||
) -> *mut OakEngineTask {
|
||||
guard_ptr(|| unsafe {
|
||||
let vh = unbox(sequence)?;
|
||||
if params.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let pod = export_params_pod(params)?;
|
||||
let color_manager = export_color_manager(vh)?;
|
||||
let h = t::oaktask_create_export(vh, color_manager, &pod);
|
||||
if h.is_null() {
|
||||
// Creation failed: release the color manager we derived.
|
||||
let mut manager = color_manager;
|
||||
n::oaknode_colormanager_free(&mut manager);
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
let mut meta = TaskMeta::new();
|
||||
meta.export_params = Some(params as usize);
|
||||
meta.export_color_manager = Some(color_manager);
|
||||
meta_insert(h.ctx as usize, meta);
|
||||
Ok(box_handle::<OakEngineTask>(h))
|
||||
})
|
||||
}
|
||||
|
||||
/// Copy the encoding-params POD the oaktask export creator reads out of the
|
||||
/// facade's opaque params handle via its public getters.
|
||||
///
|
||||
/// The oaktask crate's `convert_encoding_params` consumes exactly these
|
||||
/// fields (filename, format, video/audio/subtitle enables, codecs,
|
||||
/// dimensions, time base, pixel format, export length), so a POD carrying
|
||||
/// them is behaviorally identical to the original for the export task; all
|
||||
/// other POD fields stay zeroed.
|
||||
fn export_params_pod(params: *const OakEngineEncodingParams) -> Result<EncodingParamsPOD> {
|
||||
let mut pod = zeroed_encoding_params();
|
||||
|
||||
// filename (two-stage; writes NUL-terminated into `buf`)
|
||||
let mut buf = [0 as c_char; 1024];
|
||||
let rc = unsafe {
|
||||
crate::codec::oakengine_encoding_params_filename(params, buf.as_mut_ptr(), buf.len() as c_int)
|
||||
};
|
||||
if rc < 0 {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
|
||||
unsafe { std::ptr::copy_nonoverlapping(buf.as_ptr() as *const u8, pod.filename.as_mut_ptr(), len) };
|
||||
|
||||
pod.format = unsafe { crate::codec::oakengine_encoding_params_format(params) };
|
||||
pod.video_enabled = unsafe { crate::codec::oakengine_encoding_params_video_enabled(params) };
|
||||
pod.video_codec = unsafe { crate::codec::oakengine_encoding_params_video_codec(params) };
|
||||
pod.audio_enabled = unsafe { crate::codec::oakengine_encoding_params_audio_enabled(params) };
|
||||
pod.audio_codec = unsafe { crate::codec::oakengine_encoding_params_audio_codec(params) };
|
||||
pod.subtitles_enabled = unsafe {
|
||||
crate::codec::oakengine_encoding_params_subtitles_enabled(params)
|
||||
};
|
||||
unsafe {
|
||||
crate::codec::oakengine_encoding_params_get_export_length(
|
||||
params,
|
||||
&mut pod.export_length_num,
|
||||
&mut pod.export_length_den,
|
||||
);
|
||||
}
|
||||
|
||||
if pod.video_enabled != 0 {
|
||||
let mut video = std::mem::MaybeUninit::<OakVideoParamsPod>::uninit();
|
||||
let rc = unsafe {
|
||||
crate::codec::oakengine_encoding_params_get_video_params(params, video.as_mut_ptr())
|
||||
};
|
||||
if rc == 0 {
|
||||
let v = unsafe { video.assume_init() };
|
||||
pod.video_width = v.width;
|
||||
pod.video_height = v.height;
|
||||
pod.video_time_base_num = v.time_base_num;
|
||||
pod.video_time_base_den = v.time_base_den;
|
||||
pod.video_pixel_format = v.format;
|
||||
}
|
||||
}
|
||||
Ok(pod)
|
||||
}
|
||||
|
||||
/// Derive a color manager for an export task from the sequence's owning
|
||||
/// project (borrowed project handle released after the manager is created).
|
||||
/// Empty when the sequence has no project — the module export accepts an
|
||||
/// empty manager.
|
||||
fn export_color_manager(sequence: CHandle) -> Result<CHandle> {
|
||||
let mut project = CHandle::null();
|
||||
Error::from_module(unsafe { n::oaknode_node_get_project(sequence, &mut project) })?;
|
||||
if project.is_null() {
|
||||
return Ok(CHandle::null());
|
||||
}
|
||||
let manager = unsafe { n::oaknode_colormanager_init(project) };
|
||||
unsafe { n::oaknode_project_free(&mut project) };
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Import task results
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_task_import_file_count` — number of files the import task
|
||||
/// will process.
|
||||
///
|
||||
/// The module's only import count export is `oaktask_import_footage_count`,
|
||||
/// which reports the **imported-footage** list length — 0 before the task
|
||||
/// runs even when files were supplied. Deviation from the C++
|
||||
/// `get_file_count` (the construction-time count); "0 means nothing to
|
||||
/// import yet" holds before a run either way.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_import_file_count(task: *mut OakEngineTask) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_import_footage_count(h);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(rc)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_import_get_command` — the undo command built by a
|
||||
/// successful import run as an opaque `OakEngineClipboard` (NULL before the
|
||||
/// run, after a cancelled run, or on a second call). Ownership detaches
|
||||
/// from the task; push it with `oakengine_undo_push` or free it.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_import_get_command(task: *mut OakEngineTask) -> *mut c_void {
|
||||
guard_ptr(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let cmd = t::oaktask_import_take_command(h);
|
||||
if cmd.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_handle::<OakEngineClipboard>(cmd).cast())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_import_footage_count` — number of footage items a
|
||||
/// successful import run created (the module's `oaktask_import_footage_count`,
|
||||
/// the same count reported by `oakengine_task_import_file_count`).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_import_footage_count(task: *mut OakEngineTask) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_import_footage_count(h);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(rc)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_import_footage_at` — borrowed node handle of the
|
||||
/// imported footage at `index` (NULL when out of range or not an import
|
||||
/// task). The module addrefs the footage handle; the caller releases it
|
||||
/// with `oakengine_node_free`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_import_footage_at(
|
||||
task: *mut OakEngineTask,
|
||||
index: c_int,
|
||||
) -> *mut OakEngineNode {
|
||||
guard_ptr(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let fh = t::oaktask_import_footage_at(h, index);
|
||||
if fh.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_handle::<OakEngineNode>(fh))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_import_invalid_files_count` — number of files the import
|
||||
/// task rejected.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_import_invalid_files_count(
|
||||
task: *mut OakEngineTask,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_import_invalid_count(h);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(rc)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_task_import_invalid_file_at` — rejected file path at `index`
|
||||
/// (buf/size). Out-of-range reports the module's `OAKTASK_E_NOT_FOUND`
|
||||
/// (-80004) pass-through, the header's "E_INVALID for other tasks" being
|
||||
/// covered by the NULL-task `OAKENGINE_E_INVALID` path.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_import_invalid_file_at(
|
||||
task: *mut OakEngineTask,
|
||||
index: c_int,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
guard_int(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
let rc = t::oaktask_import_invalid_at(h, index, buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Save task results
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `oakengine_task_save_get_project` — borrowed handle of the project a
|
||||
/// save task wrote (NULL for other tasks).
|
||||
///
|
||||
/// The module has no save-project getter, so the project is kept borrowed
|
||||
/// from creation in [`TaskMeta`] (mirroring the C++ `ProjectSaveTask::
|
||||
/// get_project`, which returns the project the task was created with).
|
||||
/// Each call returns a fresh borrowed handle the caller releases with
|
||||
/// `oakengine_project_free`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_task_save_get_project(task: *mut OakEngineTask) -> *mut OakEngineProject {
|
||||
guard_ptr(|| unsafe {
|
||||
let h = unbox(task)?;
|
||||
match meta_get(h.ctx as usize).and_then(|m| m.save_project) {
|
||||
Some(p) => Ok(box_handle::<OakEngineProject>(p.addref())),
|
||||
None => Ok(std::ptr::null_mut()),
|
||||
}
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,402 @@
|
||||
// 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/>.
|
||||
|
||||
//! `engine/include/oakengine/undo.h` — the process-wide undo stack,
|
||||
//! undo groups and command lifecycle over the oakundo module.
|
||||
//!
|
||||
//! The facade owns the process-wide undo stack (module 00 analogue of
|
||||
//! `EngineCore::undo_stack()`): it is created lazily on first use and
|
||||
//! lives for the process (mirroring the C++ EngineCore shell, which is
|
||||
//! also leaked intentionally). The open undo group is facade state too:
|
||||
//! while a group is open, every command a wrapped family hands to
|
||||
//! [`push_or_run`] is added to the group instead of the stack.
|
||||
//!
|
||||
//! Command creators declared in undo.h but backed by other modules
|
||||
//! (`oakengine_node_*_command`, `oakengine_track_*_command`,
|
||||
//! `oakengine_block_*_command`, `oakengine_timeline_*_command`) live in
|
||||
//! the corresponding family modules, mirroring the C++ capi layout.
|
||||
|
||||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use crate::bridge::undo as u;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::handle::{
|
||||
box_handle, free_box, guard, guard_void, unbox, CHandle, OakEngineClipboard,
|
||||
};
|
||||
|
||||
/// The process-wide undo stack handle (oakundo `OakUndoStack`), created
|
||||
/// lazily and kept for the process lifetime.
|
||||
fn global_stack() -> &'static CHandle {
|
||||
static STACK: OnceLock<CHandle> = OnceLock::new();
|
||||
STACK.get_or_init(|| unsafe { u::oakundo_undostack_init() })
|
||||
}
|
||||
|
||||
/// Stable opaque token for `oakengine_undo_handle`: the module stack's
|
||||
/// `ctx` pointer (never dereferenced by the facade; lives for the
|
||||
/// process).
|
||||
fn stack_token() -> *mut c_void {
|
||||
global_stack().ctx
|
||||
}
|
||||
|
||||
/// The currently open undo group (a multi command handle) plus its name.
|
||||
struct OpenGroup {
|
||||
/// Multi command handle; owned by this state until end/abort.
|
||||
multi: CHandle,
|
||||
/// Group label.
|
||||
#[allow(dead_code)]
|
||||
name: String,
|
||||
}
|
||||
|
||||
static GROUP: Mutex<Option<OpenGroup>> = Mutex::new(None);
|
||||
|
||||
fn group_lock() -> std::sync::MutexGuard<'static, Option<OpenGroup>> {
|
||||
GROUP.lock().unwrap_or_else(|e| e.into_inner())
|
||||
}
|
||||
|
||||
/// Push `command` onto the stack, add it to the open group, or run it
|
||||
/// directly — whichever applies (module 00 analogue of the C++ capi's
|
||||
/// `oakengine_undo_push_or_run`). `command_box` is consumed.
|
||||
///
|
||||
/// # Safety
|
||||
/// `command_box` must be a live box created by a facade command creator.
|
||||
pub(crate) unsafe fn push_or_run(command_box: *mut OakEngineClipboard, name: *const c_char) -> Result<()> {
|
||||
let cmd = unsafe { unbox(command_box)? };
|
||||
let label = unsafe { crate::handle::read_cstr(name) };
|
||||
let g = group_lock();
|
||||
if let Some(group) = g.as_ref() {
|
||||
// The module's `oakundo_command_multi_add_child` consumes the
|
||||
// child's command value (command_take), so the eager redo must
|
||||
// happen on the still-owned handle FIRST — the group takes the
|
||||
// already-done command (C++ semantics: add_child + redo_now, net
|
||||
// effect identical for the group's reverse-order undo).
|
||||
let rc = unsafe { u::oakundo_command_redo_now(cmd) };
|
||||
if rc != 0 {
|
||||
return Err(Error::Module(rc));
|
||||
}
|
||||
let rc = unsafe { u::oakundo_command_multi_add_child(group.multi, cmd) };
|
||||
drop(g);
|
||||
unsafe { free_box(command_box) };
|
||||
return if rc == 0 { Ok(()) } else { Err(Error::Module(rc)) };
|
||||
}
|
||||
let stack = *global_stack();
|
||||
let rc = unsafe { u::oakundo_undostack_push(stack, cmd, label.as_ptr() as *const c_char) };
|
||||
if rc == 0 {
|
||||
// Stack took a reference; release ours by freeing the box.
|
||||
unsafe { free_box(command_box) };
|
||||
Ok(())
|
||||
} else {
|
||||
// Push failed (e.g. empty multi): the module deleted the command;
|
||||
// release the box shell without touching the (already consumed)
|
||||
// handle.
|
||||
unsafe { free_box(command_box) };
|
||||
Err(Error::Module(rc))
|
||||
}
|
||||
}
|
||||
|
||||
/// `oakengine_undo_handle` — borrowed token of the global undo stack
|
||||
/// (NULL never: the facade creates the stack lazily).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_handle() -> *mut c_void {
|
||||
crate::handle::guard_ptr(|| Ok(stack_token()))
|
||||
}
|
||||
|
||||
/// `oakengine_undo_push` — push `command` onto the stack and execute its
|
||||
/// redo (or add it to the open group). Takes ownership of `command`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_push(command: *mut c_void, name: *const c_char) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if command.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
push_or_run(command.cast::<OakEngineClipboard>(), name)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_group_begin` — start collecting commands into a group.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_group_begin(name: *const c_char) -> c_int {
|
||||
guard(|| {
|
||||
let mut g = group_lock();
|
||||
if g.is_some() {
|
||||
return Err(Error::State);
|
||||
}
|
||||
let multi = unsafe { u::oakundo_command_init_multi() };
|
||||
if multi.is_null() {
|
||||
return Err(Error::Failed("undo group allocation failed".into()));
|
||||
}
|
||||
*g = Some(OpenGroup {
|
||||
multi,
|
||||
name: unsafe { crate::handle::read_cstr(name) },
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_group_end` — close the group and push it as one entry.
|
||||
/// An empty group is discarded (no undo entry).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_group_end() -> c_int {
|
||||
guard(|| {
|
||||
let mut g = group_lock();
|
||||
let open = g.take().ok_or(Error::State)?;
|
||||
let multi = open.multi;
|
||||
let name = open.name;
|
||||
drop(g);
|
||||
// push_pre_executed discards an empty multi command. Either way
|
||||
// the stack took (or destroyed) the command; release our own
|
||||
// reference to the multi handle.
|
||||
let stack = *global_stack();
|
||||
let rc = unsafe {
|
||||
u::oakundo_undostack_push_pre_executed(stack, multi, name.as_ptr() as *const c_char)
|
||||
};
|
||||
let mut multi_handle = multi;
|
||||
unsafe { u::oakundo_command_free(&mut multi_handle) };
|
||||
if rc == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Module(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_group_abort` — undo all executed children and discard
|
||||
/// the group.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_group_abort() -> c_int {
|
||||
guard(|| {
|
||||
let mut g = group_lock();
|
||||
let open = g.take().ok_or(Error::State)?;
|
||||
drop(g);
|
||||
let rc = unsafe { u::oakundo_command_undo_now(open.multi) };
|
||||
if rc != 0 {
|
||||
return Err(Error::Module(rc));
|
||||
}
|
||||
let mut multi = open.multi;
|
||||
unsafe { u::oakundo_command_free(&mut multi) };
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_redo_now` — execute the redo of `command`
|
||||
/// without taking ownership.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_command_redo_now(command: *mut c_void) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let cmd = unbox(command.cast::<OakEngineClipboard>())?;
|
||||
Error::from_module(u::oakundo_command_redo_now(cmd))
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_undo_now` — execute the undo of `command`
|
||||
/// without taking ownership.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_command_undo_now(command: *mut c_void) -> c_int {
|
||||
guard(|| unsafe {
|
||||
let cmd = unbox(command.cast::<OakEngineClipboard>())?;
|
||||
Error::from_module(u::oakundo_command_undo_now(cmd))
|
||||
})
|
||||
}
|
||||
|
||||
/// Engine-side callback types for app-defined undo commands
|
||||
/// (`engine/include/oakengine/undo.h`).
|
||||
type UndoRedoFn = unsafe extern "C" fn(userdata: *mut c_void);
|
||||
type UndoFreeFn = unsafe extern "C" fn(userdata: *mut c_void);
|
||||
|
||||
/// `oakengine_undo_command_create` — create an app-defined undo command
|
||||
/// backed by C callbacks. Takes ownership of `userdata`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_command_create(
|
||||
name: *const c_char,
|
||||
redo: Option<UndoRedoFn>,
|
||||
undo: Option<UndoRedoFn>,
|
||||
free_fn: Option<UndoFreeFn>,
|
||||
userdata: *mut c_void,
|
||||
) -> *mut c_void {
|
||||
crate::handle::guard_ptr(|| unsafe {
|
||||
let _ = crate::handle::read_cstr(name);
|
||||
let vtable = crate::bridge::undo::OakUndoCommandVtable { redo, undo, free_fn };
|
||||
let cmd = u::oakundo_command_init(&vtable, userdata);
|
||||
if cmd.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_handle::<OakEngineClipboard>(cmd).cast())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_create_multi` — create an empty
|
||||
/// MultiUndoCommand as an opaque command pointer.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_command_create_multi() -> *mut c_void {
|
||||
crate::handle::guard_ptr(|| {
|
||||
let cmd = unsafe { u::oakundo_command_init_multi() };
|
||||
if cmd.is_null() {
|
||||
return Ok(std::ptr::null_mut());
|
||||
}
|
||||
Ok(box_handle::<OakEngineClipboard>(cmd).cast())
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_multi_add_child` — add `child` to `multi`
|
||||
/// (the multi takes one reference; `child`'s box is consumed).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_command_multi_add_child(
|
||||
multi: *mut c_void,
|
||||
child: *mut c_void,
|
||||
) -> c_int {
|
||||
guard(|| unsafe {
|
||||
if multi.is_null() || child.is_null() {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
let m = unbox(multi.cast::<OakEngineClipboard>())?;
|
||||
let c = unbox(child.cast::<OakEngineClipboard>())?;
|
||||
let rc = u::oakundo_command_multi_add_child(m, c);
|
||||
free_box(child.cast::<OakEngineClipboard>());
|
||||
if rc == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Module(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_multi_child_count` — children of `multi`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_command_multi_child_count(multi: *mut c_void) -> c_int {
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
let m = unbox(multi.cast::<OakEngineClipboard>())?;
|
||||
let mut count: c_int = 0;
|
||||
Error::from_module(u::oakundo_command_multi_child_count(m, &mut count))?;
|
||||
Ok(count)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_free` — destroy a command without pushing it.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_command_free(command: *mut c_void) {
|
||||
guard_void(|| unsafe {
|
||||
free_box(command.cast::<OakEngineClipboard>());
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_count` — total number of history rows.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_count() -> i64 {
|
||||
crate::handle::guard_i64(|| unsafe {
|
||||
let mut count: i64 = 0;
|
||||
Error::from_module(u::oakundo_undostack_count(*global_stack(), &mut count))?;
|
||||
Ok(count)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_index` — current position in the history.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_index() -> i64 {
|
||||
crate::handle::guard_i64(|| unsafe {
|
||||
let mut index: i64 = 0;
|
||||
Error::from_module(u::oakundo_undostack_index(*global_stack(), &mut index))?;
|
||||
Ok(index)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_text` — label of the row at `row`
|
||||
/// (buf/size; OAKENGINE_E_NOT_FOUND for an invalid row).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn oakengine_undo_command_text(
|
||||
row: i64,
|
||||
buf: *mut c_char,
|
||||
buf_size: c_int,
|
||||
) -> c_int {
|
||||
// The oakundo getter is itself two-stage: it reports the required
|
||||
// size when `buf` is NULL/too small and copies otherwise, so the
|
||||
// module return value is returned verbatim (guarded against panic),
|
||||
// converted to the engine's length-excluding-NUL convention.
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
let rc = u::oakundo_undostack_command_text(*global_stack(), row, buf, buf_size);
|
||||
if rc < 0 {
|
||||
Err(Error::Module(rc))
|
||||
} else {
|
||||
Ok(crate::handle::string_result(rc))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_command_is_done` — 1 when the row is done, 0 when
|
||||
/// undone, OAKENGINE_E_NOT_FOUND for an invalid row.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_command_is_done(row: i64) -> c_int {
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
let mut value: c_int = 0;
|
||||
Error::from_module(u::oakundo_undostack_command_is_done(*global_stack(), row, &mut value))?;
|
||||
Ok(value)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_jump` — undo/redo until the done-command count equals
|
||||
/// `index`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_jump(index: i64) -> c_int {
|
||||
guard(|| unsafe { Error::from_module(u::oakundo_undostack_jump(*global_stack(), index)) })
|
||||
}
|
||||
|
||||
/// `oakengine_undo_clear` — delete all commands and push the fresh
|
||||
/// "New/Open Project" empty command.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_clear() -> c_int {
|
||||
guard(|| unsafe { Error::from_module(u::oakundo_undostack_clear(*global_stack())) })
|
||||
}
|
||||
|
||||
/// `oakengine_undo_update_actions` — no-op: the QAction members were
|
||||
/// removed in the de-Qt pass (see notes.md), the app builds its own
|
||||
/// undo/redo actions from `oakengine_undo_can_undo/redo`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_update_actions() -> c_int {
|
||||
crate::error::OAKENGINE_OK
|
||||
}
|
||||
|
||||
/// `oakengine_undo_can_undo` — 1/0.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_can_undo() -> c_int {
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
let mut value: c_int = 0;
|
||||
Error::from_module(u::oakundo_undostack_can_undo(*global_stack(), &mut value))?;
|
||||
Ok(value)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_can_redo` — 1/0.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_can_redo() -> c_int {
|
||||
crate::handle::guard_int(|| unsafe {
|
||||
let mut value: c_int = 0;
|
||||
Error::from_module(u::oakundo_undostack_can_redo(*global_stack(), &mut value))?;
|
||||
Ok(value)
|
||||
})
|
||||
}
|
||||
|
||||
/// `oakengine_undo_undo_action` — Qt leftover: the de-Qt module world has
|
||||
/// no QAction; returns NULL. The app builds its own action.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_undo_action() -> *mut c_void {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
/// `oakengine_undo_redo_action` — Qt leftover; returns NULL (see
|
||||
/// `oakengine_undo_undo_action`).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn oakengine_undo_redo_action() -> *mut c_void {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user