chore(crates): retire oakengine facade, drop oakcommon handle module

- crates/oakengine moved to crates/oakengine.bk (excluded from the
  workspace): the frozen C-ABI cdylib had no in-workspace consumers
  left after the direct-rlib migration (M14); git history is the
  authoritative backup.
- oakcommon: remove the CHandle module (no remaining users); config
  store and shared value types are unaffected.
This commit is contained in:
2026-08-18 21:40:35 +08:00
parent f12cf3ffef
commit d61acb9e0a
122 changed files with 6 additions and 64745 deletions
Generated
-24
View File
@@ -4823,30 +4823,6 @@ dependencies = [
name = "oakcore-rs"
version = "0.1.0"
[[package]]
name = "oakengine"
version = "0.1.0"
dependencies = [
"libc",
"oakaudio",
"oakcodec",
"oakcommon",
"oakcore-rs",
"oaknode",
"oakplugin",
"oakrender",
"oakstorage",
"oaktask",
"oaktimeline",
"oakundo",
"parking_lot",
"sea-orm",
"serde",
"serde_json",
"thiserror 2.0.20",
"tokio",
]
[[package]]
name = "oakffmpeg-link"
version = "0.1.0"
+6 -9
View File
@@ -26,20 +26,17 @@
# workspace root, exactly as before the monorepo workspace existed.
[workspace]
members = ["crates/*"]
exclude = ["gpui"]
exclude = ["gpui", "crates/oakengine.bk"]
# NOTE: oakstorage (crates/oakstorage) is a workspace member but NOT a
# default member (it stays out of the default-members test matrix to keep
# `cargo test` at the root fast; the app links it as a normal path
# dependency, so it builds with the app). Build/test it explicitly with
# `cargo test -p oakstorage`.
# NOTE: `crates/oakengine` is deliberately NOT a default member (it stays a
# workspace member, so `cargo test -p oakengine` / `cargo test --workspace`
# still run its tests): it is the plugin/external-consumer cdylib (M14 R4) —
# no crate in the workspace links it (app/cli/worker call the module rlibs
# directly), so default builds skip it. Its in-crate unit tests
# (src/test_support/, the former tests/*.rs) share temp files and
# process-global facade state, which makes the parallel default-members run
# flaky.
# NOTE: `crates/oakengine` (the frozen C-ABI facade cdylib) is retired:
# every consumer (app/cli/worker/plugins) links the module rlibs
# directly, so nothing in the workspace referenced it. The sources are
# kept at crates/oakengine.bk (excluded from the workspace) as a
# reference snapshot; git history is the authoritative backup.
default-members = [".", "crates/oak-cli", "crates/oak-worker"]
resolver = "2"
-1
View File
@@ -29,7 +29,6 @@ pub mod debug;
pub mod error;
pub mod ffmpegutils;
pub mod filefunctions;
pub mod handle;
pub mod miscutils;
pub mod ocioutils;
pub mod oiioutils;
-38
View File
@@ -27,7 +27,6 @@ use oakcommon::error::{
OAKCOMMON_E_STATE, OAKCOMMON_OK,
};
use oakcommon::ffmpegutils::{RGBA_CHANNEL_COUNT, RGB_CHANNEL_COUNT};
use oakcommon::handle::{CHandle, OAKCOMMON_ABI_VERSION};
use oakcommon::miscutils::{DropWorkflowBehavior, LoopMode, DECIBEL_MINIMUM};
use oakcommon::ocioutils::PixelFormat;
use oakcommon::videoparams::{ColorRange, Interlacing, VideoType};
@@ -44,21 +43,9 @@ fn error_codes_match_header() {
}
/// Handle ABI version must match `include/common/handle.h`.
#[test]
fn handle_abi_version() {
assert_eq!(OAKCOMMON_ABI_VERSION, 1);
}
/// The handle struct must be a plain `{ctx, addref, release, abi_version}`
/// `#[repr(C)]` record: 3 pointers + a u32, padded to pointer alignment.
#[test]
fn handle_layout() {
let ptr = size_of::<*const ()>();
let align = align_of::<*const ()>();
let expected = (3 * ptr + size_of::<u32>()).div_ceil(align) * align;
assert_eq!(size_of::<CHandle>(), expected);
assert_eq!(align_of::<CHandle>(), align);
}
/// Pixel-format codes must match `olive::core::PixelFormat`.
#[test]
@@ -124,28 +111,3 @@ fn color_range_discriminants() {
assert_eq!(ColorRange::Limited as i32, 0);
assert_eq!(ColorRange::Full as i32, 1);
}
/// The public type names must exist and be usable at their intended ABI
/// shape (compile-time contract).
#[test]
fn public_types_exist() {
// Enums are plain C-like int enums.
let _ = PixelFormat::U8;
let _ = Interlacing::TopFirst;
let _ = VideoType::Still;
let _ = ColorRange::Full;
let _ = LoopMode::Loop;
let _ = DropWorkflowBehavior::Ask;
// The handle is a plain struct constructible without a panic.
let h = CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: 0,
};
assert!(h.ctx.is_null());
assert!(h.addref.is_none());
assert!(h.release.is_none());
assert_eq!(h.abi_version, 0);
}
-1745
View File
File diff suppressed because it is too large Load Diff
-70
View File
@@ -1,70 +0,0 @@
[package]
name = "oakengine"
version = "0.1.0"
edition = "2021"
description = "Oak Video Editor plugin/external-C-ABI layer: the frozen oakengine_* C ABI as a pure cdylib over the module crates' direct Rust APIs"
license = "GPL-3.0-or-later"
# Pure cdylib (M14 R4): the only consumers are OFX plugins and external
# C-ABI embedders. No Rust crate depends on this crate, so no rlib/staticlib
# artifact is produced and it stays out of the workspace default-members.
[lib]
crate-type = ["cdylib"]
[dependencies]
# Error derive (Display + std::error::Error) for the crate error enum
# (src/error.rs). Same major version the other modules use (oakotio, ...).
thiserror = "2"
# Worker/IPC wire protocol (src/worker.rs, src/ipc.rs): serde for the
# NDJSON control-plane messages and serde_json for the wire encoding.
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# POSIX shm_open/ftruncate/mmap/munmap/shm_unlink for the shared-memory
# frame-slot transport (src/ipc.rs).
libc = "0.2"
# Single-lib unification: every module call is a compile-time Rust call
# into the module crate's direct API (no module C ABI). The engine keeps
# the frozen oakengine_* C ABI upward; downward it rewires through
# src/stubs.rs (direct Rust shims for oakcommon/oakcodec/oakrender/
# oakaudio/oakplugin, and clearly marked STUBs for the handle-based
# oaknode/oaktimeline paths).
#
# The `oakcore_audioparams_*` accessors the audio paths read through are
# implemented inside the dylib too (src/stubs.rs, module `audio`, M12 P5):
# they used to be C++ liboakcore host-provided symbols left as runtime
# lookups, which blocked Windows DLL linking.
#
# src/linkage.rs anchors every crate (and oakcore-rs) so the linker pulls
# their object files into the cdylib.
oakcore-rs = { path = "../oakcore" }
oakundo = { path = "../oakundo" }
oakcommon = { path = "../oakcommon" }
oaktimeline = { path = "../oaktimeline" }
oakcodec = { path = "../oakcodec" }
oakrender = { path = "../oakrender" }
oaktask = { path = "../oaktask" }
oaknode = { path = "../oaknode" }
oakaudio = { path = "../oakaudio" }
oakplugin = { path = "../oakplugin" }
# Live write-through to the project library (plan M13 D2): the facade's
# storage session manager (src/storage.rs) drives the oakstorage database
# backend (DatabaseBackend::save/snapshot) straight from the undo path.
oakstorage = { path = "../oakstorage" }
[dev-dependencies]
parking_lot = "0.12"
# The facade is cdylib-only, so the former tests/*.rs integration tests
# now run as unit tests inside the crate (src/test_support/, pulled in
# from src/lib.rs); the module crates are reachable through the normal
# [dependencies] above. oakcommon stays here for the direct dev-only
# imports in the test support files.
oakcommon = { path = "../oakcommon" }
# The write-through tests (src/test_support/it_storage.rs) inspect the
# journal/snapshot rows of the temp libraries directly; sea-orm + tokio
# unify with oakstorage's own versions (same features), so the entities
# and the current-thread runtime pattern match the oakstorage tests.
sea-orm = { version = "2", features = ["sqlx-postgres", "sqlx-sqlite", "runtime-tokio"] }
tokio = { version = "1", features = ["rt", "macros"] }
-140
View File
@@ -1,140 +0,0 @@
# oakengine — the `liboakengine` cdylib (plugin / external C ABI)
The frozen `oakengine_*` C ABI (`engine/include/oakengine/*.h`) as a
**pure cdylib**. This is the plugin / external-consumer layer: OFX plugins
and third-party embedders link `liboakengine` and call the C ABI. The app,
oak-cli and oak-worker do not use it anymore (M14 R4) — they link the
module crates directly as Rust rlibs. The C ABI itself is frozen: only
additive changes, plus major version bumps.
Downward, every export is a direct Rust call into the module crates
(oakundo/oaknode/oaktimeline/oakcodec/oakaudio/oakrender/oaktask/
oakcommon/oakplugin/oakstorage/oakcore-rs) through `src/stubs.rs` (the
rewired replacement for the deleted `bridge/`). The facade itself owns
only the engine's box/unbox, buf/size and error-code conventions;
cross-cutting state that used to live here (the process-wide undo stack,
the open undo group) has sunk into the modules (M14 R1:
`oakundo::global`).
## Architecture
```
src/
lib.rs crate docs, module list, test-only test_link
error.rs OAKENGINE error codes (module 00 → -1..-6)
handle.rs CHandle mirror, OakEngine* opaque wrappers, box/unbox,
catch_unwind guards, buf/size string helpers
stubs.rs direct-Rust shims replacing the deleted bridge/ (the
engine's only downward path to the modules)
linkage.rs #[used] anchors pulling every module rlib into the cdylib
undo.rs engine/include/oakengine/undo.h (thin forward to
oakundo::global)
common.rs config.h + videoparams.h (facade-static tables + POD↔handle)
audio.rs audio.h (manager + sync + processor)
codec.rs encoding.h + exporter.h (metadata, params POD, exporter)
render.rs renderer.h + color.h + lut.h (renderer/frame/color processor)
plugin.rs plugin.h
node.rs node.h + project.h + footage.h (node graph / project / footage)
timeline.rs timeline.h (sequences, clips, tracks, markers, workarea)
task.rs task.h (background tasks over oaktask)
storage.rs write-through session (project ↔ oakstorage backend)
deferred.rs documented deferrals (stub detail lives here and in the
family modules' stub bodies)
test_support/ the former tests/*.rs, now in-crate unit tests
```
### Dependencies
The facade's regular dependency is `thiserror` (the `Display` +
`std::error::Error` impl for the facade error enum, `src/error.rs`). Every
module call is a compile-time Rust call into the module crate's direct API
(`src/stubs.rs`); the module crates are real dependencies, and
[`linkage`](src/linkage.rs) anchors them so their rlibs are embedded in
the `liboakengine` cdylib next to the facade's `oakengine_*` exports. The
`oakcore_audioparams_*` accessors the audio paths read through are
implemented in the dylib too (src/stubs.rs, module `audio`, M12 P5) —
they used to be C++ host symbols left as runtime lookups (macOS
`-undefined dynamic_lookup`); the cdylib now carries no undefined imports.
### Handle mapping
The engine headers' opaque pointers (`OakEngineNode*`, `OakEngineTrack*`,
...) are thin newtype wrappers around the module layer's `CHandle`
(`{ctx, addref, release, abi_version}`) values. A box is created by
`handle::box_handle` and freed by `handle::free_box` (release + dealloc);
consuming exports (`oakengine_*_free`, `oakengine_undo_push`, ...) free
their box, borrowed results never are. The process-wide undo stack and
open undo group live in the oakundo module (`oakundo::global`); the
undo.h exports are thin forwards over it (module 00 analogues of
`EngineCore::undo_stack()` and the C++ capi's `g_undo_group`).
### Error codes
Facade-local codes are -1..-6 (`OAKENGINE_E_*`); module codes pass
through **untranslated** (the -MMCCCC prefix preserves provenance, e.g.
-20004 is oakundo's NOT_FOUND). String getters follow the engine buf/size
convention: the return value is the length excluding the NUL
(`handle::string_result` converts the modules' size-including-NUL).
## Scope
| Family | Header | Wrapped | Notes |
|---|---|---|---|
| undo | undo.h | 37 | stack/group/command lifecycle over oakundo (`oakundo::global` + undocommand) + Qt leftovers (update_actions/actions → no-op/NULL) |
| common | config.h, videoparams.h | 34 | config over oakcommon; videoparams static tables ported from `engine/render/videoparams.cpp` |
| audio | audio.h | 26 | manager + sync; processor convert/output_params stubbed (interface mismatch) |
| plugin | plugin.h | 4 | callbacks are facade state; push_button stubbed (no module API) |
| codec | encoding.h, exporter.h | 81/85 | metadata family over oakcodec (`include/codec/format.h`); params handle is a facade box over the `oakcodec_encoding_params` POD; the exporter family drives the export task synchronously (integration-tested, real mp4 via FFmpeg); presets/load-save deferred |
| render | renderer.h, color.h, lut.h | 60/60 | renderer over oakrender tickets; frame accessors over `OakCodecFrame`; color processor over `oakrender_color_processor_*`; color-manager list queries + LUT library stubs |
| node | node.h, project.h, footage.h | 226/327 | the node graph, project and footage families over the oaknode crate; documented stubs where the module lacks the surface (gizmos, plugin messages, input properties, brush, thumbnail/waveform caches, shape/subtitle, keyframe enumeration, ...) — see the stub bodies |
| timeline | timeline.h | 126/139 | sequences/clips/tracks/markers/workarea over oaknode + oaktimeline; documented stubs (ripple-tracks command, default transitions, move-track/clip, marker-create, auto-cache, cache invalidation, multicam find/switch — module-surface gaps, see the stub bodies) |
| task | task.h | 27 | the background-task system over oaktask (manager + load/save/import/export creators + result accessors); `create_proxy` stubbed (the module has no proxy-task C creator); start-time/is-cancelled are facade-approximated |
Deferred/stub detail lives in [`deferred`](src/deferred.rs) and in the
stub bodies' doc comments. The worker/IPC families (`worker.h`/`ipc.h`)
are **not** part of the facade anymore: the frozen C++ ABI does not
include them, so the render worker's runtime and the shared-memory
frame-slot transport moved into the `oak-worker` crate
(`crates/oak-worker/src/{worker,ipc}.rs`, self-contained, direct Rust
calls into oakrender); the ipc.h control-plane message serializers remain
unwrapped.
## Testing
The crate is cdylib-only (no rlib artifact), so integration tests cannot
link it as a crate; the former `tests/*.rs` moved into `src/test_support/`
and run as in-crate unit tests (pulled in from `src/lib.rs` under
`#[cfg(test)]`), addressing the modules through `crate::*`. The module
crates are real dependencies, so the test binary statically links the same
rlibs the cdylib embeds:
- `src/linkage.rs` (always-on) anchors the module rlibs into the cdylib
for `cargo build`; the test-only `test_link` module in `src/lib.rs`
forces the oakrender/oaknode/oaktimeline/oaktask rlibs into the lib
unit-test binary, and `src/test_support/common/mod.rs::force_link`
covers the test-support module (same symbol list, so the anchor paths
stay proven against the current module layouts).
- Tests that touch process-wide state (the audio manager, the undo stack,
the task manager) take a shared serialization lock instead of relying
on the process isolation the old integration tests had
(`test_support/common::with_manager`, the per-family `SERIAL` mutexes).
The smoke tests exercise the module crates' real implementations. Where a
wrapped family needs module behavior the crates do not implement yet, the
engine function is a documented stub with its reason (see `deferred.rs`).
```
cargo test # in-crate unit tests (undo, common, audio, codec,
# exporter, plugin, render, node, timeline, task,
# library/storage families)
cargo build # liboakengine cdylib embeds the module rlibs + the
# folded-in oakcore_audioparams_* accessors (no
# undefined imports)
```
## FFI discipline
Every export goes through a `catch_unwind` guard
(`handle::guard*`); `*_free` is a NULL no-op; strings use the two-stage
buf/size convention; module error codes pass through untranslated;
handles are refcounted module values wrapped in opaque boxes.
-60
View File
@@ -1,60 +0,0 @@
// 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/>.
//! Build-time link configuration for the `liboakengine` cdylib.
//!
//! The dylib carries the module crates themselves (their direct-Rust
//! code, kept in the link by `src/linkage.rs` — see Cargo.toml). The
//! `oakcore_audioparams_*` accessors the audio
//! paths read through used to be host-provided C++ liboakcore symbols,
//! left as runtime lookups via `-undefined,dynamic_lookup`; M12 P5
//! implemented them inside the dylib (src/stubs.rs, module `audio`), so
//! no undefined imports remain except system frameworks/libc++, and the
//! cdylib links on every platform (Windows DLLs reject undefined symbols,
//! which was the blocker).
fn main() {
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if os == "macos" {
// The static FFmpeg's transitive system deps (libz etc.) are
// recorded as `@rpath/libz.1.dylib`; the dylib itself carries the
// rpath so standalone binaries (and the packaged app) resolve
// them without extra host rpaths.
println!("cargo:rustc-cdylib-link-arg=-Wl,-rpath,/usr/lib");
}
if os == "macos" || os == "linux" {
// The dlsym codec bridge (M12 P0) resolves `oakcodec_*` from the
// process-global scope; the engine's unit-test binary (the former
// integration tests live in src/test_support/) statically links the
// module crates, so their symbols must be exported from the test
// executable. `cargo:rustc-link-arg-tests` is NOT usable: the facade
// is cdylib-only, so cargo reports "does not have a test target" for
// that directive — use the generic `rustc-link-arg` (a no-op for the
// cdylib link itself, and not emitted on Windows where the flag is
// meaningless and would break the DLL link).
println!("cargo:rustc-link-arg=-Wl,-export_dynamic");
}
if os == "macos" {
// The bundled OpenColorIO's macOS system monitor references
// IOKit / ColorSync / CoreGraphics display APIs; link them for
// the cdylib link and the unit-test binary (which statically
// pulls the same OCIO rlib).
for fw in ["IOKit", "ColorSync", "CoreGraphics"] {
println!("cargo:rustc-link-arg=-framework");
println!("cargo:rustc-link-arg={fw}");
}
}
}
-59
View File
@@ -1,59 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_AUDIO_ERROR_H
#define OAK_EDITOR_AUDIO_ERROR_H
/**
* @brief Current ABI version stamped into every oakaudio handle.
*
* Bump whenever a handle layout or the semantics of any exported function
* change incompatibly. Consumers should compare a handle's abi_version
* field against the value they were compiled with before dereferencing
* ctx.
*/
#define OAKAUDIO_ABI_VERSION 1
#if defined(_WIN32)
#if defined(OAKAUDIO_BUILD)
#define OAKAUDIO_API __declspec(dllexport)
#else
#define OAKAUDIO_API __declspec(dllimport)
#endif
#else
#define OAKAUDIO_API __attribute__((visibility("default")))
#endif
/**
* @brief Status and error codes shared by all oakaudio C API families.
*
* Return-code convention (mirrors engine/include/oakengine/init.h):
* 0 (OAKAUDIO_OK) on success, a negative OAKAUDIO_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
#define OAKAUDIO_OK 0 /**< Success. */
#define OAKAUDIO_E_INVALID (-60001) /**< NULL handle or invalid argument. */
#define OAKAUDIO_E_STATE (-60002) /**< Call not valid in the current state. */
#define OAKAUDIO_E_FAILED (-60003) /**< The underlying operation failed. */
#define OAKAUDIO_E_NOT_FOUND (-60004) /**< Index out of range / entry not found. */
#define OAKAUDIO_E_NOMEM (-60005) /**< Allocation failed. */
#endif //OAK_EDITOR_AUDIO_ERROR_H
@@ -1,73 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_AUDIO_LEVELMETER_H
#define OAK_EDITOR_AUDIO_LEVELMETER_H
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file levelmeter.h
* @brief C ABI for the oakaudio level meter (olive::AudioLevelMeter):
* stateless peak/RMS/VU/LUFS analysis of planar float audio.
*/
/** Per-channel analysis results. dB fields floor at -200. */
typedef struct oakaudio_channel_stats {
double peak_linear;
double peak_db;
double rms_linear;
double rms_db;
double vu_db;
} oakaudio_channel_stats;
/** Buffer-wide summary. */
typedef struct oakaudio_meter_stats {
double max_peak_linear;
double integrated_lufs; /**< BS.1770-compatible unit (no K-weighting). */
int silence; /**< 1 when the buffer is (near-)silent. */
} oakaudio_meter_stats;
/**
* @brief Analyze a planar float buffer.
*
* @param planar Per-channel float planes.
* @param channel_count Number of channels (> 0).
* @param frame_count Frames per channel (>= 0).
* @param channels Receives per-channel stats; may be NULL.
* @param channels_capacity Capacity of `channels` (must be >=
* channel_count when channels is non-NULL).
* @param summary Receives the buffer-wide summary; may be NULL.
* @return OAKAUDIO_OK or OAKAUDIO_E_INVALID.
*/
OAKAUDIO_API int oakaudio_levelmeter_analyze(const float *const *planar,
int channel_count, int frame_count,
oakaudio_channel_stats *channels, int channels_capacity,
oakaudio_meter_stats *summary);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_AUDIO_LEVELMETER_H
-176
View File
@@ -1,176 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_AUDIO_MANAGER_H
#define OAK_EDITOR_AUDIO_MANAGER_H
#include <stdint.h>
#include "codec/encoder.h"
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file manager.h
* @brief C ABI for the oakaudio PortAudio output/input manager
* (olive::AudioManager singleton).
*
* OakAudioManager uses the standard handle layout (see oakcommon's
* common/handle.h) but with singleton semantics: ctx points to the
* process-wide instance created by oakaudio_manager_create_instance(), so
* addref() and release() are intentionally no-ops and never destroy
* anything (mirrors oakcommon's OakCurrent). abi_version is always
* OAKAUDIO_ABI_VERSION.
*
* Device indices are PortAudio PaDeviceIndex values (-1 = paNoDevice).
* Sample formats are olive::core::SampleFormat::Format values.
*/
typedef struct OakAudioManager {
void *ctx; /**< Opaque pointer to the singleton object. */
void (*addref)(void *ctx); /**< No-op (singleton). */
void (*release)(void *ctx); /**< No-op (singleton). */
uint32_t abi_version; /**< OAKAUDIO_ABI_VERSION. */
} OakAudioManager;
/**
* @brief Create the process-wide AudioManager (no-op when it exists).
*
* Initializes PortAudio and picks the configured/default devices.
*
* @return OAKAUDIO_OK or OAKAUDIO_E_NOMEM.
*/
OAKAUDIO_API int oakaudio_manager_create_instance(void);
/**
* @brief Destroy the process-wide AudioManager (no-op when absent).
*/
OAKAUDIO_API void oakaudio_manager_destroy_instance(void);
/**
* @brief Return a handle to the process-wide AudioManager.
*
* The returned handle is borrowed; addref/release are no-ops. When no
* instance exists the handle is empty (ctx == NULL) and all functions
* report OAKAUDIO_E_STATE.
*/
OAKAUDIO_API OakAudioManager oakaudio_manager_instance(void);
/**
* @brief Release a manager handle. No-op (singleton), safe on NULL/empty.
*/
OAKAUDIO_API void oakaudio_manager_free(OakAudioManager *self);
/**
* @brief Bytes between output-notify pulses (0 disables).
*/
OAKAUDIO_API int oakaudio_manager_set_output_notify_interval(
OakAudioManager self, int64_t bytes);
/**
* @brief Push a block of samples to the output device, opening/restarting
* the stream when the params changed.
*
* @param rate/layout/format Stream params (ffmpeg-style layout mask,
* SampleFormat::Format int).
* @param samples Packed samples in the given format.
* @param samples_size Byte count of `samples`.
* @param error_buf/error_buf_size Optional human-readable failure detail.
* @return OAKAUDIO_OK, OAKAUDIO_E_INVALID, OAKAUDIO_E_STATE (no output
* device), or OAKAUDIO_E_FAILED (PortAudio error, see error_buf).
*/
OAKAUDIO_API int oakaudio_manager_push_to_output(OakAudioManager self,
int rate, uint64_t layout, int format,
const char *samples, int64_t samples_size,
char *error_buf, int error_buf_size);
OAKAUDIO_API int oakaudio_manager_clear_buffered_output(OakAudioManager self);
OAKAUDIO_API int oakaudio_manager_stop_output(OakAudioManager self);
/**
* @brief Seconds of audio consumed by the output device since the last
* reset, compensated for output latency; negative when no stream
* is running.
*/
OAKAUDIO_API int oakaudio_manager_seconds(OakAudioManager self, double *out);
OAKAUDIO_API int oakaudio_manager_reset_output_clock(OakAudioManager self);
/**
* @brief Current output device index, paNoDevice (-1), or a negative
* OAKAUDIO_E_* code.
*/
OAKAUDIO_API int oakaudio_manager_get_output_device(OakAudioManager self);
OAKAUDIO_API int oakaudio_manager_set_output_device(OakAudioManager self,
int device);
OAKAUDIO_API int oakaudio_manager_get_input_device(OakAudioManager self);
OAKAUDIO_API int oakaudio_manager_set_input_device(OakAudioManager self,
int device);
/**
* @brief Close the output stream and re-initialize PortAudio.
*/
OAKAUDIO_API int oakaudio_manager_hard_reset(OakAudioManager self);
/**
* @brief Start recording the input device to a file via the oakcodec
* encoder C ABI.
*
* `params` must describe an audio-enabled encoding; the input stream is
* always captured as interleaved 32-bit float (the only format the
* oakcodec encoder write path accepts).
*
* @return OAKAUDIO_OK, OAKAUDIO_E_STATE (no input device), or
* OAKAUDIO_E_FAILED (see error_buf).
*/
OAKAUDIO_API int oakaudio_manager_start_recording(OakAudioManager self,
const oakcodec_encoding_params *params,
char *error_buf, int error_buf_size);
OAKAUDIO_API int oakaudio_manager_stop_recording(OakAudioManager self);
/**
* @brief Device index named by the configuration ("AudioOutput" /
* "AudioInput"), or the default device when unset/unmatched.
* Static: valid without an instance (PortAudio must be initialized
* by an instance first; returns paNoDevice otherwise).
*/
OAKAUDIO_API int oakaudio_manager_find_config_device_by_name_s(
int is_output_device);
/**
* @brief Device index whose name matches `name` exactly (empty name
* matches nothing, falls through to the default device).
*/
OAKAUDIO_API int oakaudio_manager_find_device_by_name_s(const char *name,
int is_output_device);
/**
* @brief Number of live oakaudio reference-counted objects (leak check).
*/
OAKAUDIO_API int oakaudio_debug_alive_count(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_AUDIO_MANAGER_H
@@ -1,131 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_AUDIO_PROCESSOR_H
#define OAK_EDITOR_AUDIO_PROCESSOR_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file processor.h
* @brief C ABI for the oakaudio real-time resampler/format converter
* (olive::AudioProcessor).
*
* OakAudioProcessor follows the neutral by-value handle convention (see
* oakcommon's common/handle.h): oakaudio_processor_init() returns a handle
* whose underlying object has reference count 1, the addref and release
* function pointers adjust that count atomically (release destroys the
* object at zero), and abi_version is always OAKAUDIO_ABI_VERSION.
* Functions that only use a handle take it BY VALUE; an empty handle
* (ctx == NULL) is reported as OAKAUDIO_E_INVALID.
*
* Sample formats are passed as ints matching the
* olive::core::SampleFormat::Format enum values (invalid = -1, u8_p = 0,
* s16_p, s32_p, s64_p, f32_p, f64_p, u8, s16, s32, s64, f32, f64,
* count). Channel layouts are ffmpeg-style channel masks.
*/
typedef struct OakAudioProcessor {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKAUDIO_ABI_VERSION. */
} OakAudioProcessor;
/** oakaudio_processor_convert() delivers planar 32-bit float output. */
#define OAKAUDIO_PROCESSOR_OUTPUT_FORMAT 4 /**< SampleFormat::f32_p. */
/**
* @brief Create a closed audio processor (count 1).
*
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OAKAUDIO_API OakAudioProcessor oakaudio_processor_init(void);
/**
* @brief Release one reference to a processor.
*
* Convenience wrapper around self->release(self->ctx); nulls self->ctx.
* No-op when self is NULL or self->ctx is NULL.
*/
OAKAUDIO_API void oakaudio_processor_free(OakAudioProcessor *self);
/**
* @brief Open the resampling/format-conversion graph.
*
* out_format is accepted for interface completeness but the conversion
* output is always planar 32-bit float (see
* OAKAUDIO_PROCESSOR_OUTPUT_FORMAT); passing any other format returns
* OAKAUDIO_E_INVALID. A channel layout mask of 0 falls back to the
* default layout for the channel count (stereo when unknown), matching
* the C++ implementation.
*
* @param speed Tempo factor (1.0 = unchanged).
* @return OAKAUDIO_OK, OAKAUDIO_E_STATE when already open,
* OAKAUDIO_E_INVALID for bad arguments, or OAKAUDIO_E_FAILED when
* the filter graph could not be created.
*/
OAKAUDIO_API int oakaudio_processor_open(OakAudioProcessor self,
int in_rate, uint64_t in_layout, int in_format,
int out_rate, uint64_t out_layout, int out_format, double speed);
/**
* @brief Close the graph (safe when closed; self must be non-empty).
*/
OAKAUDIO_API int oakaudio_processor_close(OakAudioProcessor self);
/**
* @brief 1 when open, 0 when closed, OAKAUDIO_E_INVALID for empty handle.
*/
OAKAUDIO_API int oakaudio_processor_is_open(OakAudioProcessor self);
/**
* @brief Push planar float input and pull converted output.
*
* @param in_planar Per-channel float input planes (in channel count);
* NULL with in_frame_count == 0 only pulls pending output.
* @param in_frame_count Frames per input channel.
* @param out_planar Per-channel float output planes (out channel count);
* NULL to discard/pull nothing (returns 0).
* @param out_capacity_frames Capacity of each output plane in frames.
* @return Number of output frames written (>= 0), or a negative
* OAKAUDIO_E_* code. Output is clamped to out_capacity_frames;
* remaining frames stay queued in the graph.
*/
OAKAUDIO_API int oakaudio_processor_convert(OakAudioProcessor self,
const float *const *in_planar, int in_frame_count,
float *const *out_planar, int out_capacity_frames);
/**
* @brief Signal end-of-input to the graph (flushes internal delay).
*/
OAKAUDIO_API int oakaudio_processor_flush(OakAudioProcessor self);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_AUDIO_PROCESSOR_H
-132
View File
@@ -1,132 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_AUDIO_SYNC_H
#define OAK_EDITOR_AUDIO_SYNC_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file sync.h
* @brief C ABI for the oakaudio synchronization helpers
* (olive::AudioSynchronizer and olive::AudioWaveformSync):
* stateless source-time placement and envelope-correlation offset
* estimation.
*/
/** Result of an offset estimation. */
typedef struct oakaudio_offset_result {
int64_t offset_samples;
double confidence; /**< 0..1 correlation score. */
int valid; /**< 1 when an estimate was found. */
} oakaudio_offset_result;
/** Result of a stretch-plus-offset estimation. */
typedef struct oakaudio_stretch_offset_result {
double rate; /**< Playback rate aligning the candidate (> 1 = speed up). */
int64_t offset_samples;
double confidence;
int valid;
} oakaudio_stretch_offset_result;
/**
* @brief Per-window RMS envelope of a planar float buffer (static).
*
* @return Number of envelope windows (>= 0) or a negative OAKAUDIO_E_*
* code. When out is NULL or too small, the required window count
* is returned and nothing is written.
*/
OAKAUDIO_API int oakaudio_sync_extract_rms_envelope(
const float *const *planar, int channel_count, int frame_count,
uint64_t window_samples, double *out, int capacity);
/**
* @brief Estimate the candidate's offset against the reference by
* normalized cross-correlation of RMS envelopes.
*
* @param reference_valid/candidate_valid Optional per-window validity
* masks (NULL = all windows valid; when non-NULL the length must
* match the corresponding envelope length).
*/
OAKAUDIO_API int oakaudio_sync_estimate_envelope_offset(
const double *reference, int reference_len,
const double *candidate, int candidate_len,
const uint8_t *reference_valid, const uint8_t *candidate_valid,
uint64_t window_samples, int64_t max_offset_windows,
oakaudio_offset_result *out);
/**
* @brief Estimate a playback-rate change plus offset aligning the
* candidate to the reference.
*
* The candidate envelope is resampled at each rate in
* [min_rate, max_rate] (step rate_step) and correlated against the
* reference. O(rates * lags * overlap); bound max_offset_windows.
*/
OAKAUDIO_API int oakaudio_sync_estimate_stretch_and_offset(
const double *reference, int reference_len,
const double *candidate, int candidate_len,
const uint8_t *reference_valid, const uint8_t *candidate_valid,
uint64_t window_samples, int64_t max_offset_windows,
double min_rate, double max_rate, double rate_step,
oakaudio_stretch_offset_result *out);
/** One clip's source-time metadata (rational seconds). */
typedef struct oakaudio_source_clip {
int64_t source_start_time_num;
int64_t source_start_time_den;
int64_t media_in_num;
int64_t media_in_den;
int has_source_start_time;
} oakaudio_source_clip;
/**
* @brief Place the candidate on the timeline so its source time aligns
* with the reference clip.
*
* @param reference_timeline_in_num/den Reference clip's timeline in point.
* @param out_num/out_den Receive the candidate's timeline in point.
* @param out_valid Receives 1 when placement succeeded.
*/
OAKAUDIO_API int oakaudio_sync_place_by_source_time(
const oakaudio_source_clip *reference,
const oakaudio_source_clip *candidate,
int64_t reference_timeline_in_num, int64_t reference_timeline_in_den,
int64_t *out_num, int64_t *out_den, int *out_valid);
/**
* @brief Timeline placement from a measured waveform offset.
*/
OAKAUDIO_API int oakaudio_sync_place_by_waveform_offset(
int64_t reference_timeline_in_num, int64_t reference_timeline_in_den,
int64_t candidate_offset_samples, int sample_rate,
int64_t *out_num, int64_t *out_den, int *out_valid);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_AUDIO_SYNC_H
@@ -1,179 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_AUDIO_WAVEFORM_H
#define OAK_EDITOR_AUDIO_WAVEFORM_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file waveform.h
* @brief C ABI for the oakaudio visual waveform store
* (olive::AudioVisualWaveform) and whole-file waveform extraction.
*
* OakAudioWaveform follows the neutral by-value handle convention (see
* oakcommon's common/handle.h). Times are rationals as (num, den) pairs
* of int64_t in seconds; den must be non-zero.
*
* Summaries are stored as channel-interleaved min/max pairs: point p of
* channel c lives at pairs[p * channel_count + c]. This matches the
* on-disk/cache layout of the engine's waveform data (min/max float
* pairs), so the extraction output is drop-in compatible.
*/
/** One summarized waveform point of one channel. */
typedef struct oakaudio_min_max {
float min;
float max;
} oakaudio_min_max;
typedef struct OakAudioWaveform {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKAUDIO_ABI_VERSION. */
} OakAudioWaveform;
/**
* @brief Create an empty waveform (count 1, channel count 0).
*/
OAKAUDIO_API OakAudioWaveform oakaudio_waveform_init(void);
/**
* @brief Release one reference. No-op on NULL/empty handle.
*/
OAKAUDIO_API void oakaudio_waveform_free(OakAudioWaveform *self);
/**
* @brief Channel count, or a negative OAKAUDIO_E_* code.
*/
OAKAUDIO_API int oakaudio_waveform_get_channel_count(OakAudioWaveform self);
OAKAUDIO_API int oakaudio_waveform_set_channel_count(OakAudioWaveform self,
int channels);
/**
* @brief Waveform length in seconds as a rational pair.
*/
OAKAUDIO_API int oakaudio_waveform_length(OakAudioWaveform self,
int64_t *num, int64_t *den);
/**
* @brief Write planar float samples into the waveform at `start` seconds,
* expanding it if necessary.
*
* @param planar Per-channel float planes; channel count is taken from the
* waveform (set it first with oakaudio_waveform_set_channel_count).
*/
OAKAUDIO_API int oakaudio_waveform_overwrite_samples(OakAudioWaveform self,
const float *const *planar, int frame_count, int sample_rate,
int64_t start_num, int64_t start_den);
/**
* @brief Copy summarized data from another waveform over this one.
*
* @param dest_num/dest_den Where in `self` the sums start being written.
* @param offset_num/offset_den Where in `src` reading starts.
* @param length_num/length_den Maximum amount to copy; 0/1 = all of src.
*/
OAKAUDIO_API int oakaudio_waveform_overwrite_sums(OakAudioWaveform self,
OakAudioWaveform src,
int64_t dest_num, int64_t dest_den,
int64_t offset_num, int64_t offset_den,
int64_t length_num, int64_t length_den);
OAKAUDIO_API int oakaudio_waveform_overwrite_silence(OakAudioWaveform self,
int64_t start_num, int64_t start_den,
int64_t length_num, int64_t length_den);
/**
* @brief Drop `length` seconds from the front (negative prepends silence).
*/
OAKAUDIO_API int oakaudio_waveform_trim_in(OakAudioWaveform self,
int64_t length_num, int64_t length_den);
OAKAUDIO_API int oakaudio_waveform_resize(OakAudioWaveform self,
int64_t length_num, int64_t length_den);
OAKAUDIO_API int oakaudio_waveform_trim_range(OakAudioWaveform self,
int64_t in_num, int64_t in_den,
int64_t length_num, int64_t length_den);
/**
* @brief Summarized min/max pairs covering [start, start+length).
*
* @param out_pairs Receives points * channel_count channel-interleaved
* pairs; may be NULL to query the point count.
* @param capacity_points Capacity of out_pairs in points.
* @return Number of points (>= 0), or a negative OAKAUDIO_E_* code.
* When out_pairs is NULL or too small the required count is
* returned and nothing is written.
*/
OAKAUDIO_API int oakaudio_waveform_get_summary(OakAudioWaveform self,
int64_t start_num, int64_t start_den,
int64_t length_num, int64_t length_den,
oakaudio_min_max *out_pairs, int capacity_points);
/**
* @brief Min/max of `length` samples starting at `start_index` for every
* channel (static, no handle).
*/
OAKAUDIO_API int oakaudio_waveform_sum_samples_s(const float *const *planar,
int channel_count, int start_index, int length,
oakaudio_min_max *out);
/**
* @brief Re-summarize channel-interleaved pairs into one point per
* channel (static, no handle).
*/
OAKAUDIO_API int oakaudio_waveform_re_sum_s(const oakaudio_min_max *in,
int nb_entries, int nb_channels, oakaudio_min_max *out);
/**
* @brief Extract a whole-file waveform summary from a media file through
* the oakcodec decoder C ABI.
*
* Decodes `filename`'s audio stream `stream_index` (index within the
* file's audio stream list) and reduces it to channel-interleaved
* min/max pairs, one point per `samples_per_point` source samples.
*
* @param out_pairs Receives the pairs; may be NULL to query the size.
* @param capacity_points Capacity of out_pairs in points.
* @param out_channel_count Receives the channel count (may be NULL).
* @return Number of points (>= 0); when out_pairs is NULL or too small,
* the required count is returned and nothing is written.
* Negative OAKAUDIO_E_* code on failure
* (OAKAUDIO_E_NOT_FOUND when the file/stream does not exist).
*/
OAKAUDIO_API int oakaudio_waveform_extract(const char *filename,
int stream_index, int samples_per_point,
oakaudio_min_max *out_pairs, int capacity_points,
int *out_channel_count);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_AUDIO_WAVEFORM_H
-106
View File
@@ -1,106 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_CONFORM_H
#define OAK_EDITOR_CODEC_CONFORM_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file conform.h
* @brief C ABI for the oakcodec audio conform manager
* (olive::ConformManager): pcm waveform cache files used for fast
* audio scrubbing.
*
* Interim state (pre-M8): actual conform work is delegated to the global
* task submit callback (see task.h). While no callback is registered,
* state queries report OAKCODEC_CONFORM_UNAVAILABLE.
*/
#define OAKCODEC_CONFORM_EXISTS 0
#define OAKCODEC_CONFORM_GENERATING 1
#define OAKCODEC_CONFORM_UNAVAILABLE 2
/**
* @brief Create the ConformManager singleton (no-op when it exists).
*/
OAKCODEC_API int oakcodec_conform_create_instance(void);
/**
* @brief Destroy the ConformManager singleton (no-op when absent).
*/
OAKCODEC_API int oakcodec_conform_destroy_instance(void);
/**
* @brief Query the conform state of one audio stream, starting the
* conform when needed and possible.
*
* Addresses the source by filename/stream_index and the target audio
* format by sample_rate/channel_layout/sample_format
* (olive::core::SampleFormat::Format as int).
*
* When the conform files do not exist and a task submit callback is
* registered (task.h), the conform is submitted synchronously and the
* filesystem is re-checked; `wait` only controls whether a post-submit
* miss is reported as OAKCODEC_CONFORM_UNAVAILABLE (wait != 0) or
* OAKCODEC_CONFORM_GENERATING (wait == 0). Without a registrar the
* result is always OAKCODEC_CONFORM_UNAVAILABLE.
*
* @return One of OAKCODEC_CONFORM_* (non-negative), or a negative
* OAKCODEC_E_* code for invalid arguments.
*/
OAKCODEC_API int oakcodec_conform_get_state(const char *cache_path,
const char *source_filename, int stream_index,
int sample_rate, uint64_t channel_layout,
int sample_format, int wait);
/**
* @brief Number of conform (pcm) files for the given stream/params — one
* per channel; 0 on invalid arguments.
*/
OAKCODEC_API int oakcodec_conform_filename_count(const char *cache_path,
const char *source_filename, int stream_index,
int sample_rate, uint64_t channel_layout,
int sample_format);
/**
* @brief The `index`-th conform filename (buf/size getter).
*
* @return Required buffer size including NUL (non-negative), or a
* negative OAKCODEC_E_* code (OAKCODEC_E_NOT_FOUND when index is
* out of range).
*/
OAKCODEC_API int oakcodec_conform_filename_at(const char *cache_path,
const char *source_filename,
int stream_index, int sample_rate,
uint64_t channel_layout, int sample_format,
int index, char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_CODEC_CONFORM_H
-245
View File
@@ -1,245 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_DECODER_H
#define OAK_EDITOR_CODEC_DECODER_H
#include <stdint.h>
#include "error.h"
#include "frame.h"
#include "render/cancelatom.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file decoder.h
* @brief C ABI for oakcodec media decoders (olive::Decoder and its
* FFmpeg/OIIO implementations): probing, stream enumeration and
* CPU-frame decoding.
*
* Handles follow the neutral by-value convention documented in frame.h
* (and oakcommon's common/handle.h). Two usage patterns share the
* OakDecoder handle:
*
* - Probe: oakcodec_decoder_probe() inspects a file WITHOUT opening a
* decode session; the stream getters describe what was found.
* - Decode: oakcodec_decoder_init() + oakcodec_decoder_open() attach a
* decoder instance to one (filename, stream) pair; the decode
* functions then produce frames/audio.
*/
typedef struct OakDecoder {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCODEC_ABI_VERSION. */
} OakDecoder;
/**
* @brief POD description of one probed video stream.
*
* duration_ts counts units of the stream's time base;
* time_base_num/den is seconds per time-base unit. color_primaries and
* color_trc carry the ISO/IEC 23001-8 code points the decoder reports
* (0 = unknown). interlaced is 1 when the stream is interlaced.
* format is an OakPixelFormat value (the decoder's native delivery
* format), channel_count its plane channel count.
*/
typedef struct oakcodec_video_stream_info {
int stream_index;
int width;
int height;
int frame_rate_num;
int frame_rate_den;
int64_t duration_ts;
int time_base_num;
int time_base_den;
int format;
int channel_count;
int color_primaries;
int color_trc;
int interlaced;
} oakcodec_video_stream_info;
/**
* @brief POD description of one probed audio stream.
*
* channel_layout is the ffmpeg-style channel mask (e.g. 0x3 = stereo).
*/
typedef struct oakcodec_audio_stream_info {
int stream_index;
int sample_rate;
uint64_t channel_layout;
int channel_count;
int64_t duration_ts;
int time_base_num;
int time_base_den;
} oakcodec_audio_stream_info;
/* ---- Probe (stateless inspection) ---------------------------------------- */
/**
* @brief Probe a media file: decoder name plus stream inventory.
*
* Tries each available decoder implementation (FFmpeg, then OIIO) and
* wraps the first one that recognizes the file. The returned handle only
* carries probe results; it cannot decode (use init + open for that).
*
* @return Handle with reference count 1, or an empty handle (ctx == NULL)
* when no decoder recognizes the file (oakcodec_probe_last_error()
* carries the reason).
*/
OAKCODEC_API OakDecoder oakcodec_decoder_probe(const char *filename);
/**
* @brief Thread-local error detail of the last failed probe on this
* thread (buf/size string getter convention).
*/
OAKCODEC_API int oakcodec_probe_last_error(char *buf, int buf_size);
/** @brief Probed decoder id ("ffmpeg"/"oiio", buf/size getter). */
OAKCODEC_API int oakcodec_decoder_probe_decoder_name(OakDecoder probe, char *buf,
int buf_size);
OAKCODEC_API int oakcodec_decoder_probe_video_stream_count(OakDecoder probe);
OAKCODEC_API int oakcodec_decoder_probe_audio_stream_count(OakDecoder probe);
OAKCODEC_API int oakcodec_decoder_probe_subtitle_stream_count(OakDecoder probe);
/**
* @brief Fill `out` with the video stream at `index` (0-based within the
* video stream list).
*
* @return OAKCODEC_OK, OAKCODEC_E_INVALID, or OAKCODEC_E_NOT_FOUND when
* index is out of range.
*/
OAKCODEC_API int oakcodec_decoder_probe_get_video_stream(OakDecoder probe, int index,
oakcodec_video_stream_info *out);
OAKCODEC_API int oakcodec_decoder_probe_get_audio_stream(OakDecoder probe, int index,
oakcodec_audio_stream_info *out);
/* ---- Decode session ------------------------------------------------------- */
/**
* @brief Create a closed decoder handle (count 1).
*/
OAKCODEC_API OakDecoder oakcodec_decoder_init(void);
/**
* @brief Release one reference to a decoder. No-op on NULL/empty.
*/
OAKCODEC_API void oakcodec_decoder_free(OakDecoder *decoder);
/**
* @brief Open `filename`'s stream `stream_index` for decoding.
*
* The decoder implementation is chosen automatically from the probe
* results. Opening an already-open decoder on the same stream is a
* successful no-op.
*
* @return OAKCODEC_OK on success, OAKCODEC_E_NOT_FOUND when the file
* does not exist, OAKCODEC_E_FAILED otherwise (see
* oakcodec_decoder_last_error()).
*/
OAKCODEC_API int oakcodec_decoder_open(OakDecoder decoder, const char *filename,
int stream_index);
/** @brief Close the current stream (safe when closed). */
OAKCODEC_API int oakcodec_decoder_close(OakDecoder decoder);
/** @brief 1 when a stream is open, 0 otherwise. */
OAKCODEC_API int oakcodec_decoder_is_open(OakDecoder decoder);
/**
* @brief Decode the video frame at `numerator/denominator` seconds.
*
* Before the start of the footage the first frame is returned, after the
* end the last frame.
*
* @return A frame handle with reference count 1 (caller releases), or an
* empty handle (ctx == NULL) on error/EOF — check
* oakcodec_decoder_last_error().
*/
OAKCODEC_API OakFrame oakcodec_decoder_decode_video(OakDecoder decoder, int numerator,
int denominator);
/**
* @brief Decode audio into a float buffer.
*
* Decodes the interleaved audio covering [in, out) seconds (rational
* pairs), resampled/laid out to `sample_rate`/`channel_layout`.
* `buf` must hold at least `buf_frames` frames worth of interleaved
* floats.
*
* @return The number of frames written (>= 0), or a negative
* OAKCODEC_E_* code. Conform generation is NOT triggered by this
* family in the current intermediate state (no task registrar);
* media requiring a conform yields OAKCODEC_E_STATE.
*/
OAKCODEC_API int oakcodec_decoder_decode_audio(OakDecoder decoder, int in_num, int in_den,
int out_num, int out_den, int sample_rate,
uint64_t channel_layout, float *buf,
int buf_frames);
/**
* @brief Conform the open stream's audio into per-channel pcm cache files
* (Decoder::conform_audio()).
*
* `output_filenames` is an array of `filename_count` final per-channel
* paths. `sample_format` is olive::core::SampleFormat::Format as int.
* `cancelled` may be an empty OakCancelAtom (ctx == NULL).
*
* @return OAKCODEC_OK on success, OAKCODEC_E_STATE when no stream is
* open, OAKCODEC_E_CANCELLED when cancelled, OAKCODEC_E_FAILED
* otherwise.
*/
OAKCODEC_API int oakcodec_decoder_conform_audio(OakDecoder decoder,
const char *const *output_filenames, int filename_count,
int sample_rate, uint64_t channel_layout, int sample_format,
OakCancelAtom cancelled);
/**
* @brief Image-sequence filename heuristics (Decoder::get_image_sequence_*).
*
* digit_count: number of trailing digits in the filename stem (0 = not an
* image sequence filename). index: the numeric value of those digits (-1
* when none). transform: substitute `number` into the digit field,
* two-stage string getter.
*/
OAKCODEC_API int oakcodec_decoder_get_image_sequence_digit_count(
const char *filename);
OAKCODEC_API int64_t oakcodec_decoder_get_image_sequence_index(
const char *filename);
OAKCODEC_API int oakcodec_decoder_transform_image_sequence_file_name(
const char *filename, int64_t number, char *buf, int buf_size);
/**
* @brief Human-readable detail of the last error on this decoder
* (buf/size string getter convention).
*/
OAKCODEC_API int oakcodec_decoder_last_error(OakDecoder decoder, char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_CODEC_DECODER_H
-223
View File
@@ -1,223 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_ENCODER_H
#define OAK_EDITOR_CODEC_ENCODER_H
#include <stdint.h>
#include "error.h"
#include "frame.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file encoder.h
* @brief C ABI for oakcodec media encoders (olive::Encoder and its
* FFmpeg/OIIO implementations).
*
* Handles follow the neutral by-value convention documented in frame.h.
* The workflow is: fill an oakcodec_encoding_params POD (all fields,
* zeroed = disabled) -> oakcodec_encoder_init() ->
* oakcodec_encoder_open() -> oakcodec_encoder_write_*() ->
* oakcodec_encoder_flush(). Encoder-specific options
* (e.g. "crf" = "18") go through oakcodec_encoder_set_video_option()
* between init and open.
*
* Enum int fields carry the engine's own enum values
* (olive::ExportFormat::Format, olive::ExportCodec::Codec,
* OakPixelFormat, olive::VideoParams::Interlacing,
* olive::core::SampleFormat::Format) — the same values
* oakengine/encoding.h documents.
*/
typedef struct OakEncoder {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCODEC_ABI_VERSION. */
} OakEncoder;
/** @brief olive::VideoParams::Interlacing values. */
#define OAKCODEC_INTERLACE_NONE 0
#define OAKCODEC_INTERLACE_TOP_FIRST 1
#define OAKCODEC_INTERLACE_BOTTOM_FIRST 2
/** @brief EncodingParams::VideoScalingMethod values. */
#define OAKCODEC_ENCODING_SCALING_FIT 0
#define OAKCODEC_ENCODING_SCALING_STRETCH 1
#define OAKCODEC_ENCODING_SCALING_CROP 2
/**
* @brief Flattened encoding parameters (olive::EncodingParams).
*
* A zeroed struct describes an all-tracks-disabled configuration. The
* filename (and image-sequence "[#####]" template when
* video_is_image_sequence is set) lives in `filename`.
* video_time_base_* is the frame duration (frame rate flipped), matching
* oak_video_params' convention.
*/
typedef struct oakcodec_encoding_params {
char filename[1024];
int format; /**< olive::ExportFormat::Format. */
int video_enabled; /**< 1/0. */
int video_codec; /**< olive::ExportCodec::Codec. */
int video_width;
int video_height;
int video_time_base_num; /**< Frame duration numerator. */
int video_time_base_den;
int video_pixel_format; /**< OakPixelFormat (delivery format). */
int video_interlacing; /**< OAKCODEC_INTERLACE_*. */
int video_pixel_aspect_num;
int video_pixel_aspect_den;
int64_t video_bit_rate; /**< bit/s, 0 = codec default. */
int64_t video_min_bit_rate;
int64_t video_max_bit_rate;
int64_t video_buffer_size; /**< bytes. */
int video_threads; /**< 0 = auto. */
char video_pix_fmt[64]; /**< Encoded pixel format name ("yuv420p"). */
int video_is_image_sequence; /**< 1/0. */
int video_scaling_method; /**< OAKCODEC_ENCODING_SCALING_*. */
int audio_enabled; /**< 1/0. */
int audio_codec; /**< olive::ExportCodec::Codec. */
int audio_sample_rate;
uint64_t audio_channel_layout; /**< ffmpeg-style channel mask. */
int audio_sample_format; /**< olive::core::SampleFormat::Format. */
int64_t audio_bit_rate; /**< bit/s. */
int subtitles_enabled; /**< 1/0. */
int subtitles_codec; /**< olive::ExportCodec::Codec. */
int subtitles_are_sidecar; /**< 1/0. */
int subtitles_sidecar_format; /**< olive::ExportFormat::Format. */
/** Output OCIO colorspace name; empty = reference space (no transform). */
char color_transform_output[256];
int export_length_num; /**< Export length in seconds (rational). */
int export_length_den;
/** Custom export range (seconds, rational pairs); used when
* has_custom_range != 0. */
int has_custom_range;
int64_t custom_range_in_num;
int64_t custom_range_in_den;
int64_t custom_range_out_num;
int64_t custom_range_out_den;
} oakcodec_encoding_params;
/**
* @brief Create an encoder for `params` (count 1).
*
* The implementation (FFmpeg/OIIO) is chosen from params.format and the
* enabled tracks. The file is NOT opened yet. Returns an empty handle
* (ctx == NULL) when the configuration is invalid.
*/
OAKCODEC_API OakEncoder oakcodec_encoder_init(const oakcodec_encoding_params *params);
/** @brief Release one reference to an encoder. No-op on NULL/empty. */
OAKCODEC_API void oakcodec_encoder_free(OakEncoder *encoder);
/**
* @brief Set an encoder-specific video option (e.g. "crf" = "18").
*
* Only valid between init and open.
*
* @return OAKCODEC_OK, or OAKCODEC_E_STATE when already open.
*/
OAKCODEC_API int oakcodec_encoder_set_video_option(OakEncoder encoder, const char *key,
const char *value);
/**
* @brief Open the output file and write stream headers.
*
* @return OAKCODEC_OK, OAKCODEC_E_STATE (already open), or
* OAKCODEC_E_FAILED (see oakcodec_encoder_last_error()).
*/
OAKCODEC_API int oakcodec_encoder_open(OakEncoder encoder);
/**
* @brief Encode one video frame.
*
* The frame's parameters must match the encoding parameters (the encoder
* converts the delivery pixel format to the encoded one internally).
*/
OAKCODEC_API int oakcodec_encoder_write_video(OakEncoder encoder, OakFrame frame);
/**
* @brief Encode interleaved float audio samples.
*
* @param samples frame_count * channel_count interleaved floats.
* @return OAKCODEC_OK or a negative OAKCODEC_E_* code.
*/
OAKCODEC_API int oakcodec_encoder_write_audio(OakEncoder encoder, const float *samples,
int frame_count);
/**
* @brief Encode one subtitle entry (times in seconds).
*/
OAKCODEC_API int oakcodec_encoder_write_subtitle(OakEncoder encoder, const char *text,
double in_seconds, double out_seconds);
/**
* @brief Flush the encoders, write the trailer and close the file.
*
* Idempotent; after a successful flush the encoder cannot be written to
* (write calls return OAKCODEC_E_STATE).
*/
OAKCODEC_API int oakcodec_encoder_flush(OakEncoder encoder);
/**
* @brief Human-readable detail of the last error on this encoder
* (buf/size string getter convention).
*/
OAKCODEC_API int oakcodec_encoder_last_error(OakEncoder encoder, char *buf, int buf_size);
/**
* @brief The pixel format the encoder wants frames in
* (Encoder::get_desired_pixel_format()), as int; -1 when
* unknown/invalid encoder.
*/
OAKCODEC_API int oakcodec_encoder_get_desired_pixel_format(OakEncoder encoder);
/**
* @brief File extension for an export format
* (ExportFormat::get_extension()), two-stage string getter.
*/
OAKCODEC_API int oakcodec_export_format_get_extension(int format, char *buf,
int buf_size);
/**
* @brief Scaling matrix for a scaling method
* (EncodingParams::generate_matrix()), row-major 4x4 into
* out_matrix[16].
*/
OAKCODEC_API int oakcodec_encoding_generate_matrix(int method, int src_width,
int src_height, int dst_width,
int dst_height, double *out_matrix);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_CODEC_ENCODER_H
-61
View File
@@ -1,61 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_ERROR_H
#define OAK_EDITOR_CODEC_ERROR_H
/**
* @brief Status and error codes shared by all oakcodec C API families.
*
* Return-code convention (mirrors the other split modules):
* 0 (OAKCODEC_OK) on success, a negative OAKCODEC_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
#define OAKCODEC_OK 0 /**< Success. */
#define OAKCODEC_E_INVALID (-50001) /**< NULL handle or invalid argument. */
#define OAKCODEC_E_STATE (-50002) /**< Call not valid in the current state. */
#define OAKCODEC_E_FAILED (-50003) /**< The underlying operation failed. */
#define OAKCODEC_E_NOT_FOUND (-50004) /**< Index out of range / entry not found. */
#define OAKCODEC_E_NOMEM (-50005) /**< Allocation failed. */
#define OAKCODEC_E_CANCELLED (-50006) /**< The operation was cancelled. */
/**
* @brief Current ABI version stamped into every oakcodec handle.
*
* Bump whenever the handle layout or the semantics of any exported
* function change incompatibly. Consumers should compare a handle's
* abi_version field against the value they were compiled with before
* dereferencing ctx.
*/
#define OAKCODEC_ABI_VERSION 1
/**
* @brief Export macro for the oakcodec C ABI.
*
* oakcodec is built with -fvisibility=hidden (01 §1 rule 5): only the
* oakcodec_* functions marked with this macro leave the shared library.
* This also keeps codec-internal C++ classes (whose olive::* names may
* collide with transition stubs inside other modules) from participating
* in cross-library weak-symbol coalescing.
*/
#define OAKCODEC_API __attribute__((visibility("default")))
#endif //OAK_EDITOR_CODEC_ERROR_H
-217
View File
@@ -1,217 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_FORMAT_H
#define OAK_EDITOR_CODEC_FORMAT_H
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file format.h
* @brief C ABI for the oakcodec container-format / codec metadata queries
* (olive::ExportFormat / olive::ExportCodec / olive::Encoder statics).
*
* This family is the module-side mirror of the facade's
* oakengine_encoding_format_* / codec_* surface (oakengine/encoding.h):
* the export dialog queries it to populate its format/codec combo boxes and
* to enable/disable the bit-rate controls. The functions are stateless —
* no handles involved.
*
* Enum int fields carry the engine's own enum values
* (olive::ExportFormat::Format, olive::ExportCodec::Codec,
* olive::core::SampleFormat::Format) — the same values oakengine/encoding.h
* documents. Return-code convention follows include/codec/error.h: 0
* (OAKCODEC_OK) on success, a negative OAKCODEC_E_* code on failure, and
* string getters return the required buffer size in bytes INCLUDING the
* terminating NUL as a non-negative value (two-stage convention). Note this
* differs from oakcodec_export_format_get_extension() (encoder.h), which
* predates this family and reports unknown formats as the empty string.
*/
/**
* @brief Container formats (olive::ExportFormat::Format) referenced by name
* in UI code. Only append; the values are serialized in project/preset
* files. The complete list lives in src/codec/src/exportformat.h.
*/
#define OAKCODEC_ENCODING_FORMAT_MATROSKA 1
#define OAKCODEC_ENCODING_FORMAT_MPEG4_VIDEO 2
#define OAKCODEC_ENCODING_FORMAT_QUICKTIME 4
#define OAKCODEC_ENCODING_FORMAT_PNG 5
#define OAKCODEC_ENCODING_FORMAT_WAV 7
#define OAKCODEC_ENCODING_FORMAT_SRT 13
/**
* @brief Codecs (olive::ExportCodec::Codec) referenced by name in UI code.
* Only append; the values are serialized. The complete list lives in
* src/codec/src/exportcodec.h.
*/
#define OAKCODEC_ENCODING_CODEC_H264 1
#define OAKCODEC_ENCODING_CODEC_H264RGB 2
#define OAKCODEC_ENCODING_CODEC_H265 3
#define OAKCODEC_ENCODING_CODEC_CINEFORM 7
#define OAKCODEC_ENCODING_CODEC_AAC 12
#define OAKCODEC_ENCODING_CODEC_PCM 13
#define OAKCODEC_ENCODING_CODEC_SRT 17
#define OAKCODEC_ENCODING_CODEC_AV1 18
/* ---- Container format / codec metadata ---------------------------------- */
/**
* @brief Number of container formats (olive::ExportFormat::k_format_count).
*/
OAKCODEC_API int oakcodec_encoding_format_count(void);
/**
* @brief Display name of a container format (buf/size, two-stage).
*
* @return The required buffer size (including the NUL), or
* OAKCODEC_E_INVALID when `format` is out of range.
*/
OAKCODEC_API int oakcodec_encoding_format_name(int format, char *buf,
int buf_size);
/**
* @brief File extension (no dot) of a container format (buf/size,
* two-stage); same return convention as
* oakcodec_encoding_format_name().
*/
OAKCODEC_API int oakcodec_encoding_format_extension(int format, char *buf,
int buf_size);
/**
* @brief Number of video codecs a container format supports, or
* OAKCODEC_E_INVALID when the format is invalid.
*/
OAKCODEC_API int oakcodec_encoding_format_video_codec_count(int format);
/**
* @brief The `index`-th video codec of `format` as an
* olive::ExportCodec::Codec value.
*
* @return OAKCODEC_E_INVALID when the format is invalid, or
* OAKCODEC_E_NOT_FOUND when the index is out of range.
*/
OAKCODEC_API int oakcodec_encoding_format_video_codec_at(int format,
int index);
/** @brief Audio-codec variant of the two functions above. */
OAKCODEC_API int oakcodec_encoding_format_audio_codec_count(int format);
OAKCODEC_API int oakcodec_encoding_format_audio_codec_at(int format,
int index);
/** @brief Subtitle-codec variant of the two functions above. */
OAKCODEC_API int oakcodec_encoding_format_subtitle_codec_count(int format);
OAKCODEC_API int oakcodec_encoding_format_subtitle_codec_at(int format,
int index);
/**
* @brief Display name of a codec (buf/size, two-stage).
*
* @return The required buffer size (including the NUL), or
* OAKCODEC_E_INVALID when `codec` is out of range.
*/
OAKCODEC_API int oakcodec_encoding_codec_name(int codec, char *buf,
int buf_size);
/** @brief 1 when `codec` encodes still images (PNG/TIFF/OpenEXR), else 0
* (0 also for an invalid codec). */
OAKCODEC_API int oakcodec_encoding_codec_is_still_image(int codec);
/** @brief 1 when `codec` is lossless (no bit-rate setting applies), else 0
* (0 also for an invalid codec). */
OAKCODEC_API int oakcodec_encoding_codec_is_lossless(int codec);
/**
* @brief Number of encoded pixel formats (e.g. "yuv420p") usable with
* `codec` inside `format`, or OAKCODEC_E_INVALID when either
* argument is out of range. The list is queried from the format's
* encoder (FFmpeg/OIIO), so codecs without an encoder report 0.
*/
OAKCODEC_API int oakcodec_encoding_pix_fmt_count(int format, int codec);
/**
* @brief The `index`-th encoded pixel format name (buf/size, two-stage).
*
* @return The required buffer size (including the NUL), or
* OAKCODEC_E_INVALID for bad format/codec, or
* OAKCODEC_E_NOT_FOUND when the index is out of range.
*/
OAKCODEC_API int oakcodec_encoding_pix_fmt_at(int format, int codec,
int index, char *buf,
int buf_size);
/**
* @brief Index of `pix_fmt` (e.g. "yuv420p") in `codec`'s supported pixel
* format list; 0 (the codec's preferred format) when absent or
* `pix_fmt` is NULL/empty or `codec` is invalid.
*/
OAKCODEC_API int oakcodec_encoding_pix_fmt_index(int codec,
const char *pix_fmt);
/**
* @brief Number of sample formats usable with `codec` inside `format`, or
* OAKCODEC_E_INVALID when either argument is out of range.
*/
OAKCODEC_API int oakcodec_encoding_sample_format_count(int format,
int codec);
/**
* @brief The `index`-th sample format as an olive::core::SampleFormat::Format
* value.
*
* @return OAKCODEC_E_INVALID for bad format/codec, or
* OAKCODEC_E_NOT_FOUND when the index is out of range.
*/
OAKCODEC_API int oakcodec_encoding_sample_format_at(int format, int codec,
int index);
/* ---- Image-sequence filename helpers (olive::Encoder statics) ----------- */
/** @brief 1 when `filename` contains a "[#####]" digit placeholder, else 0
* (0 for NULL). */
OAKCODEC_API int
oakcodec_encoding_filename_contains_digit_placeholder(const char *filename);
/** @brief Digit count of the filename's "[#####]" placeholder; 0 when none
* (0 for NULL). */
OAKCODEC_API int
oakcodec_encoding_image_sequence_digit_count(const char *filename);
/**
* @brief `filename` with the digit placeholder removed (buf/size, two-stage;
* a leading separator like "_"/"-"/"."/" " before the placeholder is
* removed along with it).
*
* @return The required buffer size (including the NUL), or
* OAKCODEC_E_INVALID when `filename` is NULL.
*/
OAKCODEC_API int
oakcodec_encoding_filename_remove_digit_placeholder(const char *filename,
char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_CODEC_FORMAT_H
-162
View File
@@ -1,162 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_FRAME_H
#define OAK_EDITOR_CODEC_FRAME_H
#include <stdint.h>
#include "common/videoparams.h"
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file frame.h
* @brief C ABI for the oakcodec frame object (olive::Frame), a CPU pixel
* buffer plus an OakVideoParams parameter set.
*
* Handle convention (all oakcodec families): neutral by-value handles with
* the same four fields as oakcommon (see oakcommon's common/handle.h):
*
* typedef struct OakFrame {
* void *ctx; // opaque, points to the impl
* void (*addref)(void *ctx); // atomic +1, owner-DLL code
* void (*release)(void *ctx); // atomic -1, destroys at 0
* uint32_t abi_version; // OAKCODEC_ABI_VERSION
* } OakFrame;
*
* oakcodec_frame_init*() returns a handle whose underlying object has
* reference count 1. Copying the struct copies the pointer, not the
* count: call handle.addref(handle.ctx) for every additional long-lived
* copy and handle.release(handle.ctx) (or oakcodec_frame_free()) when
* done with each copy. Functions that only use a handle take it BY
* VALUE; an empty handle (ctx == NULL) is reported as
* OAKCODEC_E_INVALID. oakcodec_frame_free() takes a pointer so it can
* null out the caller's ctx; NULL and ctx == NULL are no-ops.
*/
typedef struct OakFrame {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCODEC_ABI_VERSION. */
} OakFrame;
/**
* @brief Create an empty frame with default (invalid) video parameters.
*
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OAKCODEC_API OakFrame oakcodec_frame_init(void);
/**
* @brief Create a frame with a copy of the given parameter set.
*
* The params handle is addref'd internally; the caller keeps its own
* reference. The frame is not allocated; call oakcodec_frame_allocate().
*
* @return Handle with reference count 1; ctx is NULL on failure.
*/
OAKCODEC_API OakFrame oakcodec_frame_init_with_params(OakVideoParams params);
/**
* @brief Release one reference to a frame.
*
* Convenience wrapper around handle.release(handle.ctx); nulls ctx
* afterwards. No-op when frame is NULL or frame->ctx is NULL.
*/
OAKCODEC_API void oakcodec_frame_free(OakFrame *frame);
/**
* @brief Get a copy of the frame's parameter set.
*
* @param out Receives an addref'd OakVideoParams; the caller must release
* it with oakcommon_videoparams_free().
* @return OAKCODEC_OK, or OAKCODEC_E_INVALID for bad arguments.
*/
OAKCODEC_API int oakcodec_frame_get_params(OakFrame frame, OakVideoParams *out);
/**
* @brief Replace the frame's parameter set (the handle is addref'd
* internally). Recomputes the line sizes; does not reallocate the
* buffer.
*/
OAKCODEC_API int oakcodec_frame_set_params(OakFrame frame, OakVideoParams params);
/**
* @brief Allocate the pixel buffer from the current parameters.
*
* @return OAKCODEC_OK on success (including already-allocated),
* OAKCODEC_E_STATE when the parameters are invalid,
* OAKCODEC_E_INVALID for an empty handle.
*/
OAKCODEC_API int oakcodec_frame_allocate(OakFrame frame);
/** @brief 1 when the pixel buffer is allocated, 0 otherwise. */
OAKCODEC_API int oakcodec_frame_is_allocated(OakFrame frame);
/** @brief Writable pixel buffer, or NULL when unallocated/empty. */
OAKCODEC_API void *oakcodec_frame_data(OakFrame frame);
/** @brief Const variant of oakcodec_frame_data(). */
OAKCODEC_API const void *oakcodec_frame_const_data(OakFrame frame);
/** @brief Size of the pixel buffer in bytes (0 when unallocated). */
OAKCODEC_API int oakcodec_frame_allocated_size(OakFrame frame);
/** @brief Distance between two rows in bytes (0 when params are unset). */
OAKCODEC_API int oakcodec_frame_linesize_bytes(OakFrame frame);
/** @brief Distance between two rows in pixels. */
OAKCODEC_API int oakcodec_frame_linesize_pixels(OakFrame frame);
/* Query helpers; all return 0 / OAKCOMMON_PIXEL_FORMAT_INVALID on an
* empty handle. */
OAKCODEC_API int oakcodec_frame_width(OakFrame frame);
OAKCODEC_API int oakcodec_frame_height(OakFrame frame);
OAKCODEC_API int oakcodec_frame_format(OakFrame frame); /**< OakPixelFormat value. */
OAKCODEC_API int oakcodec_frame_channel_count(OakFrame frame);
/**
* @brief Frame timestamp as a rational number of seconds.
*
* @return OAKCODEC_OK, or OAKCODEC_E_INVALID for bad arguments.
*/
OAKCODEC_API int oakcodec_frame_get_timestamp(OakFrame frame, int *numerator,
int *denominator);
OAKCODEC_API int oakcodec_frame_set_timestamp(OakFrame frame, int numerator,
int denominator);
/**
* @brief Number of live oakcodec handle objects (debug/leak checking).
*
* Counts every boxed object created by oakcodec_*_init*() that has not
* been released yet, across all families (frame/decoder/encoder/...).
*/
OAKCODEC_API int oakcodec_debug_alive_count(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_CODEC_FRAME_H
-140
View File
@@ -1,140 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_PROXY_H
#define OAK_EDITOR_CODEC_PROXY_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file proxy.h
* @brief C ABI for the oakcodec proxy generation singleton
* (olive::ProxyManager).
*
* Interim state (pre-M8): actual transcodes are delegated to the global
* task submit callback (see task.h). While no callback is registered,
* oakcodec_proxy_get_or_start() reports the proxy as missing instead of
* starting background work.
*/
#define OAKCODEC_PROXY_STATE_MISSING 0
#define OAKCODEC_PROXY_STATE_GENERATING 1
#define OAKCODEC_PROXY_STATE_READY 2
#define OAKCODEC_PROXY_STATE_FAILED 3
/**
* @brief POD proxy generation parameters (olive::ProxyManager::ProxyParams).
*
* divider: source resolution divider (1 = use absolute width/height,
* 2/4/8 = fraction of the source resolution). extension/preset are the
* ffmpeg output container and encoder preset (e.g. "mp4"/"veryfast").
*/
typedef struct oakcodec_proxy_params {
int width;
int height;
int divider;
int version;
int crf;
int include_audio; /**< 1/0. */
char extension[32];
char preset[32];
} oakcodec_proxy_params;
typedef struct oakcodec_proxy_result {
int state; /**< OAKCODEC_PROXY_STATE_* */
char filename[1024];
} oakcodec_proxy_result;
/**
* @brief Create the ProxyManager singleton (no-op when it exists).
*/
OAKCODEC_API int oakcodec_proxy_create_instance(void);
/**
* @brief Destroy the ProxyManager singleton (no-op when absent).
*/
OAKCODEC_API int oakcodec_proxy_destroy_instance(void);
/**
* @brief Compiled-in default proxy parameters (1280x720, divider 1, mp4,
* crf 23, "veryfast", audio included). Interim state: until the config
* milestone wires a real store these do not reflect user settings.
*/
OAKCODEC_API int oakcodec_proxy_params_default(oakcodec_proxy_params *out);
/**
* @brief State of a proxy file on disk (OAKCODEC_PROXY_STATE_*;
* OAKCODEC_PROXY_STATE_MISSING for NULL/empty/absent).
*/
OAKCODEC_API int oakcodec_proxy_get_state(const char *proxy_filename);
/** @brief Human-readable string for a proxy state (buf/size getter). */
OAKCODEC_API int oakcodec_proxy_state_to_string(int state, char *buf, int buf_size);
/** @brief Proxy directory for a project cache path (buf/size getter). */
OAKCODEC_API int oakcodec_proxy_get_proxy_directory(const char *cache_path, char *buf,
int buf_size);
/**
* @brief Deterministic proxy filename for a source stream (buf/size
* getter).
*/
OAKCODEC_API int oakcodec_proxy_get_proxy_filename(const char *cache_path,
const char *source_filename,
int stream_index,
const oakcodec_proxy_params *params,
char *buf, int buf_size);
/** @brief Working (in-progress) filename of a proxy (buf/size getter). */
OAKCODEC_API int oakcodec_proxy_get_working_filename(const char *proxy_filename,
char *buf, int buf_size);
/**
* @brief Get or start generating a proxy for `source_filename`.
*
* `cache_path` is the project cache directory. On return `out->state`
* and `out->filename` describe the proxy. When a task submit callback is
* registered (task.h) and no proxy exists, generation is submitted
* synchronously before the state is re-derived; without a registrar the
* state stays OAKCODEC_PROXY_STATE_MISSING.
*/
OAKCODEC_API int oakcodec_proxy_get_or_start(const char *cache_path,
const char *source_filename, int stream_index,
const oakcodec_proxy_params *params,
oakcodec_proxy_result *out);
/**
* @brief Locate an ffmpeg executable for proxy generation (buf/size
* getter; empty string when none is found).
*/
OAKCODEC_API int oakcodec_proxy_find_ffmpeg(const char *configured_path, char *buf,
int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_CODEC_PROXY_H
-113
View File
@@ -1,113 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CODEC_TASK_H
#define OAK_EDITOR_CODEC_TASK_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Background task submission hook for oakcodec (interim state).
*
* The codec module occasionally needs background work (audio conforms,
* proxy transcodes). The task system itself is split out at milestone M8;
* until then oakcodec exposes a single global submit callback. A host
* (M8: oaktask) registers a callback with oakcodec_set_task_submit_cb();
* the conform/proxy managers call it whenever they need a task.
*
* While no callback is registered, managers report the work as
* unavailable (they never crash and never block).
*/
/**
* @brief Kinds of background tasks oakcodec can request.
*/
enum OakCodecTaskKind {
OAKCODEC_TASK_CONFORM = 0, /**< Audio conform to pcm cache files. */
OAKCODEC_TASK_PROXY = 1 /**< Video proxy transcode. */
};
/**
* @brief Description of one background task request.
*
* All strings are borrowed and only valid for the duration of the
* submit call; the callback must copy anything it retains.
*
* Field usage by kind:
* - OAKCODEC_TASK_CONFORM: input_filename (source media), stream_index
* (audio stream), output_filename (final path of the FIRST channel's
* pcm file; the task derives the sibling per-channel paths and the
* ".working" temporary names from the deterministic naming rule),
* sample_rate / channel_layout / sample_format (target audio params,
* sample_format is olive::core::SampleFormat::Format as int).
* - OAKCODEC_TASK_PROXY: input_filename (source media), stream_index
* (video stream), output_filename (final proxy path; the task owns
* the ".working.mp4" temporary name and the rename on success),
* proxy_width / proxy_height (absolute target size, both 0 when the
* request is divider-based).
*/
typedef struct OakCodecTaskRequest {
int kind; /**< OakCodecTaskKind. */
const char *input_filename; /**< Source media filename. */
const char *output_filename; /**< Final destination path (see above). */
int stream_index; /**< Stream inside the source media. */
int sample_rate; /**< conform: target sample rate. */
uint64_t channel_layout; /**< conform: target channel layout mask. */
int sample_format; /**< conform: target sample format (enum as int). */
int proxy_width; /**< proxy: target width, 0 = unspecified/divider. */
int proxy_height; /**< proxy: target height, 0 = unspecified/divider. */
} OakCodecTaskRequest;
/**
* @brief Task submit callback.
*
* @return 0 (OAKCODEC_OK) if the task was accepted - either completed
* synchronously or queued; a negative OAKCODEC_E_* code if the request
* was rejected.
*/
typedef int (*oakcodec_task_submit_fn)(const OakCodecTaskRequest *req,
void *userdata);
/**
* @brief Registers (or replaces) the global task submit callback.
*
* Thread-safe. Pass cb == NULL to unregister. Interim state (pre-M8):
* nobody registers and all task-dependent work reports unavailable.
*/
OAKCODEC_API void oakcodec_set_task_submit_cb(oakcodec_task_submit_fn cb, void *userdata);
/**
* @brief Returns 1 if a submit callback is currently registered, else 0.
*
* Thread-safe.
*/
OAKCODEC_API int oakcodec_task_submit_is_registered(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_CODEC_TASK_H
@@ -1,156 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_COLORTRANSFORM_H
#define OAK_EDITOR_COLORTRANSFORM_H
#include "common/error.h"
#include "common/handle.h"
#ifdef __cplusplus
namespace olive
{
class ColorTransform;
}
extern "C" {
#endif
/**
* @brief Neutral by-value handle to a color transform description
* (olive::ColorTransform).
*
* Ownership/count semantics follow the convention in common/handle.h:
* init functions return a handle whose object has reference count 1,
* addref(ctx)/release(ctx) adjust it atomically, and release destroys
* the object at zero. abi_version is always OAKCOMMON_ABI_VERSION.
*/
typedef struct OakColorTransform {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakColorTransform;
/**
* @brief Create a plain output-colorspace transform.
*
* @param output Output colorspace name. Must not be NULL.
* @return Handle with reference count 1; ctx is NULL on failure.
*/
OakColorTransform oakcommon_colortransform_init_output(
const char *output);
/**
* @brief Create a display/view/look transform.
*
* All three strings must not be NULL.
*
* @return Handle with reference count 1; ctx is NULL on failure.
*/
OakColorTransform oakcommon_colortransform_init_display(
const char *display, const char *view, const char *look);
#ifdef __cplusplus
/**
* @brief Copy a native olive::ColorTransform into a new handle.
*
* The source object is deep-copied; the handle does not keep any
* reference to @p src, which may be destroyed immediately afterwards.
* Only visible to C++ consumers.
*
* @return Handle with reference count 1; ctx is NULL if src is NULL or
* on allocation failure.
*/
OakColorTransform oakcommon_colortransform_init_from_native(
const olive::ColorTransform *src);
/**
* @brief Borrow the native object behind a handle.
*
* The returned pointer is borrowed: it stays valid while the caller
* holds a reference to the handle (i.e. until the matching release).
* Only visible to C++ consumers.
*
* @return Borrowed pointer, or NULL if transform is NULL or
* transform->ctx is NULL.
*/
const olive::ColorTransform *oakcommon_colortransform_get_native(
OakColorTransform transform);
#endif
/**
* @brief Release one reference to a transform.
*
* Convenience wrapper around handle.release(handle.ctx): decrements the
* atomic reference count and destroys the object when it reaches zero.
* No-op when transform is NULL or transform->ctx is NULL.
*/
void oakcommon_colortransform_free(OakColorTransform *transform);
/**
* @brief Query whether this is a display/view/look transform.
*
* @param is_display Receives the result. Must not be NULL.
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_colortransform_is_display(OakColorTransform transform,
int *is_display);
/**
* @brief Get the display name (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_colortransform_get_display(OakColorTransform transform,
char *buf, int buf_size);
/**
* @brief Get the output colorspace name (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_colortransform_get_output(OakColorTransform transform,
char *buf, int buf_size);
/**
* @brief Get the view name (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_colortransform_get_view(OakColorTransform transform,
char *buf, int buf_size);
/**
* @brief Get the look name (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_colortransform_get_look(OakColorTransform transform,
char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_COLORTRANSFORM_H
@@ -1,225 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_COMMANDLINEPARSER_H
#define OAK_EDITOR_COMMANDLINEPARSER_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include "common/error.h"
#include "common/handle.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Neutral by-value handle to a command-line parser instance.
*
* Ownership/count semantics follow the convention in common/handle.h:
* init returns a handle whose object has reference count 1,
* addref(ctx)/release(ctx) adjust it atomically, and release destroys
* the object at zero. abi_version is always OAKCOMMON_ABI_VERSION.
*/
typedef struct OakCommandLineParser {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakCommandLineParser;
/**
* @brief Neutral by-value handle to a registered command-line option.
*
* The handle is released with oakcommon_commandlineoption_free() (or
* handle.release(handle.ctx)); the underlying option is owned by the
* parser and stays valid until the parser is destroyed. abi_version is
* always OAKCOMMON_ABI_VERSION.
*/
typedef struct OakCommandLineOption {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakCommandLineOption;
/**
* @brief Neutral by-value handle to a registered positional argument.
*
* The handle is released with
* oakcommon_commandlinepositionalargument_free() (or
* handle.release(handle.ctx)); the underlying argument is owned by the
* parser and stays valid until the parser is destroyed. abi_version is
* always OAKCOMMON_ABI_VERSION.
*/
typedef struct OakCommandLinePositionalArgument {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakCommandLinePositionalArgument;
/**
* @brief Create a command-line parser.
*
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakCommandLineParser oakcommon_commandlineparser_init(void);
/**
* @brief Release one reference to a command-line parser.
*
* Convenience wrapper around handle.release(handle.ctx): decrements the
* atomic reference count and destroys the parser (invalidating all
* option and positional-argument handles created from it) when the
* count reaches zero. No-op when parser is NULL or parser->ctx is NULL.
*/
void oakcommon_commandlineparser_free(OakCommandLineParser *parser);
/**
* @brief Set the application name/version shown by print_help.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineparser_set_app_info(OakCommandLineParser parser,
const char *name,
const char *version);
/**
* @brief Register an option with one or more name strings.
*
* @param names Array of option name strings (without leading dash).
* @param name_count Number of entries in names. Must be > 0.
* @param description Help text, may be NULL.
* @param takes_arg Non-zero if the option consumes the following argument.
* @param arg_placeholder Placeholder shown in help, may be NULL.
* @param hidden Non-zero to omit from help output.
* @param out_option Receives the option handle (reference count 1).
* May be NULL if unused.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineparser_add_option(
OakCommandLineParser parser, const char *const *names, int name_count,
const char *description, int takes_arg, const char *arg_placeholder,
int hidden, OakCommandLineOption *out_option);
/**
* @brief Register a positional argument.
*
* @param out_argument Receives the argument handle (reference count 1).
* May be NULL if unused.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineparser_add_positional_argument(
OakCommandLineParser parser, const char *name,
const char *description, int required,
OakCommandLinePositionalArgument *out_argument);
/**
* @brief Parse an argv-style argument list.
*
* argv[0] is skipped as the program name, matching C main() convention.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineparser_process(OakCommandLineParser parser,
const char *const *argv, int argc);
/**
* @brief Print usage/help text to stdout.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineparser_print_help(OakCommandLineParser parser,
const char *filename);
/**
* @brief Query whether an option was present on the command line.
*
* @param is_set Receives the result. Must not be NULL.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineoption_is_set(OakCommandLineOption option,
bool *is_set);
/**
* @brief Release one reference to an option handle.
*
* Convenience wrapper around handle.release(handle.ctx). Does not
* unregister the option from the parser. No-op when option is NULL or
* option->ctx is NULL.
*/
void oakcommon_commandlineoption_free(OakCommandLineOption *option);
/**
* @brief Get an option's argument value (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineoption_get_setting(OakCommandLineOption option,
char *buf, int buf_size);
/**
* @brief Set an option's argument value.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlineoption_set_setting(OakCommandLineOption option,
const char *value);
/**
* @brief Get a positional argument's value (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlinepositionalargument_get_setting(
OakCommandLinePositionalArgument argument, char *buf, int buf_size);
/**
* @brief Set a positional argument's value.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_commandlinepositionalargument_set_setting(
OakCommandLinePositionalArgument argument, const char *value);
/**
* @brief Release one reference to a positional argument handle.
*
* Convenience wrapper around handle.release(handle.ctx). Does not
* unregister the argument from the parser. No-op when argument is NULL
* or argument->ctx is NULL.
*/
void oakcommon_commandlinepositionalargument_free(
OakCommandLinePositionalArgument *argument);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_COMMANDLINEPARSER_H
-189
View File
@@ -1,189 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_COMMON_CONFIG_H
#define OAK_EDITOR_COMMON_CONFIG_H
#include <stdint.h>
#include "common/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief De-Qt application configuration store, C ABI
* (M1-oakcommon.md §2.1, extended for the real consumer surface)
*
* oakcommon_config is a process-wide singleton key/value store (the de-Qt
* replacement for engine/config/config.h's QSettings/QVariant wrapper).
* Per the config-wave ruling it is NOT wrapped in the refcounted-handle
* convention of common/handle.h: there is exactly one store per process,
* so the family is a plain set of functions over that singleton (same
* singleton precedent as OakCurrent).
*
* Keys follow the frozen (group, key) convention of §2.1 and keep the
* QSettings INI shape: pass group == NULL (or "") for a top-level key,
* otherwise the entry is stored under an INI [group] section and
* addressed as "group/key" internally.
*
* Values are typed (string / int64 / double / bool). Rational settings
* are stored as strings in the "num/den" form used by
* oakcore_rational_to_string(). Typed getters take a fallback which is
* returned when the key is absent or has a different type (§2.1 special
* convention: they return values, not error codes).
*
* Persistence is an INI file at
* <FileFunctions::get_configuration_location()>/config.ini. The store
* starts up with compiled-in defaults; oakcommon_config_load() re-reads
* the file (a missing file is not an error) and oakcommon_config_save()
* writes it. The OAK_CONFIG_DIR environment override honored by
* get_configuration_location() also redirects this file (tests/tooling).
*
* NOTE (behavior change): the old Qt implementation persisted to
* config.xml (engine XML) — and on macOS QSettings used a plist — so
* previously saved settings do NOT carry over; the first run starts from
* the compiled-in defaults.
*/
typedef enum OakCommonConfigEntryType {
OAKCOMMON_CONFIG_ENTRY_NONE = 0, /**< No entry / null type. */
OAKCOMMON_CONFIG_ENTRY_STRING = 1,
OAKCOMMON_CONFIG_ENTRY_INT = 2,
OAKCOMMON_CONFIG_ENTRY_DOUBLE = 3,
OAKCOMMON_CONFIG_ENTRY_BOOL = 4
} OakCommonConfigEntryType;
/**
* @brief Handler for configuration errors that should be shown to the user
*
* The engine layer cannot show dialogs itself. The UI registers a handler
* (e.g. QMessageBox-based) at startup; without one, errors go to stderr.
* Same injection pattern as the codec task-submit callback.
*/
typedef void (*OakCommonConfigErrorHandler)(const char *title,
const char *message,
void *userdata);
/**
* @brief Resets the store to compiled-in defaults and loads config.ini
*
* A missing file leaves the defaults in place and returns OAKCOMMON_OK.
* Malformed lines are skipped. An unreadable existing file is reported
* through the error handler and returns OAKCOMMON_E_FAILED.
*/
int oakcommon_config_load(void);
/**
* @brief Writes the current store to config.ini (via a temp file + rename)
*
* On failure the error handler is invoked and OAKCOMMON_E_FAILED is
* returned.
*/
int oakcommon_config_save(void);
/**
* @brief Resets the store to compiled-in defaults (drops custom keys)
*/
int oakcommon_config_reset_defaults(void);
/**
* @brief Sets a string entry (§2.1)
*
* A new key is created as OAKCOMMON_CONFIG_ENTRY_STRING. Setting an
* existing typed (INT/DOUBLE/BOOL) entry parses the string into its
* declared type; an unparseable value returns OAKCOMMON_E_STATE and
* leaves the entry unchanged.
*/
void oakcommon_config_set(const char *group, const char *key,
const char *value_utf8);
/**
* @brief Reads an entry as a string, two-stage buffer (§2.1)
*
* Numeric/bool entries are formatted (bools as "true"/"false", doubles
* with %g).
*
* @return Required buffer size in bytes (including the terminating NUL),
* or a negative OAKCOMMON_E_* error code (OAKCOMMON_E_NOT_FOUND when the
* key is absent).
*/
int oakcommon_config_get(const char *group, const char *key, char *buf,
int buf_size);
/**
* @brief Reads an INT entry as int (§2.1)
*
* @return The stored value, or `fallback` when the key is absent or has
* a different type.
*/
int oakcommon_config_get_int(const char *group, const char *key,
int fallback);
/**
* @brief Reads a DOUBLE entry (§2.1), fallback semantics as get_int
*/
double oakcommon_config_get_double(const char *group, const char *key,
double fallback);
/**
* @brief Sets an INT entry (32-bit, §2.1)
*/
void oakcommon_config_set_int(const char *group, const char *key, int v);
/**
* @brief INT entry as int64 (extension for channel-layout style values)
*/
int64_t oakcommon_config_get_int64(const char *group, const char *key,
int64_t fallback);
void oakcommon_config_set_int64(const char *group, const char *key,
int64_t v);
/**
* @brief BOOL entry as int 0/1 (extension), fallback semantics as get_int
*/
int oakcommon_config_get_bool(const char *group, const char *key,
int fallback);
void oakcommon_config_set_bool(const char *group, const char *key, int v);
/**
* @brief Sets a DOUBLE entry (extension)
*/
void oakcommon_config_set_double(const char *group, const char *key,
double v);
/**
* @brief Returns the OakCommonConfigEntryType of a key, or a negative
* OAKCOMMON_E_* error (OAKCOMMON_E_NOT_FOUND when the key is absent)
*/
int oakcommon_config_entry_type(const char *group, const char *key);
/**
* @brief Registers (or clears, with NULL) the error handler
*/
int oakcommon_config_set_error_handler(OakCommonConfigErrorHandler handler,
void *userdata);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_COMMON_CONFIG_H
@@ -1,123 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_CURRENT_H
#define OAK_EDITOR_CURRENT_H
#include "common/error.h"
#include "common/handle.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Neutral by-value handle to the process-wide Current singleton.
*
* Uses the standard handle layout (see common/handle.h) but with
* singleton semantics: ctx points to a statically allocated object that
* lives until process exit, so addref() and release() are intentionally
* no-ops and never destroy anything. abi_version is always
* OAKCOMMON_ABI_VERSION.
*/
typedef struct OakCurrent {
void *ctx; /**< Opaque pointer to the singleton object. */
void (*addref)(void *ctx); /**< No-op (singleton). */
void (*release)(void *ctx); /**< No-op (singleton). */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakCurrent;
/**
* @brief Destructor callback for objects handed to Current slots.
*
* Called when the slot is overwritten or cleared. May be NULL if the
* caller keeps ownership of the object.
*/
typedef void (*OakDestroyFn)(void *obj);
/**
* @brief Return a handle to the process-wide Current singleton.
*
* The returned handle is borrowed: its ctx is valid for the lifetime of
* the process. addref/release on it are no-ops; calling
* oakcommon_current_free() is allowed for symmetry and does nothing.
*/
OakCurrent oakcommon_current_instance(void);
/**
* @brief Release a Current handle.
*
* No-op: the underlying object is a singleton whose release() never
* destroys anything. Safe to call with NULL or a ctx == NULL handle.
*/
void oakcommon_current_free(OakCurrent *self);
/**
* @brief Store a pointer in a Current slot, taking over destruction.
*
* Passing NULL for obj clears the slot (destroy is ignored). If a
* previous object with a destroy callback was stored, it is destroyed.
*
* @param self Handle from oakcommon_current_instance().
* @param obj Opaque pointer to the external object (e.g. a
* VideoParams), or NULL to clear.
* @param destroy Optional destructor invoked when the slot is replaced.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if self.ctx is NULL.
*/
int oakcommon_current_set_video_params(OakCurrent self, void *obj,
OakDestroyFn destroy);
int oakcommon_current_set_audio_params(OakCurrent self, void *obj,
OakDestroyFn destroy);
int oakcommon_current_set_plugin_host(OakCurrent self, void *obj,
OakDestroyFn destroy);
int oakcommon_current_set_plugin_cache(OakCurrent self, void *obj,
OakDestroyFn destroy);
/**
* @brief Fetch the raw pointer currently stored in a slot.
*
* The returned pointer is borrowed and remains valid until the slot is
* overwritten or cleared. *out is set to NULL when the slot is empty.
*
* @param self Handle from oakcommon_current_instance().
* @param out Receives the stored pointer.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if self.ctx or
* out is NULL.
*/
int oakcommon_current_get_video_params(OakCurrent self, void **out);
int oakcommon_current_get_audio_params(OakCurrent self, void **out);
int oakcommon_current_get_plugin_host(OakCurrent self, void **out);
int oakcommon_current_get_plugin_cache(OakCurrent self, void **out);
/**
* @brief Query whether the session is interactive.
*
* @param self Handle from oakcommon_current_instance().
* @param out Receives 1 for interactive, 0 otherwise.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if self.ctx or
* out is NULL.
*/
int oakcommon_current_is_interactive(OakCurrent self, int *out);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_CURRENT_H
-113
View File
@@ -1,113 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_DEBUG_H
#define OAK_EDITOR_DEBUG_H
#include "common/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Severity levels for oakcommon debug output.
*
* Mirrors olive::DebugLevel in src/common/src/debug.h.
*/
enum OakDebugLevel {
OAKCOMMON_DEBUG_DEBUG = 0, /**< Verbose debug message. */
OAKCOMMON_DEBUG_INFO = 1, /**< Informational message. */
OAKCOMMON_DEBUG_WARNING = 2, /**< Warning message. */
OAKCOMMON_DEBUG_ERROR = 3, /**< Error message. */
OAKCOMMON_DEBUG_FATAL = 4 /**< Fatal error message. */
};
/**
* @brief Print a debug message to stderr, prefixed with its level.
*
* De-Qt replacement for the old Qt message handler. The line is
* flushed immediately.
*
* @param level One of OakDebugLevel; out-of-range values print
* as "UNKNOWN".
* @param msg NUL-terminated message; NULL is treated as an empty
* string.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if msg is NULL.
*/
int oakcommon_debug_log(int level, const char *msg);
/**
* @brief Copy the printable name of a debug level into buf.
*
* Two-segment string getter: if buf is NULL or buf_size is too small,
* nothing is written.
*
* @param level One of OakDebugLevel.
* @param buf Destination buffer, may be NULL to query the size.
* @param buf_size Size of buf in bytes.
* @return Required buffer size in bytes including the terminating NUL
* (non-negative).
*/
int oakcommon_debug_level_name(int level, char *buf, int buf_size);
/**
* @brief printf-style filtered log, replacing qDebug()/qInfo()/
* qWarning()/qCritical() call sites.
*
* The message is formatted with vsnprintf into a dynamically sized
* buffer (arbitrary length, no truncation, no fixed stack buffer) and
* emitted as "[LEVEL] message\n" unless @p level is below the current
* filter level (see oakcommon_log_set_level()).
*
* @param level One of OakDebugLevel; out-of-range values print
* as "UNKNOWN" and are never filtered out below FATAL.
* @param fmt printf-style format string. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if fmt is NULL,
* OAKCOMMON_E_FAILED if formatting failed.
*/
int oakcommon_log(int level, const char *fmt, ...);
/**
* @brief Set the minimum level emitted by oakcommon_log().
*
* Messages with a lower level are dropped. The default is
* OAKCOMMON_DEBUG_INFO.
*
* @param level One of OakDebugLevel.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if level is
* outside the OakDebugLevel range.
*/
int oakcommon_log_set_level(int level);
/**
* @brief Query the current minimum level emitted by oakcommon_log().
*
* @param out_level Receives one of OakDebugLevel. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_level is
* NULL.
*/
int oakcommon_log_get_level(int *out_level);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_DEBUG_H
@@ -1,73 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_DROPWORKFLOWBEHAVIOR_H
#define OAK_EDITOR_DROPWORKFLOWBEHAVIOR_H
#include "common/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Behavior when media is dropped onto a timeline without a
* sequence.
*
* Mirrors olive::DropWithoutSequenceBehavior in
* src/common/src/dropworkflowbehavior.h; enumerator order and values
* must stay identical because the config layer persists them as ints.
*/
enum OakDropWorkflowBehavior {
OAKCOMMON_DWS_ASK = 0, /**< Ask the user every time. */
OAKCOMMON_DWS_AUTO = 1, /**< Automatically create a sequence. */
OAKCOMMON_DWS_MANUAL = 2, /**< Never create; import manually. */
OAKCOMMON_DWS_DISABLE = 3 /**< Disable dropping entirely. */
};
/**
* @brief Check whether value is a valid OakDropWorkflowBehavior.
*
* @param value Integer behavior value (e.g. read from config).
* @return 1 if valid, 0 otherwise (this is a predicate, not a status
* code).
*/
int oakcommon_drop_workflow_behavior_is_valid(int value);
/**
* @brief Copy the printable name of a behavior into buf.
*
* Two-segment string getter: if buf is NULL or buf_size is too small,
* nothing is written. Invalid values yield "UNKNOWN".
*
* @param value One of OakDropWorkflowBehavior.
* @param buf Destination buffer, may be NULL to query the size.
* @param buf_size Size of buf in bytes.
* @return Required buffer size in bytes including the terminating NUL
* (non-negative).
*/
int oakcommon_drop_workflow_behavior_name(int value, char *buf,
int buf_size);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_DROPWORKFLOWBEHAVIOR_H
@@ -1,66 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_ERROR_H
#define OAK_EDITOR_ERROR_H
/**
* @brief Status and error codes shared by all oakcommon C API families.
*
* Return-code convention (mirrors engine/include/oakengine/init.h):
* 0 (OAKCOMMON_OK) on success, a negative OAKCOMMON_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*
* Project-wide error code scheme (-MMCCCC, 2026-08):
* every module's error codes are negative integers of the form
* -(MM * 10000 + CCCC), where MM is the module number from the registry
* below and CCCC is a module-local code. The first module-local codes
* are reserved and identical across modules: 0001 INVALID, 0002 STATE,
* 0003 FAILED, 0004 NOT_FOUND, 0005 NOMEM, 0006 CANCELLED.
*
* An error code crossing a module boundary is passed through
* UNTRANSLATED — the numeric module prefix preserves provenance
* (e.g. -30004 is oaknode's NOT_FOUND no matter which module reports it
* to the caller).
*
* Module number registry (only ever appended to; numbers are frozen):
*/
#define OAK_ERROR_MODULE_COMMON 1 /**< oakcommon */
#define OAK_ERROR_MODULE_UNDO 2 /**< oakundo */
#define OAK_ERROR_MODULE_NODE 3 /**< oaknode */
#define OAK_ERROR_MODULE_TIMELINE 4 /**< oaktimeline */
#define OAK_ERROR_MODULE_CODEC 5 /**< oakcodec */
#define OAK_ERROR_MODULE_AUDIO 6 /**< oakaudio */
#define OAK_ERROR_MODULE_RENDER 7 /**< oakrender */
#define OAK_ERROR_MODULE_TASK 8 /**< oaktask */
#define OAK_ERROR_MODULE_PLUGIN 9 /**< oakplugin */
#define OAK_ERROR_MODULE_STORAGE 10 /**< oakstorage (reserved) */
#define OAKCOMMON_OK 0 /**< Success. */
#define OAKCOMMON_E_INVALID (-10001) /**< Empty handle (ctx == NULL) or invalid argument. */
#define OAKCOMMON_E_STATE (-10002) /**< Call not valid in the current state. */
#define OAKCOMMON_E_FAILED (-10003) /**< The underlying operation failed. */
#define OAKCOMMON_E_NOT_FOUND (-10004) /**< Index out of range / entry not found. */
#define OAKCOMMON_E_NOMEM (-10005) /**< Allocation failed. */
#define SUCCESS OAKCOMMON_OK /**< @deprecated Use OAKCOMMON_OK. */
#endif //OAK_EDITOR_ERROR_H
@@ -1,116 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_FFMPEGUTILS_H
#define OAK_EDITOR_FFMPEGUTILS_H
#include "common/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Stateless mappings between native pixel/sample formats and the
* opaque FBPixelFormat / FBSampleFormat constants of ffmpeg_bridge
*
* All functions are pure format conversions; there is no handle to create
* or free. Native pixel/sample formats are passed as plain ints matching
* the olive::core::PixelFormat::Format / SampleFormat::Format enum values
* (invalid = -1). Bridge formats are the fb_pix_fmt_* / fb_sample_fmt_*
* constants from ffmpeg_bridge/ffmpeg_bridge.h.
*/
/**
* @brief RGB / RGBA channel counts (flattened from VideoParams)
*/
#define OAKCOMMON_RGB_CHANNEL_COUNT 3
#define OAKCOMMON_RGBA_CHANNEL_COUNT 4
/**
* @brief Returns a bridge pixel format that a frame can be converted to
* with minimal data loss, clamped to a maximum native precision
*
* @param pix_fmt bridge pixel format to find a compatible conversion for
* @param maximum_pix_fmt maximum native pixel format, or -1 for no limit
* @param out receives the chosen bridge pixel format
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
*/
int oakcommon_ffmpegutils_get_compatible_bridge_pixel_format(
int pix_fmt, int maximum_pix_fmt, int *out);
/**
* @brief Returns a native pixel format usable to convert from a native
* frame to a bridge frame with minimal data loss
*
* @param pix_fmt native pixel format
* @param out receives the compatible native pixel format (-1 if none)
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
*/
int oakcommon_ffmpegutils_get_compatible_pixel_format(int pix_fmt,
int *out);
/**
* @brief Returns a bridge pixel format for a given native pixel format
*
* @param pix_fmt native pixel format
* @param channel_count OAKCOMMON_RGB_CHANNEL_COUNT or
* OAKCOMMON_RGBA_CHANNEL_COUNT
* @param out receives the bridge pixel format (fb_pix_fmt_none if none)
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
*/
int oakcommon_ffmpegutils_get_ffmpeg_pixel_format(int pix_fmt,
int channel_count,
int *out);
/**
* @brief Returns a native sample format for a given bridge sample format
*
* @param smp_fmt bridge sample format
* @param out receives the native sample format (-1 if unknown)
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
*/
int oakcommon_ffmpegutils_get_native_sample_format(int smp_fmt, int *out);
/**
* @brief Returns a bridge sample format for a given native sample format
*
* @param smp_fmt native sample format
* @param out receives the bridge sample format (fb_sample_fmt_none if none)
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
*/
int oakcommon_ffmpegutils_get_ffmpeg_sample_format(int smp_fmt, int *out);
/**
* @brief Converts a "JPEG" full-range bridge pixel format to its regular
* counterpart
*
* @param pix_fmt bridge pixel format
* @param out receives the regular-range format (unchanged if not JPEG)
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
*/
int oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(int pix_fmt,
int *out);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_FFMPEGUTILS_H
@@ -1,167 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_FILEFUNCTIONS_H
#define OAK_EDITOR_FILEFUNCTIONS_H
#include "common/error.h"
#include "common/handle.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Neutral by-value handle for the filefunctions family
*
* File functions are stateless; the handle only exists to keep the C API
* shape uniform across oakcommon families. Ownership/count semantics
* follow the convention in common/handle.h: init returns a handle whose
* (empty) object has reference count 1, addref(ctx)/release(ctx) adjust
* it atomically, and release destroys it at zero. abi_version is always
* OAKCOMMON_ABI_VERSION.
*/
typedef struct OakFileFunctions {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakFileFunctions;
/**
* @brief Creates a filefunctions handle
*
* @return Handle with reference count 1; ctx is NULL on failure.
*/
OakFileFunctions oakcommon_filefunctions_init(void);
/**
* @brief Releases one reference to a filefunctions handle
*
* Convenience wrapper around handle.release(handle.ctx): decrements the
* atomic reference count and destroys the object when it reaches zero.
* No-op when self is NULL or self->ctx is NULL.
*/
void oakcommon_filefunctions_free(OakFileFunctions *self);
/**
* @brief Returns a deterministic identifier string for a file
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code. Returns an empty string (required size 1) if
* the file does not exist.
*/
int oakcommon_filefunctions_get_unique_file_identifier(
OakFileFunctions self, const char *filename, char *buf,
int buf_size);
int oakcommon_filefunctions_get_configuration_location(
OakFileFunctions self, char *buf, int buf_size);
int oakcommon_filefunctions_get_application_path(
OakFileFunctions self, char *buf, int buf_size);
int oakcommon_filefunctions_get_temp_file_path(
OakFileFunctions self, char *buf, int buf_size);
int oakcommon_filefunctions_get_auto_recovery_root(
OakFileFunctions self, char *buf, int buf_size);
/**
* @brief Checks whether `source` can be copied to `dest` without
* overwriting anything
*
* @param out Receives 1 (safe) or 0 (would overwrite).
*/
int oakcommon_filefunctions_can_copy_directory_without_overwriting(
OakFileFunctions self, const char *source, const char *dest,
int *out);
/**
* @brief Recursively copies a directory
*/
int oakcommon_filefunctions_copy_directory(OakFileFunctions self,
const char *source,
const char *dest, int overwrite);
/**
* @brief Checks whether a directory exists, optionally creating it
*
* @param out Receives 1 (valid) or 0 (invalid).
*/
int oakcommon_filefunctions_directory_is_valid(
OakFileFunctions self, const char *dir,
int try_to_create_if_not_exists, int *out);
/**
* @brief Ensures a filename ends with the given extension (no dot)
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code.
*/
int oakcommon_filefunctions_ensure_filename_extension(
OakFileFunctions self, const char *filename,
const char *extension, char *buf, int buf_size);
/**
* @brief Reads an entire file into a string
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code. Returns an empty string (required size 1) if
* the file cannot be read.
*/
int oakcommon_filefunctions_read_file_as_string(
OakFileFunctions self, const char *filename, char *buf,
int buf_size);
/**
* @brief Returns a non-existing temporary variant of `original`
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code.
*/
int oakcommon_filefunctions_get_safe_temporary_filename(
OakFileFunctions self, const char *original, char *buf,
int buf_size);
/**
* @brief Renames `from` to `to`, deleting `to` first if it exists
*
* @param out Receives 1 (renamed) or 0 (failed).
*/
int oakcommon_filefunctions_rename_file_allow_overwrite(
OakFileFunctions self, const char *from, const char *to,
int *out);
/**
* @brief Appends the platform executable suffix (".exe" on Windows)
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code.
*/
int oakcommon_filefunctions_get_formatted_executable_for_platform(
OakFileFunctions self, const char *unformatted, char *buf,
int buf_size);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_FILEFUNCTIONS_H
@@ -1,69 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_HANDLE_H
#define OAK_EDITOR_HANDLE_H
#include <stdint.h>
/**
* @brief Current ABI version stamped into every oakcommon handle.
*
* Bump whenever the handle layout or the semantics of any exported
* function change incompatibly. Consumers should compare a handle's
* abi_version field against the value they were compiled with before
* dereferencing ctx.
*/
#define OAKCOMMON_ABI_VERSION 1
/**
* @brief Neutral handle convention shared by all oakcommon wrappers.
*
* Every wrapper type is a by-value struct with the same four fields:
*
* typedef struct OakXxx {
* void *ctx; // opaque, points to the impl
* void (*addref)(void *ctx); // atomic +1, owner-DLL code
* void (*release)(void *ctx); // atomic -1, destroys at 0
* uint32_t abi_version; // OAKCOMMON_ABI_VERSION
* } OakXxx;
*
* Rules:
* - oakcommon_<name>_init*() returns a handle whose underlying object
* has reference count 1.
* - Copying the struct copies the pointer, not the count: call
* handle.addref(handle.ctx) for every additional long-lived copy and
* handle.release(handle.ctx) (or the oakcommon_<name>_free()
* convenience wrapper) when done with each copy.
* - release() decrements the atomic count and destroys the underlying
* object when it reaches zero; the destructor runs in the DLL that
* created the object, so cross-DLL handing is safe.
* - The struct itself carries no ownership: it is never heap-allocated
* by the API, so it needs no destruction of its own.
* - Functions that only read a handle take it BY VALUE (OakXxx self);
* an empty handle (ctx == NULL) is reported as OAKCOMMON_E_INVALID.
* oakcommon_<name>_free() deliberately stays a pointer API
* (OakXxx *h, like av_frame_unref()/av_buffer_unref()) so it can
* null out the caller's ctx after the final release; NULL and
* ctx == NULL are no-ops. Out parameters that produce a handle
* (e.g. option/positional-argument registration) also stay pointers.
*/
#endif //OAK_EDITOR_HANDLE_H
@@ -1,44 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_LOOPMODE_H
#define OAK_EDITOR_LOOPMODE_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Playback loop mode, mirroring olive::LoopMode.
*
* The numeric values must stay in sync with src/common/src/loopmode.h.
* Pure enum: no functions are needed.
*/
enum OakLoopMode {
OAKCOMMON_LOOP_MODE_OFF = 0, /**< Looping disabled. */
OAKCOMMON_LOOP_MODE_LOOP = 1, /**< Loop playback. */
OAKCOMMON_LOOP_MODE_CLAMP = 2 /**< Clamp at the end. */
};
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_LOOPMODE_H
@@ -1,117 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_MISCUTILS_H
#define OAK_EDITOR_MISCUTILS_H
#include "common/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Minimum decibel value used by the editor (-200.0 dB).
*
* In basically all circumstances, this calculates to 0.0 linear.
*/
#define OAKCOMMON_DECIBEL_MINIMUM (-200.0)
/**
* @brief Convert a linear amplitude to decibels.
*
* A linear value of 0.0 (or anything yielding an infinite result) returns
* OAKCOMMON_DECIBEL_MINIMUM.
*
* @param linear Linear amplitude.
* @param out_db Receives the decibel value. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_db is NULL.
*/
int oakcommon_decibel_from_linear(double linear, double *out_db);
/**
* @brief Convert decibels to a linear amplitude.
*
* Results below 1e-6 are clamped to 0.0.
*
* @param db Decibel value.
* @param out_linear Receives the linear amplitude. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_linear is NULL.
*/
int oakcommon_decibel_to_linear(double db, double *out_linear);
/**
* @brief Convert a logarithmic slider position (0..1) to decibels.
*
* @param logarithmic Logarithmic position.
* @param out_db Receives the decibel value. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_db is NULL.
*/
int oakcommon_decibel_from_logarithmic(double logarithmic, double *out_db);
/**
* @brief Convert decibels to a logarithmic slider position (0..1).
*
* @param db Decibel value.
* @param out_logarithmic Receives the logarithmic position. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_logarithmic
* is NULL.
*/
int oakcommon_decibel_to_logarithmic(double db, double *out_logarithmic);
/**
* @brief Convert a linear amplitude directly to a logarithmic position.
*
* @param linear Linear amplitude.
* @param out_logarithmic Receives the logarithmic position. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_logarithmic
* is NULL.
*/
int oakcommon_decibel_linear_to_logarithmic(double linear,
double *out_logarithmic);
/**
* @brief Convert a logarithmic position directly to a linear amplitude.
*
* @param logarithmic Logarithmic position.
* @param out_linear Receives the linear amplitude. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_linear is NULL.
*/
int oakcommon_decibel_logarithmic_to_linear(double logarithmic,
double *out_linear);
/**
* @brief Linearly interpolate between a and b using t.
*
* t should be between 0.0 and 1.0: 0.0 returns a, 1.0 returns b.
*
* @param a Start value.
* @param b End value.
* @param t Interpolation factor.
* @param out_value Receives the interpolated value. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_value is NULL.
*/
int oakcommon_lerp(double a, double b, double t, double *out_value);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_MISCUTILS_H
@@ -1,105 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_OCIOUTILS_H
#define OAK_EDITOR_OCIOUTILS_H
#include "common/error.h"
#include "common/handle.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Native pixel format codes, mirroring olive::core::PixelFormat
*
* The numeric values must stay in sync with
* olive/core/render/pixelformat.h (Format enum).
*/
enum OakPixelFormat {
OAKCOMMON_PIXEL_FORMAT_INVALID = -1, /**< Invalid/unknown format. */
OAKCOMMON_PIXEL_FORMAT_U8 = 0, /**< 8-bit unsigned integer. */
OAKCOMMON_PIXEL_FORMAT_U10 = 1, /**< 10-bit unsigned integer. */
OAKCOMMON_PIXEL_FORMAT_U16 = 2, /**< 16-bit unsigned integer. */
OAKCOMMON_PIXEL_FORMAT_F16 = 3, /**< 16-bit float (half). */
OAKCOMMON_PIXEL_FORMAT_F32 = 4, /**< 32-bit float. */
OAKCOMMON_PIXEL_FORMAT_COUNT = 5 /**< Sentinel, not a valid format. */
};
/**
* @brief OpenColorIO bit depth codes, matching OCIO::BitDepth
*
* Returned through the out parameter of
* oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format() as a plain
* int so that callers never see OCIO types. Values match the OCIO
* BitDepth enum: 0 = unknown, 1 = uint8, 2 = uint10, 3 = uint12,
* 4 = uint14, 5 = uint16, 6 = uint32, 7 = f16, 8 = f32 (OCIO v2).
*/
/**
* @brief Neutral by-value handle for the OCIO utils family
*
* The object is stateless; the handle exists only to satisfy the C API
* lifetime contract. Ownership/count semantics follow common/handle.h:
* init returns a handle whose (empty) object has reference count 1 and
* release destroys it at zero. abi_version is always
* OAKCOMMON_ABI_VERSION.
*/
typedef struct OakOCIOUtils {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakOCIOUtils;
/**
* @brief Creates an OCIOUtils handle
*
* @return Handle with reference count 1; ctx is NULL on failure.
*/
OakOCIOUtils oakcommon_ocioutils_init(void);
/**
* @brief Releases one reference to an OCIOUtils handle
*
* Convenience wrapper around handle.release(handle.ctx); no-op when
* self is NULL or self->ctx is NULL.
*/
void oakcommon_ocioutils_free(OakOCIOUtils *self);
/**
* @brief Maps a native pixel format to an OCIO bit depth
*
* @param self handle from oakcommon_ocioutils_init()
* @param pixel_format one of the OakPixelFormat values
* @param out_bit_depth receives the OCIO bit depth as an int (see the
* OakOCIOUtils typedef documentation); set to 0
* (BIT_DEPTH_UNKNOWN) for invalid formats
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self.ctx or
* out_bit_depth is NULL or pixel_format is not a known code
*/
int oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
OakOCIOUtils self, int pixel_format, int *out_bit_depth);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_OCIOUTILS_H
@@ -1,125 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_OIIOUTILS_H
#define OAK_EDITOR_OIIOUTILS_H
#include "common/error.h"
/* Reuses the OakPixelFormat enum (mirroring
* olive::core::PixelFormat) rather than redefining it here. */
#include "common/ocioutils.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief OIIO base type codes, matching OIIO::TypeDesc::BASETYPE
*
* Passed through the C API as plain ints so callers never see OIIO
* types. Values match the OIIO TypeDesc::BASETYPE enum: 0 = UNKNOWN,
* 1 = NONE, 2 = UINT8, 3 = INT8, 4 = UINT16, 5 = INT16, 6 = UINT32,
* 7 = INT32, 8 = UINT64, 9 = INT64, 10 = HALF, 11 = FLOAT, 12 = DOUBLE,
* 13 = STRING, 14 = PTR. OIIO >= 2.5 adds 15 = USTRINGHASH and shifts
* LASTBASE, so the exact LASTBASE value is version-dependent.
*/
/**
* @brief Neutral by-value handle for the OIIO utils family
*
* The object is stateless; the handle exists only to satisfy the C API
* lifetime contract. Ownership/count semantics follow common/handle.h:
* init returns a handle whose (empty) object has reference count 1 and
* release destroys it at zero. abi_version is always
* OAKCOMMON_ABI_VERSION.
*/
typedef struct OakOIIOUtils {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakOIIOUtils;
/**
* @brief Creates an OIIOUtils handle
*
* @return Handle with reference count 1; ctx is NULL on failure.
*/
OakOIIOUtils oakcommon_oiioutils_init(void);
/**
* @brief Releases one reference to an OIIOUtils handle
*
* Convenience wrapper around handle.release(handle.ctx); no-op when
* self is NULL or self->ctx is NULL.
*/
void oakcommon_oiioutils_free(OakOIIOUtils *self);
/**
* @brief Maps a native pixel format to an OIIO base type
*
* @param self handle from oakcommon_oiioutils_init()
* @param pixel_format one of the OakPixelFormat values
* @param out_base_type receives the OIIO base type as an int (see the
* OakOIIOUtils typedef documentation); set to 0
* (TypeDesc::UNKNOWN) for invalid or unmappable formats
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self.ctx or
* out_base_type is NULL or pixel_format is not a known code
*/
int oakcommon_oiioutils_get_oiio_base_type_from_format(
OakOIIOUtils self, int pixel_format, int *out_base_type);
/**
* @brief Maps an OIIO base type to a native pixel format
*
* @param self handle from oakcommon_oiioutils_init()
* @param base_type an OIIO TypeDesc::BASETYPE value as an int
* @param out_pixel_format receives one of the OakPixelFormat
* values; set to OAKCOMMON_PIXEL_FORMAT_INVALID for unknown or
* unmappable base types
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self.ctx or
* out_pixel_format is NULL or base_type is negative
*/
int oakcommon_oiioutils_get_format_from_oiio_basetype(
OakOIIOUtils self, int base_type, int *out_pixel_format);
/**
* @brief Converts a PixelAspectRatio attribute value to a rational
*
* Flattened form of the former ImageSpec-based helper: the caller reads
* the "PixelAspectRatio" float attribute from the OIIO::ImageSpec
* (defaulting to 1.0 when absent) and passes it here.
*
* @param self handle from oakcommon_oiioutils_init()
* @param pixel_aspect_ratio the PixelAspectRatio attribute value
* @param out_numerator receives the rational numerator
* @param out_denominator receives the rational denominator
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self.ctx,
* out_numerator or out_denominator is NULL
*/
int oakcommon_oiioutils_get_pixel_aspect_ratio(
OakOIIOUtils self, double pixel_aspect_ratio, int *out_numerator,
int *out_denominator);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_OIIOUTILS_H
@@ -1,58 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_POWER_H
#define OAK_EDITOR_POWER_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Round `value` up to the next power of two
*
* Stateless pure function, no handle required. Writes the result to `out`.
*
* @param value Input value.
* @param out Receives the rounded value. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if `out` is NULL.
*/
int oakcommon_power_ceil_to_power_of_2(uint32_t value, uint32_t *out);
/**
* @brief Round `value` down to the nearest power of two
*
* Stateless pure function, no handle required. Writes the result to `out`.
*
* @param value Input value.
* @param out Receives the rounded value. Must not be NULL.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if `out` is NULL.
*/
int oakcommon_power_floor_to_power_of_2(uint32_t value, uint32_t *out);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_POWER_H
@@ -1,67 +0,0 @@
/***
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/>.
***/
#ifndef OAK_COMMON_QTUTILS_H
#define OAK_COMMON_QTUTILS_H
#include <stdint.h>
#include "common/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Convert a pointer to an integer value
*
* @param ptr Pointer to convert (may be NULL, yielding 0).
* @param out_value Receives the integer representation of ptr.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_value is NULL.
*/
int oakcommon_qtutils_ptr_to_value(void *ptr, uint64_t *out_value);
/**
* @brief Convert an integer produced by oakcommon_qtutils_ptr_to_value() back to a pointer
*
* @param value Integer representation of a pointer.
* @param out_ptr Receives the decoded pointer.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_ptr is NULL.
*/
int oakcommon_qtutils_value_to_ptr(uint64_t value, void **out_ptr);
/**
* @brief Get the creation (birth) time of a file as seconds since the Unix epoch
*
* Falls back to the last metadata change time when the filesystem does not
* record birth times.
*
* @param path NUL-terminated filesystem path.
* @param out_secs Receives the creation time in seconds since the epoch.
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID for NULL arguments,
* OAKCOMMON_E_NOT_FOUND if the file does not exist or cannot be stat'ed.
*/
int oakcommon_qtutils_get_creation_date(const char *path, int64_t *out_secs);
#ifdef __cplusplus
}
#endif
#endif // OAK_COMMON_QTUTILS_H
@@ -1,176 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_SUBTITLEPARAMS_H
#define OAK_EDITOR_SUBTITLEPARAMS_H
#include "common/error.h"
#include "common/handle.h"
#ifdef __cplusplus
namespace olive
{
class SubtitleParams;
}
extern "C" {
#endif
/**
* @brief Neutral by-value handle to a subtitle parameter set
* (olive::SubtitleParams).
*
* Ownership/count semantics follow the convention in common/handle.h:
* init functions return a handle whose object has reference count 1,
* addref(ctx)/release(ctx) adjust it atomically, and release destroys
* the object at zero. abi_version is always OAKCOMMON_ABI_VERSION.
*/
typedef struct OakSubtitleParams {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakSubtitleParams;
/**
* @brief Create an empty subtitle parameter set.
*
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakSubtitleParams oakcommon_subtitleparams_init(void);
#ifdef __cplusplus
/**
* @brief Copy a native olive::SubtitleParams into a new handle.
*
* The source object is deep-copied; the handle does not keep any
* reference to @p src, which may be destroyed immediately afterwards.
* Only visible to C++ consumers.
*
* @return Handle with reference count 1; ctx is NULL if src is NULL or
* on allocation failure.
*/
OakSubtitleParams oakcommon_subtitleparams_init_from_native(
const olive::SubtitleParams *src);
#endif
/**
* @brief Release one reference to a subtitle parameter set.
*
* Convenience wrapper around handle.release(handle.ctx): decrements the
* atomic reference count and destroys the object when it reaches zero.
* No-op when params is NULL or params->ctx is NULL.
*/
void oakcommon_subtitleparams_free(OakSubtitleParams *params);
int oakcommon_subtitleparams_get_stream_index(
OakSubtitleParams params, int *index);
int oakcommon_subtitleparams_set_stream_index(
OakSubtitleParams params, int index);
int oakcommon_subtitleparams_get_enabled(OakSubtitleParams params,
int *enabled);
int oakcommon_subtitleparams_set_enabled(OakSubtitleParams params,
int enabled);
/**
* @brief Query whether the set contains at least one subtitle.
*/
int oakcommon_subtitleparams_is_valid(OakSubtitleParams params,
int *is_valid);
/**
* @brief Number of subtitle entries.
*/
int oakcommon_subtitleparams_count(OakSubtitleParams params,
int *count);
/**
* @brief Out time of the last subtitle (0/1 when empty).
*/
int oakcommon_subtitleparams_duration(OakSubtitleParams params,
int *numerator, int *denominator);
/**
* @brief Append a subtitle entry.
*
* @param text Subtitle text. Must not be NULL.
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_subtitleparams_add_subtitle(OakSubtitleParams params,
int in_num, int in_den, int out_num,
int out_den, const char *text);
/**
* @brief Remove all subtitle entries.
*/
int oakcommon_subtitleparams_clear(OakSubtitleParams params);
/**
* @brief Get the time range of the subtitle at @p index.
*
* @return OAKCOMMON_OK, OAKCOMMON_E_NOT_FOUND if @p index is out of range,
* or another negative OAKCOMMON_E_* error code.
*/
int oakcommon_subtitleparams_get_subtitle(OakSubtitleParams params,
int index, int *in_num, int *in_den,
int *out_num, int *out_den);
/**
* @brief Get the text of the subtitle at @p index (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), OAKCOMMON_E_NOT_FOUND if @p index is out of
* range, or another negative OAKCOMMON_E_* error code.
*/
int oakcommon_subtitleparams_get_subtitle_text(OakSubtitleParams params,
int index, char *buf,
int buf_size);
/**
* @brief Generate a default ASS header (static, no handle required).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_subtitleparams_generate_ass_header(char *buf, int buf_size);
/**
* @brief Load subtitles from an XML fragment.
*
* @param xml NUL-terminated XML text. Must not be NULL.
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_subtitleparams_load_xml(OakSubtitleParams params,
const char *xml);
/**
* @brief Save subtitles to an XML fragment (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_subtitleparams_save_xml(OakSubtitleParams params,
char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_SUBTITLEPARAMS_H
@@ -1,367 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_VIDEOPARAMS_H
#define OAK_EDITOR_VIDEOPARAMS_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stdint.h>
#include "common/error.h"
#include "common/handle.h"
#include "common/ocioutils.h"
#ifdef __cplusplus
namespace olive
{
class VideoParams;
}
extern "C" {
#endif
/**
* @brief Neutral by-value handle to a video parameter set
* (olive::VideoParams).
*
* Ownership/count semantics follow the convention in common/handle.h:
* init functions return a handle whose object has reference count 1,
* addref(ctx)/release(ctx) adjust it atomically, and release destroys
* the object at zero. abi_version is always OAKCOMMON_ABI_VERSION.
*/
typedef struct OakVideoParams {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakVideoParams;
/**
* @brief Interlacing modes, mirroring olive::VideoParams::Interlacing.
*/
enum OakVideoInterlacing {
OAKCOMMON_VIDEO_INTERLACE_NONE = 0,
OAKCOMMON_VIDEO_INTERLACED_TOP_FIRST = 1,
OAKCOMMON_VIDEO_INTERLACED_BOTTOM_FIRST = 2
};
/**
* @brief Video stream types, mirroring olive::VideoParams::Type.
*/
enum OakVideoType {
OAKCOMMON_VIDEO_TYPE_VIDEO = 0,
OAKCOMMON_VIDEO_TYPE_STILL = 1,
OAKCOMMON_VIDEO_TYPE_IMAGE_SEQUENCE = 2
};
/**
* @brief Color range codes, mirroring olive::VideoParams::ColorRange.
*/
enum OakVideoColorRange {
OAKCOMMON_COLOR_RANGE_LIMITED = 0, /**< 16-235 */
OAKCOMMON_COLOR_RANGE_FULL = 1 /**< 0-255 */
};
/**
* @brief Create a default (invalid) video parameter set.
*
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakVideoParams oakcommon_videoparams_init(void);
/**
* @brief Create a video parameter set without a time base.
*
* @param pixel_format One of the OakPixelFormat values.
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakVideoParams oakcommon_videoparams_init_basic(
int width, int height, int pixel_format, int nb_channels,
int pixel_aspect_num, int pixel_aspect_den, int interlacing, int divider);
/**
* @brief Create a video parameter set with a time base.
*
* The frame rate is derived as the flipped time base.
*
* @param pixel_format One of the OakPixelFormat values.
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakVideoParams oakcommon_videoparams_init_with_time_base(
int width, int height, int time_base_num, int time_base_den,
int pixel_format, int nb_channels, int pixel_aspect_num,
int pixel_aspect_den, int interlacing, int divider);
#ifdef __cplusplus
/**
* @brief Copy a native olive::VideoParams into a new handle.
*
* The source object is deep-copied; the handle does not keep any
* reference to @p src, which may be destroyed immediately afterwards.
* Only visible to C++ consumers.
*
* @return Handle with reference count 1; ctx is NULL if src is NULL or
* on allocation failure.
*/
OakVideoParams oakcommon_videoparams_init_from_native(
const olive::VideoParams *src);
/**
* @brief Borrow the native object behind a handle.
*
* The returned pointer is borrowed: it stays valid while the caller
* holds a reference to the handle (i.e. until the matching release).
* Only visible to C++ consumers.
*
* @return Borrowed pointer, or NULL if params is NULL or params->ctx is
* NULL.
*/
const olive::VideoParams *oakcommon_videoparams_get_native(
OakVideoParams params);
#endif
/**
* @brief Release one reference to a video parameter set.
*
* Convenience wrapper around handle.release(handle.ctx): decrements the
* atomic reference count and destroys the object when it reaches zero.
* No-op when params is NULL or params->ctx is NULL.
*/
void oakcommon_videoparams_free(OakVideoParams *params);
int oakcommon_videoparams_get_width(OakVideoParams params, int *width);
int oakcommon_videoparams_set_width(OakVideoParams params, int width);
int oakcommon_videoparams_get_height(OakVideoParams params, int *height);
int oakcommon_videoparams_set_height(OakVideoParams params, int height);
int oakcommon_videoparams_get_depth(OakVideoParams params, int *depth);
int oakcommon_videoparams_set_depth(OakVideoParams params, int depth);
int oakcommon_videoparams_get_is_3d(OakVideoParams params, int *is_3d);
/**
* @brief Rational getters return the value as a numerator/denominator pair.
*/
int oakcommon_videoparams_get_time_base(OakVideoParams params,
int *numerator, int *denominator);
int oakcommon_videoparams_set_time_base(OakVideoParams params,
int numerator, int denominator);
int oakcommon_videoparams_get_frame_rate(OakVideoParams params,
int *numerator, int *denominator);
int oakcommon_videoparams_set_frame_rate(OakVideoParams params,
int numerator, int denominator);
int oakcommon_videoparams_frame_rate_as_time_base(OakVideoParams params,
int *numerator,
int *denominator);
int oakcommon_videoparams_get_pixel_aspect_ratio(OakVideoParams params,
int *numerator,
int *denominator);
int oakcommon_videoparams_set_pixel_aspect_ratio(OakVideoParams params,
int numerator, int denominator);
/**
* @brief Format getters/setters use the OakPixelFormat codes.
*/
int oakcommon_videoparams_get_format(OakVideoParams params, int *format);
int oakcommon_videoparams_set_format(OakVideoParams params, int format);
int oakcommon_videoparams_get_channel_count(OakVideoParams params,
int *count);
int oakcommon_videoparams_set_channel_count(OakVideoParams params,
int count);
int oakcommon_videoparams_get_interlacing(OakVideoParams params,
int *interlacing);
int oakcommon_videoparams_set_interlacing(OakVideoParams params,
int interlacing);
int oakcommon_videoparams_get_divider(OakVideoParams params,
int *divider);
int oakcommon_videoparams_set_divider(OakVideoParams params,
int divider);
int oakcommon_videoparams_get_enabled(OakVideoParams params,
int *enabled);
int oakcommon_videoparams_set_enabled(OakVideoParams params,
int enabled);
int oakcommon_videoparams_get_x(OakVideoParams params, float *x);
int oakcommon_videoparams_set_x(OakVideoParams params, float x);
int oakcommon_videoparams_get_y(OakVideoParams params, float *y);
int oakcommon_videoparams_set_y(OakVideoParams params, float y);
int oakcommon_videoparams_get_stream_index(OakVideoParams params,
int *index);
int oakcommon_videoparams_set_stream_index(OakVideoParams params,
int index);
int oakcommon_videoparams_get_video_type(OakVideoParams params,
int *type);
int oakcommon_videoparams_set_video_type(OakVideoParams params,
int type);
int oakcommon_videoparams_get_start_time(OakVideoParams params,
int64_t *start_time);
int oakcommon_videoparams_set_start_time(OakVideoParams params,
int64_t start_time);
int oakcommon_videoparams_get_duration(OakVideoParams params,
int64_t *duration);
int oakcommon_videoparams_set_duration(OakVideoParams params,
int64_t duration);
int oakcommon_videoparams_get_premultiplied_alpha(OakVideoParams params,
int *premultiplied);
int oakcommon_videoparams_set_premultiplied_alpha(OakVideoParams params,
int premultiplied);
int oakcommon_videoparams_get_color_range(OakVideoParams params,
int *color_range);
int oakcommon_videoparams_set_color_range(OakVideoParams params,
int color_range);
int oakcommon_videoparams_get_color_primaries(OakVideoParams params,
int *primaries);
int oakcommon_videoparams_set_color_primaries(OakVideoParams params,
int primaries);
int oakcommon_videoparams_get_color_transfer(OakVideoParams params,
int *transfer);
int oakcommon_videoparams_set_color_transfer(OakVideoParams params,
int transfer);
/**
* @brief Get the colorspace name (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_videoparams_get_colorspace(OakVideoParams params,
char *buf, int buf_size);
int oakcommon_videoparams_set_colorspace(OakVideoParams params,
const char *colorspace);
/**
* @brief Width multiplied by the pixel aspect ratio.
*/
int oakcommon_videoparams_get_square_pixel_width(OakVideoParams params,
int *width);
int oakcommon_videoparams_get_effective_width(OakVideoParams params,
int *width);
int oakcommon_videoparams_get_effective_height(OakVideoParams params,
int *height);
int oakcommon_videoparams_get_effective_depth(OakVideoParams params,
int *depth);
int oakcommon_videoparams_get_is_valid(OakVideoParams params,
int *is_valid);
int oakcommon_videoparams_get_bytes_per_channel(OakVideoParams params,
int *bytes);
int oakcommon_videoparams_get_bytes_per_pixel(OakVideoParams params,
int *bytes);
int oakcommon_videoparams_get_buffer_size(OakVideoParams params,
int *size);
/**
* @brief Convert a time (in seconds, as a rational) to time base units.
*
* Returns INT64_MIN (AV_NOPTS_VALUE) in @p timestamp when no time base is
* set.
*/
int oakcommon_videoparams_get_time_in_timebase_units(
OakVideoParams params, int time_num, int time_den,
int64_t *timestamp);
/**
* @brief Compare two parameter sets for equality.
*/
int oakcommon_videoparams_equals(OakVideoParams params,
OakVideoParams other, int *equal);
/**
* @brief Load parameters from an XML fragment.
*
* @param xml NUL-terminated XML text. Must not be NULL.
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_videoparams_load_xml(OakVideoParams params,
const char *xml);
/**
* @brief Save parameters to an XML fragment (two-stage string getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_videoparams_save_xml(OakVideoParams params, char *buf,
int buf_size);
/* Static helpers (no handle required). */
int oakcommon_videoparams_get_bytes_per_channel_for_format(int pixel_format);
int oakcommon_videoparams_get_bytes_per_pixel_for_format(int pixel_format,
int channels);
int oakcommon_videoparams_calculate_buffer_size(int width, int height,
int pixel_format,
int channels);
int oakcommon_videoparams_format_is_float(int pixel_format);
int oakcommon_videoparams_generate_auto_divider(int64_t width, int64_t height);
int oakcommon_videoparams_get_scaled_dimension(int dimension, int divider);
int oakcommon_videoparams_get_divider_for_target_resolution(int src_width,
int src_height,
int dst_width,
int dst_height);
/**
* @brief Human-readable name for a divider ("Full", "1/2", ...).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_videoparams_get_name_for_divider(int divider, char *buf,
int buf_size);
/**
* @brief Human-readable name for a pixel format.
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_videoparams_get_format_name(int pixel_format, char *buf,
int buf_size);
/**
* @brief Human-readable frame rate string ("23.976 FPS").
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_videoparams_frame_rate_to_string(int numerator, int denominator,
char *buf, int buf_size);
/**
* @brief Get bytes per channel.
*
* @return Bytes per channel.
*/
int oakcommon_videoparams_static_get_bytes_per_channel(OakPixelFormat format);
/**
* @brief Get bytes per pixel.
*
* @return Bytes per pixel.
*/
int oakcommon_videoparams_static_get_bytes_per_pixel(OakPixelFormat format,
int channels);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_VIDEOPARAMS_H
@@ -1,215 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_XMLUTILS_H
#define OAK_EDITOR_XMLUTILS_H
#include "common/error.h"
#include "common/handle.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Neutral by-value handle to a streaming XML reader.
*
* Ownership/count semantics follow the convention in common/handle.h:
* init returns a handle whose object has reference count 1,
* addref(ctx)/release(ctx) adjust it atomically, and release destroys
* the object at zero. abi_version is always OAKCOMMON_ABI_VERSION.
*/
typedef struct OakXmlReader {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakXmlReader;
/**
* @brief Neutral by-value handle to a streaming XML writer.
*
* Same ownership/count semantics as OakXmlReader.
*/
typedef struct OakXmlWriter {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKCOMMON_ABI_VERSION. */
} OakXmlWriter;
/**
* @brief Create a streaming XML reader over a complete document.
*
* @param data NUL-terminated XML text. Must not be NULL.
* @return Handle with reference count 1; ctx is NULL on failure
* (NULL data, out of memory).
*/
OakXmlReader oakcommon_xml_reader_init(const char *data);
#ifdef __cplusplus
} /* extern "C" */
namespace olive { class XmlStreamReader; class XmlStreamWriter; }
extern "C" {
#endif
/**
* @brief Borrowed access to the underlying C++ reader/writer (C++ only,
* for adapter layers). Valid while the handle is held. NULL-safe.
*/
olive::XmlStreamReader *oakcommon_xml_reader_get_native(OakXmlReader reader);
olive::XmlStreamWriter *oakcommon_xml_writer_get_native(OakXmlWriter writer);
/**
* @brief Wrap an existing C++ reader/writer in a borrowed handle (C++
* only, for adapter layers). The box never owns the object; the caller
* must keep it alive and release the box with
* oakcommon_xml_reader_free()/oakcommon_xml_writer_free(). Empty handle
* for a NULL object or on allocation failure.
*/
OakXmlReader oakcommon_xml_reader_wrap_native(olive::XmlStreamReader *reader);
OakXmlWriter oakcommon_xml_writer_wrap_native(olive::XmlStreamWriter *writer);
/**
* @brief Release one reference to a reader.
*
* Convenience wrapper around handle.release(handle.ctx): decrements the
* atomic reference count and destroys the object when it reaches zero.
* No-op when reader is NULL or reader->ctx is NULL.
*/
void oakcommon_xml_reader_free(OakXmlReader *reader);
/**
* @brief Advance until the next start element, an end element, or the end
* of the document.
*
* @param reader Reader handle.
* @param found Out: 1 if positioned on a start element, 0 otherwise.
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_xml_reader_read_next_start_element(OakXmlReader reader,
int *found);
/**
* @brief Name of the current element token.
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code.
*/
int oakcommon_xml_reader_name(OakXmlReader reader, char *buf,
int buf_size);
/**
* @brief Read the concatenated character data of the current element.
*
* Must be called on a start element; consumes up to the matching end
* element.
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code.
*/
int oakcommon_xml_reader_read_element_text(OakXmlReader reader,
char *buf, int buf_size);
/**
* @brief Skip the current element and all of its children.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_xml_reader_skip_current_element(OakXmlReader reader);
/**
* @brief Number of attributes on the current start element.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_xml_reader_attribute_count(OakXmlReader reader,
int *count);
/**
* @brief Name of the attribute at @p index on the current start element.
*
* @return Required buffer size in bytes (including NUL), OAKCOMMON_E_NOT_FOUND
* if @p index is out of range, or another negative OAKCOMMON_E_* code.
*/
int oakcommon_xml_reader_attribute_name(OakXmlReader reader, int index,
char *buf, int buf_size);
/**
* @brief Value of the attribute at @p index on the current start element.
*
* @return Required buffer size in bytes (including NUL), OAKCOMMON_E_NOT_FOUND
* if @p index is out of range, or another negative OAKCOMMON_E_* code.
*/
int oakcommon_xml_reader_attribute_value(OakXmlReader reader,
int index, char *buf, int buf_size);
/**
* @brief Whether the document failed to parse.
*
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
*/
int oakcommon_xml_reader_has_error(OakXmlReader reader,
int *has_error);
/**
* @brief Create a streaming XML writer.
*
* @return Handle with reference count 1; ctx is NULL on failure.
*/
OakXmlWriter oakcommon_xml_writer_init(void);
/**
* @brief Release one reference to a writer.
*
* Convenience wrapper around handle.release(handle.ctx): decrements the
* atomic reference count and destroys the object when it reaches zero.
* No-op when writer is NULL or writer->ctx is NULL.
*/
void oakcommon_xml_writer_free(OakXmlWriter *writer);
int oakcommon_xml_writer_write_start_element(OakXmlWriter writer,
const char *name);
int oakcommon_xml_writer_write_attribute(OakXmlWriter writer,
const char *name, const char *value);
int oakcommon_xml_writer_write_characters(OakXmlWriter writer,
const char *text);
int oakcommon_xml_writer_write_text_element(OakXmlWriter writer,
const char *name,
const char *text);
int oakcommon_xml_writer_write_end_element(OakXmlWriter writer);
int oakcommon_xml_writer_write_end_document(OakXmlWriter writer);
/**
* @brief The document written so far.
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKCOMMON_E_* error code.
*/
int oakcommon_xml_writer_output(OakXmlWriter writer, char *buf,
int buf_size);
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_XMLUTILS_H
-313
View File
@@ -1,313 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_BLOCK_H
#define OAK_EDITOR_NODE_BLOCK_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stdint.h>
#include "node/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to a timeline block (olive::Block).
*
* Covers the whole Block family: ClipBlock, GapBlock and the concrete
* TransitionBlock subclasses. The object never leaves the library that
* created it; every external reference is one of these handles.
* Semantics are shared_ptr-like: the oaknode_block_*_create() factories
* below return a handle with count 1, addref(ctx) takes another
* reference, release(ctx) drops one and the library destroys the object
* when the count reaches zero. Callers never touch C++ subclasses
* directly.
*
* Placing a block on a track (the oaknode_track_*_block() primitives)
* transfers ownership to the track; handles obtained from accessors
* (neighbours, lookups) are borrowed and never destroy the underlying
* object.
*/
typedef struct OakNodeBlock {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeBlock;
/**
* @brief Reference-counted handle to a track (olive::Track), see
* node/track.h.
*
* Re-declared here so block.h is self-contained; the typedef is identical.
*/
typedef struct OakNodeTrack OakNodeTrack;
/**
* @brief Reference-counted handle to a node (olive::Node), see
* node/node.h.
*
* Re-declared here so block.h is self-contained; the typedef is identical.
*/
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Concrete transition kinds for oaknode_block_transition_create().
*/
enum OakNodeTransitionKind {
OAKNODE_TRANSITION_CROSS_DISSOLVE = 0, /**< CrossDissolveTransition. */
OAKNODE_TRANSITION_DIP_TO_COLOR = 1 /**< DipToColorTransition. */
};
/**
* @brief Input ids of a TransitionBlock's block connections
* (TransitionBlock::k_out_block_input / k_in_block_input). Pinned by
* test; pass to oaknode_node_connect()/oaknode_node_disconnect().
*/
#define OAKNODE_TRANSITION_OUT_BLOCK_INPUT "out_block_in"
#define OAKNODE_TRANSITION_IN_BLOCK_INPUT "in_block_in"
/**
* @brief Create a ClipBlock.
*
* The caller owns the block until it is placed on a track that belongs to
* a project; a block that was never placed must be released with
* oaknode_block_free().
*
* @return Block handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakNodeBlock oaknode_block_clip_create(void);
/**
* @brief Create a GapBlock. Ownership as oaknode_block_clip_create().
*
* @return Block handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakNodeBlock oaknode_block_gap_create(void);
/**
* @brief Create a concrete TransitionBlock.
*
* @param kind One of the OakNodeTransitionKind values.
* @return Block handle with reference count 1; ctx is NULL on invalid
* kind / allocation failure.
*/
OakNodeBlock oaknode_block_transition_create(int kind);
/**
* @brief Release one reference to a block handle.
*
* Destroys the block when the reference count reaches zero. NULL handle
* or NULL ctx is a no-op; clears `block->ctx` after releasing.
*
* The block must not be placed on a track or linked to other nodes; the
* caller is responsible for detaching it first.
*/
void oaknode_block_free(OakNodeBlock *block);
enum OakNodeBlockKind {
OAKNODE_BLOCK_OTHER = 0,
OAKNODE_BLOCK_CLIP = 1,
OAKNODE_BLOCK_GAP = 2,
OAKNODE_BLOCK_TRANSITION = 3
};
/**
* @brief Concrete kind of a block (dynamic_cast query).
*/
int oaknode_block_get_kind(OakNodeBlock block, int *out_kind);
/**
* @brief Borrowed cast from a block handle to its node handle.
*
* Every Block is a Node; releasing the result never destroys the block.
* Empty handle for an empty handle.
*/
OakNodeNode oaknode_block_as_node(OakNodeBlock block);
/**
* @brief Borrowed cast from a node handle to a block handle.
*
* Returns an empty handle if the node is not a Block (or is empty).
*/
OakNodeBlock oaknode_block_from_node(OakNodeNode node);
/**
* @brief Rational getters/setters use numerator/denominator out pairs.
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_in(OakNodeBlock block, int *numerator, int *denominator);
int oaknode_block_set_in(OakNodeBlock block, int numerator, int denominator);
int oaknode_block_get_out(OakNodeBlock block, int *numerator, int *denominator);
int oaknode_block_set_out(OakNodeBlock block, int numerator, int denominator);
int oaknode_block_get_length(OakNodeBlock block, int *numerator,
int *denominator);
/**
* @brief Set the block length, keeping the media out/in point anchored
* (olive::Block::set_length_and_media_out / _media_in).
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_set_length_and_media_out(OakNodeBlock block, int numerator,
int denominator);
int oaknode_block_set_length_and_media_in(OakNodeBlock block, int numerator,
int denominator);
/**
* @brief Enabled flag (olive::Block::is_enabled/set_enabled).
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_enabled(OakNodeBlock block, int *enabled);
int oaknode_block_set_enabled(OakNodeBlock block, int enabled);
/**
* @brief Adjacency accessors. `out` receives a borrowed handle (empty when
* there is no neighbour / the block is not on a track).
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_previous(OakNodeBlock block, OakNodeBlock *out);
int oaknode_block_get_next(OakNodeBlock block, OakNodeBlock *out);
int oaknode_block_get_track(OakNodeBlock block, OakNodeTrack *out);
/**
* @brief Link two blocks (olive::Node::link/unlink/are_linked).
*
* Linked blocks move together in timeline edits.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_FAILED (already
* linked / not linked).
*/
int oaknode_block_link(OakNodeBlock a, OakNodeBlock b);
int oaknode_block_unlink(OakNodeBlock a, OakNodeBlock b);
int oaknode_block_are_linked(OakNodeBlock a, OakNodeBlock b, int *linked);
/**
* @brief Number of blocks linked to `block` (olive::Node::links()).
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_link_count(OakNodeBlock block, int *count);
/**
* @brief Borrowed handle to the linked block at `index`.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_block_get_link_at(OakNodeBlock block, int index,
OakNodeBlock *out);
/* ---------------------------------------------------------------- Clip */
/**
* @brief Media in/out accessors (olive::ClipBlock). Non-clip blocks return
* OAKNODE_E_INVALID.
*/
int oaknode_clip_get_media_in(OakNodeBlock clip, int *numerator,
int *denominator);
int oaknode_clip_set_media_in(OakNodeBlock clip, int numerator,
int denominator);
/**
* @brief Playback speed factor, 1.0 = normal (olive::ClipBlock speed input).
*/
int oaknode_clip_get_speed(OakNodeBlock clip, double *speed);
int oaknode_clip_set_speed(OakNodeBlock clip, double speed);
/**
* @brief Reverse playback flag.
*/
int oaknode_clip_get_reverse(OakNodeBlock clip, int *reverse);
int oaknode_clip_set_reverse(OakNodeBlock clip, int reverse);
/**
* @brief Maintain-audio-pitch flag.
*/
int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock clip, int *maintain);
int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock clip, int maintain);
/**
* @brief Loop mode, one of the OakLoopMode values
* (olive::ClipBlock::loop_mode/set_loop_mode).
*/
int oaknode_clip_get_loop_mode(OakNodeBlock clip, int *loop_mode);
int oaknode_clip_set_loop_mode(OakNodeBlock clip, int loop_mode);
/**
* @brief Type of the track the clip sits on (OakNodeTrackType values,
* OAKNODE_TRACK_TYPE_NONE when trackless).
*/
int oaknode_clip_get_track_type(OakNodeBlock clip, int *type);
/* ----------------------------------------------------------- Transition */
/**
* @brief Transition offsets (olive::TransitionBlock). Non-transition blocks
* return OAKNODE_E_INVALID.
*/
int oaknode_transition_get_in_offset(OakNodeBlock transition, int *numerator,
int *denominator);
int oaknode_transition_get_out_offset(OakNodeBlock transition, int *numerator,
int *denominator);
int oaknode_transition_get_offset_center(OakNodeBlock transition,
int *numerator, int *denominator);
int oaknode_transition_set_offset_center(OakNodeBlock transition,
int numerator, int denominator);
int oaknode_transition_set_offsets_and_length(OakNodeBlock transition,
int in_num, int in_den,
int out_num, int out_den);
/**
* @brief Whether both sides of the transition are connected to clips.
*/
int oaknode_transition_is_dual(OakNodeBlock transition, int *dual);
/**
* @brief Borrowed handles to the connected out/in side blocks (empty when
* unconnected).
*/
int oaknode_transition_get_connected_out_block(OakNodeBlock transition,
OakNodeBlock *out);
int oaknode_transition_get_connected_in_block(OakNodeBlock transition,
OakNodeBlock *out);
/**
* @brief Forward cache passthroughs from another clip
* (ClipBlock::add_cache_passthrough_from()). Used after splitting a
* clip so the new part shares the render caches.
*/
int oaknode_clip_add_cache_passthrough_from(OakNodeBlock clip,
OakNodeBlock other);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_BLOCK_H
@@ -1,221 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_COLORMANAGER_H
#define OAK_EDITOR_NODE_COLORMANAGER_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stdint.h>
#include "common/colortransform.h"
#include "node/error.h"
#include "node/project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to a color manager
* (olive::ColorManager).
*
* Semantics are shared_ptr-like: oaknode_colormanager_init() returns a
* handle whose object has reference count 1, addref(ctx) takes another
* reference, and release(ctx) (or oaknode_colormanager_free()) drops
* one; the library destroys the object when the count reaches zero.
*/
typedef struct OakNodeColorManager {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeColorManager;
/**
* @brief Create a color manager bound to `project` (borrowed).
*
* The manager is created without a config; call
* oaknode_colormanager_initialize() (or set a config filename and
* oaknode_colormanager_update_config_from_filename()) before using the
* config-dependent queries.
*
* @return Manager handle with reference count 1 (release with
* oaknode_colormanager_free()); ctx is NULL on an empty project
* handle or allocation failure.
*/
OakNodeColorManager oaknode_colormanager_init(OakNodeProject project);
/**
* @brief Release the caller's reference to the color manager and null
* out the handle. No-op on NULL or an empty handle; the object is
* destroyed when its reference count reaches zero.
*/
void oaknode_colormanager_free(OakNodeColorManager *manager);
/**
* @brief Borrowed handle wrapping a native manager pointer held by a
* node (olive::OCIOBaseNode::manager()).
*
* The manager stays owned by its project: release() on this handle
* only frees the box. Empty handle (ctx == NULL) for a NULL native
* pointer.
*/
OakNodeColorManager oaknode_colormanager_wrap_borrowed(void *native_manager);
/**
* @brief Load the built-in default OCIO config and set the default input
* colorspace (olive::ColorManager::init()).
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_FAILED (the OCIO
* config could not be created).
*/
int oaknode_colormanager_initialize(OakNodeColorManager manager);
/**
* @brief (Re)build the process-wide default OCIO config
* (olive::ColorManager::set_up_default_config()).
*
* @return OAKNODE_OK or OAKNODE_E_FAILED.
*/
int oaknode_colormanager_set_up_default_config(void);
/**
* @brief Config filename stored on the project. Two-stage string getter:
* returns the required buffer size in bytes including NUL; pass
* buf == NULL or a too-small buffer to query the size.
*/
int oaknode_colormanager_get_config_filename(OakNodeColorManager manager,
char *buf, int buf_size);
int oaknode_colormanager_set_config_filename(OakNodeColorManager manager,
const char *filename);
/**
* @brief Reload the OCIO config from the stored filename. Missing/invalid
* files are tolerated (the previous config is kept), matching
* olive::ColorManager::update_config_from_filename().
*/
int oaknode_colormanager_update_config_from_filename(
OakNodeColorManager manager);
/**
* @brief Default input colorspace. Two-stage string accessor.
*/
int oaknode_colormanager_get_default_input_color_space(
OakNodeColorManager manager, char *buf, int buf_size);
int oaknode_colormanager_set_default_input_color_space(
OakNodeColorManager manager, const char *colorspace);
/**
* @brief Reference (working) colorspace. Two-stage string getter.
*/
int oaknode_colormanager_get_reference_color_space(
OakNodeColorManager manager, char *buf, int buf_size);
/**
* @brief Return `colorspace` when the active config lists it, otherwise the
* default input colorspace. Two-stage string getter. Requires a config
* (OAKNODE_E_STATE when none is loaded).
*/
int oaknode_colormanager_get_compliant_color_space(
OakNodeColorManager manager, const char *colorspace, char *buf,
int buf_size);
/**
* @brief Map FFmpeg color primaries/transfer codes to a colorspace of the
* active config. Two-stage string getter; an empty result (required size
* 1) means "unknown tags, use the default". Requires a config
* (OAKNODE_E_STATE when none is loaded).
*/
int oaknode_colormanager_get_colorspace_for_ffmpeg_tags(
OakNodeColorManager manager, int primaries, int trc, char *buf,
int buf_size);
/**
* @brief Config listings. Count + per-index two-stage string getters.
* All require a loaded config (OAKNODE_E_STATE otherwise); index out of
* range yields OAKNODE_E_NOT_FOUND.
*/
int oaknode_colormanager_get_display_count(OakNodeColorManager manager,
int *count);
int oaknode_colormanager_get_display_at(OakNodeColorManager manager,
int index, char *buf, int buf_size);
int oaknode_colormanager_get_default_display(OakNodeColorManager manager,
char *buf, int buf_size);
int oaknode_colormanager_get_view_count(OakNodeColorManager manager,
const char *display, int *count);
int oaknode_colormanager_get_view_at(OakNodeColorManager manager,
const char *display, int index, char *buf,
int buf_size);
int oaknode_colormanager_get_default_view(OakNodeColorManager manager,
const char *display, char *buf,
int buf_size);
int oaknode_colormanager_get_look_count(OakNodeColorManager manager,
int *count);
int oaknode_colormanager_get_look_at(OakNodeColorManager manager, int index,
char *buf, int buf_size);
int oaknode_colormanager_get_colorspace_count(OakNodeColorManager manager,
int *count);
int oaknode_colormanager_get_colorspace_at(OakNodeColorManager manager,
int index, char *buf,
int buf_size);
/**
* @brief Default luma coefficients of the active config into rgb[3].
* Requires a loaded config (OAKNODE_E_STATE otherwise).
*/
int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager manager,
double rgb[3]);
/**
* @brief Return a copy of `transform` whose display/view/look (or output
* colorspace) is clamped to what the active config offers
* (olive::ColorManager::get_compliant_color_space(ColorTransform, bool)).
*
* `out` receives a NEW by-value handle owned by the caller (reference
* count 1, release with oakcommon_colortransform_free()). Requires a
* loaded config (OAKNODE_E_STATE otherwise).
*/
int oaknode_colormanager_get_compliant_color_transform(
OakNodeColorManager manager, OakColorTransform transform,
int force_display, OakColorTransform *out);
#ifdef __cplusplus
} /* extern "C" */
namespace olive { class ColorManager; }
extern "C" {
#endif
/**
* @brief Borrowed access to the underlying C++ manager (C++ only, for
* adapter layers). Valid while the handle is held. NULL-safe.
*/
olive::ColorManager *oaknode_colormanager_get_native(
OakNodeColorManager manager);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_COLORMANAGER_H
-137
View File
@@ -1,137 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_DRAGGER_H
#define OAK_EDITOR_NODE_DRAGGER_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file dragger.h
* @brief C ABI for olive::NodeInputDragger (src/node/src/inputdragger.h):
* live drag of an input's value with a single commit command.
*
* A dragger wraps the engine's NodeInputDragger state machine
* (start -> drag* -> end). start() records the drag anchor and, when the
* input is keyframing, creates one keyframe at the drag time (on every
* track when requested); drag() live-sets the dragged component (clamped
* by the input's min/max properties when present); end() returns ONE
* undoable command that commits the whole drag -- undo removes the
* created keyframe(s) (restoring the pre-drag keyframe count), redo
* re-creates them with the final value.
*
* A dragger must be ended before it is freed; freeing a started dragger
* leaks the created keyframe(s) (the same ownership rule as the C++
* class).
*/
/**
* @brief Reference-counted handle to an input dragger
* (olive::NodeInputDragger).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_dragger_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*/
typedef struct OakNodeDragger {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeDragger;
/**
* @brief Create an input dragger for live-drag of an input's value.
*
* `input_id` must name an existing input of `node`; `element` addresses
* an array input's element (-1 for non-array inputs). `track` is the
* create-time default; the track passed to oaknode_dragger_start()
* establishes the actual drag track.
*
* @return Dragger handle with count 1; ctx is NULL on invalid arguments
* or allocation failure.
*/
OakNodeDragger oaknode_dragger_create(OakNodeNode node, const char *input_id,
int element, int track);
/**
* @brief Start the drag at the given rational time (creates a keyframe
* when the input is keyframing).
*
* `insert_on_all_tracks` != 0 also creates sibling keyframes on every
* other track of the input. OAKNODE_E_STATE when the dragger was already
* started.
*/
int oaknode_dragger_start(OakNodeDragger dragger, int64_t time_num,
int64_t time_den, int track,
int insert_on_all_tracks);
/**
* @brief Drag to a new per-track component value (live; no undo).
*
* `value` carries the dragged component of the input's declared type:
* scalar types in f[0]/num; for split-track types (VEC2/3/4/COLOR) the
* POD type must match the input's declared type and the dragged
* component sits in f[0] (the facade's dragger convention). The value is
* clamped to the input's min/max properties when present.
* OAKNODE_E_STATE when the dragger was not started.
*/
int oaknode_dragger_drag(OakNodeDragger dragger, const oaknode_value *value);
/**
* @brief End the drag, returning ONE undoable command for the whole drag.
*
* `*out_command` receives an owned command handle (execute it with
* oakundo_command_redo_now(), push it onto an OakUndoStack, or release
* it with oakundo_command_free()). OAKNODE_E_STATE when the dragger was
* not started.
*/
int oaknode_dragger_end(OakNodeDragger dragger, OakUndoCommand *out_command);
/**
* @brief 1 if the dragger has been started and not yet ended.
*/
int oaknode_dragger_is_started(OakNodeDragger dragger, int *out_started);
/**
* @brief Release one reference to a dragger handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* dragger when the count reaches zero. NULL handle or NULL ctx is a
* no-op; clears `dragger->ctx` after releasing. The dragger must have
* been ended (see the file comment).
*/
void oaknode_dragger_free(OakNodeDragger *dragger);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_DRAGGER_H
-49
View File
@@ -1,49 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_ERROR_H
#define OAK_EDITOR_NODE_ERROR_H
/**
* @brief Status and error codes shared by all oaknode C API families.
*
* Return-code convention (mirrors engine/include/oakengine/init.h):
* 0 (OAKNODE_OK) on success, a negative OAKNODE_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
/**
* @brief Current ABI version stamped into every oaknode handle.
*
* Bump whenever a handle layout or the semantics of any exported
* function change incompatibly. Consumers should compare a handle's
* abi_version field against the value they were compiled with before
* dereferencing ctx.
*/
#define OAKNODE_ABI_VERSION 1
#define OAKNODE_OK 0 /**< Success. */
#define OAKNODE_E_INVALID (-30001) /**< NULL handle or invalid argument. */
#define OAKNODE_E_STATE (-30002) /**< Call not valid in the current state. */
#define OAKNODE_E_FAILED (-30003) /**< The underlying operation failed. */
#define OAKNODE_E_NOT_FOUND (-30004) /**< Index out of range / entry not found. */
#define OAKNODE_E_NOMEM (-30005) /**< Allocation failed. */
#endif //OAK_EDITOR_NODE_ERROR_H
-100
View File
@@ -1,100 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_FACTORY_H
#define OAK_EDITOR_NODE_FACTORY_H
#include "node/error.h"
#include "node/node.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file factory.h
* @brief C ABI for olive::NodeFactory (src/node/src/factory.h): the
* internal node-type library.
*
* The library must be populated with oaknode_factory_initialize() before
* any other call; oaknode_factory_destroy() releases it. The factory is
* a process-wide singleton (static olive::NodeFactory), so there is no
* OakNodeFactory handle type. Prototype nodes from
* oaknode_factory_node_at() are owned by the library: read-only metadata
* queries only, never add them to a graph.
*/
/**
* @brief Populate the internal node library (NodeFactory::initialize()).
* Idempotent: calling twice is a no-op.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_factory_initialize(void);
/**
* @brief Release the internal node library (NodeFactory::destroy()).
* Safe when not initialized.
*/
void oaknode_factory_destroy(void);
/**
* @brief Number of registered node types (the library size).
* OAKNODE_E_STATE when not initialized.
*/
int oaknode_factory_id_count(int *out_count);
/**
* @brief The type id of the registered node at `index`. Two-stage
* getter; OAKNODE_E_NOT_FOUND for an out-of-range index,
* OAKNODE_E_STATE when not initialized.
*/
int oaknode_factory_id_at(int index, char *buf, int buf_size);
/**
* @brief The display name of the node type `type_id`
* (NodeFactory::get_name_from_id()). Two-stage getter; an unknown id
* yields an empty string (required size 1).
*/
int oaknode_factory_name_from_id(const char *type_id, char *buf,
int buf_size);
/**
* @brief Create a node of `type_id` WITHOUT adding it to any graph
* (NodeFactory::create_from_id()). The caller owns the returned node
* (reference count 1) and must release it with oaknode_node_free() while
* it is still orphaned. ctx is NULL when the id is unknown or not
* initialized.
*/
OakNodeNode oaknode_factory_create_from_id(const char *type_id);
/**
* @brief Borrow the prototype node at `index` in the library (non-owning
* handle written to `out_node`; release it with oaknode_node_free()).
* OAKNODE_E_NOT_FOUND for an out-of-range index, OAKNODE_E_STATE when
* not initialized.
*/
int oaknode_factory_node_at(int index, OakNodeNode *out_node);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_FACTORY_H
-170
View File
@@ -1,170 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_FOLDER_H
#define OAK_EDITOR_NODE_FOLDER_H
#include <stdint.h>
#include "node/error.h"
#include "undo/undocommand.h"
#include "node/project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file folder.h
* @brief C ABI for olive::Folder (oaknode)
*
* A folder is a project node that organizes item children (footage,
* sequences, subfolders). Folder handles are borrowed from the owning
* project; they become invalid when the project is freed or cleared.
*
* Child add/remove/move operations execute the underlying undo commands
* live (redo_now); wiring them onto an undo stack is the oakundo /
* facade layer's job, not this layer's.
*/
/**
* @brief Reference-counted handle to a folder node (olive::Folder).
*
* Semantics are shared_ptr-like (see OakNodeProject): addref(ctx) takes a
* reference, release(ctx) drops one. Folder handles handed out by this API
* are borrowed views into the owning project's graph: releasing them only
* releases the handle itself, never the folder.
*/
typedef struct OakNodeFolder {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeFolder;
/**
* @brief Create a folder node owned by `project`.
*
* The folder is added to the project's graph (Project::add_node()) but is
* NOT attached under any parent folder; use oaknode_folder_add_child() to
* place it. The returned handle is borrowed: the project owns the folder,
* so releasing the handle only releases the handle itself.
*
* @return Folder handle; ctx is NULL on failure.
*/
OakNodeFolder oaknode_folder_create(OakNodeProject project);
/**
* @brief Number of direct item children (Folder::item_child_count()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_folder_child_count(OakNodeFolder folder);
/**
* @brief Borrowed node handle of the item child at `index`
* (Folder::item_child()).
*
* The returned handle only releases the handle itself. Empty handle
* (ctx == NULL) when out of range.
*/
OakNodeNode oaknode_folder_child_at(OakNodeFolder folder, int index);
/**
* @brief Add `child` as a direct item child of `folder` (live, non-undoable;
* executes FolderAddChild::redo()).
*
* After a successful call the graph owns `child`: releasing the child
* handle only releases the handle itself.
*
* @return OAKNODE_OK, OAKNODE_E_STATE if `child` already belongs to a
* folder, or another negative OAKNODE_E_* error code.
*/
int oaknode_folder_add_child(OakNodeFolder folder, OakNodeNode child);
/**
* @brief Borrowed cast from a folder handle to its node handle.
*
* The returned handle only releases the handle itself. Empty handle for an
* empty handle.
*/
OakNodeNode oaknode_folder_as_node(OakNodeFolder folder);
/**
* @brief Create an undoable FolderAddChild command.
*
* @return Command handle with reference count 1 (release with
* oakundo_command_free()); ctx is NULL on failure.
*/
OakUndoCommand oaknode_command_create_folder_add_child(
OakNodeFolder folder, OakNodeNode child);
/**
* @brief Remove `child` from `folder` without deleting it (live,
* non-undoable; executes Folder::RemoveElementCommand::redo()).
*
* @return OAKNODE_OK, OAKNODE_E_NOT_FOUND if `child` is not a direct child,
* or another negative OAKNODE_E_* error code.
*/
int oaknode_folder_remove_child(OakNodeFolder folder, OakNodeNode child);
/**
* @brief Move several nodes into `dest_folder` (live, non-undoable).
*
* Each node is removed from its current folder (if any) and appended to
* `dest_folder`; the graph assumes the lifetime of every moved node. Nodes
* already directly inside `dest_folder` are skipped.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_folder_move_children(const OakNodeNode *nodes, int count,
OakNodeFolder dest_folder);
/**
* @brief 1 if `folder` recursively contains `child`, 0 otherwise
* (Folder::has_child_recursive()). Negative OAKNODE_E_* code on empty
* handles.
*/
int oaknode_folder_has_child_recursive(OakNodeFolder folder,
OakNodeNode child);
/**
* @brief Index of `child` in `folder`'s direct children
* (Folder::index_of_child()).
*
* @return The index, OAKNODE_E_NOT_FOUND if not a direct child, or
* OAKNODE_E_INVALID on empty handles.
*/
int oaknode_folder_index_of_child(OakNodeFolder folder,
OakNodeNode child);
/**
* @brief Borrowed handle of the folder a node currently belongs to
* (Node::folder()).
*
* The returned handle only releases the handle itself. Empty handle
* (ctx == NULL) if the node is not in any folder.
*/
OakNodeFolder oaknode_folder_parent_of(OakNodeNode node);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_FOLDER_H
-256
View File
@@ -1,256 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_FOOTAGE_H
#define OAK_EDITOR_NODE_FOOTAGE_H
#include <stdint.h>
#include "common/videoparams.h"
#include "node/error.h"
// NOTE: quoted-relative to bypass the "render/cancelatom.h" transition
// bridge (oakrender's C++ olive::CancelAtom) that shadows the C ABI
// header on oaknode's include path.
#include "../../include/render/cancelatom.h"
#include "node/project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file footage.h
* @brief C ABI for olive::Footage (oaknode)
*
* A footage node references an external media file and caches its stream
* metadata. Footage handles are borrowed from the owning project; they
* become invalid when the project is freed or cleared.
*
* NOTE: setting a filename whose file exists on disk triggers a probe,
* which requires the codec/render modules (outside oaknode). Tests and
* pure-graph consumers should use nonexistent paths; probing is the
* facade layer's job.
*/
/**
* @brief Reference-counted handle to a footage node (olive::Footage).
*
* Semantics are shared_ptr-like (see OakNodeProject): addref(ctx) takes a
* reference, release(ctx) drops one. Footage handles handed out by this
* API are borrowed views into the owning project's graph: releasing them
* only releases the handle itself, never the footage.
*/
typedef struct OakNodeFootage {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeFootage;
/**
* @brief Create a footage node owned by `project` (added to the project's
* graph, not attached to any folder).
*
* The returned handle is borrowed: the project owns the footage, so
* releasing the handle only releases the handle itself.
*
* @param filename Initial media path, may be NULL/empty.
*
* @return Footage handle; ctx is NULL on failure.
*/
OakNodeFootage oaknode_footage_create(OakNodeProject project,
const char *filename);
/**
* @brief Borrowed cast from a footage handle to its node handle.
*
* The returned handle only releases the handle itself. Empty handle for an
* empty handle.
*/
OakNodeNode oaknode_footage_as_node(OakNodeFootage footage);
/**
* @brief Current media path (Footage::filename()). Two-stage string getter.
*
* @return Required buffer size in bytes including the NUL, or a negative
* OAKNODE_E_* error code.
*/
int oaknode_footage_filename(OakNodeFootage footage, char *buf,
int buf_size);
/**
* @brief Set the media path (Footage::set_filename()). Does not re-probe
* unless the file exists (see the file comment above).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_set_filename(OakNodeFootage footage, const char *filename);
/**
* @brief 1 if the footage was successfully probed and is ready for use
* (Footage::is_valid()), 0 otherwise. Negative OAKNODE_E_* code on an
* empty handle.
*/
int oaknode_footage_is_valid(OakNodeFootage footage);
/**
* @brief Last-modified timestamp of the media file in milliseconds since the
* epoch (Footage::timestamp()).
*
* @param out_timestamp Receives the timestamp. Must not be NULL.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_timestamp(OakNodeFootage footage,
int64_t *out_timestamp);
/**
* @brief Set the last-modified timestamp (Footage::set_timestamp()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_set_timestamp(OakNodeFootage footage, int64_t timestamp);
/**
* @brief Decoder ID recorded when the footage was probed
* (Footage::decoder()). Two-stage string getter.
*/
int oaknode_footage_decoder(OakNodeFootage footage, char *buf,
int buf_size);
/**
* @brief Total number of streams (Footage::get_total_stream_count()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_total_stream_count(OakNodeFootage footage);
/**
* @brief Number of video streams (ViewerOutput::get_video_stream_count()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_video_stream_count(OakNodeFootage footage);
/**
* @brief Number of audio streams (ViewerOutput::get_audio_stream_count()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_audio_stream_count(OakNodeFootage footage);
/**
* @brief Number of subtitle streams (ViewerOutput::get_subtitle_stream_count()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_subtitle_stream_count(OakNodeFootage footage);
/**
* @brief Footage duration as a rational number of seconds
* (ViewerOutput::get_length()).
*
* @param out_numerator Receives the numerator. Must not be NULL.
* @param out_denominator Receives the denominator. Must not be NULL.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_duration(OakNodeFootage footage, int *out_numerator,
int *out_denominator);
/**
* @brief 1 if proxy playback is enabled (Footage::proxy_enabled()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_proxy_enabled(OakNodeFootage footage);
/**
* @brief Enable/disable proxy playback (Footage::set_proxy_enabled()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_set_proxy_enabled(OakNodeFootage footage, int enabled);
/**
* @brief Proxy file path, or "" when none (Footage::proxy_path()).
* Two-stage string getter.
*/
int oaknode_footage_proxy_path(OakNodeFootage footage, char *buf,
int buf_size);
/**
* @brief Proxy state enum value (Footage::proxy_state():
* ProxyManager::ProxyState). Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_proxy_state(OakNodeFootage footage);
/**
* @brief Set all proxy fields at once (Footage::set_proxy()).
*
* @param path Proxy file path, may be NULL/empty.
* @param state ProxyManager::ProxyState enum value.
* @param video_stream_index Proxy's video stream index (-1 when none).
* @param preset_version Proxy preset version.
* @param enabled Non-zero to enable proxy playback.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_set_proxy(OakNodeFootage footage, const char *path,
int state, int video_stream_index,
int preset_version, int enabled);
/**
* @brief Clear all proxy fields (Footage::clear_proxy()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_clear_proxy(OakNodeFootage footage);
/**
* @brief Video stream parameters as an oakcommon video-params handle
* (ViewerOutput::get_video_params()). `out` receives a handle with
* reference count 1 (release with oakcommon_videoparams_free()).
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_footage_get_video_params(OakNodeFootage footage, int index,
OakVideoParams *out);
/**
* @brief Set a video stream's parameters from an oakcommon handle
* (ViewerOutput::set_video_params()).
*/
int oaknode_footage_set_video_params(OakNodeFootage footage, int index,
const OakVideoParams *params);
/**
* @brief Video length as a rational pair (ViewerOutput::get_video_length()).
*/
int oaknode_footage_get_video_length(OakNodeFootage footage,
int64_t *out_num, int64_t *out_den);
/**
* @brief Set the footage's cancellation atom used during probing
* (Footage::set_cancel_pointer()). `atom` may be an empty OakCancelAtom
* (ctx == NULL) to clear.
*/
int oaknode_footage_set_cancel_atom(OakNodeFootage footage,
OakCancelAtom atom);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_FOOTAGE_H
-186
View File
@@ -1,186 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_GROUP_H
#define OAK_EDITOR_NODE_GROUP_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file group.h
* @brief C ABI for olive::NodeGroup (src/node/src/group/group.h):
* input passthrough management and input resolution.
*
* An OakNodeGroup wraps an olive::NodeGroup (a Node subclass); group
* handles share the reference-counted lifetime rules of OakNodeNode.
*/
/**
* @brief Reference-counted handle to a node group (olive::NodeGroup).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_group_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero. Handles returned by
* oaknode_group_cast() are borrowed views of a node: releasing them
* never destroys the underlying group.
*/
typedef struct OakNodeGroup {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeGroup;
/**
* @brief Create a standalone NodeGroup (owned; release with
* oaknode_group_free() while still orphaned).
*
* @return Group handle with count 1; ctx is NULL on allocation failure.
*/
OakNodeGroup oaknode_group_create(void);
/**
* @brief Borrow a group view of a node (dynamic_cast). The returned
* handle is non-owning; release it with oaknode_group_free().
*
* @return Borrowed group handle; ctx is NULL when the node is not a
* NodeGroup.
*/
OakNodeGroup oaknode_group_cast(OakNodeNode node);
/**
* @brief Release one reference to a group handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* group when the count reaches zero and the handle owns it. NULL handle
* or NULL ctx is a no-op; clears `group->ctx` after releasing.
*/
void oaknode_group_free(OakNodeGroup *group);
/**
* @brief Add an input passthrough for (`node`, `input_id`, `element`)
* (live, NodeGroup::add_input_passthrough()). The generated passthrough
* id is returned through the two-stage string convention.
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
int oaknode_group_add_input_passthrough(OakNodeGroup group,
OakNodeNode node,
const char *input_id, int element,
char *buf, int buf_size);
/**
* @brief Create an add-passthrough command
* (olive::NodeGroupAddInputPassthrough). The generated id is NOT
* retrievable through this call (the command computes it on redo).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_group_add_input_passthrough_undoable(OakNodeGroup group,
OakNodeNode node,
const char *input_id,
int element,
OakUndoCommand *out_command);
/**
* @brief Remove the passthrough for (`node`, `input_id`, `element`)
* (live). OAKNODE_E_NOT_FOUND when no such passthrough exists.
*/
int oaknode_group_remove_input_passthrough(OakNodeGroup group,
OakNodeNode node,
const char *input_id, int element);
/**
* @brief Number of registered input passthroughs.
*/
int oaknode_group_passthrough_count(OakNodeGroup group, int *out_count);
/**
* @brief The passthrough id at `index`. Two-stage getter;
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_group_passthrough_id_at(OakNodeGroup group, int index,
char *buf, int buf_size);
/**
* @brief The inner input behind passthrough `index`: node (borrowed
* handle written to `out_node` when non-NULL; release it with
* oaknode_node_free()), input id (two-stage string) and element.
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_group_passthrough_input_at(OakNodeGroup group, int index,
OakNodeNode *out_node, char *buf,
int buf_size, int *out_element);
/**
* @brief The output passthrough node (borrowed handle written to
* `out_node`; release it with oaknode_node_free()), an empty handle when
* unset. OAKNODE_OK is returned either way.
*/
int oaknode_group_get_output_passthrough(OakNodeGroup group,
OakNodeNode *out_node);
/**
* @brief Set the output passthrough node directly (live). `node` may be
* an empty handle to clear the passthrough.
*/
int oaknode_group_set_output_passthrough(OakNodeGroup group,
OakNodeNode node);
/**
* @brief Create a set-output-passthrough command
* (olive::NodeGroupSetOutputPassthrough).
*/
int oaknode_group_set_output_passthrough_undoable(
OakNodeGroup group, OakNodeNode node, OakUndoCommand *out_command);
/**
* @brief Resolve an input through group passthroughs
* (NodeGroup::resolve_input()): follows a group's passthrough id to the
* inner node input. Non-group inputs resolve to themselves.
*
* `out_node` (may be NULL) receives a borrowed handle (release it with
* oaknode_node_free()); the resolved input id uses the two-stage string
* convention; `out_element` (may be NULL) receives the element.
* OAKNODE_E_NOT_FOUND when the input does not resolve to a valid target.
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
int oaknode_group_resolve_input(OakNodeNode node, const char *input_id,
int element, OakNodeNode *out_node,
char *buf, int buf_size, int *out_element);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_GROUP_H
-295
View File
@@ -1,295 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_KEYFRAME_H
#define OAK_EDITOR_NODE_KEYFRAME_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file keyframe.h
* @brief C ABI for olive::NodeKeyframe (src/node/src/keyframe.h).
*
* An OakNodeKeyframe wraps an olive::NodeKeyframe. Handles created by
* oaknode_keyframe_create() are owned and must be released with
* oaknode_keyframe_free(); keyframes attached to a node input's track
* are owned by the node.
*
* Every setter comes in a live variant and an undoable variant (suffix
* _undoable) returning an owned, un-executed OakUndoCommand.
*/
/**
* @brief Interpolation type of a keyframe (olive::NodeKeyframe::Type).
*/
typedef enum oaknode_keyframe_type {
OAKNODE_KEYFRAME_INVALID = -1,
OAKNODE_KEYFRAME_LINEAR = 0,
OAKNODE_KEYFRAME_HOLD = 1,
OAKNODE_KEYFRAME_BEZIER = 2
} oaknode_keyframe_type;
/**
* @brief Bezier handle selector (olive::NodeKeyframe::BezierType).
*/
typedef enum oaknode_keyframe_bezier {
OAKNODE_KEYFRAME_IN_HANDLE = 0,
OAKNODE_KEYFRAME_OUT_HANDLE = 1
} oaknode_keyframe_bezier;
/**
* @brief Reference-counted handle to a keyframe (olive::NodeKeyframe).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_keyframe_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*/
typedef struct OakNodeKeyframe {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeKeyframe;
/**
* @brief Create a standalone keyframe (owned; release with
* oaknode_keyframe_free()).
*
* `value` may be NULL (null variant); OAKNODE_VALUE_STRING is rejected
* (use oaknode_keyframe_set_value_string() after creation). `type` is an
* oaknode_keyframe_type. `parent_or_null` may be an empty handle.
*
* @return Keyframe handle with count 1; ctx is NULL on invalid argument
* or allocation failure.
*/
OakNodeKeyframe oaknode_keyframe_create(int64_t time_num, int64_t time_den,
const oaknode_value *value, int type,
int track, int element,
const char *input_id,
OakNodeNode parent_or_null);
/**
* @brief Release one reference to a keyframe handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* keyframe when the count reaches zero and the handle owns it. NULL
* handle or NULL ctx is a no-op; clears `keyframe->ctx` after releasing.
* Never free a keyframe that is attached to a node's track.
*/
void oaknode_keyframe_free(OakNodeKeyframe *keyframe);
/**
* @brief The keyframe's time as a rational (numerator/denominator).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_keyframe_get_time(OakNodeKeyframe keyframe,
int64_t *out_num, int64_t *out_den);
/**
* @brief Set the keyframe's time directly (live).
*/
int oaknode_keyframe_set_time(OakNodeKeyframe keyframe, int64_t time_num,
int64_t time_den);
/**
* @brief Create a set-time command (olive::NodeParamSetKeyframeTimeCommand).
*/
int oaknode_keyframe_set_time_undoable(OakNodeKeyframe keyframe,
int64_t time_num, int64_t time_den,
OakUndoCommand *out_command);
/**
* @brief Read the keyframe's value mapped into `out`. Values without a
* POD representation fail with OAKNODE_E_FAILED.
*/
int oaknode_keyframe_get_value(OakNodeKeyframe keyframe,
oaknode_value *out);
/**
* @brief Set the keyframe's value directly (live).
* OAKNODE_VALUE_STRING is rejected (use
* oaknode_keyframe_set_value_string()).
*/
int oaknode_keyframe_set_value(OakNodeKeyframe keyframe,
const oaknode_value *v);
/**
* @brief Create a set-value command
* (olive::NodeParamSetKeyframeValueCommand).
*/
int oaknode_keyframe_set_value_undoable(OakNodeKeyframe keyframe,
const oaknode_value *v,
OakUndoCommand *out_command);
/**
* @brief Read a string value. Two-stage getter.
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
int oaknode_keyframe_get_value_string(OakNodeKeyframe keyframe,
char *buf, int buf_size);
/**
* @brief Set a string value directly (live).
*/
int oaknode_keyframe_set_value_string(OakNodeKeyframe keyframe,
const char *value);
/**
* @brief Create a set-string-value command.
*/
int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe keyframe,
const char *value,
OakUndoCommand *out_command);
/**
* @brief The keyframe's interpolation type (oaknode_keyframe_type).
*/
int oaknode_keyframe_get_type(OakNodeKeyframe keyframe, int *out_type);
/**
* @brief Set the interpolation type directly (live,
* NodeKeyframe::set_type(), which adjusts neighbouring bezier handles).
*/
int oaknode_keyframe_set_type(OakNodeKeyframe keyframe, int type);
/**
* @brief Create a set-type command (same semantics as the live variant).
*/
int oaknode_keyframe_set_type_undoable(OakNodeKeyframe keyframe, int type,
OakUndoCommand *out_command);
/**
* @brief A bezier control point (`handle` is an
* oaknode_keyframe_bezier).
*/
int oaknode_keyframe_get_bezier_control(OakNodeKeyframe keyframe,
int handle, double *out_x,
double *out_y);
/**
* @brief Set a bezier control point directly (live).
*/
int oaknode_keyframe_set_bezier_control(OakNodeKeyframe keyframe, int handle,
double x, double y);
/**
* @brief Create a set-bezier-control command.
*/
int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe keyframe,
int handle, double x, double y,
OakUndoCommand *out_command);
/**
* @brief The keyframe's track index.
*/
int oaknode_keyframe_get_track(OakNodeKeyframe keyframe,
int *out_track);
/**
* @brief The keyframe's element index.
*/
int oaknode_keyframe_get_element(OakNodeKeyframe keyframe,
int *out_element);
/**
* @brief The id of the input this keyframe belongs to. Two-stage getter.
*/
int oaknode_keyframe_get_input(OakNodeKeyframe keyframe, char *buf,
int buf_size);
/**
* @brief The node this keyframe belongs to (borrowed handle written to
* `out_node`; release it with oaknode_node_free()), an empty handle when
* orphaned. OAKNODE_OK either way.
*/
int oaknode_keyframe_get_parent(OakNodeKeyframe keyframe,
OakNodeNode *out_node);
/**
* @brief A bezier control point guaranteed valid for animation
* (NodeKeyframe::valid_bezier_control_in()/out()).
*
* Unlike oaknode_keyframe_get_bezier_control(), the returned point is
* clamped so the curve never overlaps: the in-handle's x cannot pass the
* previous keyframe's time and the out-handle's x cannot pass the next
* keyframe's time. `handle` is an oaknode_keyframe_bezier.
*/
int oaknode_keyframe_get_valid_bezier_control(OakNodeKeyframe keyframe,
int handle, double *out_x,
double *out_y);
/**
* @brief The opposing bezier handle type
* (NodeKeyframe::get_opposing_bezier_type): OAKNODE_KEYFRAME_IN_HANDLE
* (0) <-> OAKNODE_KEYFRAME_OUT_HANDLE (1).
*
* @return The opposing handle type, or OAKNODE_E_INVALID for a type
* outside the two handle values.
*/
int oaknode_keyframe_opposing_bezier_type(int type);
/**
* @brief Compute the combined node value to use when inserting
* `keyframe` onto `target_node` (the keyframe paste path).
*
* Takes the target node's split value at the keyframe's time, replaces
* the keyframe's own track with the keyframe's value, and combines the
* per-track components into a single normal value (mirrors the facade's
* oakengine_keyframe_compute_paste_value). OAKNODE_E_NOT_FOUND when the
* keyframe's input id does not exist on `target_node`; OAKNODE_E_FAILED
* for input types without a POD representation.
*/
int oaknode_keyframe_compute_paste_value(OakNodeNode target_node,
OakNodeKeyframe keyframe,
oaknode_value *out);
/**
* @brief 1 if a sibling keyframe exists at the given rational time on
* this keyframe's own track (NodeKeyframe::has_sibling_at_time(): the
* track's key at `time` that is not this keyframe — the move-collision
* check). Unlike the facade, the time is an exact rational rather than a
* whole-second frame timestamp, and no track argument is needed (the
* lookup is relative to this keyframe's track).
*
* An orphaned keyframe (no parent node) has no siblings: `*out_value`
* is set to 0 and OAKNODE_OK is returned.
*/
int oaknode_keyframe_has_sibling_at_time(OakNodeKeyframe keyframe,
int64_t time_num, int64_t time_den,
int *out_value);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_KEYFRAME_H
-119
View File
@@ -1,119 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_MULTICAM_H
#define OAK_EDITOR_NODE_MULTICAM_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file multicam.h
* @brief C ABI for olive::MultiCamNode (src/node/src/input/multicam/
* multicamnode.h): multi-camera source switching and the source-grid
* math used by the multicam viewer.
*
* The input-id getters return static strings (never freed) naming the
* multicam node's inputs: current source (combo), sources (array),
* sequence and sequence type. A node that is not a MultiCamNode (or a
* NULL handle) fails the per-node queries with OAKNODE_E_INVALID.
*
* The grid helpers are static and pure: they only depend on their
* arguments, not on a node.
*/
/**
* @brief The input id string for the current camera ("current_in").
*/
const char *oaknode_multicam_input_current(void);
/**
* @brief The input id string for the sources array ("sources_in").
*/
const char *oaknode_multicam_input_sources(void);
/**
* @brief The input id string for the sequence ("sequence_in").
*/
const char *oaknode_multicam_input_sequence(void);
/**
* @brief The input id string for the sequence type ("sequence_type_in").
*/
const char *oaknode_multicam_input_sequence_type(void);
/**
* @brief Number of connected source cameras (MultiCamNode::
* get_source_count(); the connected sequence's track count, or the
* sources array size when no sequence is connected).
*
* OAKNODE_E_INVALID when `node` is not a multicam.
*/
int oaknode_multicam_get_source_count(OakNodeNode node, int *out_count);
/**
* @brief Compute the grid (rows, cols) that holds `source_count` cells.
*
* Mirrors MultiCamNode::get_rows_and_columns(): the grid grows from
* 1x1, widening the smaller dimension, until rows * cols >= source_count
* (0 sources yields 1x1). OAKNODE_E_INVALID for a negative count or
* NULL out pointers.
*/
int oaknode_multicam_get_rows_and_columns(int source_count, int *rows,
int *cols);
/**
* @brief Convert a flat source index to (row, col) in a rows x cols grid
* (row-major: col = index % cols, row = index / cols).
*
* OAKNODE_E_INVALID for a negative index, degenerate grid or NULL out
* pointers.
*/
int oaknode_multicam_index_to_row_cols(int index, int rows, int cols,
int *out_row, int *out_col);
/**
* @brief Convert (row, col) to a flat source index (col + row * cols).
*
* @return The flat index (>= 0), or OAKNODE_E_INVALID when the cell is
* out of range or the grid is degenerate.
*/
int oaknode_multicam_rows_cols_to_index(int row, int col, int rows,
int cols);
/**
* @brief The current source index (MultiCamNode::get_current_source(),
* the "current_in" combo value).
*
* OAKNODE_E_INVALID when `node` is not a multicam.
*/
int oaknode_multicam_get_current_source(OakNodeNode node, int *out_source);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_MULTICAM_H
-679
View File
@@ -1,679 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_NODE_H
#define OAK_EDITOR_NODE_NODE_H
#include <stdint.h>
#include "common/videoparams.h"
#include "node/error.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file node.h
* @brief C ABI for olive::Node (src/node/src/node.h).
*
* Handles are by-value reference-counted structs (see
* include/common/handle.h): every OakNodeNode carries ctx/addref/release/
* abi_version and behaves like a shared_ptr at the ABI level. Factory
* functions return a handle with reference count 1; release it with
* oaknode_node_free(). Handles borrowed from a graph only release the
* handle itself when freed; once a node lives in a project graph its
* lifetime belongs to the graph (the implementation flips ownership
* internally), and borrowed handles become invalid when the owning project
* or node is destroyed.
*
* Parameter values cross the boundary as the POD oaknode_value; the
* meaningful fields depend on its type (oaknode_value_type). String-typed
* inputs (NodeValue::k_file/k_text/k_font/k_str_combo) do not fit the POD
* and use the dedicated *_input_string() pair (two-stage buf/size getters
* return the required size including the terminating NUL).
*
* Every mutating function comes in a live variant (applies immediately)
* and an undoable variant (suffix _undoable) that creates an
* olive::UndoCommand without executing it and returns it as an owned
* OakUndoCommand handle. Execute it with oakundo_command_redo_now(),
* push it onto an OakUndoStack, or release it with
* oakundo_command_free().
*/
/**
* @brief Value type of an oaknode_value / a node input.
*
* Pinned mapping to olive::NodeValue::Type (src/node/src/value.h):
* NONE -> k_none, INT -> k_int, FLOAT -> k_float, BOOL -> k_boolean,
* RATIONAL -> k_rational, COLOR -> k_color, VEC2 -> k_vec2,
* VEC3 -> k_vec3, VEC4 -> k_vec4, COMBO -> k_combo,
* STRING -> k_file (string-family inputs: k_file/k_text/k_font/
* k_str_combo, handled by the dedicated string functions). Types without
* a POD representation (texture, samples, matrix, params, bezier, binary,
* ...) report as OAKNODE_VALUE_NONE.
*/
typedef enum oaknode_value_type {
OAKNODE_VALUE_NONE = 0,
OAKNODE_VALUE_INT, /**< num (olive k_int, int64_t) */
OAKNODE_VALUE_FLOAT, /**< f[0] (olive k_float, double) */
OAKNODE_VALUE_BOOL, /**< num 0/1 (olive k_boolean) */
OAKNODE_VALUE_RATIONAL, /**< num/den (olive k_rational) */
OAKNODE_VALUE_COLOR, /**< f[0..3] = r,g,b,a (olive k_color) */
OAKNODE_VALUE_VEC2, /**< f[0..1] (olive k_vec2) */
OAKNODE_VALUE_VEC3, /**< f[0..2] (olive k_vec3) */
OAKNODE_VALUE_VEC4, /**< f[0..3] (olive k_vec4) */
OAKNODE_VALUE_COMBO, /**< num = selected index (olive k_combo) */
OAKNODE_VALUE_STRING, /**< k_file string family; string APIs only */
OAKNODE_VALUE_COUNT
} oaknode_value_type;
/**
* @brief POD parameter value. Only the fields documented for the value's
* `type` are meaningful.
*/
typedef struct oaknode_value {
int type; /**< oaknode_value_type. */
int64_t num; /**< INT/COMBO value, BOOL 0/1, RATIONAL numerator. */
int64_t den; /**< RATIONAL denominator. */
double f[4]; /**< FLOAT f[0]; VEC2/3/4 f[0..n-1]; COLOR r,g,b,a. */
} oaknode_value;
/**
* @brief Reference-counted handle to a node (olive::Node).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* init/factory functions return a handle with reference count 1,
* addref(ctx) takes another reference, release(ctx) drops one; release a
* handle with oaknode_node_free(). Borrowed handles into graph-owned
* objects only release the handle itself.
*/
typedef struct OakNodeNode {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeNode;
/* Re-declared here so node.h is self-contained; see node/project.h. */
typedef struct OakNodeProject OakNodeProject;
/* Re-declared here so node.h is self-contained; see node/footage.h. */
typedef struct OakNodeFootage OakNodeFootage;
/**
* @brief Timeline data owned by viewer nodes (TimelineMarkerList /
* TimelineWorkArea in oaktimeline) cross the boundary as oaktimeline
* value handles. Forward-declared here so node.h stays self-contained;
* include timeline/marker.h / timeline/workarea.h for the definitions.
*/
struct OakTimelineMarkerList;
struct OakTimelineWorkArea;
/**
* @brief Opaque borrowed handle to a node's video frame cache
* (olive::FrameHashCache in oakrender). oakrender reinterprets this into
* its own handle types.
*/
struct OakRenderCache;
/* oakcore handles used by the viewer setters. */
typedef struct OakAudioParams OakAudioParams;
/**
* @brief Number of live owned objects created through this API
* (nodes from oaknode_factory_create_from_id()/oaknode_node_create_copy(),
* keyframes, groups, traversers, traverser databases). Debug aid for
* leak checking; thread-unsafe, test/diagnostic use only.
*/
int oaknode_debug_alive_count(void);
/* ---- Metadata --------------------------------------------------------- */
/**
* @brief The node's unique type id (Node::id(), e.g.
* "org.olivevideoeditor.Olive.solidgenerator"). Two-stage getter.
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
int oaknode_node_get_id(OakNodeNode node, char *buf, int buf_size);
/**
* @brief The node's display name (Node::name()). Two-stage getter,
* same return convention as oaknode_node_get_id().
*/
int oaknode_node_get_name(OakNodeNode node, char *buf, int buf_size);
/**
* @brief The node's user label (Node::get_label()). Two-stage getter,
* same return convention as oaknode_node_get_id().
*/
int oaknode_node_get_label(OakNodeNode node, char *buf, int buf_size);
/**
* @brief Set the node's user label directly (Node::set_label(), live).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_label(OakNodeNode node, const char *label);
/**
* @brief Create a label-change command (olive::NodeRenameCommand).
*
* The command is NOT executed; `out_command` receives an owned command
* handle.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_label_undoable(OakNodeNode node, const char *label,
OakUndoCommand *out_command);
/**
* @brief The node's override color index (Node::get_override_color();
* -1 = none).
*
* @param out_value Receives the result. Must not be NULL.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_get_override_color(OakNodeNode node, int *out_value);
/**
* @brief Set the override color index directly (-1 = none; live).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_override_color(OakNodeNode node, int index);
/**
* @brief Create an override-color command (olive::NodeOverrideColorCommand).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_override_color_undoable(OakNodeNode node, int index,
OakUndoCommand *out_command);
/**
* @brief 1 if the node is enabled (the boolean "enabled_in" input's
* standard value).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_is_enabled(OakNodeNode node, int *out_value);
/**
* @brief Set the node's enabled state directly (live).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_enabled(OakNodeNode node, int enabled);
/**
* @brief Create an enabled-state command
* (olive::NodeParamSetStandardValueCommand on "enabled_in").
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_enabled_undoable(OakNodeNode node, int enabled,
OakUndoCommand *out_command);
/* ---- Input introspection ------------------------------------------------ */
/**
* @brief Number of declared inputs (Node::inputs(); array elements are not
* counted separately).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_input_count(OakNodeNode node, int *out_count);
/**
* @brief The input id at `index` (Node::inputs()). Two-stage getter;
* returns OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_input_id(OakNodeNode node, int index, char *buf,
int buf_size);
/**
* @brief The input's value type mapped to oaknode_value_type (see the
* pinned mapping on oaknode_value_type). OAKNODE_E_NOT_FOUND for an
* unknown input id.
*/
int oaknode_node_input_get_type(OakNodeNode node, const char *input_id,
int *out_type);
/**
* @brief 1 if the input currently has a connected edge
* (Node::is_input_connected()). OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_input_is_connected(OakNodeNode node, const char *input_id,
int *out_value);
/**
* @brief 1 if the input accepts connections (Node::is_input_connectable()).
* OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_input_is_connectable(OakNodeNode node, const char *input_id,
int *out_value);
/**
* @brief The human-readable name of the input (Node::get_input_name()).
* Two-stage getter; OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_get_input_name(OakNodeNode node, const char *input_id,
char *buf, int buf_size);
/**
* @brief The node feeding this input (Node::get_connected_output(),
* element -1). `out_node` receives a borrowed handle (empty, ctx == NULL,
* when not connected; releasing it only releases the handle).
* OAKNODE_E_NOT_FOUND for an unknown input id.
*/
int oaknode_node_input_get_connected_node(OakNodeNode node,
const char *input_id,
OakNodeNode *out_node);
/* ---- Parameter access ----------------------------------------------------- */
/**
* @brief Read an input's standard value (Node::get_standard_value())
* mapped into `out`.
*
* String-family inputs fail with OAKNODE_E_INVALID (use
* oaknode_node_get_input_string()); types without a POD representation
* fail with OAKNODE_E_FAILED; an unknown input id fails with
* OAKNODE_E_NOT_FOUND.
*/
int oaknode_node_get_input(OakNodeNode node, const char *input_id,
oaknode_value *out);
/**
* @brief Write an input's standard value directly (live,
* Node::set_standard_value()).
*
* `v->type` must match the input's declared type; OAKNODE_VALUE_STRING is
* rejected (use oaknode_node_set_input_string()).
*/
int oaknode_node_set_input(OakNodeNode node, const char *input_id,
const oaknode_value *v);
/**
* @brief Create a set-standard-value command
* (olive::NodeParamSetStandardValueCommand, track -1 semantics via the
* whole-value reference on track 0).
*
* Same type rules as oaknode_node_set_input().
*/
int oaknode_node_set_input_undoable(OakNodeNode node, const char *input_id,
const oaknode_value *v,
OakUndoCommand *out_command);
/**
* @brief Read a string-family input's standard value. Two-stage getter.
*/
int oaknode_node_get_input_string(OakNodeNode node, const char *input_id,
char *buf, int buf_size);
/**
* @brief Write a string-family input's standard value directly (live).
*/
int oaknode_node_set_input_string(OakNodeNode node, const char *input_id,
const char *value);
/**
* @brief Create a set-standard-value command for a string-family input.
*/
int oaknode_node_set_input_string_undoable(OakNodeNode node,
const char *input_id,
const char *value,
OakUndoCommand *out_command);
/* ---- Graph editing -------------------------------------------------------- */
/**
* @brief Connect `output_node`'s output into `input_node`'s `input_id`
* directly (live, Node::connect_edge(), element -1).
*
* Fails with OAKNODE_E_NOT_FOUND for an unknown input id,
* OAKNODE_E_INVALID when the input is not connectable, and
* OAKNODE_E_STATE when the input is already connected or the nodes belong
* to different graphs.
*/
int oaknode_node_connect(OakNodeNode output_node, OakNodeNode input_node,
const char *input_id);
/**
* @brief Create an edge-add command (olive::NodeEdgeAddCommand,
* element -1). Same validation as oaknode_node_connect() except the
* different-graph check (the command may legitimately be redone after
* graph changes).
*/
int oaknode_node_connect_undoable(OakNodeNode output_node,
OakNodeNode input_node,
const char *input_id,
OakUndoCommand *out_command);
/**
* @brief Remove the edge feeding `input_node`'s `input_id` directly
* (live, Node::disconnect_edge(), element -1). OAKNODE_E_NOT_FOUND when
* the input is unknown or not connected.
*/
int oaknode_node_disconnect(OakNodeNode input_node, const char *input_id);
/**
* @brief Create an edge-remove command (olive::NodeEdgeRemoveCommand,
* element -1). OAKNODE_E_NOT_FOUND when not connected.
*/
int oaknode_node_disconnect_undoable(OakNodeNode input_node,
const char *input_id,
OakUndoCommand *out_command);
/**
* @brief Number of outgoing edges (Node::output_connections()).
*/
int oaknode_node_output_connection_count(OakNodeNode node, int *out_count);
/**
* @brief The node at the input end of outgoing edge `index` (borrowed
* handle; releasing it only releases the handle). OAKNODE_E_NOT_FOUND for
* an out-of-range index.
*/
int oaknode_node_output_connection_node_at(OakNodeNode node, int index,
OakNodeNode *out_node);
/**
* @brief The input id at the input end of outgoing edge `index`.
* Two-stage getter; OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_output_connection_input_id_at(OakNodeNode node, int index,
char *buf, int buf_size);
/**
* @brief The input element at the input end of outgoing edge `index`
* (-1 for non-array inputs). OAKNODE_E_NOT_FOUND for an out-of-range
* index.
*/
int oaknode_node_output_connection_element_at(OakNodeNode node, int index,
int *out_element);
/* ---- Links --------------------------------------------------------------- */
/**
* @brief Link two nodes directly (live, Node::link()). `out_linked`
* receives 1 on success, 0 when the link was rejected (e.g. either node
* rejects links). `out_linked` may be NULL.
*/
int oaknode_node_link(OakNodeNode a, OakNodeNode b, int *out_linked);
/**
* @brief Unlink two nodes directly (live, Node::unlink()).
* `out_unlinked` receives 1 on success, 0 otherwise; may be NULL.
*/
int oaknode_node_unlink(OakNodeNode a, OakNodeNode b, int *out_unlinked);
/**
* @brief Create a link/unlink command (olive::NodeLinkCommand;
* `link` != 0 links, 0 unlinks).
*/
int oaknode_node_link_undoable(OakNodeNode a, OakNodeNode b, int link,
OakUndoCommand *out_command);
/**
* @brief 1 if the two nodes are linked (Node::are_linked()).
*/
int oaknode_node_are_linked(OakNodeNode a, OakNodeNode b, int *out_value);
/**
* @brief Number of linked nodes (Node::links()).
*/
int oaknode_node_link_count(OakNodeNode node, int *out_count);
/**
* @brief The linked node at `index` (borrowed handle; releasing it only
* releases the handle). OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_link_at(OakNodeNode node, int index,
OakNodeNode *out_node);
/* ---- Context positions ---------------------------------------------------- */
/**
* @brief Number of context entries (Node::get_context_positions()).
*/
int oaknode_node_context_count(OakNodeNode node, int *out_count);
/**
* @brief The context node at `index` (borrowed handle; releasing it only
* releases the handle). OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_context_node_at(OakNodeNode node, int index,
OakNodeNode *out_node);
/**
* @brief The node's position in `context` (any out pointer may be NULL).
* OAKNODE_E_NOT_FOUND when the context does not contain this node.
*/
int oaknode_node_get_context_position(OakNodeNode node, OakNodeNode context,
double *out_x, double *out_y,
int *out_expanded);
/**
* @brief Set the node's position in `context` directly (live,
* Node::set_node_position_in_context() + set_node_expanded_in_context()).
*/
int oaknode_node_set_context_position(OakNodeNode node, OakNodeNode context,
double x, double y, int expanded);
/**
* @brief Create a set-position command (olive::NodeSetPositionCommand).
*/
int oaknode_node_set_context_position_undoable(OakNodeNode node,
OakNodeNode context, double x,
double y, int expanded,
OakUndoCommand *out_command);
/**
* @brief Remove the node from `context` directly (live).
* OAKNODE_E_NOT_FOUND when not contained.
*/
int oaknode_node_remove_from_context(OakNodeNode node, OakNodeNode context);
/* ---- Lifetime --------------------------------------------------------------- */
/**
* @brief Create a standalone copy of the node (Node::copy()). The copy is
* NOT added to any graph; the returned handle has reference count 1 and
* must be released with oaknode_node_free() while it is still orphaned.
* Returns an empty handle (ctx == NULL) for an empty handle or on failure.
*/
OakNodeNode oaknode_node_create_copy(OakNodeNode node);
/**
* @brief Copy a node inside its graph (Node::copy_node_in_graph()),
* recording the reconnect operations in a new MultiUndoCommand.
*
* `*out_command` receives an owned undo command handle (free with
* oakundo_command_free()). The copy is inserted into the graph only when
* the returned command is redone; treat it as owned (oaknode_node_free())
* until then. Returns an empty handle (ctx == NULL) on failure.
*/
OakNodeNode oaknode_node_copy_in_graph(OakNodeNode node,
OakUndoCommand *out_command);
/**
* @brief Get the project this node belongs to. `out` receives a borrowed
* handle (empty, ctx == NULL, if the node is orphaned; releasing it only
* releases the handle).
*/
int oaknode_node_get_project(OakNodeNode node, OakNodeProject *out);
/**
* @brief Insert/remove an element in an input array (live,
* Node::input_array_insert/remove()). OAKNODE_E_NOT_FOUND for an
* unknown input id.
*/
int oaknode_node_input_array_insert(OakNodeNode node, const char *input_id,
int index);
int oaknode_node_input_array_remove(OakNodeNode node, const char *input_id,
int index);
/**
* @brief Element-aware variants of oaknode_node_connect()/disconnect()
* (NodeInput element != -1, e.g. Sequence's track_in_N array inputs).
*/
int oaknode_node_connect_element(OakNodeNode output_node,
OakNodeNode input_node,
const char *input_id, int element);
int oaknode_node_disconnect_element(OakNodeNode input_node,
const char *input_id, int element);
/**
* @brief Create a command that adds a node to a project's graph
* (olive::NodeAddCommand). Owned; free with oakundo_command_free().
*/
OakUndoCommand oaknode_command_create_add_node(OakNodeProject graph,
OakNodeNode node);
/**
* @brief Create a command that sets a node's position in a context and
* repositions its dependencies recursively
* (olive::NodeSetPositionAndDependenciesRecursivelyCommand). Owned.
*/
OakUndoCommand oaknode_command_create_set_position_recursive(
OakNodeNode node, OakNodeNode context, double x, double y);
/**
* @brief Marker list / work area of a viewer node, as addref'd
* oaktimeline value handles (release with
* oaktimeline_marker_list_free()/oaktimeline_workarea_free()). *out is
* an empty handle (ctx == NULL) when the node is not a viewer or for
* an empty node handle.
*/
int oaknode_node_get_markers(OakNodeNode node,
struct OakTimelineMarkerList *out);
int oaknode_node_get_work_area(OakNodeNode node,
struct OakTimelineWorkArea *out);
/**
* @brief Video frame cache of a node as an addref'd oakrender value
* handle (release with oakrender_cache_free()). *out is an
* empty handle (ctx == NULL) when the node has none or for an
* empty node handle. struct OakRenderCache is forward-declared
* here; include render/cache.h for the definition.
*/
int oaknode_node_get_video_frame_cache(OakNodeNode node,
struct OakRenderCache *out);
/**
* @brief Copy input values/connections from one node to another
* (Node::copy_inputs()). include_connections != 0 also copies
* input connections.
*/
int oaknode_node_copy_inputs(OakNodeNode dst, OakNodeNode src,
int include_connections);
/**
* @brief Set a track-routing value hint on an input
* (Node::set_value_hint_for_input() with a single texture type
* and a Track::Reference string).
*/
int oaknode_node_set_value_hint_track(OakNodeNode node, const char *input_id,
int track_type, int track_index);
/**
* @brief Set a viewer node's video/audio params (ViewerOutput::
* set_video_params/set_audio_params, stream index 0). `params` is an
* oakcommon handle (video) or borrowed oakcore handle (audio).
*/
int oaknode_viewer_set_video_params(OakNodeNode viewer,
const OakVideoParams *params);
int oaknode_viewer_set_audio_params(OakNodeNode viewer,
const OakAudioParams *params);
/**
* @brief Find a footage node upstream of this node's inputs
* (Node::find_input_nodes<Footage>(), first match). `out` receives
* a borrowed handle (empty, ctx == NULL, when none; releasing it
* only releases the handle).
*/
int oaknode_node_find_input_footage(OakNodeNode node, OakNodeFootage *out);
/**
* @brief Value of an input at a specific time (Node::get_value_at_time(),
* element -1). Same POD rules as oaknode_node_get_input().
*/
int oaknode_node_get_input_at_time(OakNodeNode node,
const char *input_id, int64_t time_num,
int64_t time_den, oaknode_value *out);
/**
* @brief Set an input's value at a specific time with keyframe logic
* (Node::set_value_at_time(), element -1, track 0,
* insert_on_all_tracks_if_no_key = true). `*out_command` receives
* an owned undo command handle.
*/
int oaknode_node_set_input_at_time_undoable(OakNodeNode node,
const char *input_id, int64_t time_num, int64_t time_den,
const oaknode_value *v, int track, OakUndoCommand *out_command);
/**
* @brief Identity of the underlying node object as an opaque integer
* (address-cast; for registry keys only, never dereference).
*/
uintptr_t oaknode_node_identity(OakNodeNode node);
/**
* @brief Append a value-at-time set into an existing multi command
* (same semantics as oaknode_node_set_input_at_time_undoable but
* batches into `multi_command` from oakundo_command_init_multi()).
*/
int oaknode_node_set_input_at_time_into(OakNodeNode node,
const char *input_id, int64_t time_num, int64_t time_den,
const oaknode_value *v, int track, OakUndoCommand multi_command);
/**
* @brief Create a command that removes a node from its graph together
* with its exclusive dependencies and disconnects its edges
* (NodeRemoveWithExclusiveDependenciesAndDisconnect).
*
* Owned command handle; free with oakundo_command_free(). Returns an
* empty handle (ctx == NULL) on failure.
*/
OakUndoCommand oaknode_command_create_remove_node(OakNodeNode node);
/**
* @brief Release one reference to a node handle.
*
* Convenience wrapper around handle.release(handle.ctx): the underlying
* node is destroyed only when the last reference of an OWNED handle is
* released; releasing a borrowed handle into a graph-owned object only
* destroys the handle itself. NULL handle or NULL ctx is a no-op; clears
* `node->ctx` after releasing.
*/
void oaknode_node_free(OakNodeNode *node);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_NODE_H
-267
View File
@@ -1,267 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_PROJECT_H
#define OAK_EDITOR_NODE_PROJECT_H
#include <stdint.h>
#include "node/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file project.h
* @brief C ABI for olive::Project (oaknode)
*
* An OakNodeProject owns its whole node graph: nodes added with
* oaknode_project_add_node() (directly, or indirectly through the folder and
* footage families) are deleted when the project's last reference is
* released. Handles to nodes, folders and footage obtained from a project
* are borrowed views: releasing them only releases the handle itself.
*
* Conventions (shared by all oaknode C API families):
* - Return codes: 0 (OAKNODE_OK) on success, a negative OAKNODE_E_* code on
* failure.
* - String getters are two-stage: pass buf == NULL (or a short buffer) to
* query the required size; the return value is the required buffer size in
* bytes INCLUDING the terminating NUL. The output is NUL-terminated
* whenever buf_size > 0.
* - Empty handles (ctx == NULL) yield OAKNODE_E_INVALID (or a no-op for
* free()).
* - Disk save/load of project files is NOT part of this layer; it belongs to
* oakstorage (milestone M10).
*/
/**
* @brief Reference-counted handle to a project (olive::Project).
*
* Semantics are shared_ptr-like: oaknode_project_init() returns a handle
* whose underlying object has reference count 1, addref(ctx) takes another
* reference, and release(ctx) (or oaknode_project_free()) drops one; the
* project and every node it owns are destroyed when the count reaches zero.
*/
typedef struct OakNodeProject {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeProject;
/**
* @brief Node handle (defined by the node family; forward-declared
* here so the headers can be included in any order).
*/
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Folder handle (defined in node/folder.h; forward-declared
* here so the headers can be included in any order). Handles obtained from
* a project are borrowed from it.
*/
typedef struct OakNodeFolder OakNodeFolder;
/**
* @brief Create an empty project shell.
*
* The project has no root folder until oaknode_project_initialize() is
* called (mirrors Project::initialize()).
*
* @return Project handle with reference count 1 (release with
* oaknode_project_free()); ctx is NULL on allocation failure.
*/
OakNodeProject oaknode_project_init(void);
/**
* @brief Release one reference to a project handle.
*
* Destroys the project and every node it owns when the count reaches zero.
* NULL handle or NULL ctx is a no-op; clears `project->ctx` after releasing.
*/
void oaknode_project_free(OakNodeProject *project);
/**
* @brief Initialize the project: create the root folder (Project::initialize()).
*
* @return OAKNODE_OK, or OAKNODE_E_STATE if already initialized.
*/
int oaknode_project_initialize(OakNodeProject project);
/**
* @brief Destructively destroy all nodes in the graph (Project::clear()).
*
* The project shell stays usable; oaknode_project_initialize() may be called
* again afterwards.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_clear(OakNodeProject project);
/**
* @brief Borrowed handle of the project's root folder (Project::root()).
*
* The returned handle only releases the handle itself; the project owns the
* folder. Empty handle (ctx == NULL) if the project has not been
* initialized.
*/
OakNodeFolder oaknode_project_root(OakNodeProject project);
/**
* @brief Project display name (Project::name(): the filename's base name, or
* "(untitled)"). Two-stage string getter.
*
* @return Required buffer size in bytes including the NUL, or a negative
* OAKNODE_E_* error code.
*/
int oaknode_project_name(OakNodeProject project, char *buf, int buf_size);
/**
* @brief Full path the project was saved as, or "" if untitled
* (Project::filename()). Two-stage string getter.
*/
int oaknode_project_filename(OakNodeProject project, char *buf,
int buf_size);
/**
* @brief Display name safe for window titles (Project::pretty_filename()).
* Two-stage string getter.
*/
int oaknode_project_pretty_filename(OakNodeProject project, char *buf,
int buf_size);
/**
* @brief Set the project's filename (Project::set_filename()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_filename(OakNodeProject project, const char *filename);
/**
* @brief 1 if the project has unsaved changes, 0 otherwise
* (Project::is_modified()). Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_project_is_modified(OakNodeProject project);
/**
* @brief Set the modified flag (Project::set_modified()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_modified(OakNodeProject project, int modified);
/**
* @brief 1 if the project is new (untitled and unmodified, Project::is_new()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_project_is_new(OakNodeProject project);
/**
* @brief Effective cache directory (Project::cache_path(), honoring the cache
* location setting). Two-stage string getter.
*/
int oaknode_project_cache_path(OakNodeProject project, char *buf,
int buf_size);
/**
* @brief Copy all project settings (Project::copy_settings()).
*/
int oaknode_project_copy_settings(OakNodeProject dst,
OakNodeProject src);
/**
* @brief Cache location setting enum value
* (Project::get_cache_location_setting(): 0 = default location,
* 1 = alongside project, 2 = custom path). Negative OAKNODE_E_* code on an
* empty handle.
*/
int oaknode_project_get_cache_location_setting(OakNodeProject project);
/**
* @brief Set the cache location setting (0/1/2, see
* oaknode_project_get_cache_location_setting()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_cache_location_setting(OakNodeProject project,
int setting);
/**
* @brief Custom cache directory, or "" when none is set
* (Project::get_custom_cache_path()). Two-stage string getter.
*/
int oaknode_project_get_custom_cache_path(OakNodeProject project,
char *buf, int buf_size);
/**
* @brief Set a custom cache directory (Project::set_custom_cache_path()).
* NULL clears it.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_custom_cache_path(OakNodeProject project,
const char *path);
/**
* @brief Project UUID string (Project::get_uuid()). Two-stage string getter.
*/
int oaknode_project_get_uuid(OakNodeProject project, char *buf,
int buf_size);
/**
* @brief Add a node to the graph; the graph assumes the node's lifetime
* (Project::add_node()).
*
* After a successful call the graph owns the node: releasing `node` only
* releases the handle itself.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_add_node(OakNodeProject project, OakNodeNode node);
/**
* @brief Detach a node from the graph without deleting it
* (Project::remove_node()).
*
* @return OAKNODE_OK, OAKNODE_E_NOT_FOUND if the node is not in the graph, or
* another negative OAKNODE_E_* error code.
*/
int oaknode_project_remove_node(OakNodeProject project, OakNodeNode node);
/**
* @brief Number of nodes belonging to the graph (Project::nodes().size()).
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_project_node_count(OakNodeProject project);
/**
* @brief Borrowed handle of the graph node at `index`.
*
* The returned handle only releases the handle itself. Empty handle
* (ctx == NULL) when out of range.
*/
OakNodeNode oaknode_project_node_at(OakNodeProject project, int index);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_PROJECT_H
-219
View File
@@ -1,219 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_SEQUENCE_H
#define OAK_EDITOR_NODE_SEQUENCE_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stdint.h>
#include "common/videoparams.h"
#include "node/error.h"
#include "olive/core/oakcore/audioparams.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Sequence texture/samples input ids (ViewerOutput::k_texture_input
* / k_samples_input) and the track input id format
* (Sequence::k_track_input_format). Pinned by test.
*/
#define OAKNODE_SEQUENCE_TEXTURE_INPUT "tex_in"
#define OAKNODE_SEQUENCE_SAMPLES_INPUT "samples_in"
#define OAKNODE_SEQUENCE_TRACK_INPUT_FORMAT "track_in_%1"
/* Re-declared here so sequence.h is self-contained; see node/node.h. */
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Reference-counted handle to a sequence (olive::Sequence).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_sequence_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*
* Handles obtained from accessors (track lists, tracks) are borrowed:
* releasing them does not destroy the underlying object, which stays
* owned by the sequence graph.
*/
typedef struct OakNodeSequence {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeSequence;
/**
* @brief Reference-counted handle to a track list (olive::TrackList),
* see node/track.h.
*/
typedef struct OakNodeTrackList OakNodeTrackList;
/**
* @brief Reference-counted handle to a track (olive::Track), see
* node/track.h.
*/
typedef struct OakNodeTrack OakNodeTrack;
/**
* @brief Create an empty sequence with zero tracks.
*
* @return Sequence handle with reference count 1 (release with
* oaknode_sequence_free()); ctx is NULL on allocation failure.
*/
OakNodeSequence oaknode_sequence_create(void);
/**
* @brief Release one reference to a sequence handle.
*
* Destroys the sequence (and its owned track lists) when the reference
* count reaches zero. NULL handle or NULL ctx is a no-op; clears
* `sequence->ctx` after releasing.
*
* Tracks and blocks connected to the sequence are owned by the graph and
* are not deleted here; the caller must have torn them down first.
*/
void oaknode_sequence_free(OakNodeSequence *sequence);
/**
* @brief Apply the default video/audio parameters
* (ViewerOutput::set_default_parameters()).
*/
int oaknode_sequence_set_default_parameters(OakNodeSequence sequence);
/**
* @brief Borrowed cast from a sequence handle to its node handle.
* Empty handle for an empty handle.
*/
OakNodeNode oaknode_sequence_as_node(OakNodeSequence sequence);
/**
* @brief Non-owning cast from a node handle to a sequence handle (empty
* ctx when the node is not a Sequence).
*/
OakNodeSequence oaknode_sequence_from_node(OakNodeNode node);
/**
* @brief Borrowed handle to the per-type track list.
*
* @param type One of OAKNODE_TRACK_TYPE_VIDEO / _AUDIO / _SUBTITLE.
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND (bad type).
*/
int oaknode_sequence_get_track_list(OakNodeSequence sequence, int type,
OakNodeTrackList *out);
/**
* @brief Number of connected tracks of the given type.
*/
int oaknode_sequence_get_track_count(OakNodeSequence sequence, int type,
int *count);
/**
* @brief Borrowed handle to the track of `type` at `index`.
*/
int oaknode_sequence_get_track_at(OakNodeSequence sequence, int type,
int index, OakNodeTrack *out);
/**
* @brief Flat track cache across all types (olive::Sequence::get_tracks()).
*/
int oaknode_sequence_get_all_track_count(OakNodeSequence sequence, int *count);
int oaknode_sequence_get_all_track_at(OakNodeSequence sequence, int index,
OakNodeTrack *out);
/**
* @brief Playhead position in sequence time.
*/
int oaknode_sequence_get_playhead(OakNodeSequence sequence, int *numerator,
int *denominator);
int oaknode_sequence_set_playhead(OakNodeSequence sequence, int numerator,
int denominator);
/**
* @brief Cached overall/video/audio lengths (olive::ViewerOutput).
*/
int oaknode_sequence_get_length(OakNodeSequence sequence, int *numerator,
int *denominator);
int oaknode_sequence_get_video_length(OakNodeSequence sequence,
int *numerator, int *denominator);
int oaknode_sequence_get_audio_length(OakNodeSequence sequence,
int *numerator, int *denominator);
/**
* @brief Recompute the cached lengths from the track lists
* (olive::ViewerOutput::verify_length()).
*/
int oaknode_sequence_verify_length(OakNodeSequence sequence);
/* --------------------------------------------------- Video/audio params */
/**
* @brief Number of video/audio parameter slots.
*/
int oaknode_sequence_get_video_stream_count(OakNodeSequence sequence,
int *count);
int oaknode_sequence_get_audio_stream_count(OakNodeSequence sequence,
int *count);
/**
* @brief Video parameters at `index` as a NEW by-value handle owned by
* the caller (reference count 1, release with
* oakcommon_videoparams_free()).
*
* @return OAKNODE_OK, OAKNODE_E_INVALID, OAKNODE_E_NOT_FOUND or
* OAKNODE_E_NOMEM.
*/
int oaknode_sequence_get_video_params(OakNodeSequence sequence, int index,
OakVideoParams *out);
/**
* @brief Replace the video parameters at `index` with a copy of `params`.
*
* @return OAKNODE_E_INVALID if the sequence handle is empty, params.ctx is
* NULL, or index is negative.
*/
int oaknode_sequence_set_video_params(OakNodeSequence sequence, int index,
OakVideoParams params);
/**
* @brief Audio parameters at `index` as a NEW handle owned by the caller
* (release with oakcore_audioparams_free()).
*/
int oaknode_sequence_get_audio_params(OakNodeSequence sequence, int index,
OakAudioParams **out);
/**
* @brief Replace the audio parameters at `index` with a copy of `params`.
*/
int oaknode_sequence_set_audio_params(OakNodeSequence sequence, int index,
const OakAudioParams *params);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_SEQUENCE_H
@@ -1,310 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_SERIALIZER_H
#define OAK_EDITOR_NODE_SERIALIZER_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#include "node/project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file serializer.h
* @brief C ABI for olive::ProjectSerializer (oaknode), in-memory form
*
* Clipboard copy/paste and node-graph XML round trips without touching the
* filesystem: "copy" is oaknode_serializer_save_to_xml() (serialize a
* SaveData to an XML string), "paste" is oaknode_serializer_load_from_xml()
* (parse an XML string into a project, exposing the resulting LoadData).
* System-clipboard integration and on-disk .ove save/load live in the
* facade / oakstorage layers (M9/M10), not here.
*
* oaknode_serializer_initialize() must be called before any save/load; it
* registers the versioned serializers and the node factory the loaders use
* to instantiate nodes by id.
*/
/** @brief Load type: a whole project. */
#define OAKNODE_SERIALIZER_LOAD_PROJECT 0
/** @brief Load type: only nodes (clipboard node-graph paste). */
#define OAKNODE_SERIALIZER_LOAD_ONLY_NODES 1
/** @brief Load type: only clips (timeline family). */
#define OAKNODE_SERIALIZER_LOAD_ONLY_CLIPS 2
/** @brief Load type: only markers (timeline family). */
#define OAKNODE_SERIALIZER_LOAD_ONLY_MARKERS 3
/** @brief Load type: only keyframes (keyframe family). */
#define OAKNODE_SERIALIZER_LOAD_ONLY_KEYFRAMES 4
/** @brief Serializer result code: success. */
#define OAKNODE_SERIALIZER_OK 0
/** @brief Serializer result code: data written by a too-old format. */
#define OAKNODE_SERIALIZER_TOO_OLD 1
/** @brief Serializer result code: data written by a too-new format. */
#define OAKNODE_SERIALIZER_TOO_NEW 2
/** @brief Serializer result code: unrecognizable format version. */
#define OAKNODE_SERIALIZER_UNKNOWN_VERSION 3
/** @brief Serializer result code: file I/O error (unused in-memory). */
#define OAKNODE_SERIALIZER_FILE_ERROR 4
/** @brief Serializer result code: XML parse error. */
#define OAKNODE_SERIALIZER_XML_ERROR 5
/** @brief Serializer result code: overwrite error (unused in-memory). */
#define OAKNODE_SERIALIZER_OVERWRITE_ERROR 6
/** @brief Serializer result code: no data to load. */
#define OAKNODE_SERIALIZER_NO_DATA 7
/**
* @brief Reference-counted save descriptor (wraps
* olive::ProjectSerializer::SaveData).
*
* oaknode_serializer_savedata_create() returns a handle whose object has
* reference count 1; release it with oaknode_serializer_savedata_free().
*/
typedef struct OakNodeSerializerSaveData {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeSerializerSaveData;
/**
* @brief Reference-counted load result (wraps
* olive::ProjectSerializer::LoadData).
*
* The handle returned through oaknode_serializer_load_from_xml() has
* reference count 1; release it with oaknode_serializer_loaddata_free().
* Node handles obtained from it are borrowed from the target project.
*/
typedef struct OakNodeSerializerLoadData {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeSerializerLoadData;
/**
* @brief Register the versioned serializers and initialize the node factory.
* Idempotent. Must be called before any save/load.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_serializer_initialize(void);
/**
* @brief Tear down the serializers and the node factory registered by
* oaknode_serializer_initialize(). Safe to call when not initialized.
*/
void oaknode_serializer_shutdown(void);
/**
* @brief Create a save descriptor.
*
* @param load_type One of OAKNODE_SERIALIZER_LOAD_*; use
* OAKNODE_SERIALIZER_LOAD_ONLY_NODES for clipboard-style node copies.
* @param project Context project (borrowed), may be an empty handle for
* load types that do not require it.
*
* @return Save-data handle with reference count 1 (release with
* oaknode_serializer_savedata_free()); ctx is NULL on failure.
*/
OakNodeSerializerSaveData oaknode_serializer_savedata_create(
int load_type, OakNodeProject project);
/**
* @brief Release the caller's reference to the save descriptor and null
* out the handle. NULL and empty handles are a no-op; the object is
* destroyed when its reference count reaches zero.
*/
void oaknode_serializer_savedata_free(OakNodeSerializerSaveData *save_data);
/**
* @brief Restrict serialization to the given nodes
* (SaveData::set_only_serialize_nodes()). `nodes` is an array of `count`
* borrowed node handles.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_serializer_savedata_set_nodes(
OakNodeSerializerSaveData save_data, const OakNodeNode *nodes, int count);
/**
* @brief Attach a free-form (key, value) property to a node in the
* serialized output (SaveData::set_properties()); used for graph positions
* and clip metadata. Replaces the value if the (node, key) pair exists.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_serializer_savedata_set_property(
OakNodeSerializerSaveData save_data, OakNodeNode node, const char *key,
const char *value);
/**
* @brief Serialize to an in-memory XML document ("copy"). Two-stage string
* getter: pass buf == NULL to query the size.
*
* @return Required buffer size in bytes including the NUL, or a negative
* OAKNODE_E_* error code (OAKNODE_E_STATE if the serializers have
* not been initialized).
*/
int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData save_data,
char *buf, int buf_size);
/**
* @brief Parse an in-memory XML document into `project` ("paste").
*
* @param project Target project (borrowed), may be an empty handle for
* load types that do not attach nodes to a project.
* @param xml Complete XML document text. Must not be NULL.
* @param load_type One of OAKNODE_SERIALIZER_LOAD_*.
* @param out_result Receives one of the OAKNODE_SERIALIZER_* result codes.
* Must not be NULL.
* @param out_load_data Receives the load result on OAKNODE_SERIALIZER_OK
* (reference count 1, release with oaknode_serializer_loaddata_free();
* may be NULL if the caller does not need it; receives an empty
* handle on failure).
* @param details_buf Optional human-readable error detail buffer
* (two-stage convention is NOT used; truncation is silent). May be
* NULL.
* @param details_buf_size Size of details_buf.
*
* @return OAKNODE_OK if the call itself succeeded (inspect *out_result for
* the serializer outcome), or a negative OAKNODE_E_* error code.
*/
int oaknode_serializer_load_from_xml(OakNodeProject project, const char *xml,
int load_type, int *out_result,
OakNodeSerializerLoadData *out_load_data,
char *details_buf, int details_buf_size);
/**
* @brief Release the caller's reference to the load result and null out
* the handle. NULL and empty handles are a no-op.
*
* Does not delete the loaded nodes: they are newly created objects owned by
* the CALLER until adopted into a project with oaknode_project_add_node()
* (or attached under a folder); otherwise they leak.
*/
void oaknode_serializer_loaddata_free(OakNodeSerializerLoadData *load_data);
/**
* @brief Number of nodes created by the load. Negative OAKNODE_E_* code on
* an empty handle.
*/
int oaknode_serializer_loaddata_node_count(
OakNodeSerializerLoadData load_data);
/**
* @brief Borrowed handle of the loaded node at `index`, or an empty handle
* when out of range.
*/
OakNodeNode oaknode_serializer_loaddata_node_at(
OakNodeSerializerLoadData load_data, int index);
/**
* @brief Look up a serialized property attached to a loaded node.
* Two-stage string getter.
*
* @return Required buffer size in bytes including the NUL,
* OAKNODE_E_NOT_FOUND if the (node, key) pair is absent, or another
* negative OAKNODE_E_* error code.
*/
int oaknode_serializer_loaddata_get_property(
OakNodeSerializerLoadData load_data, OakNodeNode node, const char *key,
char *buf, int buf_size);
/**
* @brief Number of promised (deferred) connections in the load result.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_serializer_loaddata_connection_count(
OakNodeSerializerLoadData load_data);
/**
* @brief Read the promised connection at `index`.
*
* All output parameters except the input-id buffer are required;
* `input_id_buf` follows the two-stage string convention inside a
* fixed call: pass NULL/0 to skip copying the id.
*
* @param out_output_node Receives the output (source) node (borrowed).
* @param out_input_node Receives the input (destination) node (borrowed).
* @param input_id_buf Receives the input id string, may be NULL.
* @param input_id_buf_size Size of input_id_buf.
* @param out_element Receives the input element index.
*
* @return OAKNODE_OK, OAKNODE_E_NOT_FOUND when out of range, or another
* negative OAKNODE_E_* error code.
*/
int oaknode_serializer_loaddata_connection_at(
OakNodeSerializerLoadData load_data, int index,
OakNodeNode *out_output_node, OakNodeNode *out_input_node,
char *input_id_buf, int input_id_buf_size, int *out_element);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_SERIALIZER_H
/**
* @brief Result codes for file-level save/load (mirror
* ProjectSerializer::ResultCode; pinned by test).
*/
enum OakNodeSerializerResultCode {
OAKNODE_SERIALIZER_RESULT_SUCCESS = 0,
OAKNODE_SERIALIZER_RESULT_PROJECT_TOO_OLD = 1,
OAKNODE_SERIALIZER_RESULT_PROJECT_TOO_NEW = 2,
OAKNODE_SERIALIZER_RESULT_UNKNOWN_VERSION = 3,
OAKNODE_SERIALIZER_RESULT_FILE_ERROR = 4,
OAKNODE_SERIALIZER_RESULT_XML_ERROR = 5,
OAKNODE_SERIALIZER_RESULT_OVERWRITE_ERROR = 6,
OAKNODE_SERIALIZER_RESULT_NO_DATA = 7
};
/**
* @brief Save a project to a file (ProjectSerializer::save(), project
* type, optional OVEC compression). Layout data is not serialized
* through this API (app-layer concern, see oakstorage/M10).
*
* @param out_code Receives an OakNodeSerializerResultCode (may be NULL).
* @param details Optional two-stage buffer for the result details
* string (e.g. the fallback filename on overwrite errors).
* @return OAKNODE_OK when the result code is
* OAKNODE_SERIALIZER_RESULT_SUCCESS, OAKNODE_E_FAILED otherwise
* (details in out_code/details), OAKNODE_E_INVALID for empty
* handles/NULL args.
*/
int oaknode_serializer_save_to_file(OakNodeProject project,
const char *filename, int use_compression, int *out_code,
char *details, int details_size);
/**
* @brief Load a project from a file into `project`
* (ProjectSerializer::load(), project type).
*
* Same return/out-param convention as oaknode_serializer_save_to_file().
*/
int oaknode_serializer_load_from_file(OakNodeProject project,
const char *filename, int *out_code, char *details,
int details_size);
-355
View File
@@ -1,355 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_TRACK_H
#define OAK_EDITOR_NODE_TRACK_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stdint.h>
#include "node/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to a track (olive::Track).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_track_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*
* Adding a track to a track list (oaknode_tracklist_add_track())
* transfers ownership to the graph; handles obtained from accessors
* (sequence/track-list lookups) are borrowed and never destroy the
* underlying object.
*/
typedef struct OakNodeTrack {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeTrack;
/**
* @brief Reference-counted handle to a per-type track container
* (olive::TrackList).
*
* Always borrowed from oaknode_sequence_get_track_list(); releasing the
* handle never destroys the list, which stays owned by its sequence.
*/
typedef struct OakNodeTrackList {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeTrackList;
/**
* @brief Reference-counted handle to a block (olive::Block), see
* node/block.h.
*/
typedef struct OakNodeBlock OakNodeBlock;
/**
* @brief Reference-counted handle to a sequence (olive::Sequence), see
* node/sequence.h.
*/
typedef struct OakNodeSequence OakNodeSequence;
/**
* @brief Track types, matching olive::Track::Type.
*/
enum OakNodeTrackType {
OAKNODE_TRACK_TYPE_NONE = -1,
OAKNODE_TRACK_TYPE_VIDEO = 0,
OAKNODE_TRACK_TYPE_AUDIO = 1,
OAKNODE_TRACK_TYPE_SUBTITLE = 2,
OAKNODE_TRACK_TYPE_COUNT = 3
};
/* Re-declared here so track.h is self-contained; see node/node.h. */
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Borrowed cast from a track handle to its node handle.
* Empty handle for an empty handle.
*/
OakNodeNode oaknode_track_as_node(OakNodeTrack track);
/* ---------------------------------------------------------------- Track */
/**
* @brief Create a track of the given type (OakNodeTrackType value).
*
* The caller owns the track until it is added to a track list; a track
* that was never added must be released with oaknode_track_free().
*
* @return Track handle with reference count 1; ctx is NULL on invalid
* type / allocation failure.
*/
OakNodeTrack oaknode_track_create(int type);
/**
* @brief Release one reference to a track handle.
*
* Destroys the track when the reference count reaches zero. NULL handle
* or NULL ctx is a no-op; clears `track->ctx` after releasing.
*
* The track must have been removed from its track list first.
*/
void oaknode_track_free(OakNodeTrack *track);
/**
* @brief Track type (OakNodeTrackType values).
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_track_get_type(OakNodeTrack track, int *type);
int oaknode_track_set_type(OakNodeTrack track, int type);
/**
* @brief Track height in internal units (olive::Track::get/set_track_height).
*/
int oaknode_track_get_height(OakNodeTrack track, double *height);
int oaknode_track_set_height(OakNodeTrack track, double height);
/**
* @brief Track height in pixels (converted through the default font height).
*/
int oaknode_track_get_height_in_pixels(OakNodeTrack track, int *height);
int oaknode_track_set_height_in_pixels(OakNodeTrack track, int height);
/**
* @brief Default / minimum track heights in pixels (static).
*/
int oaknode_track_get_default_height_in_pixels(void);
int oaknode_track_get_minimum_height_in_pixels(void);
/**
* @brief Index of the track inside its track list.
*/
int oaknode_track_get_index(OakNodeTrack track, int *index);
int oaknode_track_set_index(OakNodeTrack track, int index);
/**
* @brief Mute / lock flags.
*/
int oaknode_track_get_muted(OakNodeTrack track, int *muted);
int oaknode_track_set_muted(OakNodeTrack track, int muted);
int oaknode_track_get_locked(OakNodeTrack track, int *locked);
int oaknode_track_set_locked(OakNodeTrack track, int locked);
/**
* @brief Track reference as a (type, index) pair (olive::Track::Reference).
*/
int oaknode_track_get_reference(OakNodeTrack track, int *type, int *index);
/**
* @brief Total length of the track (end of the last block).
*/
int oaknode_track_get_length(OakNodeTrack track, int *numerator,
int *denominator);
/**
* @brief Owning sequence as a borrowed handle (empty when trackless).
*/
int oaknode_track_get_sequence(OakNodeTrack track, OakNodeSequence *out);
/* ------------------------------------------------------- Track blocks */
/**
* @brief Number of blocks on the track.
*/
int oaknode_track_get_block_count(OakNodeTrack track, int *count);
/**
* @brief Borrowed handle to the block at `index`.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_track_get_block_at(OakNodeTrack track, int index,
OakNodeBlock *out);
/**
* @brief Append/prepend/insert primitives (olive::Track::*_block).
*
* The track takes over graph membership of the block; the block must have
* a valid length before insertion.
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_track_append_block(OakNodeTrack track, OakNodeBlock block);
int oaknode_track_prepend_block(OakNodeTrack track, OakNodeBlock block);
int oaknode_track_insert_block_at_index(OakNodeTrack track,
OakNodeBlock block, int index);
int oaknode_track_insert_block_after(OakNodeTrack track, OakNodeBlock block,
OakNodeBlock before);
int oaknode_track_insert_block_before(OakNodeTrack track, OakNodeBlock block,
OakNodeBlock after);
/**
* @brief Remove `block` and shift all subsequent blocks earlier
* (olive::Track::ripple_remove_block). The block is NOT deleted; ownership
* returns to the caller.
*/
int oaknode_track_ripple_remove_block(OakNodeTrack track, OakNodeBlock block);
/**
* @brief Replace `old_block` with `new_block`; both must have equal lengths.
*/
int oaknode_track_replace_block(OakNodeTrack track, OakNodeBlock old_block,
OakNodeBlock new_block);
/**
* @brief Index of `block` in the track's block array, or OAKNODE_E_NOT_FOUND.
*/
int oaknode_track_get_block_index(OakNodeTrack track, OakNodeBlock block,
int *index);
/**
* @brief Block strictly containing `time` (in < time < out), or
* OAKNODE_E_NOT_FOUND.
*/
int oaknode_track_get_block_containing_time(OakNodeTrack track, int numerator,
int denominator,
OakNodeBlock *out);
/**
* @brief Block visible at `time` (in <= time < out), or OAKNODE_E_NOT_FOUND.
*/
int oaknode_track_get_visible_block_at_time(OakNodeTrack track, int numerator,
int denominator,
OakNodeBlock *out);
/**
* @brief Whether the [in, out) range holds no block or only a gap
* (olive::Track::is_range_free). `is_free` receives 1/0.
*/
int oaknode_track_is_range_free(OakNodeTrack track, int in_num, int in_den,
int out_num, int out_den, int *is_free);
/* ------------------------------------------------------------ TrackList */
/**
* @brief Track list type (OakNodeTrackType values).
*/
/**
* @brief Nearest block lookups (Track::nearest_block_before_or_at /
* nearest_block_after_or_at). *out is a borrowed handle (empty when none).
*/
int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack track,
int numerator, int denominator, OakNodeBlock *out);
int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack track,
int numerator, int denominator, OakNodeBlock *out);
/**
* @brief Borrowed sequence owning this track list.
*/
int oaknode_tracklist_get_sequence(OakNodeTrackList list,
OakNodeSequence *out);
/**
* @brief The list's track input id on the parent sequence
* (e.g. "track_in_0"). Two-stage string getter.
*/
int oaknode_tracklist_get_track_input_id(OakNodeTrackList list,
char *buf, int buf_size);
/**
* @brief Live input-array append/remove on the parent sequence for this
* list's track input (TrackList::array_append/array_remove_last()).
*/
int oaknode_tracklist_array_append(OakNodeTrackList list);
int oaknode_tracklist_array_remove_last(OakNodeTrackList list);
/**
* @brief Map a cached track index to the input-array element index
* (TrackList::get_array_index_from_cache_index()).
*/
int oaknode_tracklist_get_array_index_from_cache_index(
OakNodeTrackList list, int cache_index, int *out_index);
int oaknode_tracklist_get_type(OakNodeTrackList list, int *type);
/**
* @brief Number of connected tracks.
*/
int oaknode_tracklist_get_track_count(OakNodeTrackList list, int *count);
/**
* @brief Borrowed handle to the track at `index`.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_tracklist_get_track_at(OakNodeTrackList list, int index,
OakNodeTrack *out);
/**
* @brief Combined length of the longest track in the list.
*/
int oaknode_tracklist_get_total_length(OakNodeTrackList list, int *numerator,
int *denominator);
/**
* @brief Size of the underlying input array (>= track count; may contain
* disconnected slots).
*/
int oaknode_tracklist_get_array_size(OakNodeTrackList list, int *size);
/**
* @brief Add `track` to the list (non-undoable primitive).
*
* Mirrors the graph steps of TimelineAddTrackCommand::redo() minus the
* auto-merge: the track is parented to the list's graph (when any),
* inherits the previous track's height, a new array slot is appended and
* the track is connected to it. The sequence's flat track cache and
* lengths are refreshed before returning.
*
* The list takes ownership of the track on success; the caller's handle
* becomes a non-owning reference.
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_tracklist_add_track(OakNodeTrackList list, OakNodeTrack track);
/**
* @brief Remove `track` from the list (non-undoable primitive).
*
* Disconnects the track from its array slot and removes the slot
* (Node::input_array_remove). The track is NOT deleted; ownership returns
* to the caller.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_tracklist_remove_track(OakNodeTrackList list,
OakNodeTrack track);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_TRACK_H
@@ -1,154 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_NODE_TRAVERSER_H
#define OAK_EDITOR_NODE_TRAVERSER_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file traverser.h
* @brief C ABI for olive::NodeTraverser (src/node/src/traverser.h),
* limited to database generation: generating the value database of a node
* over a time range and enumerating its rows.
*
* The base NodeTraverser resolves no render jobs (textures/samples stay
* dummy); only value-producing nodes are meaningful here.
*/
/**
* @brief Reference-counted handle to a traverser (olive::NodeTraverser).
*
* Semantics are shared_ptr-like: oaknode_traverser_init() returns a
* handle with count 1, addref(ctx) takes another reference, release(ctx)
* drops one and the library destroys the object when the count reaches
* zero.
*/
typedef struct OakNodeTraverser {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeTraverser;
/**
* @brief Reference-counted handle to an owned copy of an
* olive::NodeValueDatabase. Same reference-counting rules as
* OakNodeTraverser; release with oaknode_traverser_database_free().
*/
typedef struct OakNodeValueDatabase {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeValueDatabase;
/**
* @brief Create a traverser.
*
* @return Traverser handle with count 1; ctx is NULL on allocation
* failure.
*/
OakNodeTraverser oaknode_traverser_init(void);
/**
* @brief Release one reference to a traverser handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* traverser when the count reaches zero. NULL handle or NULL ctx is a
* no-op; clears `traverser->ctx` after releasing.
*/
void oaknode_traverser_free(OakNodeTraverser *traverser);
/**
* @brief Generate the value database of `node` over the time range
* [`in_num`/`in_den`, `out_num`/`out_den`) seconds
* (NodeTraverser::generate_database()).
*
* `out_db` receives an owned database handle with count 1.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_traverser_generate_database(OakNodeTraverser traverser,
OakNodeNode node, int64_t in_num,
int64_t in_den, int64_t out_num,
int64_t out_den,
OakNodeValueDatabase *out_db);
/**
* @brief Release one reference to a database handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* database when the count reaches zero. NULL handle or NULL ctx is a
* no-op; clears `db->ctx` after releasing.
*/
void oaknode_traverser_database_free(OakNodeValueDatabase *db);
/**
* @brief Number of rows (input tables) in the database.
*/
int oaknode_traverser_database_row_count(OakNodeValueDatabase db,
int *out_count);
/**
* @brief The input id (key) of the row at `index`. Two-stage getter;
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_traverser_database_row_key_at(OakNodeValueDatabase db,
int index, char *buf, int buf_size);
/**
* @brief Number of values in the row named `key`.
* OAKNODE_E_NOT_FOUND for an unknown key.
*/
int oaknode_traverser_database_row_value_count(OakNodeValueDatabase db,
const char *key,
int *out_count);
/**
* @brief Read the value at `index` of row `key` mapped into `out`.
* Values without a POD representation fail with OAKNODE_E_FAILED;
* OAKNODE_E_NOT_FOUND for an unknown key or out-of-range index.
*/
int oaknode_traverser_database_value_at(OakNodeValueDatabase db,
const char *key, int index,
oaknode_value *out);
/**
* @brief Read the value at `index` of row `key` as a string
* (NodeValue::value_to_string()). Two-stage getter;
* OAKNODE_E_NOT_FOUND for an unknown key or out-of-range index.
*/
int oaknode_traverser_database_value_string_at(OakNodeValueDatabase db,
const char *key, int index,
char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_TRAVERSER_H
@@ -1,40 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_PLUGIN_ERROR_H
#define OAK_EDITOR_PLUGIN_ERROR_H
#include <stdint.h>
/**
* @brief Status and error codes shared by all oakplugin C API families.
*/
#define OAKPLUGIN_OK 0 /**< Success. */
#define OAKPLUGIN_E_INVALID (-90001) /**< NULL handle or invalid argument. */
#define OAKPLUGIN_E_STATE (-90002) /**< Call not valid in the current state. */
#define OAKPLUGIN_E_FAILED (-90003) /**< The underlying operation failed. */
#define OAKPLUGIN_E_NOT_FOUND (-90004) /**< Entry not found. */
#define OAKPLUGIN_E_NOMEM (-90005) /**< Allocation failed. */
#define OAKPLUGIN_E_CANCELLED (-90006) /**< The operation was cancelled. */
/** @brief ABI version stamped into every oakplugin handle. */
#define OAKPLUGIN_ABI_VERSION 1
#endif //OAK_EDITOR_PLUGIN_ERROR_H
-69
View File
@@ -1,69 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_PLUGIN_HOST_H
#define OAK_EDITOR_PLUGIN_HOST_H
#include "plugin/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize the OFX host (olive::plugin::load_plugins() with the
* default search paths). Idempotent.
*/
int oakplugin_host_init(void);
/** @brief Shut the host down (persistent messages cleared). */
void oakplugin_host_shutdown(void);
/** @brief Scan additional bundle directories. */
int oakplugin_host_scan(const char *const *bundle_dirs, int dir_count);
/** @brief Number of discovered plugins (>= 0), or a negative error. */
int oakplugin_host_plugin_count(void);
/** @brief Plugin identifier at index (two-stage string getter). */
int oakplugin_host_plugin_id_at(int index, char *buf, int buf_size);
/** @brief Plugin label for an identifier (two-stage; currently the
* identifier itself). OAKPLUGIN_E_NOT_FOUND for unknown ids. */
int oakplugin_host_plugin_label(const char *plugin_id, char *buf,
int buf_size);
/**
* @brief UI message handler for OFX host messages (question replies use
* OAKPLUGIN_MESSAGE_ANSWER_YES/NO). Without a handler, messages
* are logged and questions get "no".
*/
#define OAKPLUGIN_MESSAGE_ANSWER_NO 0
#define OAKPLUGIN_MESSAGE_ANSWER_YES 1
typedef int (*oakplugin_message_fn)(const char *type, const char *message,
void *userdata);
void oakplugin_host_set_message_handler(oakplugin_message_fn fn,
void *userdata);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_PLUGIN_HOST_H
@@ -1,171 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_PLUGIN_INSTANCE_H
#define OAK_EDITOR_PLUGIN_INSTANCE_H
#include <stdint.h>
#include "node/node.h"
#include "plugin/error.h"
#include "render/renderer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to an OFX plugin instance
* (olive::plugin::OlivePluginInstance).
*
* Ownership/count semantics follow include/common/handle.h: create
* returns count 1, addref/release adjust it, release destroys at zero.
*/
typedef struct OakPluginInstance {
void *ctx;
void (*addref)(void *ctx);
void (*release)(void *ctx);
uint32_t abi_version; /**< OAKPLUGIN_ABI_VERSION. */
} OakPluginInstance;
/**
* @brief Create an instance of a discovered plugin (filter context).
* Returns an empty handle (ctx == NULL) for unknown ids/failure.
*/
OakPluginInstance oakplugin_instance_create(const char *plugin_id);
/** @brief Release one reference. NULL/empty no-op; clears ctx. */
void oakplugin_instance_free(OakPluginInstance *instance);
/**
* @brief Set/get a parameter as an oaknode_value POD (type rules from
* node/node.h). String-typed params use
* oakplugin_instance_set_param_string()/get_param_string().
*/
int oakplugin_instance_set_param(OakPluginInstance instance,
const char *param_id,
const oaknode_value *value);
int oakplugin_instance_get_param(OakPluginInstance instance,
const char *param_id, oaknode_value *out);
int oakplugin_instance_set_param_string(OakPluginInstance instance,
const char *param_id,
const char *value);
int oakplugin_instance_get_param_string(OakPluginInstance instance,
const char *param_id, char *buf,
int buf_size);
/**
* @brief Render one frame through the instance (renderAction).
*
* `src` may be an empty handle for generator plugins. Textures stay
* owned by the caller (borrowed for the call).
*/
int oakplugin_instance_render(OakPluginInstance instance,
OakRenderTexture dst, OakRenderTexture src,
double time_seconds);
/**
* @brief Progress callback for long renders (async return channel,
* 01 §4 exception). Return non-zero to abort processing.
*/
typedef int (*oakplugin_progress_fn)(double progress, void *userdata);
int oakplugin_instance_set_progress_cb(OakPluginInstance instance,
oakplugin_progress_fn fn,
void *userdata);
/** @brief Cancel any in-progress render/progress reporting. */
int oakplugin_instance_cancel(OakPluginInstance instance);
/** @brief Alive-count for leak assertions in tests. */
int oakplugin_debug_alive_count(void);
/*
* M11 §4GL 路径 + render 驱动收编)新增声明。既有签名不变。
*
* oakrender 的 PluginJob 经本组入口把整帧渲染流程(RoI/RoD、
* 多输入收集、isIdentity 短路、参数覆盖、CPU/GL 渲染与输出装配)
* 委托给 oakplugin 的 render 驱动(Rust 侧 render_driver 模块,
* 语义对照 src/render/src/plugin/pluginrenderer.cpp)。
*/
/** @brief 一帧渲染任务的参数覆盖条目(参数名 → oaknode_value POD
* 字符串参数走 oakplugin_instance_set_param_string)。 */
typedef struct oakplugin_job_value {
const char *key;
oaknode_value value;
} oakplugin_job_value;
/** @brief 一帧渲染任务的输入 clip 纹理条目。纹理为借用句柄
* (job 内有效)。 */
typedef struct oakplugin_job_texture {
const char *clip;
OakRenderTexture texture;
} oakplugin_job_texture;
/**
* @brief beginSequenceRender 括号。oakrender 对同一实例的一批帧先
* begin 后 end,中间逐帧 oakplugin_instance_render_job
* OFXrender action 由 begin/end sequence render 括号包围)。
* `interactive` 为信息性标记(Phase 2 不传入 action)。
*/
int oakplugin_instance_render_begin_sequence(OakPluginInstance instance,
double start_time,
double end_time,
int interactive);
/** @brief endSequenceRender 括号(与 render_begin_sequence 配对)。 */
int oakplugin_instance_render_end_sequence(OakPluginInstance instance,
double start_time,
double end_time,
int interactive);
/**
* @brief 一帧渲染的单一 C ABI 调用(PluginJob 的载体)。
*
* @param dst 目标纹理(oakrender 创建)。GL 模式下调用方须先把
* dst 附着为渲染器输出目标并保持 GL 上下文 current
* OFX "OpenGL Current Context" 规则;等价 C++
* PluginRenderer::attach_output_texture)。
* @param src 主输入纹理(effect_input_id / SimpleSource;可空句柄)。
* @param effect_input_id job.src 落点的 clip 名(可 NULL)。
* @param inputs / input_count 其余输入 clip 的纹理表。
* @param values / value_count 参数覆盖表。
* @param renderer GL 渲染器(空句柄 → CPU 路径)。
* @param clear_destination / interactive 信息性标记(Phase 2
* render 驱动暂不处理;上层渲染器负责目标清空)。
*/
int oakplugin_instance_render_job(OakPluginInstance instance,
OakRenderTexture dst,
double time_seconds,
int clear_destination,
int interactive,
const char *effect_input_id,
OakRenderTexture src,
const oakplugin_job_texture *inputs,
int input_count,
const oakplugin_job_value *values,
int value_count,
OakRenderRenderer renderer);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_PLUGIN_INSTANCE_H
-315
View File
@@ -1,315 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_CACHE_H
#define OAK_EDITOR_RENDER_CACHE_H
#include <stdint.h>
// Same-dir quoted includes: inside this build the engine-style spelling
// "render/renderer.h" resolves to the transition bridge headers, so the
// public headers reference each other relative to their own directory.
#include "error.h"
#include "renderer.h" /* OakCodecFrame */
#include "node/node.h" /* OakNodeNode */
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file cache.h
* @brief C ABI for the oakrender playback/frame-hash caches
* (olive::PlaybackCache / olive::FrameHashCache), M7 §2.2.
*
* An OakRenderCache is a by-value reference-counted handle (shared_ptr
* semantics, see oakcommon's common/handle.h) boxing an
* olive::FrameHashCache (created without a parent node). Handles from
* oakrender_cache_create() are owned by the caller (reference count 1)
* and must be released with oakrender_cache_free(); handles from
* oakrender_cache_wrap_borrowed() are borrowed (release only frees the
* box).
*
* All timestamps are int64 frame numbers in the cache's timebase (see
* oakrender_cache_set_timebase()); a cache without a valid timebase
* treats timestamps as whole seconds.
*
* No cache events cross the boundary (M7 §2.2, 2026-08 revision):
* invalidate/validate are triggered by and known to the caller; the
* facade re-emits notifications after the triggering command.
*/
typedef struct OakRenderCache {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakRenderCache;
/**
* @brief Create a detached frame hash cache (no parent node, no
* timebase). Owned by the caller.
*
* @return Cache handle with reference count 1; ctx is NULL on
* allocation failure.
*/
OakRenderCache oakrender_cache_create(void);
/**
* @brief Release one reference to a cache created by
* oakrender_cache_create(). Convenience wrapper around
* cache->release(cache->ctx). NULL / empty-handle no-op; clears
* cache->ctx after releasing.
*/
void oakrender_cache_free(OakRenderCache *cache);
/**
* @brief Borrowed handle wrapping a native frame cache pointer obtained
* through oaknode (oaknode_node_get_video_frame_cache()).
*
* The cache itself stays owned by its node: release() on this handle
* only frees the box. Empty handle (ctx == NULL) for a NULL native
* pointer.
*/
OakRenderCache oakrender_cache_wrap_borrowed(void *native_cache);
/**
* @brief Cache flavours owned by a node
* (olive::Node's video/thumbnail/audio/waveform caches).
*/
enum OakRenderCacheKind {
OAKRENDER_CACHE_VIDEO_FRAME = 0, /**< olive::FrameHashCache */
OAKRENDER_CACHE_THUMBNAIL = 1, /**< olive::ThumbnailCache */
OAKRENDER_CACHE_AUDIO_PLAYBACK = 2, /**< olive::AudioPlaybackCache */
OAKRENDER_CACHE_AUDIO_WAVEFORM = 3 /**< olive::AudioWaveformCache */
};
/**
* @brief Create a cache of the given kind with a parent node (the
* native back-pointer stays inside oakrender; it is used for
* project cache-path resolution and job bookkeeping only).
*
* Owned by the caller (reference count 1); release with
* oakrender_cache_free(). Empty handle for an empty parent handle, an
* unknown kind, or on allocation failure.
*/
OakRenderCache oakrender_cache_create_for_node(OakNodeNode parent,
int kind);
/**
* @brief Cache UUID as canonical text, two-stage
* (PlaybackCache::get_uuid()).
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKRENDER_E_* code for an empty cache.
*/
int oakrender_cache_get_uuid(OakRenderCache cache, char *buf,
int buf_size);
/**
* @brief Request caching of a time range on behalf of a viewer
* (PlaybackCache::request()).
*
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty cache /
* context handle or a context that is not a viewer.
*/
int oakrender_cache_request(OakRenderCache cache, OakNodeNode context,
int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den);
/**
* @brief Load/save the cache's on-disk state (PlaybackCache::load_state()
* / save_state()). OAKRENDER_E_INVALID for an empty cache.
*/
int oakrender_cache_load_state(OakRenderCache cache);
int oakrender_cache_save_state(OakRenderCache cache);
/**
* @brief Enable/disable persisting this cache
* (PlaybackCache::set_saving_enabled()).
*/
int oakrender_cache_set_saving_enabled(OakRenderCache cache, int enabled);
/**
* @brief Pass this cache's ranges through to another cache
* (PlaybackCache::set_passthrough()). OAKRENDER_E_INVALID for an
* empty cache or an empty `other`.
*/
int oakrender_cache_set_passthrough(OakRenderCache cache,
OakRenderCache other);
/**
* @brief The on-disk filename for the frame at a time
* (FrameHashCache::get_valid_cache_filename()), two-stage.
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKRENDER_E_* code (OAKRENDER_E_INVALID when the cache is not
* a frame hash cache).
*/
int oakrender_cache_get_valid_cache_filename(OakRenderCache cache,
int64_t time_num,
int64_t time_den, char *buf,
int buf_size);
/**
* @brief The passthrough ranges as flat {in_n, in_d, out_n, out_d}
* quadruples (PlaybackCache::get_passthroughs(); only the ranges
* cross the boundary, the per-range cache UUID text stays
* internal).
*
* Two-stage: call with ranges == NULL (or max_ranges == 0) to get the
* count; then call with a buffer of max_ranges * 4 int64_t values.
*
* @return Range count (>= 0), or a negative OAKRENDER_E_* code.
*/
int oakrender_cache_get_passthroughs(OakRenderCache cache, int64_t *ranges,
int max_ranges);
/**
* @brief The cache's frame timebase (FrameHashCache::get_timebase()).
* Out params may individually be NULL. OAKRENDER_E_INVALID for
* an empty cache or a non-frame-hash cache.
*/
int oakrender_cache_get_timebase(OakRenderCache cache, int *num,
int *den);
/**
* @brief Lock/unlock the cache's internal mutex (PlaybackCache::mutex()).
* Empty cache is a no-op. Always pair the calls.
*/
void oakrender_cache_lock(OakRenderCache cache);
void oakrender_cache_unlock(OakRenderCache cache);
#ifdef __cplusplus
} /* extern "C" */
namespace olive { class PlaybackCache; }
extern "C" {
#endif
/**
* @brief Borrowed access to the underlying C++ cache (C++ only, for
* oakrender-internal adapters such as PreviewAutoCacher). Valid
* while the handle is held. NULL-safe.
*/
olive::PlaybackCache *oakrender_cache_get_native(OakRenderCache cache);
/**
* @brief Set the frame timebase used to interpret all timestamps of this
* cache (FrameHashCache::set_timebase()).
*
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty cache or
* non-positive num/den.
*/
int oakrender_cache_set_timebase(OakRenderCache cache, int num, int den);
/**
* @brief Set the cache UUID used in on-disk frame cache filenames
* (PlaybackCache::set_uuid()).
*
* @return OAKRENDER_OK or OAKRENDER_E_INVALID.
*/
int oakrender_cache_set_uuid(OakRenderCache cache, const char *uuid);
/**
* @brief Mark the timestamp range [in_ts, out_ts) invalidated
* (PlaybackCache::invalidate()). Empty cache is a no-op.
*/
void oakrender_cache_invalidate(OakRenderCache cache, int64_t in_ts,
int64_t out_ts);
/**
* @brief Mark a rational time range invalidated
* (PlaybackCache::invalidate(TimeRange)). Empty cache is a no-op.
*/
void oakrender_cache_invalidate_range(OakRenderCache cache,
int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den);
/**
* @brief Mark the timestamp range [in_ts, out_ts) validated
* (PlaybackCache::validate()). Empty cache is a no-op.
*/
void oakrender_cache_validate(OakRenderCache cache, int64_t in_ts,
int64_t out_ts);
/**
* @brief 1 when the cache holds any validated range
* (PlaybackCache::has_validated_ranges()), 0 otherwise / empty.
*/
int oakrender_cache_has_validated_ranges(OakRenderCache cache);
/**
* @brief Timeline cache indicator height in pixels
* (PlaybackCache::get_cache_indicator_height()). Constant query.
*/
int oakrender_cache_indicator_height(void);
/**
* @brief The invalidated sub-ranges of [in, out) as flat
* {in_n, in_d, out_n, out_d} quadruples
* (PlaybackCache::get_invalidated_ranges()).
*
* Two-stage: call with ranges == NULL (or max_ranges == 0) to get the
* count; then call with a buffer of max_ranges * 4 int64_t values.
*
* @return Range count (>= 0), or a negative OAKRENDER_E_* code.
*/
int oakrender_cache_get_invalidated_ranges(OakRenderCache c,
int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den,
int64_t *ranges, int max_ranges);
/**
* @brief Load a cached frame from disk
* (FrameHashCache::load_cache_frame(cache_path, uuid, ts)).
*
* @param path Cache directory (e.g. oakrender_disk_cache_path()).
* @param uuid Cache UUID of the producing node.
* @param out_frame Receives an owned frame handle (release with
* oakrender_codec_frame_free()).
*
* @return OAKRENDER_OK, OAKRENDER_E_INVALID (empty/NULL argument), or
* OAKRENDER_E_NOT_FOUND (no cached frame at `ts` / undecodable).
*/
int oakrender_frame_cache_load(OakRenderCache cache, const char *path,
const char *uuid, int64_t ts,
OakCodecFrame *out_frame);
/**
* @brief Save a frame to the disk cache under the cache's timebase and
* the frame's own timestamp (FrameHashCache::save_cache_frame()).
* Empty/NULL arguments are a no-op.
*/
void oakrender_frame_cache_save(OakRenderCache cache, const char *path,
const char *uuid, OakCodecFrame frame);
/* ---- Debug --------------------------------------------------------------- */
/**
* @brief Number of live oakrender-owned objects (caches, textures,
* frames, color processors) for leak assertions in tests.
*/
int oakrender_debug_alive_count(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_RENDER_CACHE_H
@@ -1,120 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_CANCELATOM_H
#define OAK_EDITOR_RENDER_CANCELATOM_H
#include <stdint.h>
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} /* extern "C" */
namespace olive { class CancelAtom; }
extern "C" {
#endif
/**
* @file cancelatom.h
* @brief C ABI for the oakrender cancellation primitive
* (olive::CancelAtom), a thread-safe cancel flag shared between a
* render/encode caller and its worker.
*
* OakCancelAtom follows the neutral by-value handle convention (see
* oakcommon's common/handle.h): oakrender_cancelatom_init() returns a
* handle whose underlying object has reference count 1, the addref and
* release function pointers adjust that count atomically (release
* destroys the object at zero), and abi_version is always
* OAKRENDER_ABI_VERSION. Copying the struct copies the pointer, not the
* count: call addref for every additional long-lived copy and release (or
* oakrender_cancelatom_free()) when done with each copy. Functions that
* only use a handle take it BY VALUE; an empty handle (ctx == NULL) is
* reported as OAKRENDER_E_INVALID.
*/
typedef struct OakCancelAtom {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakCancelAtom;
/**
* @brief Create a cancellation atom in the not-cancelled state.
*
* @return Handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakCancelAtom oakrender_cancelatom_init(void);
/**
* @brief Release one reference to a cancellation atom.
*
* Convenience wrapper around atom->release(atom->ctx): decrements the
* atomic reference count and destroys the object when it reaches zero,
* then nulls atom->ctx. No-op when atom is NULL or atom->ctx is NULL.
*/
void oakrender_cancelatom_free(OakCancelAtom *atom);
/**
* @brief Set the cancel flag (CancelAtom::cancel()). Thread-safe.
*
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty handle.
*/
int oakrender_cancelatom_cancel(OakCancelAtom atom);
/**
* @brief Read the cancel flag (CancelAtom::is_cancelled()).
*
* Reading a set flag also records that a consumer heard the
* cancellation; see oakrender_cancelatom_heard_cancel().
*
* @param cancelled Receives 1 when cancelled, 0 otherwise.
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty handle or a
* NULL out parameter.
*/
int oakrender_cancelatom_is_cancelled(OakCancelAtom atom, int *cancelled);
/**
* @brief Whether any consumer has observed the cancel flag through
* oakrender_cancelatom_is_cancelled() (CancelAtom::heard_cancel()).
*
* @param heard Receives 1 when the cancellation was heard, 0 otherwise.
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty handle or a
* NULL out parameter.
*/
int oakrender_cancelatom_heard_cancel(OakCancelAtom atom, int *heard);
#ifdef __cplusplus
/**
* @brief Borrowed access to the underlying C++ atom (C++ only, for
* adapter layers). Valid while the handle is held. NULL-safe.
*/
olive::CancelAtom *oakrender_cancelatom_get_native(OakCancelAtom atom);
}
#endif
#endif //OAK_EDITOR_RENDER_CANCELATOM_H
-255
View File
@@ -1,255 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_COLOR_H
#define OAK_EDITOR_RENDER_COLOR_H
#include "error.h"
#include "renderer.h"
#include "common/colortransform.h" /* OakColorTransform */
#include "node/colormanager.h" /* OakNodeColorManager */
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file color.h
* @brief C ABI for oakrender color processing (olive::ColorProcessor) and
* the process-wide default OCIO config (olive::ColorManager
* statics), M7 §2.3.
*
* An OakColorProcessor is a by-value reference-counted handle (shared_ptr
* semantics, see oakcommon's common/handle.h) boxing a ColorProcessorPtr
* (ColorProcessor is shared_ptr-managed); release with
* oakrender_color_processor_free(). Empty handles (ctx == NULL) are
* accepted by every function and yield a no-op / OAKRENDER_E_INVALID.
*
* Processors are built against the process-wide default OCIO config
* (olive::ColorManager::get_default_config()): the $OCIO config when the
* environment variable is set, otherwise the config extracted to the
* user configuration location. oakrender_color_manager_set_up_default_config()
* (re)builds it.
*/
/** Direction values for oakrender_color_processor_create(). */
enum {
OAKRENDER_COLOR_DIRECTION_NORMAL = 0,
OAKRENDER_COLOR_DIRECTION_INVERSE = 1
};
typedef struct OakColorProcessor {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakColorProcessor;
/**
* @brief Create a colorspace-to-colorspace processor on the default
* OCIO config.
*
* @param src_space Source colorspace name (role names are resolved).
* @param dst_transform Destination colorspace / output transform name.
* @param direction OAKRENDER_COLOR_DIRECTION_NORMAL (src -> dst) or
* OAKRENDER_COLOR_DIRECTION_INVERSE (dst -> src).
*
* OCIO failures are non-fatal (matching the C++ behavior): the handle is
* still returned but oakrender_color_processor_is_valid() reports 0 and
* conversions are pass-through.
*
* @return Processor handle with reference count 1; ctx is NULL for
* NULL/empty strings, an unknown direction, no default config,
* or allocation failure.
*/
OakColorProcessor oakrender_color_processor_create(const char *src_space,
const char *dst_transform,
int direction);
/**
* @brief Release one reference to a processor handle. Convenience
* wrapper around processor->release(processor->ctx). NULL /
* empty-handle no-op; clears processor->ctx after releasing.
*/
void oakrender_color_processor_free(OakColorProcessor *processor);
/**
* @brief 1 when the processor holds a valid OCIO processor
* (ColorProcessor::get_processor() != null), 0 otherwise / empty.
*/
int oakrender_color_processor_is_valid(OakColorProcessor processor);
/**
* @brief Create a processor from an input colorspace and a destination
* transform on a node's color manager
* (ColorProcessor::create(ColorManager*, input, dest, dir)).
*
* @param manager Borrowed manager handle (e.g.
* oaknode_colormanager_wrap_borrowed()).
* @param direction OAKRENDER_COLOR_DIRECTION_NORMAL / _INVERSE.
* @return Processor handle with reference count 1; ctx is NULL for
* empty/invalid arguments or allocation failure.
*/
OakColorProcessor oakrender_color_processor_create_transform(
OakNodeColorManager manager, const char *input,
OakColorTransform dest, int direction);
/**
* @brief Create a processor from a LUT file on a node's color manager
* (OCIO FileTransform with linear interpolation; direction
* selects forward/inverse).
*
* @return Processor handle with reference count 1; ctx is NULL for
* empty/invalid arguments, an unreadable LUT, or allocation
* failure.
*/
OakColorProcessor oakrender_color_processor_create_lut(
OakNodeColorManager manager, const char *path, int direction);
/**
* @brief Grading-primary transform styles for
* oakrender_color_processor_create_grading_primary().
*/
enum OakRenderGradingPrimaryStyle {
OAKRENDER_GRADING_PRIMARY_LIN = 0, /**< OCIO GRADING_LIN */
OAKRENDER_GRADING_PRIMARY_LOG = 1 /**< OCIO GRADING_LOG */
};
/**
* @brief Create a dynamic grading-primary processor on a node's color
* manager (OCIO GradingPrimaryTransform, forward direction).
*
* @return Processor handle with reference count 1; ctx is NULL for
* invalid arguments or allocation failure.
*/
OakColorProcessor oakrender_color_processor_create_grading_primary(
OakNodeColorManager manager, int style);
/* ---- LUT library ---------------------------------------------------- */
/**
* @brief 1 when `extension` (without dot, case-insensitive) is a
* supported LUT extension (LUTLibrary::is_supported_extension()).
*/
int oakrender_lut_is_supported_extension(const char *extension);
/**
* @brief Number of supported LUT extensions
* (LUTLibrary::supported_extensions()).
*/
int oakrender_lut_supported_extensions_count(void);
/**
* @brief Supported LUT extension at `index`, two-stage string.
*
* @return Required buffer size in bytes (including NUL), or a negative
* OAKRENDER_E_* code for an out-of-range index.
*/
int oakrender_lut_supported_extension_at(int index, char *buf,
int buf_size);
/**
* @brief Convert a single RGBA color (ColorProcessor::convert_color()).
* On an invalid processor the input is copied through.
*
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for empty/NULL arguments.
*/
int oakrender_color_processor_convert(OakColorProcessor processor,
double ir, double ig, double ib,
double ia, double *out_r, double *out_g,
double *out_b, double *out_a);
/**
* @brief Convert a CPU frame's pixels through the processor, in place
* (olive::ColorProcessor::convert_frame()).
*
* The frame's data buffer is rewritten through an OCIO PackedImageDesc
* view; nothing is allocated and the frame handle stays owned by the
* caller. A processor whose underlying OCIO processor is null
* (oakrender_color_processor_create() treats lookup failure as
* non-fatal) is a pass-through and returns OAKRENDER_OK, mirroring the
* C++ API.
*
* @return OAKRENDER_OK, OAKRENDER_E_INVALID for empty/uninitialized
* arguments, or OAKRENDER_E_FAILED on an internal exception.
*/
int oakrender_color_processor_convert_frame(OakColorProcessor processor,
OakCodecFrame frame);
/* ---- ColorManager statics ------------------------------------------------- */
/**
* @brief (Re)build the process-wide default OCIO config
* (ColorManager::set_up_default_config()).
*
* @return OAKRENDER_OK, or OAKRENDER_E_FAILED when no config could be
* created.
*/
int oakrender_color_manager_set_up_default_config(void);
/**
* @brief Describe the active default config: the $OCIO path when set,
* otherwise the extracted default config's path. Two-stage string
* getter: returns the required buffer size including NUL; pass
* buf == NULL or too small a buffer to query the size.
*
* @return Required size (non-negative), or OAKRENDER_E_STATE when no
* default config exists.
*/
int oakrender_color_manager_get_config(char *buf, int n);
/**
* @brief OCIO cache id of the display/view transform of the active
* default config, computed from the config's reference colorspace
* (a stable identifier usable as a conversion cache key).
*
* Two-stage string getter (same convention as
* oakrender_color_manager_get_config()).
*
* @return Required size (non-negative), OAKRENDER_E_INVALID (NULL/empty
* display or view), OAKRENDER_E_STATE (no default config), or
* OAKRENDER_E_NOT_FOUND (unknown display/view).
*/
int oakrender_color_manager_display_transform(const char *display,
const char *view, char *buf,
int n);
#ifdef __cplusplus
} /* extern "C" */
#include <memory>
namespace olive { class ColorProcessor; }
extern "C" {
#endif
/**
* @brief Borrowed access to the underlying C++ processor (C++ only, for
* adapter layers; a shared_ptr copy keeps the object alive).
* Empty shared_ptr for an empty handle.
*/
std::shared_ptr<olive::ColorProcessor> oakrender_color_processor_get_native(
OakColorProcessor processor);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_RENDER_COLOR_H
@@ -1,84 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_COPIER_H
#define OAK_EDITOR_RENDER_COPIER_H
#include "node/node.h"
#include "node/project.h"
#include "render/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to a project copier
* (olive::ProjectCopier): deep-copies a project graph for
* background processing (export/precache).
*
* By-value handle (shared_ptr semantics, see oakcommon's
* common/handle.h): oakrender_project_copier_create() returns a handle
* with reference count 1; release it with
* oakrender_project_copier_free().
*/
typedef struct OakRenderProjectCopier {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakRenderProjectCopier;
/**
* @brief Create a copier. The copy is built by
* oakrender_project_copier_set_project().
*
* @return Copier handle with reference count 1; ctx is NULL on
* allocation failure.
*/
OakRenderProjectCopier oakrender_project_copier_create(void);
/**
* @brief Release one reference to a copier; the final release frees the
* copier AND its copied project. NULL / empty-handle no-op; clears
* copier->ctx after releasing.
*/
void oakrender_project_copier_free(OakRenderProjectCopier *copier);
/** @brief (Re)build the copy from `project` (borrowed handle). */
int oakrender_project_copier_set_project(OakRenderProjectCopier copier,
OakNodeProject project);
/** @brief The copied counterpart of an original node (borrowed handle;
* freeing it only releases the handle box), empty handle when the
* node is not in the copied project. */
OakNodeNode oakrender_project_copier_get_copy(
OakRenderProjectCopier copier, OakNodeNode original);
/** @brief The copied project (borrowed handle; freeing it only releases
* the handle box). */
OakNodeProject oakrender_project_copier_get_copied_project(
OakRenderProjectCopier copier);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_RENDER_COPIER_H
@@ -1,49 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_ERROR_H
#define OAK_EDITOR_RENDER_ERROR_H
/**
* @brief Status and error codes shared by all oakrender C API families.
*
* Return-code convention (mirrors include/node/error.h):
* 0 (OAKRENDER_OK) on success, a negative OAKRENDER_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
/**
* @brief Current ABI version stamped into every oakrender handle.
*
* Bump whenever a handle layout or the semantics of any exported function
* change incompatibly. Consumers should compare a handle's abi_version
* field against the value they were compiled with before dereferencing
* ctx.
*/
#define OAKRENDER_ABI_VERSION 1
#define OAKRENDER_OK 0 /**< Success. */
#define OAKRENDER_E_INVALID (-70001) /**< NULL handle or invalid argument. */
#define OAKRENDER_E_STATE (-70002) /**< Call not valid in the current state. */
#define OAKRENDER_E_FAILED (-70003) /**< The underlying operation failed. */
#define OAKRENDER_E_NOT_FOUND (-70004) /**< Index out of range / entry not found. */
#define OAKRENDER_E_NOMEM (-70005) /**< Allocation failed. */
#endif //OAK_EDITOR_RENDER_ERROR_H
@@ -1,167 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_MANAGER_H
#define OAK_EDITOR_RENDER_MANAGER_H
#include <stdint.h>
// See cache.h for why these are same-dir relative includes.
#include "node/node.h" /* OakNodeNode (by-value handle) */
#include "cache.h" /* OakCodecFrame */
#include "color.h" /* OakColorProcessor */
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file manager.h
* @brief C ABI for the oakrender render manager / preview auto-cacher /
* disk cache singletons (olive::RenderManager,
* olive::PreviewAutoCacher, olive::DiskManager), M7 §2.4.
*
* The render manager is a process-wide singleton gated by
* oakrender_manager_init() / oakrender_manager_shutdown(). Functions
* that need it return OAKRENDER_E_STATE when it is not up.
*
* The frame request callback is the asynchronous command return channel
* (M7 §2.2 note): it fires on a render worker thread, possibly after
* cancellation. The delivered OakCodecFrame is owned by the callback
* recipient (release with oakrender_codec_frame_free()); an empty frame
* (ctx == NULL) signals "no result" (cancelled or failed). Beyond this
* callback there are no event subscription interfaces.
*/
/**
* @brief Create the RenderManager singleton (spawns render/audio
* threads, loads the configured backend).
*
* @return OAKRENDER_OK, OAKRENDER_E_STATE (already initialized), or
* OAKRENDER_E_FAILED.
*/
int oakrender_manager_init(void);
/**
* @brief Destroy the RenderManager singleton. No-op when not
* initialized.
*/
void oakrender_manager_shutdown(void);
/**
* @brief Completion callback of an asynchronous frame request.
*
* @param frame Owned frame handle, or an empty handle (ctx == NULL)
* when the request finished without a result (cancelled/failed).
* @param ts The request's timestamp, passed back verbatim.
*/
typedef void (*oakrender_frame_ready_fn)(OakCodecFrame frame, int64_t ts,
void *userdata);
/**
* @brief Asynchronously render one frame of `viewer` at `ts`
* (PreviewAutoCacher::get_single_frame()).
*
* `ts` is a frame number in the viewer node's video timebase (a whole
* second count when the viewer carries no valid timebase). The
* completion is delivered through `cb`; until then the request can be
* cancelled with oakrender_cancel_request().
*
* @return A positive request id, or a negative OAKRENDER_E_* code
* (OAKRENDER_E_INVALID for an empty viewer handle or NULL
* callback, OAKRENDER_E_STATE when the manager is not
* initialized, OAKRENDER_E_FAILED when no ticket could be
* created).
*/
int64_t oakrender_request_frame(OakNodeNode viewer, int64_t ts,
oakrender_frame_ready_fn cb, void *userdata);
/**
* @brief Cancel a pending frame request. The callback still fires with a
* NULL frame.
*
* @return OAKRENDER_OK, or OAKRENDER_E_NOT_FOUND for an unknown id.
*/
int oakrender_cancel_request(int64_t request_id);
/**
* @brief Set the multicam node on the manager's auto-cacher
* (PreviewAutoCacher::set_multicam_node()). `multicam_or_NULL` is a
* borrowed oaknode handle to a MultiCamNode (empty handle to clear).
*
* @return OAKRENDER_OK or OAKRENDER_E_STATE.
*/
int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL);
/**
* @brief Set the display color processor on the manager's auto-cacher
* (PreviewAutoCacher::set_display_color_processor()). Borrowed handle,
* empty ctx to clear.
*
* @return OAKRENDER_OK or OAKRENDER_E_STATE.
*/
int oakrender_set_display_color_processor(OakColorProcessor p_or_NULL);
/**
* @brief 1 when the process-wide RenderManager singleton exists
* (RenderManager::instance() != nullptr; only the main GUI
* process creates one), 0 otherwise.
*/
int oakrender_manager_available(void);
/**
* @brief Cancel in-flight video cache tasks on the manager's
* auto-cacher (PreviewAutoCacher::cancel_video_tasks()). No-op
* when no manager/auto-cacher exists (e.g. a worker process).
*/
void oakrender_cancel_video_tasks(int wait_for_done);
/* ---- Disk cache (olive::DiskManager) -------------------------------------- */
/**
* @brief The default disk cache directory
* (DiskManager::get_default_disk_cache_path()). Two-stage string getter:
* returns the required buffer size including NUL; pass buf == NULL or
* too small a buffer to query the size. Does not require the manager.
*/
int oakrender_disk_cache_path(char *buf, int n);
/**
* @brief Bytes currently consumed by the default disk cache folder.
* Lazily creates the DiskManager singleton on first use.
*
* @return Consumption in bytes (>= 0), or OAKRENDER_E_FAILED.
*/
int64_t oakrender_disk_cache_size(void);
/**
* @brief Clear the default disk cache folder
* (DiskManager::clear_disk_cache()).
*
* @return OAKRENDER_OK or OAKRENDER_E_FAILED.
*/
int oakrender_disk_cache_clear(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_RENDER_MANAGER_H
@@ -1,374 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_RENDERER_H
#define OAK_EDITOR_RENDER_RENDERER_H
#include <stdint.h>
#ifdef __cplusplus
#include <memory>
#endif
#include "error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file renderer.h
* @brief C ABI for the oakrender display renderer (olive::Renderer) —
* renderer/texture/frame/blit families plus backend management.
*
* Signatures follow the R7-A display.h rewrite
* (docs/zh/plans/completed/r7-pure-abi-plan.md §A.2) with the
* oakrender_ prefix (M7 §2.1).
*
* Ownership protocol: every public handle is a by-value
* reference-counted struct (see oakcommon's common/handle.h; shared_ptr
* semantics). init/create functions return a handle with reference
* count 1, handle.addref(handle.ctx) takes another reference, and
* handle.release(handle.ctx) (or the oakrender_*_free() convenience
* wrappers, which also null the caller's ctx) drops one; the object is
* destroyed in this library when the count reaches zero. Empty handles
* (ctx == NULL) are accepted by every function and yield a no-op / zero
* result / OAKRENDER_E_INVALID.
*
* Cross-thread handoff (§A.3): the producing side addrefs before
* publishing a handle into a shared slot; the consuming side releases
* the handle it replaced. The side holding the slot when it is torn
* down releases the remaining handle.
*
* Handles:
* - OakRenderRenderer wraps a native olive::Renderer.
* - OakRenderTexture / OakCodecFrame box shared_ptr-managed engine
* objects.
* - `gl_context` is an opaque borrowed olive::OpenGLContext* (or NULL
* to let the backend create its own offscreen surface).
*/
/**
* @brief POD mirror of olive::VideoParams' user-facing fields.
*
* Same layout and field semantics as oak_video_params
* (engine/include/oakengine/videoparams.h): `time_base_*` is the frame
* duration (frame rate flipped), `format` an olive::PixelFormat::Format
* value, `interlacing` an olive::VideoParams::Interlacing value,
* `color_range` an olive::VideoParams::ColorRange value. The video
* channel count is an engine-internal constant and not exposed.
*/
typedef struct oakrender_video_params {
int width;
int height;
int time_base_num; /**< Frame duration numerator (e.g. 1001/30000 s). */
int time_base_den;
int format; /**< olive::PixelFormat::Format. */
int pixel_aspect_num;
int pixel_aspect_den;
int interlacing; /**< olive::VideoParams::Interlacing. */
int color_range; /**< olive::VideoParams::ColorRange. */
int divider; /**< Preview resolution divider (1 = full). */
int video_type; /**< olive::VideoParams::Type (0 = video). */
int premultiplied_alpha; /**< 0/1. */
} oakrender_video_params;
/**
* @brief Reference-counted handle to a display renderer
* (olive::Renderer). See the file-level ownership protocol.
*/
typedef struct OakRenderRenderer {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakRenderRenderer;
/**
* @brief Reference-counted handle to a GPU texture (olive::Texture).
* See the file-level ownership protocol.
*/
typedef struct OakRenderTexture {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakRenderTexture;
/**
* @brief Reference-counted handle to a CPU frame (an olive::FramePtr
* boxed in a control block). Declared here so the cache family
* (render/cache.h) can use the same type; the frame functions live in
* this header.
* Named OakCodecFrame per the M7 §2.2 contract; the oakcodec wave (M5)
* adopts the same handle.
*/
typedef struct OakCodecFrame {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakCodecFrame;
/**
* @brief Flattened POD of olive::ColorTransformJob for the display blit
* path. `matrix`/`crop_matrix` are column-major 4x4; an all-zero matrix
* means identity.
*/
typedef struct oakrender_color_transform_job {
const void *processor; /**< OakColorProcessor ctx (borrowed), may be NULL. */
void *input_texture; /**< OakRenderTexture ctx (borrowed, not retained). */
int input_alpha_association; /**< 0=none, 1=associated. */
int clear_destination; /**< 0/1. */
int force_opaque; /**< 0/1. */
float matrix[16];
float crop_matrix[16];
} oakrender_color_transform_job;
/* ---- Renderer lifecycle -------------------------------------------------- */
/**
* @brief Create a renderer on the named dynamic backend ("opengl",
* "vulkan"; olive::DynamicRenderer). Loads the backend shared library;
* falls back per DynamicRenderer rules.
*
* @return Renderer handle with reference count 1; ctx is NULL on
* NULL/empty backend id, load failure, or allocation failure.
*/
OakRenderRenderer oakrender_display_renderer_create_dynamic(
const char *backend_id);
/**
* @brief Create an OpenGL renderer (olive::OpenGLRenderer). The renderer
* is not initialized; call oakrender_display_renderer_init() before use.
*
* @return Renderer handle with reference count 1; ctx is NULL on
* allocation failure.
*/
OakRenderRenderer oakrender_display_renderer_create_opengl(void);
/**
* @brief Initialize a renderer. `gl_context` is a borrowed opaque
* olive::OpenGLContext*, or NULL to use the backend's default
* device/context path (Renderer::init()).
*
* @return OAKRENDER_OK, OAKRENDER_E_INVALID (empty renderer), or
* OAKRENDER_E_FAILED (backend init failed).
*/
int oakrender_display_renderer_init(OakRenderRenderer renderer,
void *gl_context);
/**
* @brief Release one reference to a renderer (the final release runs
* Renderer::destroy() + delete). Convenience wrapper around
* renderer->release(renderer->ctx): NULL / empty-handle no-op; clears
* renderer->ctx after releasing.
*/
void oakrender_display_renderer_destroy(OakRenderRenderer *renderer);
/* ---- Renderer queries ---------------------------------------------------- */
/** @brief 1 when the renderer is OpenGL-based, 0 otherwise / empty. */
int oakrender_display_renderer_is_open_gl(OakRenderRenderer renderer);
/** @brief 1 when the renderer is Vulkan-based, 0 otherwise / empty. */
int oakrender_display_renderer_is_vulkan(OakRenderRenderer renderer);
/* ---- Texture handle ------------------------------------------------------ */
/**
* @brief Create a GPU texture on `renderer`.
*
* @param pixels Initial pixel data, or NULL for an uninitialized texture.
* @param linesize Stride of `pixels` in bytes (0 when pixels is NULL).
* @return New texture handle (reference count 1); ctx is NULL on invalid
* arguments / allocation failure.
*/
OakRenderTexture oakrender_display_texture_create(
OakRenderRenderer renderer, const oakrender_video_params *params,
const void *pixels, int linesize);
/**
* @brief Take another reference to a texture and return the same handle.
*
* Convenience wrapper around handle.addref(handle.ctx). An empty handle
* in yields an empty handle out. Every retain must be paired with
* exactly one free/release.
*/
OakRenderTexture oakrender_display_texture_retain(OakRenderTexture texture);
/**
* @brief Release one reference to a texture. Convenience wrapper around
* texture->release(texture->ctx): frees the texture when the count
* reaches zero. NULL / empty-handle no-op; clears texture->ctx after
* releasing.
*/
void oakrender_display_texture_free(OakRenderTexture *texture);
int oakrender_display_texture_upload(OakRenderTexture texture,
const void *pixels, int linesize);
int oakrender_display_texture_download(OakRenderTexture texture, void *pixels,
int linesize);
/* ---- Texture queries ----------------------------------------------------- */
int oakrender_display_texture_get_params(OakRenderTexture texture,
oakrender_video_params *out);
/** @brief Frame width/height in pixels (0 on empty). */
int oakrender_codec_frame_width(OakCodecFrame frame);
int oakrender_codec_frame_height(OakCodecFrame frame);
/** @brief ffmpeg_bridge pixel format when the frame wraps a texture's
* CPU copy (an AVFramePtr); -1 otherwise. */
int oakrender_codec_frame_fb_format(OakCodecFrame frame);
/** @brief Native texture id (0 on empty or a dummy/id-less texture). */
int oakrender_display_texture_id(OakRenderTexture texture);
/** @brief 1 when the texture is a placeholder dummy (Texture::is_dummy()). */
int oakrender_display_texture_is_dummy(OakRenderTexture texture);
/**
* @brief The CPU frame stored in the texture, if any (Texture::frame()).
* *out receives a retained frame handle (empty when none).
*/
int oakrender_display_texture_get_frame(OakRenderTexture texture,
OakCodecFrame *out);
#ifdef __cplusplus
} /* extern "C" */
namespace olive { class Texture; using TexturePtr = std::shared_ptr<Texture>; }
/**
* @brief Wrap a native TexturePtr in a retained handle (C++ only; used
* by oakrender internals when handing textures across the C ABI).
*/
OakRenderTexture oakrender_display_texture_wrap_native(
const olive::TexturePtr &texture);
extern "C" {
#endif
/* ---- Frame handle -------------------------------------------------------- */
/** @brief Create an empty CPU frame. Returns a handle with count 1. */
OakCodecFrame oakrender_codec_frame_create(void);
/**
* @brief Take another reference to a frame and return the same handle.
* Empty in yields empty out (see oakrender_display_texture_retain()).
*/
OakCodecFrame oakrender_codec_frame_retain(OakCodecFrame frame);
/**
* @brief Release one reference to a frame. Convenience wrapper around
* frame->release(frame->ctx). NULL / empty-handle no-op; clears
* frame->ctx after releasing.
*/
void oakrender_codec_frame_free(OakCodecFrame *frame);
int oakrender_codec_frame_set_video_params(
OakCodecFrame frame, const oakrender_video_params *params);
int oakrender_codec_frame_get_params(OakCodecFrame frame,
oakrender_video_params *out);
/**
* @brief Allocate the pixel buffer per the frame's video params
* (Frame::allocate()).
*
* @return OAKRENDER_OK, OAKRENDER_E_INVALID (empty frame), or
* OAKRENDER_E_FAILED (invalid params / allocation failed).
*/
int oakrender_codec_frame_allocate(OakCodecFrame frame);
/** @brief Borrowed pixel data pointer (valid until the final release). */
void *oakrender_codec_frame_data(OakCodecFrame frame);
/** @brief Borrowed const pixel data pointer. */
const void *oakrender_codec_frame_const_data(OakCodecFrame frame);
/** @brief Line stride in bytes. */
int oakrender_codec_frame_linesize_bytes(OakCodecFrame frame);
/** @brief 1 when the pixel buffer is allocated, 0 otherwise / empty. */
int oakrender_codec_frame_is_allocated(OakCodecFrame frame);
/* ---- Color-managed blit -------------------------------------------------- */
/**
* @brief Blit a color-managed image through the OCIO pipeline
* (Renderer::blit_color_managed()).
*
* @param dst_texture Destination texture handle, or an empty handle for
* the current output target.
* @param params Destination video params, or NULL to use dst_texture's.
*/
int oakrender_display_renderer_blit_color_managed(
OakRenderRenderer renderer, const oakrender_color_transform_job *job,
OakRenderTexture dst_texture, const oakrender_video_params *params);
/* ---- Cross-backend texture download -------------------------------------- */
int oakrender_display_renderer_download_from_texture(
OakRenderRenderer renderer, int texture_id,
const oakrender_video_params *params, void *dst_pixels, int linesize);
/* ---- Backend management (M7 §2.1) ---------------------------------------- */
/**
* @brief Number of known render backends (olive::RenderManager::Backend:
* opengl, vulkan, multiprocess, dummy).
*/
int oakrender_backend_count(void);
/**
* @brief Id string of the `i`-th backend ("opengl", ...). Two-stage
* string getter: returns the required buffer size including NUL; pass
* buf == NULL or too small a buffer to query the size.
*
* @return Required size (non-negative), or OAKRENDER_E_NOT_FOUND when
* `i` is out of range.
*/
int oakrender_backend_id_at(int i, char *buf, int n);
/**
* @brief Record the requested backend id (applied to the RenderManager
* instance when one exists).
*
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for a NULL/unknown id.
*/
int oakrender_set_backend(const char *backend_id);
/**
* @brief The effective backend: the RenderManager instance's backend when
* an instance exists, otherwise the requested backend. Two-stage string
* getter (same convention as oakrender_backend_id_at()).
*/
int oakrender_current_backend(char *buf, int n);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_RENDER_RENDERER_H
-171
View File
@@ -1,171 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_RENDER_TICKET_H
#define OAK_EDITOR_RENDER_TICKET_H
#include <stdint.h>
#include "common/colortransform.h"
#include "common/videoparams.h"
#include "node/colormanager.h"
#include "node/node.h"
#include "olive/core/oakcore/audioparams.h"
#include "olive/core/oakcore/samplebuffer.h"
#include "render/error.h"
#include "render/cache.h"
#include "render/color.h"
#include "render/renderer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to a render ticket
* (olive::RenderTicketWatcher).
*
* By-value handle (shared_ptr semantics, see oakcommon's
* common/handle.h). Created by oakrender_ticket_render_frame() /
* oakrender_ticket_render_audio() with reference count 1; release with
* oakrender_ticket_free().
*/
typedef struct OakRenderTicket {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
} OakRenderTicket;
/**
* @brief Finished callback (async command return channel, 01 §4
* exception). Fires on the ticket's finishing thread, exactly
* once (cancelled tickets fire with a NULL result). The ticket
* handle is a borrowed copy of the submitter's handle; the
* submitter keeps ownership and releases it.
*/
typedef void (*oakrender_ticket_finished_fn)(OakRenderTicket ticket,
void *userdata);
/** @brief Ticket types (RenderManager::TicketType). */
enum OakRenderTicketType {
OAKRENDER_TICKET_VIDEO = 0,
OAKRENDER_TICKET_AUDIO = 1
};
/**
* @brief Parameters for a video frame ticket
* (RenderManager::RenderVideoParams).
*/
typedef struct oakrender_video_ticket_params {
OakNodeNode output_node; /**< Connected texture output node (borrowed). */
OakVideoParams video_params; /**< By value (oakcommon handle). */
OakAudioParams *audio_params; /**< Borrowed oakcore handle, may be NULL. */
int64_t time_num; /**< Frame timestamp as rational. */
int64_t time_den;
OakNodeColorManager color_manager; /**< Borrowed, empty ctx = NULL. */
int mode; /**< olive::RenderMode::Mode as int. */
int force_width; /**< 0/0 = off. */
int force_height;
double force_matrix[16]; /**< Used when has_force_matrix != 0. */
int has_force_matrix;
int force_format; /**< PixelFormat as int, -1 = off. */
int force_channel_count; /**< 0 = off. */
OakColorProcessor force_color_output; /**< Borrowed; empty ctx = none. */
OakColorTransform force_color_transform; /**< By value; empty ctx = default. */
OakRenderCache cache; /**< Borrowed frame cache; empty ctx = none. */
} oakrender_video_ticket_params;
/**
* @brief Submit a video frame render ticket.
*
* @return Ticket handle with reference count 1 (caller releases); ctx is
* NULL on failure. The finished callback fires exactly once;
* NULL `cb` is allowed (poll with
* oakrender_ticket_wait()/oakrender_ticket_is_finished()).
*/
OakRenderTicket oakrender_ticket_render_frame(
const oakrender_video_ticket_params *params,
oakrender_ticket_finished_fn cb, void *userdata);
/**
* @brief Submit an audio render ticket (RenderManager::render_audio()).
*
* @param output_node Connected sample output node.
* @param params Audio params (borrowed oakcore handle).
*/
OakRenderTicket oakrender_ticket_render_audio(
OakNodeNode output_node, int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den, const OakAudioParams *params,
int mode, oakrender_ticket_finished_fn cb, void *userdata);
int oakrender_ticket_is_finished(OakRenderTicket ticket);
/** @brief Block until the ticket finishes. */
int oakrender_ticket_wait(OakRenderTicket ticket);
int oakrender_ticket_cancel(OakRenderTicket ticket);
/** @brief OAKRENDER_TICKET_* or negative error. */
int oakrender_ticket_get_type(OakRenderTicket ticket);
/** @brief Ticket timestamp (video tickets). */
int oakrender_ticket_get_time(OakRenderTicket ticket, int64_t *out_num,
int64_t *out_den);
/** @brief Ticket time range (audio tickets). */
int oakrender_ticket_get_range(OakRenderTicket ticket, int64_t *in_num,
int64_t *in_den, int64_t *out_num,
int64_t *out_den);
/**
* @brief The resulting frame (video tickets). *out receives an owned
* OakCodecFrame (release with oakrender_codec_frame_free()).
* OAKRENDER_E_STATE when unfinished, OAKRENDER_E_FAILED when the
* ticket has no frame result.
*/
int oakrender_ticket_get_frame(OakRenderTicket ticket, OakCodecFrame *out);
/**
* @brief The resulting samples (audio tickets). *out receives a copy
* (release with oakcore_samplebuffer_free()).
*/
int oakrender_ticket_get_samples(OakRenderTicket ticket,
OakSampleBuffer **out);
/**
* @brief Release one reference to a ticket (the final release is safe on
* finished tickets; cancels and waits on running ones). Convenience
* wrapper around ticket->release(ticket->ctx). NULL / empty-handle
* no-op; clears ticket->ctx after releasing.
*/
void oakrender_ticket_free(OakRenderTicket *ticket);
/**
* @brief Toggle aggressive garbage collection on the render manager
* (RenderManager::set_aggressive_garbage_collection()).
*/
int oakrender_manager_set_aggressive_gc(int enabled);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_RENDER_TICKET_H
-42
View File
@@ -1,42 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TASK_ERROR_H
#define OAK_EDITOR_TASK_ERROR_H
/**
* @brief Status and error codes shared by all oaktask C API families.
*
* Return-code convention (mirrors engine/include/oakengine/init.h):
* 0 (OAKTASK_OK) on success, a negative OAKTASK_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
#define OAKTASK_ABI_VERSION 1
#define OAKTASK_OK 0 /**< Success. */
#define OAKTASK_E_INVALID (-80001) /**< NULL handle or invalid argument. */
#define OAKTASK_E_STATE (-80002) /**< Call not valid in the current state. */
#define OAKTASK_E_FAILED (-80003) /**< The underlying operation failed. */
#define OAKTASK_E_NOT_FOUND (-80004) /**< Index out of range / entry not found. */
#define OAKTASK_E_NOMEM (-80005) /**< Allocation failed. */
#define OAKTASK_E_CANCELLED (-80006) /**< The operation was cancelled. */
#endif //OAK_EDITOR_TASK_ERROR_H
@@ -1,55 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TASK_MANAGER_H
#define OAK_EDITOR_TASK_MANAGER_H
#include "task/task.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Task manager singleton lifecycle.
*/
int oaktask_manager_init(void);
void oaktask_manager_shutdown(void);
/**
* @brief Register oaktask as oakcodec's background task submitter
* (olive::register_codec_task_submitter()). Called by
* oaktask_manager_init(); exposed for manual control.
*/
int oaktask_register_codec_submitter(void);
int oaktask_manager_count(void);
/** @brief Borrowed task at index (release only frees the box), empty
* handle when out of range or no manager. */
OakTaskTask oaktask_manager_at(int i);
void oaktask_manager_delete_finished(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TASK_MANAGER_H
-126
View File
@@ -1,126 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TASK_PROJECT_H
#define OAK_EDITOR_TASK_PROJECT_H
#include "codec/encoder.h"
#include "node/colormanager.h"
#include "node/footage.h"
#include "node/node.h"
#include "node/project.h"
#include "node/sequence.h"
#include "task/task.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Project task factories and result accessors (M8 §2.2).
*/
/** @brief olive::ProjectLoadTask. Empty handle (ctx == NULL) on
* failure. */
OakTaskTask oaktask_create_project_load(const char *filename);
/** @brief Take the loaded project (ownership transfer). Empty handle
* (ctx == NULL) when the task has not succeeded or the project was
* already taken. */
OakNodeProject oaktask_load_take_project(OakTaskTask t);
/** @brief olive::ProjectSaveTask. `filename_or_NULL` overrides the
* project's own filename. `project` is borrowed by the task. */
OakTaskTask oaktask_create_project_save(OakNodeProject project,
const char *filename_or_NULL,
int use_compression);
/** @brief olive::ProjectImportTask. `folder`/`project` are borrowed by
* the task. */
OakTaskTask oaktask_create_project_import(OakNodeFolder folder,
OakNodeProject project,
const char *const *urls,
int url_count);
/** @brief Take the import's undo command (ownership transfer). */
OakUndoCommand oaktask_import_take_command(OakTaskTask t);
int oaktask_import_footage_count(OakTaskTask t);
/** @brief Footage handle at index (addref'd; release with
* handle.release(handle.ctx) - box only, the project owns the
* footage). Empty handle when out of range. */
OakNodeFootage oaktask_import_footage_at(OakTaskTask t, int index);
int oaktask_import_invalid_count(OakTaskTask t);
/** @brief Invalid filename at index (two-stage). */
int oaktask_import_invalid_at(OakTaskTask t, int index, char *buf,
int buf_size);
/** @brief olive::LoadOTIOTask. Empty handle (ctx == NULL) on failure. */
OakTaskTask oaktask_create_project_load_otio(const char *filename);
/** @brief Take the loaded project (ownership transfer). Empty handle
* (ctx == NULL) when the task has not succeeded or the project was
* already taken. */
OakNodeProject oaktask_load_otio_take_project(OakTaskTask t);
/** @brief olive::SaveOTIOTask. `project` is borrowed by the task. */
OakTaskTask oaktask_create_project_save_otio(OakNodeProject project,
const char *filename);
/**
* @brief OTIO import confirmation callback (facade concern; default
* accepts everything). Return non-zero to accept.
*/
typedef int (*oaktask_otio_import_confirm_fn)(
const char *const *sequence_names, int count, void *userdata);
void oaktask_load_otio_set_confirm_cb(oaktask_otio_import_confirm_fn fn,
void *userdata);
/** @brief olive::PreCacheTask. `footage`/`sequence` are borrowed by the
* task. */
OakTaskTask oaktask_create_precache(OakNodeFootage footage, int index,
OakNodeSequence sequence);
/** @brief olive::ExportTask (params POD from codec/encoder.h).
* `viewer`/`color_manager` are borrowed by the task. */
OakTaskTask oaktask_create_export(OakNodeNode viewer,
OakNodeColorManager color_manager,
const oakcodec_encoding_params *params);
/**
* @brief Image-sequence confirmation callback (facade/UI concern;
* olive::ProjectImportTask::set_image_sequence_confirm_callback).
* Return non-zero to treat numbered stills as a sequence.
* Default (no callback): not a sequence.
*/
typedef int (*oaktask_image_sequence_confirm_fn)(const char *filename,
void *userdata);
void oaktask_import_set_image_sequence_confirm_cb(
oaktask_image_sequence_confirm_fn fn, void *userdata);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TASK_PROJECT_H
-108
View File
@@ -1,108 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TASK_TASK_H
#define OAK_EDITOR_TASK_TASK_H
#include <stdint.h>
#include "task/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to a background task (olive::Task).
*
* By-value handle (shared_ptr semantics, see oakcommon's
* common/handle.h). Tasks are created through the factories in
* task/project.h (and future family headers) with reference count 1 and
* must be released with oaktask_task_free(). oaktask_task_start()
* transfers the task's lifetime to the task manager: releasing the
* handle afterwards only frees the box.
*/
typedef struct OakTaskTask {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKTASK_ABI_VERSION. */
} OakTaskTask;
/** @brief Lifecycle event ids for oaktask_task_subscribe(). */
enum OakTaskEvent {
OAKTASK_EVENT_STARTED = 0,
OAKTASK_EVENT_PROGRESS = 1,
OAKTASK_EVENT_FINISHED = 2
};
/**
* @brief Event callback (async command return channel, 01 §4 exception).
*
* For OAKTASK_EVENT_FINISHED, `value` is 1.0 on success / 0.0 on failure;
* for OAKTASK_EVENT_PROGRESS it is 0..1; for OAKTASK_EVENT_STARTED it is
* the start time in milliseconds.
*/
typedef void (*oaktask_event_fn)(int event_id, double value,
void *userdata);
/**
* @brief Release one reference to a task. Convenience wrapper around
* t->release(t->ctx): NULL / empty-handle no-op; clears t->ctx
* after releasing. The task must not be running on the manager
* (oaktask_task_cancel + wait first if it is).
*/
void oaktask_task_free(OakTaskTask *t);
/** @brief Run synchronously in the calling thread. 1 = succeeded. */
int oaktask_task_start_sync(OakTaskTask t);
/** @brief Run asynchronously on the task manager. */
int oaktask_task_start(OakTaskTask t);
int oaktask_task_cancel(OakTaskTask t);
/** @brief Wait for an asynchronously started task. */
int oaktask_task_wait(OakTaskTask t);
int oaktask_task_is_finished(OakTaskTask t);
int oaktask_task_succeeded(OakTaskTask t);
/** @brief Two-stage string getters. */
int oaktask_task_title(OakTaskTask t, char *buf, int buf_size);
int oaktask_task_error(OakTaskTask t, char *buf, int buf_size);
/**
* @brief Subscribe to lifecycle events (returns a subscription id >= 0,
* or a negative error code). One-shot per event stream: the
* subscription is dropped after OAKTASK_EVENT_FINISHED.
*/
int64_t oaktask_task_subscribe(OakTaskTask t, oaktask_event_fn fn,
void *userdata);
/** @brief Alive-count for leak assertions in tests. */
int oaktask_debug_alive_count(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TASK_TASK_H
@@ -1,51 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TIMELINE_DISPLAYMODE_H
#define OAK_EDITOR_TIMELINE_DISPLAYMODE_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Shared timeline display-mode constants.
*
* Neutral home for the enum values behind the TimelineThumbnailMode /
* TimelineWaveformMode config keys; mirrors olive::Timeline::ThumbnailMode
* / WaveformMode (src/timeline/src/timelinecommon.h) and must stay
* value-compatible with them.
*/
enum OakTimelineThumbnailMode {
OAK_TIMELINE_THUMBNAIL_OFF = 0,
OAK_TIMELINE_THUMBNAIL_IN_OUT = 1,
OAK_TIMELINE_THUMBNAIL_ON = 2
};
enum OakTimelineWaveformMode {
OAK_TIMELINE_WAVEFORMS_DISABLED = 0,
OAK_TIMELINE_WAVEFORMS_ENABLED = 1
};
#ifdef __cplusplus
}
#endif
#endif // OAK_EDITOR_TIMELINE_DISPLAYMODE_H
-131
View File
@@ -1,131 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TIMELINE_EDIT_H
#define OAK_EDITOR_TIMELINE_EDIT_H
#include "node/block.h"
#include "node/sequence.h"
#include "node/track.h"
#include "timeline/error.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Timeline edit primitives (M4 §2.3).
*
* The timeline undo command classes stay inside oaktimeline (01 §5);
* consumers create commands through these factories, receiving base
* OakUndoCommand handles (owned; free with oakundo_command_free()).
* Redo a command directly or push it on an undo stack.
*
* OakNode* handles are passed by value per the oaknode handle
* convention; an empty handle (ctx == NULL) yields an empty
* OakUndoCommand result.
*/
/** @brief olive::TimelineAddTrackCommand. */
OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList list);
/** @brief olive::TimelineRemoveTrackCommand. */
OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack track);
/** @brief olive::TrackPlaceBlockCommand. */
OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList list,
int track_index,
OakNodeBlock block,
int64_t in_num,
int64_t in_den);
/** @brief olive::TrackReplaceBlockWithGapCommand. */
OakUndoCommand oaktimeline_replace_block_with_gap_command(
OakNodeTrack track, OakNodeBlock block);
/**
* @brief The capi's move-clip assembly: gap the block's old spot and place
* it at `in` on `track_index` of `list` as ONE undoable entry
* (olive::TrackReplaceBlockWithGapCommand + olive::TrackPlaceBlockCommand
* inside a MultiUndoCommand).
*/
OakUndoCommand oaktimeline_move_block_command(OakNodeTrackList list,
int track_index,
OakNodeBlock block,
int64_t in_num,
int64_t in_den);
/**
* @brief olive::BlockTrimCommand. `mode` is an OakTimelineMovementMode
* value (k_trim_in / k_trim_out).
*/
OakUndoCommand oaktimeline_trim_command(OakNodeTrack track,
OakNodeBlock block,
int64_t new_length_num,
int64_t new_length_den, int mode);
/** @brief olive::BlockSplitCommand on a set of blocks at one point. */
OakUndoCommand oaktimeline_split_command(const OakNodeBlock *blocks,
int count, int64_t point_num,
int64_t point_den);
/** @brief olive::BlockSplitPreservingLinksCommand. */
OakUndoCommand oaktimeline_split_preserving_links_command(
const OakNodeBlock *blocks, int count, const int64_t *point_nums,
const int64_t *point_dens, int time_count);
/** @brief olive::TimelineRippleDeleteGapsAtRegionsCommand. */
OakUndoCommand oaktimeline_ripple_delete_gaps_command(
OakNodeSequence sequence, const int64_t *in_nums,
const int64_t *in_dens, const int64_t *out_nums,
const int64_t *out_dens, const OakNodeTrack *tracks, int range_count);
/** @brief olive::TrackSlideCommand. */
OakUndoCommand oaktimeline_slide_command(
OakNodeTrack track, const OakNodeBlock *blocks, int block_count,
OakNodeBlock in_adjacent, OakNodeBlock out_adjacent,
int64_t movement_num, int64_t movement_den);
/** @brief olive::TrackRippleRemoveAreaCommand. */
OakUndoCommand oaktimeline_ripple_remove_area_command(
OakNodeTrack track, int64_t in_num, int64_t in_den, int64_t out_num,
int64_t out_den);
/** @brief olive::TrackListInsertGaps. */
OakUndoCommand oaktimeline_insert_gaps_command(OakNodeTrackList list,
int64_t point_num,
int64_t point_den,
int64_t length_num,
int64_t length_den);
/** @brief Movement modes (olive::Timeline::MovementMode). */
enum OakTimelineMovementMode {
OAKTIMELINE_MOVEMENT_NONE = 0,
OAKTIMELINE_MOVEMENT_MOVE = 1,
OAKTIMELINE_MOVEMENT_TRIM_IN = 2,
OAKTIMELINE_MOVEMENT_TRIM_OUT = 3
};
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TIMELINE_EDIT_H
@@ -1,41 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TIMELINE_ERROR_H
#define OAK_EDITOR_TIMELINE_ERROR_H
/**
* @brief Status and error codes shared by all oaktimeline C API families.
*
* Return-code convention (mirrors engine/include/oakengine/init.h):
* 0 (OAKTIMELINE_OK) on success, a negative OAKTIMELINE_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
#define OAKTIMELINE_ABI_VERSION 1
#define OAKTIMELINE_OK 0 /**< Success. */
#define OAKTIMELINE_E_INVALID (-40001) /**< NULL handle or invalid argument. */
#define OAKTIMELINE_E_STATE (-40002) /**< Call not valid in the current state. */
#define OAKTIMELINE_E_FAILED (-40003) /**< The underlying operation failed. */
#define OAKTIMELINE_E_NOT_FOUND (-40004) /**< Index out of range / entry not found. */
#define OAKTIMELINE_E_NOMEM (-40005) /**< Allocation failed. */
#endif //OAK_EDITOR_TIMELINE_ERROR_H
@@ -1,138 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TIMELINE_MARKER_H
#define OAK_EDITOR_TIMELINE_MARKER_H
#include "common/xmlutils.h"
#include "node/node.h"
#include "timeline/error.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief By-value handle to a timeline marker list
* (olive::TimelineMarkerList).
*
* Borrowed handles are obtained via oaktimeline_marker_list_of() and box
* a reference into the owning node; owning handles are created by
* oaktimeline_marker_list_create(). Either way, release with
* oaktimeline_marker_list_free() (or handle.release(handle.ctx)) when
* done — release destroys the list only for owning handles.
*/
typedef struct OakTimelineMarkerList {
void *ctx; /**< Opaque pointer to the object's box. */
void (*addref)(void *ctx); /**< Atomically increments the box count. */
void (*release)(void *ctx); /**< Decrements the count, frees the box. */
uint32_t abi_version; /**< OAKTIMELINE_ABI_VERSION. */
} OakTimelineMarkerList;
/**
* @brief Create an owning handle to a new, empty marker list. Empty
* handle (ctx == NULL) on allocation failure.
*/
OakTimelineMarkerList oaktimeline_marker_list_create(void);
/**
* @brief Borrowed marker list of a viewer node (sequence). Empty handle
* (ctx == NULL) for an empty node handle or when the node is not a
* viewer.
*/
OakTimelineMarkerList oaktimeline_marker_list_of(OakNodeNode owner);
/**
* @brief Release a marker list handle (destroys the list itself only
* for owning handles). NULL / empty-handle no-op; clears
* list->ctx after releasing.
*/
void oaktimeline_marker_list_free(OakTimelineMarkerList *list);
/**
* @brief Append a marker directly (no undo command). name may be NULL
* for an empty name.
*/
int oaktimeline_marker_add(OakTimelineMarkerList list, int in_num,
int in_den, int out_num, int out_den,
const char *name, int color);
/**
* @brief Number of markers. Out-param convention; OAKTIMELINE_E_INVALID
* for empty/NULL arguments.
*/
int oaktimeline_marker_count(OakTimelineMarkerList list, int *out_count);
/**
* @brief Marker at index: time as num/den pairs, color and name
* (two-stage string). OAKTIMELINE_E_NOT_FOUND when out of range.
*/
int oaktimeline_marker_at(OakTimelineMarkerList list, int index,
int *in_num, int *in_den, int *out_num, int *out_den,
int *color, char *name_buf, int buf_size);
/**
* @brief Create a command that adds a marker (olive::MarkerAddCommand).
*
* Owned command; free with oakundo_command_free(). Redo it directly or
* push it on an undo stack. Empty handle on failure.
*/
OakUndoCommand oaktimeline_marker_add_command(
OakTimelineMarkerList list, int in_num, int in_den, int out_num,
int out_den, const char *name, int color);
/**
* @brief Create a command that removes the marker at `index`.
* OAKTIMELINE_E_NOT_FOUND (as an empty result documented by error) is
* reported by returning an empty handle.
*/
OakUndoCommand oaktimeline_marker_remove_at_command(
OakTimelineMarkerList list, int index);
/**
* @brief Create a command that sets a marker's time range.
*/
OakUndoCommand oaktimeline_marker_set_time_command(
OakTimelineMarkerList list, int index, int in_num, int in_den,
int out_num, int out_den);
/**
* @brief Create a command that sets a marker's color and/or name.
* `name` may be NULL to leave the name unchanged (color still applies
* when >= 0; both NULL-name and color < 0 is a no-op error).
*/
OakUndoCommand oaktimeline_marker_set_props_command(
OakTimelineMarkerList list, int index, int color, const char *name);
/**
* @brief Load/save the list through oakcommon XML handles. The reader
* must be positioned on the wrapping element (e.g. "markers").
*/
int oaktimeline_marker_list_load(OakTimelineMarkerList list,
OakXmlReader reader);
int oaktimeline_marker_list_save(OakTimelineMarkerList list,
OakXmlWriter writer);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TIMELINE_MARKER_H
@@ -1,122 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_TIMELINE_WORKAREA_H
#define OAK_EDITOR_TIMELINE_WORKAREA_H
#include "common/xmlutils.h"
#include "node/node.h"
#include "timeline/error.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief By-value handle to a timeline work area
* (olive::TimelineWorkArea).
*
* Borrowed handles are obtained via oaktimeline_workarea_of() and box a
* reference into the owning node; owning handles are created by
* oaktimeline_workarea_create(). Either way, release with
* oaktimeline_workarea_free() (or handle.release(handle.ctx)) when
* done — release destroys the work area only for owning handles.
*/
typedef struct OakTimelineWorkArea {
void *ctx; /**< Opaque pointer to the object's box. */
void (*addref)(void *ctx); /**< Atomically increments the box count. */
void (*release)(void *ctx); /**< Decrements the count, frees the box. */
uint32_t abi_version; /**< OAKTIMELINE_ABI_VERSION. */
} OakTimelineWorkArea;
/**
* @brief Create an owning handle to a new, default-constructed work
* area. Empty handle (ctx == NULL) on allocation failure.
*/
OakTimelineWorkArea oaktimeline_workarea_create(void);
/**
* @brief Borrowed work area of a viewer node (sequence). Empty handle
* (ctx == NULL) for an empty node handle or when the node is not a
* viewer.
*/
OakTimelineWorkArea oaktimeline_workarea_of(OakNodeNode owner);
/**
* @brief Release a work area handle (destroys the work area itself only
* for owning handles). NULL / empty-handle no-op; clears w->ctx
* after releasing.
*/
void oaktimeline_workarea_free(OakTimelineWorkArea *w);
/**
* @brief Set enabled directly (live).
*/
int oaktimeline_workarea_set_enabled(OakTimelineWorkArea w, int enabled);
/**
* @brief Read the work area state. Out params may individually be NULL.
*/
int oaktimeline_workarea_get(OakTimelineWorkArea w, int *in_num,
int *in_den, int *out_num, int *out_den,
int *enabled);
/**
* @brief Set the range directly (live).
*/
int oaktimeline_workarea_set_range(OakTimelineWorkArea w, int in_num,
int in_den, int out_num, int out_den);
/**
* @brief Create a set-range command (olive::WorkareaSetRangeCommand).
* The old range must be supplied by the caller (facade knows what it
* changed from). Owned; free with oakundo_command_free().
*/
OakUndoCommand oaktimeline_workarea_set_range_command(
OakTimelineWorkArea w, int in_num, int in_den, int out_num,
int out_den, int old_in_num, int old_in_den, int old_out_num,
int old_out_den);
/**
* @brief Create a set-enabled command (olive::WorkareaSetEnabledCommand).
*/
OakUndoCommand oaktimeline_workarea_set_enabled_command(
OakTimelineWorkArea w, int enabled);
/**
* @brief The reset sentinel range (TimelineWorkArea::k_reset_in/out).
*/
int oaktimeline_workarea_reset(int *in_num, int *in_den, int *out_num,
int *out_den);
/**
* @brief Load/save through oakcommon XML handles. The reader must be
* positioned on the "workarea" element.
*/
int oaktimeline_workarea_load(OakTimelineWorkArea w, OakXmlReader reader);
int oaktimeline_workarea_save(OakTimelineWorkArea w,
OakXmlWriter writer);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TIMELINE_WORKAREA_H
-39
View File
@@ -1,39 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_UNDO_ERROR_H
#define OAK_EDITOR_UNDO_ERROR_H
/**
* @brief Status and error codes shared by all oakundo C API families.
*
* Return-code convention (mirrors engine/include/oakengine/init.h):
* 0 (OAKUNDO_OK) on success, a negative OAKUNDO_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
#define OAKUNDO_OK 0 /**< Success. */
#define OAKUNDO_E_INVALID (-20001) /**< NULL handle or invalid argument. */
#define OAKUNDO_E_STATE (-20002) /**< Call not valid in the current state. */
#define OAKUNDO_E_FAILED (-20003) /**< The underlying operation failed. */
#define OAKUNDO_E_NOT_FOUND (-20004) /**< Index out of range / entry not found. */
#define OAKUNDO_E_NOMEM (-20005) /**< Allocation failed. */
#endif //OAK_EDITOR_UNDO_ERROR_H
@@ -1,149 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_UNDO_UNDOCOMMAND_H
#define OAK_EDITOR_UNDO_UNDOCOMMAND_H
#include <stdint.h>
#include "undo/error.h"
#ifdef __cplusplus
extern "C" {
#endif
#define OAKUNDO_ABI_VERSION 1
/**
* @brief Reference-counted handle to an undo command
* (olive::UndoCommand).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* init/factory functions return a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*
* Pushing a command onto an OakUndoStack transfers one reference to the
* stack (the stack releases it when the command is discarded); callers
* may keep their own reference or release it right after the push.
*/
typedef struct OakUndoCommand {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKUNDO_ABI_VERSION. */
} OakUndoCommand;
/**
* @brief Callback table backing a caller-defined undo command.
*
* Any callback may be NULL; a NULL redo/undo makes that direction a
* no-op. free_fn is invoked when the command is destroyed (whether held
* by a stack or released directly) and releases userdata.
*/
typedef struct OakUndoCommandVtable {
void (*redo)(void *userdata);
void (*undo)(void *userdata);
void (*free_fn)(void *userdata);
} OakUndoCommandVtable;
/**
* @brief Create an undo command backed by C callbacks.
*
* The command takes ownership of `userdata`; `vtable` is copied.
*
* @return Command handle with count 1; ctx is NULL on invalid argument
* or allocation failure.
*/
OakUndoCommand oakundo_command_init(const OakUndoCommandVtable *vtable,
void *userdata);
/**
* @brief Create an empty multi command (olive::MultiUndoCommand).
*
* @return Command handle with count 1; ctx is NULL on allocation
* failure.
*/
OakUndoCommand oakundo_command_init_multi(void);
/**
* @brief Add `child` to the multi command `multi`.
*
* The multi command takes one reference to the child; the caller keeps
* its own reference and may release it after the call.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_command_multi_add_child(OakUndoCommand multi,
OakUndoCommand child);
/**
* @brief Query the number of children in a multi command.
*
* @param out_count Receives the result. Must not be NULL.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_command_multi_child_count(OakUndoCommand multi,
int *out_count);
/**
* @brief Reference to the child at `index` of a multi command.
*
* The returned handle carries its own reference; release it with
* oakundo_command_free().
*
* @return OAKUNDO_OK, OAKUNDO_E_NOT_FOUND for an out-of-range index, or
* another negative OAKUNDO_E_* error code.
*/
int oakundo_command_multi_child(OakUndoCommand multi, int index,
OakUndoCommand *out_child);
/**
* @brief Execute the command's redo without a stack
* (olive::UndoCommand::redo_now semantics; a no-op if already done).
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_command_redo_now(OakUndoCommand command);
/**
* @brief Execute the command's undo without a stack
* (olive::UndoCommand::undo_now semantics; a no-op if not done).
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_command_undo_now(OakUndoCommand command);
/**
* @brief Release one reference to a command handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* command when the count reaches zero. NULL handle or NULL ctx is a
* no-op; clears `command->ctx` after releasing.
*/
void oakundo_command_free(OakUndoCommand *command);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_UNDO_UNDOCOMMAND_H
@@ -1,173 +0,0 @@
/***
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/>.
***/
#ifndef OAK_EDITOR_UNDO_UNDOSTACK_H
#define OAK_EDITOR_UNDO_UNDOSTACK_H
#include <stdint.h>
#include "undo/error.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference-counted handle to an undo stack (olive::UndoStack).
*
* Same ownership/count semantics as OakUndoCommand (see
* undo/undocommand.h).
*/
typedef struct OakUndoStack {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKUNDO_ABI_VERSION. */
} OakUndoStack;
/**
* @brief Create an undo stack (count 1).
*
* A fresh stack contains a single "New/Open Project" empty command,
* matching olive::UndoStack::clear().
*
* @return Stack handle; ctx is NULL on allocation failure.
*/
OakUndoStack oakundo_undostack_init(void);
/**
* @brief Release one reference to an undo stack.
*
* NULL handle or NULL ctx is a no-op; clears `stack->ctx` after
* releasing.
*/
void oakundo_undostack_free(OakUndoStack *stack);
/**
* @brief Push `command` onto the stack and execute its redo.
*
* The stack takes one reference to the command; the caller keeps its
* own reference and may release it after the call. An empty multi
* command is deleted immediately (not pushed), matching
* olive::UndoStack::push. `name` is the user-visible label (NULL
* behaves like an empty label).
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_push(OakUndoStack stack, OakUndoCommand command,
const char *name);
/**
* @brief Push a command that has already been executed (redo skipped).
*
* Reference rules match oakundo_undostack_push().
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_push_pre_executed(OakUndoStack stack,
OakUndoCommand command,
const char *name);
/**
* @brief Undo the most recently done command, if any.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_undo(OakUndoStack stack);
/**
* @brief Redo the most recently undone command, if any.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_redo(OakUndoStack stack);
/**
* @brief Undo/redo until the done-command count equals `index`
* (olive::UndoStack::jump semantics). Negative values are clamped to 0.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_jump(OakUndoStack stack, int64_t index);
/**
* @brief Delete all commands and push the fresh "New/Open Project" empty
* command (olive::UndoStack::clear).
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_clear(OakUndoStack stack);
/**
* @brief Query whether undo (redo) is currently possible.
*
* @param out_value Receives 1/0. Must not be NULL.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_can_undo(OakUndoStack stack, int *out_value);
int oakundo_undostack_can_redo(OakUndoStack stack, int *out_value);
/**
* @brief Total number of history rows (done + undone commands).
*
* @param out_count Receives the result. Must not be NULL.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_count(OakUndoStack stack, int64_t *out_count);
/**
* @brief Current position in the history: the number of done commands
* (rows at or above this index are undone).
*
* @param out_index Receives the result. Must not be NULL.
*
* @return OAKUNDO_OK or a negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_index(OakUndoStack stack, int64_t *out_index);
/**
* @brief Label of the history row at `row` (0-based, two-stage getter).
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), OAKUNDO_E_NOT_FOUND for an invalid row, or
* another negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_command_text(OakUndoStack stack, int64_t row,
char *buf, int buf_size);
/**
* @brief Query whether the row at `row` is currently done (not undone).
*
* @param out_value Receives 1 (done) / 0 (undone). Must not be NULL.
*
* @return OAKUNDO_OK, OAKUNDO_E_NOT_FOUND for an invalid row, or another
* negative OAKUNDO_E_* error code.
*/
int oakundo_undostack_command_is_done(OakUndoStack stack, int64_t row,
int *out_value);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_UNDO_UNDOSTACK_H
-700
View File
@@ -1,700 +0,0 @@
// 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 `oakcore_audioparams_*` accessors the facade provides itself
//! (crate::stubs::audio, folded in M12 P5 — they used to be host-provided
//! liboakcore symbols).
use std::ffi::{c_char, c_double, c_int, c_void};
use crate::stubs::audio as a;
use crate::error::Error;
use crate::handle::{
box_handle, free_box, guard, guard_i64, guard_int, 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_output_device_count` — the number of host output
/// devices; the list index is the device index
/// `oakengine_audio_set_output_device` takes. Needs no AudioManager
/// instance.
#[no_mangle]
pub extern "C" fn oakengine_audio_output_device_count() -> c_int {
guard_int(|| Ok(unsafe { a::oakaudio_output_device_count() }))
}
/// `oakengine_audio_input_device_count` — the input side of
/// [`oakengine_audio_output_device_count`].
#[no_mangle]
pub extern "C" fn oakengine_audio_input_device_count() -> c_int {
guard_int(|| Ok(unsafe { a::oakaudio_input_device_count() }))
}
/// `oakengine_audio_output_device_name` — the name of output device
/// `index` (buf/size; the length excludes the NUL).
#[no_mangle]
pub unsafe extern "C" fn oakengine_audio_output_device_name(
index: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
guard_int(|| unsafe {
let rc = a::oakaudio_output_device_name(index, buf, buf_size);
if rc < 0 {
Err(Error::Module(rc))
} else {
Ok(crate::handle::string_result(rc))
}
})
}
/// `oakengine_audio_input_device_name` — the input side of
/// [`oakengine_audio_output_device_name`].
#[no_mangle]
pub unsafe extern "C" fn oakengine_audio_input_device_name(
index: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
guard_int(|| unsafe {
let rc = a::oakaudio_input_device_name(index, buf, buf_size);
if rc < 0 {
Err(Error::Module(rc))
} else {
Ok(crate::handle::string_result(rc))
}
})
}
/// `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_output_levels` — per-channel linear peaks of the
/// buffered, not-yet-consumed output into `peaks` (up to `capacity`
/// entries). Returns the channel count (0 when nothing is buffered).
/// The UI's audio meter reads this; there is no C++ counterpart (the Qt
/// side metered inside the output callback, which is not bridged).
#[no_mangle]
pub unsafe extern "C" fn oakengine_audio_output_levels(peaks: *mut f32, capacity: c_int) -> c_int {
guard_int(|| {
if peaks.is_null() || capacity <= 0 {
return Err(Error::Invalid);
}
let m = manager();
if m.is_null() {
return Err(Error::Failed("no AudioManager instance".into()));
}
// The module returns the channel count (>= 0) or a negative
// OAKAUDIO_E_* code, which passes through untouched.
let n = a::oakaudio_manager_output_levels(m, peaks, capacity);
if n < 0 {
return Err(Error::Module(n));
}
Ok(n)
})
}
/// `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(())
})
}
// ---------------------------------------------------------------------------
// Waveform extraction
// ---------------------------------------------------------------------------
/// `oakengine_waveform_extract` — two-stage whole-file min/max waveform
/// extraction of `filename`'s audio stream (the module's
/// `oakaudio_waveform_extract`, M12 P4).
///
/// First call with `out_pairs == NULL` / `capacity_points == 0` returns the
/// required point count without writing; the channel count is reported
/// whenever `out_channel_count` is non-NULL. The data pass writes
/// `point_count * channel_count` channel-interleaved pairs (the module's
/// `oakaudio_min_max` POD; `capacity_points` counts points) and returns the
/// point count. Returns a negative facade `OAKENGINE_E_INVALID` for NULL
/// `filename` / negative `stream_index` / non-positive `samples_per_point`
/// / negative `capacity_points`; module decode errors (`OAKAUDIO_E_NOT_FOUND`,
/// ...) pass through untranslated.
#[no_mangle]
pub unsafe extern "C" fn oakengine_waveform_extract(
filename: *const c_char,
stream_index: c_int,
samples_per_point: c_int,
out_pairs: *mut a::MinMax,
capacity_points: c_int,
out_channel_count: *mut c_int,
) -> c_int {
guard_int(|| unsafe {
if filename.is_null() || stream_index < 0 || samples_per_point <= 0 || capacity_points < 0 {
return Err(Error::Invalid);
}
let n = a::oakaudio_waveform_extract(
filename,
stream_index,
samples_per_point,
out_pairs,
capacity_points,
out_channel_count,
);
if n < 0 {
return Err(Error::Module(n));
}
Ok(n)
})
}
// ---------------------------------------------------------------------------
// 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,
}
File diff suppressed because it is too large Load Diff
-628
View File
@@ -1,628 +0,0 @@
// 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::stubs::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,
}
-92
View File
@@ -1,92 +0,0 @@
// 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, 82/85 wrapped): the preset path/count/name,
//! preset load/save and the sequence-bound last-used entry points
//! (`oakengine_encoding_preset_*`,
//! `oakengine_encoding_params_load_file/save_file`,
//! `oakengine_encoding_params_get/set_last_used`) are stubs — the
//! oakcodec crate has no preset API and the last-used pair needs the
//! deferred node/timeline families. The exporter entry point
//! (`oakengine_export_render_with_params`) is backed since M12 (see
//! `crate::codec`, "Exporter family").
//! - **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).
-155
View File
@@ -1,155 +0,0 @@
// 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.
use thiserror::Error;
/// 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, Error)]
pub enum Error {
/// Empty handle or invalid argument.
#[error("engine: invalid argument")]
Invalid,
/// Wrong state.
#[error("engine: call not valid in the current state")]
State,
/// The underlying operation failed (context string is log-only).
#[error("engine: operation failed: {0}")]
Failed(String),
/// Not found.
#[error("engine: not found")]
NotFound,
/// Out of memory.
#[error("engine: out of memory")]
NoMem,
/// Cancelled.
#[error("engine: cancelled")]
Cancelled,
/// A module error code that must pass through untranslated.
#[error("engine: module error code {0}")]
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))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
/// One instance of every variant (data-carrying ones get a sample
/// payload).
fn all_errors() -> Vec<Error> {
vec![
Error::Invalid,
Error::State,
Error::Failed("boom".to_string()),
Error::NotFound,
Error::NoMem,
Error::Cancelled,
Error::Module(-20004),
]
}
#[test]
fn display_is_non_empty_for_every_variant() {
for e in all_errors() {
let s = e.to_string();
assert!(!s.is_empty(), "Display produced an empty message for {e:?}");
}
}
#[test]
fn error_is_object_safe() {
// `Box<dyn std::error::Error>` must be constructible for every
// variant; `source()` stays None (no wrapped downstream error).
let errors: Vec<Box<dyn std::error::Error>> = all_errors()
.into_iter()
.map(|e| Box::new(e) as Box<dyn std::error::Error>)
.collect();
for e in &errors {
assert!(!e.to_string().is_empty());
assert!(e.source().is_none());
}
}
#[test]
fn code_is_unaffected_by_trait_impl() {
assert_eq!(Error::Invalid.code(), OAKENGINE_E_INVALID);
assert_eq!(Error::State.code(), OAKENGINE_E_STATE);
assert_eq!(Error::Failed("boom".to_string()).code(), OAKENGINE_E_FAILED);
assert_eq!(Error::NotFound.code(), OAKENGINE_E_NOT_FOUND);
assert_eq!(Error::NoMem.code(), OAKENGINE_E_NOMEM);
assert_eq!(Error::Cancelled.code(), OAKENGINE_E_CANCELLED);
// Module codes pass through verbatim, untranslated.
assert_eq!(Error::Module(-20004).code(), -20004);
}
#[test]
fn from_module_wraps_negative_and_accepts_ok() {
assert!(Error::from_module(0).is_ok());
assert_eq!(Error::from_module(-20004).unwrap_err().code(), -20004);
}
}
-344
View File
@@ -1,344 +0,0 @@
// 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};
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-side boxed payloads holding the oaknode domain (single-lib
/// unification). Every `oakengine_*` node-family handle ultimately wraps
/// one of these behind a [`CHandle`]:
///
/// - projects box [`domain::ProjectArc`] (`Arc<Mutex<Project>>`);
/// - nodes, blocks, tracks, footage, sequences and folders box a
/// [`domain::NodeRef`] (`(Arc<Mutex<Project>>, NodeId)` — the
/// oaknode crate's `project::NodeRef` value type).
///
/// The box is created through `oaknode::handle::make_owned` (refcounted
/// shell + release callback), so the facade's existing
/// [`box_handle`]/[`free_box`] discipline (and the addref copies the
/// engine takes) works unchanged.
pub mod domain {
use std::sync::{Arc, Mutex};
use oaknode::id::NodeId;
use crate::handle::CHandle;
/// Engine-side boxed payload for project handles: shared ownership of
/// the oaknode domain project (its graph, settings, filename state).
pub type ProjectArc = Arc<Mutex<oaknode::project::Project>>;
/// Engine-side boxed payload for node/block/track/footage/sequence/
/// folder handles: a reference into a project's graph. Reuses the
/// oaknode crate's own `NodeRef` value type (project + id + owned
/// flag); a stale id fails validation instead of aliasing.
pub type NodeRef = oaknode::project::NodeRef;
/// Box a project payload behind a refcounted handle.
pub fn box_project(project: ProjectArc) -> CHandle {
oaknode::handle::make_owned(project)
}
/// Box a node reference behind a refcounted handle. `owned` marks
/// detached (factory-created) nodes so the engine's debug alive
/// counter accounts them exactly once.
pub fn box_node(project: ProjectArc, id: NodeId, owned: bool) -> CHandle {
oaknode::handle::make_owned(NodeRef::new(project, id, owned))
}
/// Borrow the project payload behind a handle.
///
/// # Safety
/// `h` must be a live handle created by [`box_project`] (or empty).
pub unsafe fn project_of(h: &CHandle) -> Option<&ProjectArc> {
// SAFETY: forwarded to the oaknode handle contract.
unsafe { oaknode::handle::get::<ProjectArc>(h) }
}
/// Borrow the node-reference payload behind a handle.
///
/// # Safety
/// `h` must be a live handle created by [`box_node`] (or empty).
pub unsafe fn node_ref_of(h: &CHandle) -> Option<&NodeRef> {
// SAFETY: forwarded to the oaknode handle contract.
unsafe { oaknode::handle::get::<NodeRef>(h) }
}
/// Mutable view of the node-reference payload (used by the graph
/// transfer paths, which rewrite the shared box in place — the
/// "write_node_ref" semantics).
///
/// # Safety
/// `h` must be a live handle created by [`box_node`]; the caller must
/// hold exclusive access to the boxed value.
pub unsafe fn node_ref_mut(h: &CHandle) -> Option<&mut NodeRef> {
// SAFETY: forwarded to the shared-box contract.
unsafe { boxed_mut::<NodeRef>(h) }
}
/// Mutable typed view into an oaknode-style `RefBox` payload (the
/// oaknode crate exposes only a read-only `get`; this mirrors its
/// box layout — `refs`/`value` are `pub` fields).
///
/// # Safety
/// `h` must be a live handle boxing `T`; the caller must hold
/// exclusive access to the boxed value.
pub unsafe fn boxed_mut<T: 'static>(h: &CHandle) -> Option<&mut T> {
if h.ctx.is_null() {
return None;
}
// SAFETY: contract above; the box is an
// `oaknode::handle::RefBox<T>`.
unsafe { Some(&mut (*(h.ctx as *mut oaknode::handle::RefBox<T>)).value) }
}
}
/// 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
-111
View File
@@ -1,111 +0,0 @@
// 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` cdylib (plugin / external C ABI)
//!
//! The frozen `oakengine_*` C ABI (`engine/include/oakengine/*.h`) as a
//! **pure cdylib** (M14 R4). This is the plugin / external-consumer layer:
//! OFX plugins and third-party embedders link `liboakengine` and call the
//! C ABI; the app, oak-cli and oak-worker do not use it anymore — they
//! link the module crates directly as Rust rlibs.
//!
//! Downward, every `oakengine_*` export is a direct Rust call into the
//! module crates (oakundo/oaknode/oaktimeline/oakcodec/oakaudio/oakrender/
//! oaktask/oakcommon/oakplugin/oakstorage/oakcore-rs) through [`stubs`]
//! (the rewired replacement for the deleted `bridge/`); the C ABI itself
//! stays frozen (only additive changes + major version bumps).
//! Cross-cutting state that used to live here (the process-wide undo stack,
//! the open undo group) has sunk into the modules (M14 R1:
//! [`oakundo::global`]); [`undo`] is a thin forward that adds the engine's
//! box/unbox, buf/size and error-code conventions.
//!
//! ## 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 value,
//! calls the module's direct Rust API 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 crate is cdylib-only, so the former `tests/*.rs` integration tests
//! run as in-crate unit tests under `src/test_support/` (pulled in from
//! here under `#[cfg(test)]`; they address the crate's modules through
//! `crate::*`). The module crates are real dependencies (see Cargo.toml),
//! so the test binary statically links the same rlibs the cdylib embeds;
//! [`linkage`] anchors every crate into the cdylib link, and the test-only
//! [`test_link`] module does the same for the unit-test binary. Where a
//! wrapped family needs module behavior the crates do not implement yet,
//! the engine function is a documented stub with its reason (see
//! `deferred.rs` and README.md).
#![deny(unsafe_op_in_unsafe_fn)]
#![warn(missing_docs)]
pub mod audio;
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 library;
pub mod node;
pub mod plugin;
pub mod pods;
pub mod render;
pub mod stubs;
pub mod storage;
pub mod task;
pub mod testmedia;
pub mod timeline;
pub mod undo;
pub mod worker;
/// The former `tests/*.rs` integration tests, now unit tests (the facade
/// is cdylib-only, so integration tests cannot link it as an rlib crate;
/// see `test_support/mod.rs`).
#[cfg(test)]
#[path = "test_support/mod.rs"]
mod tests;
#[cfg(test)]
mod test_link {
// The lib's own unit-test binary must link the module crates' rlibs to
// satisfy the facade's imports that the unit tests compile in — e.g.
// the render family's oakrender display renderer (src/render.rs).
// The in-crate tests do the same through test_support/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::backend::DisplayRenderer::new as *const () as usize,
oaknode::project::Project::new as *const () as usize,
oaktimeline::marker::TimelineMarkerList::new as *const () as usize,
oaktask::manager::TaskManager::init as *const () as usize,
];
fns.iter().sum()
}
}
-368
View File
@@ -1,368 +0,0 @@
// 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/>.
//! The project-library manager C ABI (plan M13 §4): list / create / open /
//! rename / duplicate / delete / import / export over the oakstorage
//! database backend the write-through binds to ([`crate::storage`]).
//!
//! These exports are additive (D4): the app talks to the engine dylib only
//! through the frozen `oakengine_*` surface, so the manager's data source
//! crosses the boundary here instead of linking oakstorage directly (which
//! would give the app a second copy of the handle/serializer types).
//!
//! All operations address the configured default library (the same
//! `Storage/Backend` + `Storage/SqlitePath` configuration the write-through
//! uses); with storage disabled every call fails with `OAKENGINE_E_STATE`
//! except [`oakengine_library_list`], which reports an empty library
//! (`"[]"`) so the manager window can still open.
use std::ffi::{c_char, c_int};
use oakstorage::backend::StorageBackend;
use oakstorage::uri::StorageUri;
use crate::error::{Error, Result};
use crate::handle::{guard, guard_int, read_cstr, write_string, OakEngineProject};
use crate::stubs::node as n;
/// One library row as the project manager shows it: the project metadata
/// plus the stats derived from the head state (plan §4).
#[derive(serde::Serialize)]
struct LibraryRow {
/// Library row uuid (the open/duplicate/export selector).
uuid: String,
/// Display name.
name: String,
/// Row creation time (unix seconds, UTC).
created_at: i64,
/// Last-write time (unix seconds, UTC; the manager sort key).
modified_at: i64,
/// Longest sequence duration, milliseconds.
duration_ms: i64,
/// Total tracks across all sequences.
track_count: i32,
/// Total clip blocks.
clip_count: i32,
/// Total footage nodes.
footage_count: i32,
}
/// Map an oakstorage error onto the facade error space (the context string
/// is log-only per the error contract).
fn map_err(e: oakstorage::error::Error) -> Error {
use oakstorage::error::Error as E;
match e {
E::Invalid => Error::Invalid,
E::State => Error::State,
E::NotFound => Error::NotFound,
E::NoMem => Error::NoMem,
other => Error::Failed(other.to_string()),
}
}
/// The configured default library as a parsed URI; [`Error::State`] when
/// the write-through backend is disabled or the path does not resolve.
fn library() -> Result<StorageUri> {
if !crate::storage::storage_enabled() {
return Err(Error::State);
}
let uri = crate::storage::library_uri().ok_or(Error::State)?;
StorageUri::parse(&uri).map_err(map_err)
}
/// The library URI selecting one row (`…?project=<uuid>`).
fn project_uri(uuid: &str) -> Result<StorageUri> {
let uri = library()?;
StorageUri::parse(&format!("{}?project={uuid}", uri.to_uri_string())).map_err(map_err)
}
/// Load one library row as an owned project handle (refcount 1).
fn load_handle(uuid: &str) -> Result<crate::handle::CHandle> {
let uri = project_uri(uuid)?;
let result = crate::storage::backend().load(&uri).map_err(map_err)?;
if result.project.is_null() {
return Err(Error::Failed(format!(
"library load of {uuid} returned no project (info code {})",
result.version_info
)));
}
Ok(result.project)
}
/// Release an owned handle (refcount 1).
fn release(h: crate::handle::CHandle) {
if let Some(release) = h.release {
unsafe { release(h.ctx) };
}
}
/// `oakengine_library_list` — the library rows as a JSON array (buf/size
/// convention), most recently modified first. Each row carries the manager
/// stats derived from the head state; a row whose stats fail to replay
/// degrades to zeros instead of failing the whole list. With storage
/// disabled the result is the empty array (`"[]"`), not an error.
#[no_mangle]
pub unsafe extern "C" fn oakengine_library_list(buf: *mut c_char, buf_size: c_int) -> c_int {
guard_int(|| unsafe {
if !crate::storage::storage_enabled() {
return Ok(write_string("[]", buf, buf_size));
}
let uri = library()?;
let infos = crate::storage::backend()
.list_projects(&uri)
.map_err(map_err)?;
let mut rows = Vec::with_capacity(infos.len());
for info in infos {
let stats = crate::storage::backend()
.project_stats(&uri, &info.uuid)
.unwrap_or_default();
rows.push(LibraryRow {
uuid: info.uuid,
name: info.name,
created_at: info.created_at.and_utc().timestamp(),
modified_at: info.modified_at.and_utc().timestamp(),
duration_ms: stats.duration_ms,
track_count: stats.track_count,
clip_count: stats.clip_count,
footage_count: stats.footage_count,
});
}
let json = serde_json::to_string(&rows)
.map_err(|e| Error::Failed(format!("library list encode: {e}")))?;
Ok(write_string(&json, buf, buf_size))
})
}
/// `oakengine_library_create` — create a blank project named `name` as a
/// new library row and report its uuid (buf/size convention on
/// `out_uuid`; the return value is the uuid length, negative on error).
/// The row lands immediately (one `kind='import'` command), so the
/// manager list shows it before the first edit.
#[no_mangle]
pub unsafe extern "C" fn oakengine_library_create(
name: *const c_char,
out_uuid: *mut c_char,
out_size: c_int,
) -> c_int {
guard_int(|| unsafe {
if name.is_null() {
return Err(Error::Invalid);
}
let name = read_cstr(name);
if name.trim().is_empty() {
return Err(Error::Invalid);
}
let uri = library()?;
let mut h = n::oaknode_project_init();
if h.is_null() {
return Err(Error::NoMem);
}
let outcome = (|| -> Result<String> {
Error::from_module(n::oaknode_project_initialize(h))?;
let uuid = {
let arc = crate::handle::domain::project_of(&h).ok_or(Error::Invalid)?;
let mut guard = arc.lock().unwrap_or_else(|e| e.into_inner());
guard.settings.insert("projectname".to_string(), name);
guard.uuid.clone()
};
crate::storage::backend().save(h, &uri, 0).map_err(map_err)?;
Ok(uuid)
})();
n::oaknode_project_free(&mut h);
let uuid = outcome?;
Ok(write_string(&uuid, out_uuid, out_size))
})
}
/// `oakengine_library_delete` — delete the library row `uuid` (cascades
/// settings / snapshots / journal; `OAKENGINE_E_NOT_FOUND` when absent).
/// The manager confirms with the user before calling.
#[no_mangle]
pub unsafe extern "C" fn oakengine_library_delete(uuid: *const c_char) -> c_int {
guard(|| unsafe {
if uuid.is_null() {
return Err(Error::Invalid);
}
let uuid = read_cstr(uuid);
if uuid.is_empty() {
return Err(Error::Invalid);
}
crate::storage::backend()
.delete_project(&library()?, &uuid)
.map_err(map_err)
})
}
/// `oakengine_library_rename` — rename the library row `uuid` (the
/// manager's list name; the in-project `projectname` setting is
/// untouched).
#[no_mangle]
pub unsafe extern "C" fn oakengine_library_rename(
uuid: *const c_char,
name: *const c_char,
) -> c_int {
guard(|| unsafe {
if uuid.is_null() || name.is_null() {
return Err(Error::Invalid);
}
let (uuid, name) = (read_cstr(uuid), read_cstr(name));
if uuid.is_empty() || name.trim().is_empty() {
return Err(Error::Invalid);
}
crate::storage::backend()
.rename_project(&library()?, &uuid, name.trim())
.map_err(map_err)
})
}
/// `oakengine_library_duplicate` — copy the library row `uuid` (settings,
/// snapshots and the full journal history included) under a fresh uuid,
/// reporting the new row's uuid (buf/size convention on `out_uuid`; the
/// return value is the uuid length, negative on error).
/// `name` is the copy's display name; NULL/empty defaults to
/// `<name> (copy)`.
#[no_mangle]
pub unsafe extern "C" fn oakengine_library_duplicate(
uuid: *const c_char,
name: *const c_char,
out_uuid: *mut c_char,
out_size: c_int,
) -> c_int {
guard_int(|| unsafe {
if uuid.is_null() {
return Err(Error::Invalid);
}
let uuid = read_cstr(uuid);
if uuid.is_empty() {
return Err(Error::Invalid);
}
let name = read_cstr(name);
let name = match name.trim() {
"" => None,
trimmed => Some(trimmed),
};
let info = crate::storage::backend()
.duplicate_project(&library()?, &uuid, name)
.map_err(map_err)?;
Ok(write_string(&info.uuid, out_uuid, out_size))
})
}
/// `oakengine_library_import` — import a `.ove` / `.otio` / `.fcpxml`
/// project file as a new library row (the file backend parses it, a fresh
/// uuid is assigned, and the first save journals the whole project as one
/// `kind='import'` command). Reports the new row's uuid (buf/size
/// convention on `out_uuid`; the return value is the uuid length,
/// negative on error).
#[no_mangle]
pub unsafe extern "C" fn oakengine_library_import(
path: *const c_char,
out_uuid: *mut c_char,
out_size: c_int,
) -> c_int {
guard_int(|| unsafe {
if path.is_null() {
return Err(Error::Invalid);
}
let path = read_cstr(path);
if path.is_empty() {
return Err(Error::Invalid);
}
let file_uri = StorageUri::parse(&path).map_err(map_err)?;
let uuid = crate::storage::backend()
.import_from_file(&library()?, &file_uri)
.map_err(map_err)?;
Ok(write_string(&uuid, out_uuid, out_size))
})
}
/// `oakengine_library_export` — export the library row `uuid` to the file
/// `path`; the format is dispatched by extension through the oakstorage
/// registry (`.ove` / `.ovexml` → ove-xml, `.otio` / `.fcpxml` → the
/// interchange backend). Nothing is written back to the library.
#[no_mangle]
pub unsafe extern "C" fn oakengine_library_export(
uuid: *const c_char,
path: *const c_char,
) -> c_int {
guard(|| unsafe {
if uuid.is_null() || path.is_null() {
return Err(Error::Invalid);
}
let (uuid, path) = (read_cstr(uuid), read_cstr(path));
if uuid.is_empty() || path.is_empty() {
return Err(Error::Invalid);
}
let file_uri = StorageUri::parse(&path).map_err(map_err)?;
if file_uri.scheme != "file" {
return Err(Error::Invalid);
}
let handle = load_handle(&uuid)?;
let backend = oakstorage::registry::Registry::global()
.resolve(&file_uri)
.map_err(map_err)?;
let result = backend.save(handle, &file_uri, 0).map_err(map_err);
release(handle);
result
})
}
/// `oakengine_project_load_library` — load the library row `uuid` into a
/// fresh project shell (same contract as `oakengine_project_load`: the
/// shell must carry no content). On success the undo stack is cleared, the
/// modified flag is reset, and the project is bound to the library session
/// (the write-through continues the row's journal from its head seq).
#[no_mangle]
pub unsafe extern "C" fn oakengine_project_load_library(
self_: *mut OakEngineProject,
uuid: *const c_char,
err: *mut c_char,
err_size: c_int,
) -> c_int {
guard(|| unsafe {
if self_.is_null() || uuid.is_null() {
return Err(Error::Invalid);
}
let h = crate::handle::unbox(self_)?;
if !n::oaknode_project_root(h).is_null() {
return Err(Error::State);
}
let uuid = read_cstr(uuid);
if uuid.is_empty() {
return Err(Error::Invalid);
}
let loaded = match load_handle(&uuid) {
Ok(handle) => handle,
Err(e) => {
write_string(&e.to_string(), err, err_size);
return Err(e);
}
};
// Swap the loaded content into the caller's shell box, releasing
// the empty shell handle the box was created with.
let mut old = (*self_).handle;
(*self_).handle = loaded;
n::oaknode_project_free(&mut old);
crate::undo::oakengine_undo_clear();
Error::from_module(n::oaknode_project_set_modified(loaded, 0))?;
crate::storage::bind_project(loaded);
if !err.is_null() && err_size > 0 {
*err = 0;
}
Ok(())
})
}
-69
View File
@@ -1,69 +0,0 @@
// 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 calls the module crates' direct Rust APIs (single-lib; the
//! deleted `src/bridge/` no longer exists), and most modules reach the
//! linker through those normal references. The anchors below reference one
//! direct-Rust symbol of every module crate (and `oakcore-rs`) from a
//! `#[used]` static, which (a) marks each crate as used so its rlib
//! reaches the linker even when the facade only touches it indirectly and
//! (b) keeps the anchor alive so the referenced object files are pulled.
//! For the `liboakengine` cdylib this is what embeds the module crates
//! next to the facade's own `oakengine_*` exports — including the
//! oakcommon XML/undo symbols oaknode's serializer resolves at runtime
//! via dlsym(RTLD_DEFAULT) (see the per-anchor comments in `force_link`).
//!
//! The per-crate symbol mirrors the test-force-link in
//! test_support/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; 11] = [
// oakcore-rs (pure value types; referenced so its rlib is linked).
oakcore_rs::Rational::new(1, 2).numerator() as usize,
// One public direct-Rust symbol per module crate. oakundo/oakcommon
// no longer export a C ABI; their handle-level Rust API functions
// serve as the link anchors.
oakundo::undostack::undostack_init as usize,
oakcommon::configstore::ConfigStore::instance as usize,
oaktimeline::marker::TimelineMarkerList::new as usize,
oakcodec::exportformat::Format::get_name as usize,
oakrender::manager::RenderManager::init as usize,
oaktask::manager::TaskManager::init as usize,
oaknode::project::Project::new as usize,
// oaknode's serializer resolves oakcommon XML/undo symbols at
// runtime; anchors for the dylib.
oakcommon::xmlutils::XmlWriter::new as usize,
oakcommon::xmlutils::XmlReader::new as usize,
oakundo::undocommand::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
-132
View File
@@ -1,132 +0,0 @@
// 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::stubs::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
}
-275
View File
@@ -1,275 +0,0 @@
// 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/>.
//! POD mirrors for the deleted `src/bridge/` (single-lib unification).
//!
//! The facade still exchanges plain-`repr(C)` PODs with the module crates
//! (and, upward, with the host app through the frozen `oakengine_*` C
//! ABI). The deleted bridge aliased these types to the module crates'
//! `ffi` declarations; those ffi modules are gone, so the engine keeps
//! its own mirrors here. Where a module crate still owns the canonical
//! POD (oakcodec's [`EncodingParamsPOD`]) the facade aliases it directly.
use std::ffi::c_int;
/// `oakcodec_encoding_params` (`include/codec/encoder.h`) — single-lib
/// unification: aliases the oakcodec crate's POD
/// ([`oakcodec::encodingparams::EncodingParams`], identical `#[repr(C)]`
/// layout), so the facade's encoding-params handle reads/writes fields of
/// exactly the struct the oakcodec/oakaudio creators consume.
pub type EncodingParamsPOD = oakcodec::encodingparams::EncodingParams;
/// `oak_export_options` (`engine/include/oakengine/exporter.h`) — POD
/// export parameters for [`crate::codec::oakengine_export_render`]. 0 (or
/// negative) fields select the documented per-field default; the codec
/// fields carry the exporter.h `OAKENGINE_EXPORT_VIDEO_*` /
/// `OAKENGINE_EXPORT_AUDIO_*` values (NOT the engine's `ExportCodec`
/// ids — see the mapping notes on `oakengine_export_render`).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakExportOptions {
/// `OAKENGINE_EXPORT_VIDEO_*` value; default H264.
pub video_codec: c_int,
/// `OAKENGINE_EXPORT_AUDIO_*` value; default AAC;
/// [`OAKENGINE_EXPORT_AUDIO_NONE`] disables the audio track.
pub audio_codec: c_int,
/// Video bit rate in bit/s; <= 0 lets the encoder choose (FFmpeg
/// defaults).
pub video_bit_rate: i64,
/// Audio sample rate in Hz; <= 0 uses the engine's default (48 kHz).
pub audio_sample_rate: c_int,
/// Audio channel count (1 = mono, 2 = stereo); <= 0 uses the engine's
/// default (stereo).
pub audio_channel_count: c_int,
}
/// `OAKENGINE_EXPORT_VIDEO_*` — video codecs for
/// [`OakExportOptions::video_codec`].
pub const OAKENGINE_EXPORT_VIDEO_H264: c_int = 0;
/// H.265/HEVC in an MP4 container.
pub const OAKENGINE_EXPORT_VIDEO_H265: c_int = 1;
/// PNG still-image sequence.
pub const OAKENGINE_EXPORT_VIDEO_PNG_SEQUENCE: c_int = 2;
/// `OAKENGINE_EXPORT_AUDIO_*` — audio codecs for
/// [`OakExportOptions::audio_codec`].
pub const OAKENGINE_EXPORT_AUDIO_AAC: c_int = 0;
/// Uncompressed PCM.
pub const OAKENGINE_EXPORT_AUDIO_PCM: c_int = 1;
/// Disable the audio track entirely (not a codec).
pub const OAKENGINE_EXPORT_AUDIO_NONE: c_int = -1;
/// The oakaudio recording-params POD is the same shared codec POD.
pub type AudioEncodingParams = EncodingParamsPOD;
/// 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-field zero is a valid value (enums carry their 0 variants).
unsafe { std::mem::zeroed() }
}
/// `oakrender_video_params` (`include/render/renderer.h`) — identical
/// layout to the engine's own video-params POD.
pub type OakRenderVideoParams = crate::common::OakVideoParamsPod;
/// `oakrender_video_ticket_params` (`include/render/ticket.h`), the POD
/// the deleted bridge aliased from `oakrender::ffi`. The oakrender crate
/// now exposes value-typed `ticket::VideoTicketParams`; the facade keeps
/// this mirror for its synchronous render path (see [`crate::render`]).
/// All handle fields are the shared [`crate::handle::CHandle`].
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakVideoTicketParams {
/// Connected texture output node (borrowed).
pub output_node: crate::handle::CHandle,
/// By-value oakcommon handle.
pub video_params: crate::handle::CHandle,
/// Borrowed oakcore audio-params handle, may be null.
pub audio_params: *const std::ffi::c_void,
/// Frame timestamp as rational.
pub time_num: i64,
/// Frame timestamp as rational.
pub time_den: i64,
/// Borrowed, empty ctx = null.
pub color_manager: crate::handle::CHandle,
/// RenderMode::Mode as int.
pub mode: c_int,
/// 0/0 = off.
pub force_width: c_int,
/// 0/0 = off.
pub force_height: c_int,
/// Used when has_force_matrix != 0.
pub force_matrix: [f64; 16],
/// 0/1.
pub has_force_matrix: c_int,
/// PixelFormat as int, -1 = off.
pub force_format: c_int,
/// 0 = off.
pub force_channel_count: c_int,
/// Borrowed; empty ctx = none.
pub force_color_output: crate::handle::CHandle,
/// By value; empty ctx = default.
pub force_color_transform: crate::handle::CHandle,
/// Borrowed frame cache; empty ctx = none.
pub cache: crate::handle::CHandle,
/// Single-footage decode filename (null = off; M12 P0).
pub footage_filename: *const std::ffi::c_char,
/// Media stream index for `footage_filename`.
pub footage_stream: c_int,
/// Sequence montage clip array (null = off; M12 P0). Clips are
/// ordered bottom-to-top; the last element is the topmost.
pub montage: *const MontagePod,
/// `montage` element count.
pub montage_count: c_int,
}
/// One sequence-montage clip (`oakrender::ffi::OakMontageClip`, M12 P0):
/// the facade resolves the timeline into this POD list; the render
/// producer decodes and composites.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct MontagePod {
/// Footage filename (borrowed; alive for the render call).
pub filename: *const std::ffi::c_char,
/// Media stream index.
pub stream_index: c_int,
/// Clip in point (sequence time), rational.
pub in_num: i64,
/// Clip in point denominator.
pub in_den: i64,
/// Clip out point (sequence time), rational.
pub out_num: i64,
/// Clip out point denominator.
pub out_den: i64,
/// Media in point, rational.
pub media_in_num: i64,
/// Media in point denominator.
pub media_in_den: i64,
/// Playback gain (1.0 = unity).
pub gain: f32,
}
/// The samples block handed out by `oakrender_ticket_get_samples`
/// (caller-owned; release with `oakrender_audio_samples_free`). Read by
/// the facade through the Rust type.
pub struct OakAudioSamplesOut {
/// Interleaved f32 samples.
pub data: Box<[f32]>,
/// Frame count.
pub frame_count: c_int,
/// Sample rate (Hz).
pub sample_rate: c_int,
/// Channel layout mask.
pub channel_layout: u64,
/// Channel count.
pub channel_count: c_int,
}
/// `oakaudio_min_max` (`include/audio/waveform.h`) — one summarized
/// waveform point of one channel.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct MinMax {
/// Minimum of the summarized samples.
pub min: f32,
/// Maximum of the summarized samples.
pub max: f32,
}
/// `oakaudio_offset_result` (`include/audio/sync.h`).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OffsetResult {
/// Offset in samples.
pub offset_samples: i64,
/// Correlation confidence 0..1.
pub confidence: f64,
/// 1 when an estimate was found.
pub valid: c_int,
}
/// `oakaudio_stretch_offset_result` (`include/audio/sync.h`).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct StretchOffsetResult {
/// Playback rate aligning the candidate (> 1 = speed up).
pub rate: f64,
/// Offset in samples.
pub offset_samples: i64,
/// Correlation confidence 0..1.
pub confidence: f64,
/// 1 when an estimate was found.
pub valid: c_int,
}
/// `oakaudio_source_clip` (`include/audio/sync.h`) — one clip's
/// source-time metadata (rational seconds).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SourceClip {
/// Source start time numerator.
pub source_start_time_num: i64,
/// Source start time denominator.
pub source_start_time_den: i64,
/// Media in numerator.
pub media_in_num: i64,
/// Media in denominator.
pub media_in_den: i64,
/// 1 when the source start time is meaningful.
pub has_source_start_time: c_int,
}
/// `i32` code -> `PixelFormat` (`repr(i32)` enum; unknown -> `Invalid`).
pub fn pixel_format_from_code(v: c_int) -> oakcore_rs::PixelFormat {
match v {
0 => oakcore_rs::PixelFormat::U8,
1 => oakcore_rs::PixelFormat::U10,
2 => oakcore_rs::PixelFormat::U16,
3 => oakcore_rs::PixelFormat::F16,
4 => oakcore_rs::PixelFormat::F32,
_ => oakcore_rs::PixelFormat::Invalid,
}
}
/// `i32` code -> `SampleFormat` (`repr(i32)` enum; unknown -> `Invalid`).
pub fn sample_format_from_code(v: c_int) -> oakcore_rs::SampleFormat {
match v {
0 => oakcore_rs::SampleFormat::U8Planar,
1 => oakcore_rs::SampleFormat::S16Planar,
2 => oakcore_rs::SampleFormat::S32Planar,
3 => oakcore_rs::SampleFormat::S64Planar,
4 => oakcore_rs::SampleFormat::F32Planar,
5 => oakcore_rs::SampleFormat::F64Planar,
6 => oakcore_rs::SampleFormat::U8,
7 => oakcore_rs::SampleFormat::S16,
8 => oakcore_rs::SampleFormat::S32,
9 => oakcore_rs::SampleFormat::S64,
10 => oakcore_rs::SampleFormat::F32,
11 => oakcore_rs::SampleFormat::F64,
_ => oakcore_rs::SampleFormat::Invalid,
}
}
/// `i32` code -> `VideoScalingMethod` (unknown -> `Stretch`, the C++
/// default).
pub fn scaling_from_code(v: c_int) -> oakcodec::encodingparams::VideoScalingMethod {
match v {
0 => oakcodec::encodingparams::VideoScalingMethod::Fit,
2 => oakcodec::encodingparams::VideoScalingMethod::Crop,
_ => oakcodec::encodingparams::VideoScalingMethod::Stretch,
}
}
File diff suppressed because it is too large Load Diff
-217
View File
@@ -1,217 +0,0 @@
// 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/>.
//! Live write-through to the oakstorage project library — a thin
//! forward to the module's session manager (plan M13 §2/§3; M14 R1:
//! the binding map, snapshot thread and exit flush moved into
//! [`oakstorage::writethrough`]).
//!
//! The write-through itself is subscribed directly to the oakundo
//! process-wide stack's command-success observers (see
//! [`oakstorage::writethrough`]); this module only keeps the frozen
//! `oakengine_storage_*` C ABI exports and the box/buf-size glue, and
//! re-exports the manager entry points the rest of the facade (the
//! library manager, the project family) calls.
//!
//! See [`oakstorage::writethrough`] for the binding model, the config
//! keys (`Storage/Backend`, `Storage/SqlitePath`, `Storage/PgUrl`,
//! `Storage/SnapshotIntervalSec`) and the graceful-degradation rules.
use std::ffi::{c_char, c_int};
use oakstorage::backends::database::DatabaseBackend;
use crate::handle::{guard_int, CHandle, OakEngineProject};
/// Bind `project` to the configured default library (no-op when the
/// backend is disabled, the project has no uuid, or it is already bound).
pub fn bind_project(project: CHandle) {
oakstorage::writethrough::bind_project(project);
}
/// Flush `project`'s pending writes and drop its binding (closing the
/// project).
pub fn unbind_project(project: CHandle) {
oakstorage::writethrough::unbind_project(project);
}
/// Whether `project` currently has a binding (status-bar / D5 surface).
pub fn is_bound(project: CHandle) -> bool {
oakstorage::writethrough::is_bound(project)
}
/// The last write-through / snapshot error of `project` (empty when none
/// or not bound).
pub fn last_error(project: CHandle) -> Option<String> {
oakstorage::writethrough::last_error(project)
}
/// Persist every bound project after a successful undo-path operation.
/// No-op when nothing is bound (also called by the module's command
/// observer; kept here for the facade's own call sites).
pub fn note_command() {
oakstorage::writethrough::note_command();
}
/// Whether the write-through backend is enabled (config-driven).
pub(crate) fn storage_enabled() -> bool {
oakstorage::writethrough::storage_enabled()
}
/// The `oakdb+…` uri of the configured library (None when it cannot be
/// resolved). Shared with the library manager exports.
pub(crate) fn library_uri() -> Option<String> {
oakstorage::writethrough::library_uri()
}
/// The default library file path (config-driven data directory).
pub(crate) fn default_library_path() -> String {
oakstorage::writethrough::default_library_path()
}
/// The process-wide oakstorage backend (shared with the library manager
/// exports in [`crate::library`]).
pub(crate) fn backend() -> &'static DatabaseBackend {
oakstorage::writethrough::backend()
}
/// The exit path: stop the snapshot thread and drain every still-bound
/// project (save + snapshot).
pub fn flush_all() {
oakstorage::writethrough::flush_all();
}
// ---------------------------------------------------------------------------
// Facade exports
// ---------------------------------------------------------------------------
/// `oakengine_storage_flush` — flush every bound project (write-through +
/// snapshot) and stop the snapshot thread. The app calls this on exit
/// (the facade's shutdown path; the write-through is already per-command,
/// so this only drains the periodic snapshot backlog).
#[no_mangle]
pub extern "C" fn oakengine_storage_flush() -> c_int {
flush_all();
crate::error::OAKENGINE_OK
}
/// `oakengine_storage_is_bound` — 1 when `project` is bound to a library
/// session, 0 otherwise (NULL project -> 0).
#[no_mangle]
pub unsafe extern "C" fn oakengine_storage_is_bound(
project: *mut OakEngineProject,
) -> c_int {
guard_int(|| unsafe {
let h = crate::handle::unbox(project)?;
Ok(is_bound(h) as c_int)
})
}
/// `oakengine_storage_last_error` — the last write-through / snapshot
/// error of `project` (buf/size convention; empty when none or not
/// bound).
#[no_mangle]
pub unsafe extern "C" fn oakengine_storage_last_error(
project: *mut OakEngineProject,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
guard_int(|| unsafe {
let h = crate::handle::unbox(project)?;
let msg = last_error(h).unwrap_or_default();
Ok(crate::handle::write_string(&msg, buf, buf_size))
})
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn library_uri_resolves_configured_path() {
use oakcommon::configstore::ConfigStore;
let store = ConfigStore::instance();
let _g = crate::tests::common::STORAGE_CONFIG_LOCK
.lock()
.unwrap_or_else(|e| e.into_inner());
// A configured path wins and yields an absolute oakdb+sqlite uri.
let dir =
std::env::temp_dir().join(format!("oakengine_storage_uri_{}", std::process::id()));
let _ = std::fs::create_dir_all(&dir);
let lib = dir.join("lib.db");
store.set(Some("Storage"), "Backend", "sqlite");
store.set(Some("Storage"), "SqlitePath", &lib.to_string_lossy());
let uri = library_uri().expect("configured path resolves");
assert!(uri.starts_with("oakdb+sqlite://"), "{uri}");
assert!(uri.ends_with("lib.db"), "{uri}");
assert!(storage_enabled());
// Leave the store in a safe state: backend off (the config store
// has no remove API, so later unguarded tests see "off" and never
// bind a project to a library).
store.set(Some("Storage"), "Backend", "off");
assert!(!storage_enabled());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn library_uri_resolves_pg_config() {
use oakcommon::configstore::ConfigStore;
let store = ConfigStore::instance();
let _g = crate::tests::common::STORAGE_CONFIG_LOCK
.lock()
.unwrap_or_else(|e| e.into_inner());
// Backend = "pg" yields an oakdb+pg uri from Storage/PgUrl; a
// postgres:// scheme on the config value is stripped.
store.set(Some("Storage"), "Backend", "pg");
store.set(
Some("Storage"),
"PgUrl",
"postgres://user:pass@host:5432/oak",
);
assert!(storage_enabled());
assert_eq!(
library_uri().as_deref(),
Some("oakdb+pg://user:pass@host:5432/oak")
);
// postgresql:// is accepted too.
store.set(
Some("Storage"),
"PgUrl",
"postgresql://u@h/db?sslmode=disable",
);
assert_eq!(
library_uri().as_deref(),
Some("oakdb+pg://u@h/db?sslmode=disable")
);
// Backend = "pg" with no PgUrl = no library (graceful
// degradation, same as an absent sqlite path).
store.set(Some("Storage"), "PgUrl", "");
assert_eq!(library_uri(), None);
// Leave the store in a safe state.
store.set(Some("Storage"), "Backend", "off");
assert!(!storage_enabled());
}
}
File diff suppressed because it is too large Load Diff
-962
View File
@@ -1,962 +0,0 @@
// 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::pods::{zeroed_encoding_params, EncodingParamsPOD};
use crate::stubs::node as n;
use crate::stubs::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 project an import task borrows (addref'd at creation, released
/// at free): the module's import task stores its project handle WITHOUT
/// addref, so this facade-side ref keeps the shared box alive while the
/// task runs (see `oakengine_task_create_project_import`).
import_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,
import_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/import projects, 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(mut project) = meta.import_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.
///
/// The module's import task stores the project handle WITHOUT addref, so
/// the facade keeps an addref'd copy in [`TaskMeta::import_project`]
/// (released at free) — without it, releasing the transient borrowed
/// handle here would drop the shared box while the task still references
/// it, and the run would read freed memory (the former SIGSEGV reproduced
/// by `it_task::import_run_single_file`).
#[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);
if h.is_null() {
// Creation failed: release the transient borrowed handle.
n::oaknode_project_free(&mut project);
return Ok(std::ptr::null_mut());
}
// Keep the project borrowed for the task's lifetime (the module's
// import task stores the handle without addref): addref before the
// transient handle below is released, so the shared box stays alive
// until `oakengine_task_free` drops the meta-side copy.
let mut meta = TaskMeta::new();
meta.import_project = Some(project.addref());
// Release the transient borrowed project handle (the task's copy and
// the facade-side addref above keep the box alive).
n::oaknode_project_free(&mut project);
meta_insert(h.ctx as usize, meta);
Ok(box_handle::<OakEngineTask>(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, audio rate/layout, 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) };
// Audio rate/layout flow through so the encoder opens with the
// requested rate (the module export reads them from the POD). The
// sample format is NOT carried: the FFmpeg encoder always runs in the
// codec's native format and resamples (see `FFmpegEncoder::open`).
if pod.audio_enabled != 0 {
let mut sample_rate: c_int = 0;
let mut channel_layout: u64 = 0;
unsafe {
crate::codec::oakengine_encoding_params_get_audio_params(
params,
&mut sample_rate,
&mut channel_layout,
std::ptr::null_mut(),
);
}
pod.audio_sample_rate = sample_rate;
pod.audio_channel_layout = channel_layout;
}
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,
);
}
// Custom in/out range (work-area export): copied so the export task
// renders exactly [in, out) instead of the whole viewer length.
if unsafe { crate::codec::oakengine_encoding_params_has_custom_range(params) } != 0 {
let mut in_num: i64 = 0;
let mut in_den: i64 = 0;
let mut out_num: i64 = 0;
let mut out_den: i64 = 0;
let rc = unsafe {
crate::codec::oakengine_encoding_params_get_custom_range(
params,
&mut in_num,
&mut in_den,
&mut out_num,
&mut out_den,
)
};
if rc == 0 {
pod.has_custom_range = 1;
pod.custom_range_in_num = in_num;
pod.custom_range_in_den = in_den;
pod.custom_range_out_num = out_num;
pod.custom_range_out_den = out_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 = crate::pods::pixel_format_from_code(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()),
}
})
}
// ---------------------------------------------------------------------------
// Load task results / event subscription
// ---------------------------------------------------------------------------
/// `oakengine_task_load_take_project` — take the project an interchange
/// (OVE/OTIO) load task produced after a successful run; ownership moves to
/// the caller (release with `oakengine_project_free`). NULL for a NULL
/// task, a task that never ran, or a task that is not a load task.
///
/// The module's `oaktask_load_take_project` is the load-result getter the
/// engine facade has no export for (the app's interchange-open path); it is
/// wrapped here so the app can stay on the `oakengine_*` surface. A taken
/// project is bound to the default library (plan M13 D2 — the "task load
/// 完成" hook).
#[no_mangle]
pub unsafe extern "C" fn oakengine_task_load_take_project(
task: *mut OakEngineTask,
) -> *mut OakEngineProject {
guard_ptr(|| unsafe {
let h = unbox(task)?;
let project = t::oaktask_load_take_project(h);
if project.is_null() {
return Ok(std::ptr::null_mut());
}
crate::storage::bind_project(project);
Ok(box_handle::<OakEngineProject>(project))
})
}
/// `oakengine_task_subscribe` — register the task event callback invoked on
/// the task's own thread (`OAKTASK_EVENT_STARTED`=0, `OAKTASK_EVENT_PROGRESS`=1,
/// `OAKTASK_EVENT_FINISHED`=2); one subscription replaces the previous one.
///
/// Returns 0 on success; facade `OAKENGINE_E_INVALID` (-1) for a NULL task
/// or NULL callback; module error codes pass through untranslated. The
/// callback and `userdata` follow the module's `oaktask_event_fn` contract
/// (the engine facade has no subscription export of its own; the app's
/// export-progress path uses this wrapper).
#[no_mangle]
pub unsafe extern "C" fn oakengine_task_subscribe(
task: *mut OakEngineTask,
cb: Option<t::OakTaskEventFn>,
userdata: *mut c_void,
) -> i64 {
guard_i64(|| unsafe {
let h = unbox(task)?;
if cb.is_none() {
return Err(Error::Invalid);
}
Ok(t::oaktask_task_subscribe(h, cb, userdata))
})
}
@@ -1,163 +0,0 @@
// 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/>.
//! Smoke tests for the audio family (`engine/include/oakengine/audio.h`).
//! The AudioManager singleton is process-wide, so manager tests run in a
//! single serialized test function; processor and sync tests are
//! independent.
use super::common;
use crate::audio::{
oakengine_audio_clear_buffered_output, oakengine_audio_create_instance,
oakengine_audio_destroy_instance, oakengine_audio_estimate_envelope_offset,
oakengine_audio_get_output_device, oakengine_audio_hard_reset, oakengine_audio_processor_close,
oakengine_audio_processor_create, oakengine_audio_processor_free,
oakengine_audio_processor_is_open, oakengine_audio_processor_open,
oakengine_audio_push_to_output, oakengine_audio_reset_output_clock,
oakengine_audio_set_output_device, oakengine_audio_set_output_notify_interval,
oakengine_audio_stop_output, oakengine_audio_sync_place_by_waveform_offset,
OakAudioSyncPlacement, OakAudioWaveformOffset,
};
/// Manager lifecycle: create/destroy round-trip and device accessors
/// (serialized — the singleton is process-wide).
#[test]
fn manager_lifecycle() {
common::with_manager(|| manager_lifecycle_inner());
}
fn manager_lifecycle_inner() {
// Start from a destroyed state.
unsafe { oakengine_audio_destroy_instance() };
// No instance → create succeeds, destroy is idempotent.
assert_eq!(unsafe { oakengine_audio_create_instance() }, 0);
assert_eq!(unsafe { oakengine_audio_destroy_instance() }, 0);
assert_eq!(unsafe { oakengine_audio_destroy_instance() }, 0);
// Recreate for the device tests.
assert_eq!(unsafe { oakengine_audio_create_instance() }, 0);
// paNoDevice (-1) until a device is set.
assert_eq!(unsafe { oakengine_audio_get_output_device() }, -1);
// The module records any device index (PortAudio validation is not
// bridged), so setting succeeds and reads back.
assert_eq!(unsafe { oakengine_audio_set_output_device(999999) }, 0);
assert_eq!(unsafe { oakengine_audio_get_output_device() }, 999999);
assert_eq!(unsafe { oakengine_audio_set_output_device(-1) }, 0);
// Stateless no-op calls succeed with a live manager.
assert_eq!(unsafe { oakengine_audio_reset_output_clock() }, 0);
assert_eq!(unsafe { oakengine_audio_stop_output() }, 0);
assert_eq!(unsafe { oakengine_audio_clear_buffered_output() }, 0);
assert_eq!(
unsafe { oakengine_audio_set_output_notify_interval(1024) },
0
);
assert_eq!(unsafe { oakengine_audio_hard_reset() }, 0);
// push with a NULL params handle fails cleanly.
assert_eq!(
unsafe {
oakengine_audio_push_to_output(
std::ptr::null(),
c"data".as_ptr(),
4,
std::ptr::null_mut(),
0,
)
},
-3 // OAKENGINE_E_FAILED
);
unsafe { oakengine_audio_destroy_instance() };
}
/// Sync envelope-offset correlation runs and fills the result struct.
#[test]
fn sync_envelope_offset() {
let reference = [0.0_f64, 0.5, 1.0, 0.5, 0.0];
let candidate = [0.0_f64, 0.0, 0.5, 1.0, 0.5];
let mut out = OakAudioWaveformOffset {
offset_samples: 0,
confidence: 0.0,
valid: 0,
};
let rc = unsafe {
oakengine_audio_estimate_envelope_offset(
reference.as_ptr(),
5,
candidate.as_ptr(),
5,
std::ptr::null(),
0,
std::ptr::null(),
0,
128,
16,
&mut out,
)
};
assert_eq!(rc, 0);
// The result is filled in either way; the correlation may or may not
// find a valid offset for this tiny synthetic input.
assert!(out.confidence >= 0.0 && out.confidence <= 1.0);
}
/// Waveform-offset placement runs and reports validity.
#[test]
fn sync_place_by_waveform_offset() {
let mut out = OakAudioSyncPlacement {
timeline_in_num: 0,
timeline_in_den: 1,
valid: 0,
};
let rc = unsafe { oakengine_audio_sync_place_by_waveform_offset(0, 1, 48000, 48000, &mut out) };
assert_eq!(rc, 0);
// 48000 samples at 48 kHz = 1 second.
assert_eq!(out.timeline_in_num, 1);
assert_eq!(out.timeline_in_den, 1);
assert_eq!(out.valid, 1);
// NULL out → E_INVALID.
assert_eq!(
unsafe {
oakengine_audio_sync_place_by_waveform_offset(0, 1, 0, 48000, std::ptr::null_mut())
},
-1
);
}
/// Processor lifecycle: create/free round-trip; open with NULL params
/// fails with E_INVALID.
#[test]
fn processor_lifecycle() {
let p = unsafe { oakengine_audio_processor_create() };
assert!(!p.is_null());
assert_eq!(unsafe { oakengine_audio_processor_is_open(p) }, 0);
assert_eq!(unsafe { oakengine_audio_processor_close(p) }, 0);
// open with a NULL `to` params handle → E_INVALID.
assert_eq!(
unsafe { oakengine_audio_processor_open(p, std::ptr::null(), std::ptr::null(), 1.0) },
-1
);
unsafe { oakengine_audio_processor_free(p) };
// NULL free is a no-op.
unsafe { oakengine_audio_processor_free(std::ptr::null_mut()) };
}
@@ -1,313 +0,0 @@
// 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/>.
//! Smoke tests for the encoding family (`engine/include/oakengine/encoding.h`):
//! container/codec metadata queries and the encoding-params handle.
use super::common;
use std::ffi::{c_char, c_int};
use crate::audio::oakengine_audio_destroy_instance;
use crate::codec::{
oakengine_encoding_codec_is_lossless, oakengine_encoding_codec_is_still_image,
oakengine_encoding_codec_name, oakengine_encoding_filename_contains_digit_placeholder,
oakengine_encoding_filename_remove_digit_placeholder,
oakengine_encoding_format_audio_codec_count, oakengine_encoding_format_count,
oakengine_encoding_format_extension, oakengine_encoding_format_name,
oakengine_encoding_format_video_codec_at, oakengine_encoding_format_video_codec_count,
oakengine_encoding_generate_matrix, oakengine_encoding_image_sequence_digit_count,
oakengine_encoding_params_audio_enabled, oakengine_encoding_params_color_transform_output,
oakengine_encoding_params_create, oakengine_encoding_params_destroy,
oakengine_encoding_params_enable_audio, oakengine_encoding_params_enable_video,
oakengine_encoding_params_filename, oakengine_encoding_params_format,
oakengine_encoding_params_get_audio_params, oakengine_encoding_params_get_custom_range,
oakengine_encoding_params_get_video_params, oakengine_encoding_params_has_custom_range,
oakengine_encoding_params_is_valid, oakengine_encoding_params_set_color_transform,
oakengine_encoding_params_set_custom_range, oakengine_encoding_params_set_filename,
oakengine_encoding_params_set_format, oakengine_encoding_params_set_video_bit_rate,
oakengine_encoding_params_set_video_option, oakengine_encoding_params_set_video_pix_fmt,
oakengine_encoding_params_video_bit_rate, oakengine_encoding_params_video_codec,
oakengine_encoding_params_video_enabled, oakengine_encoding_params_video_option,
oakengine_encoding_params_video_pix_fmt, oakengine_encoding_pix_fmt_index,
oakengine_encoding_start_audio_recording,
};
use crate::common::OakVideoParamsPod;
/// Container format / codec metadata queries.
#[test]
fn encoding_metadata() {
// Format enumeration: at least the six named formats exist.
let count = unsafe { oakengine_encoding_format_count() };
assert!(count >= 6);
// Matroska (1): name + extension via two-stage getters.
let mut buf = [0 as c_char; 64];
let len = unsafe { oakengine_encoding_format_name(1, buf.as_mut_ptr(), 64) };
assert!(len > 0);
assert!(unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap()
.contains("Matroska"));
let len = unsafe { oakengine_encoding_format_extension(1, buf.as_mut_ptr(), 64) };
assert!(len > 0);
assert_eq!(
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap(),
"mkv"
);
// Per-format codec lists.
assert!(unsafe { oakengine_encoding_format_video_codec_count(1) } >= 1);
let codec = unsafe { oakengine_encoding_format_video_codec_at(1, 0) };
assert!(codec >= 1);
assert!(unsafe { oakengine_encoding_format_audio_codec_count(1) } >= 1);
// Codec metadata: name, still-image (PNG = 5), lossless.
let len = unsafe { oakengine_encoding_codec_name(1, buf.as_mut_ptr(), 64) };
assert!(len > 0);
assert_eq!(unsafe { oakengine_encoding_codec_is_still_image(5) }, 1); // PNG
assert_eq!(unsafe { oakengine_encoding_codec_is_still_image(1) }, 0); // H264
assert!(unsafe { oakengine_encoding_codec_is_lossless(13) } == 1); // PCM
// pix_fmt_index: preferred format index when absent.
assert_eq!(
unsafe { oakengine_encoding_pix_fmt_index(1, c"yuv420p".as_ptr()) },
0
);
}
/// Image-sequence filename helpers.
#[test]
fn filename_helpers() {
assert_eq!(
unsafe {
oakengine_encoding_filename_contains_digit_placeholder(c"img[#####].png".as_ptr())
},
1
);
assert_eq!(
unsafe { oakengine_encoding_filename_contains_digit_placeholder(c"img.png".as_ptr()) },
0
);
assert_eq!(
unsafe { oakengine_encoding_image_sequence_digit_count(c"img[#####].png".as_ptr()) },
5
);
let mut buf = [0 as c_char; 64];
let len = unsafe {
oakengine_encoding_filename_remove_digit_placeholder(
c"img[#####].png".as_ptr(),
buf.as_mut_ptr(),
64,
)
};
assert!(len > 0);
assert_eq!(
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap(),
"img.png"
);
}
/// Transform matrix: fit produces a valid 16-float matrix.
#[test]
fn generate_matrix() {
let mut m = [0.0_f32; 16];
assert_eq!(
unsafe { oakengine_encoding_generate_matrix(0, 1920, 1080, 960, 540, m.as_mut_ptr()) },
0
);
// The 4x4 identity-ish matrix has a non-zero top-left.
assert!(m[0] > 0.0 || m[5] > 0.0);
// NULL output → E_INVALID.
assert_eq!(
unsafe { oakengine_encoding_generate_matrix(0, 1, 1, 1, 1, std::ptr::null_mut()) },
-1
);
}
/// Encoding-params handle lifecycle: create → configure → read back →
/// destroy (serialized inside one test; the handle is per-call state).
#[test]
fn params_handle_round_trip() {
let p = unsafe { oakengine_encoding_params_create() };
assert!(!p.is_null());
// Fresh: nothing enabled, format unset (-1).
assert_eq!(unsafe { oakengine_encoding_params_is_valid(p) }, 0);
assert_eq!(unsafe { oakengine_encoding_params_format(p) }, -1);
// Format: set + get, and reject out-of-range.
assert_eq!(unsafe { oakengine_encoding_params_set_format(p, 1) }, 0); // Matroska
assert_eq!(unsafe { oakengine_encoding_params_format(p) }, 1);
assert_eq!(unsafe { oakengine_encoding_params_set_format(p, 9999) }, -1);
// Filename round-trip.
assert_eq!(
unsafe { oakengine_encoding_params_set_filename(p, c"out.mkv".as_ptr()) },
0
);
let mut buf = [0 as c_char; 64];
let len = unsafe { oakengine_encoding_params_filename(p, buf.as_mut_ptr(), 64) };
assert_eq!(len, 7);
assert_eq!(
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap(),
"out.mkv"
);
// Enable video: valid, get_video_params reads back.
let mut vp: OakVideoParamsPod = unsafe { std::mem::zeroed() };
assert_eq!(
unsafe {
crate::common::oakengine_video_params_make(
&mut vp, 1920, 1080, 1001, 30000, 4, 1, 1, 0, 1, 1,
)
},
0
);
assert_eq!(
unsafe { oakengine_encoding_params_enable_video(p, &vp, 1) },
0
);
assert_eq!(unsafe { oakengine_encoding_params_is_valid(p) }, 1);
assert_eq!(unsafe { oakengine_encoding_params_video_enabled(p) }, 1);
assert_eq!(unsafe { oakengine_encoding_params_video_codec(p) }, 1);
let mut out_vp: OakVideoParamsPod = unsafe { std::mem::zeroed() };
assert_eq!(
unsafe { oakengine_encoding_params_get_video_params(p, &mut out_vp) },
0
);
assert_eq!(out_vp.width, 1920);
assert_eq!(out_vp.height, 1080);
assert_eq!(out_vp.time_base_num, 1001);
// Video bit rate round-trip.
unsafe { oakengine_encoding_params_set_video_bit_rate(p, 8_000_000) };
assert_eq!(
unsafe { oakengine_encoding_params_video_bit_rate(p) },
8_000_000
);
// Encoded pixel format round-trip.
assert_eq!(
unsafe { oakengine_encoding_params_set_video_pix_fmt(p, c"yuv420p".as_ptr()) },
0
);
let len = unsafe { oakengine_encoding_params_video_pix_fmt(p, buf.as_mut_ptr(), 64) };
assert_eq!(len, 7);
assert_eq!(
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap(),
"yuv420p"
);
// Audio: disabled get_video/audio → E_STATE; enable then read back.
let mut sr: c_int = 0;
let mut layout: u64 = 0;
let mut sf: c_int = 0;
assert_eq!(
unsafe { oakengine_encoding_params_get_audio_params(p, &mut sr, &mut layout, &mut sf) },
-2
);
assert_eq!(
unsafe { oakengine_encoding_params_enable_audio(p, 48000, 3, 0, 13) },
0
);
assert_eq!(unsafe { oakengine_encoding_params_audio_enabled(p) }, 1);
assert_eq!(
unsafe { oakengine_encoding_params_get_audio_params(p, &mut sr, &mut layout, &mut sf) },
0
);
assert_eq!(sr, 48000);
assert_eq!(layout, 3);
// Custom range: not set → E_NOT_FOUND; set → reads back.
let (mut inn, mut ind, mut outn, mut outd) = (0i64, 0i64, 0i64, 0i64);
assert_eq!(
unsafe {
oakengine_encoding_params_get_custom_range(p, &mut inn, &mut ind, &mut outn, &mut outd)
},
-4
);
unsafe { oakengine_encoding_params_set_custom_range(p, 0, 1, 100, 1) };
assert_eq!(unsafe { oakengine_encoding_params_has_custom_range(p) }, 1);
assert_eq!(
unsafe {
oakengine_encoding_params_get_custom_range(p, &mut inn, &mut ind, &mut outn, &mut outd)
},
0
);
assert_eq!((inn, ind, outn, outd), (0, 1, 100, 1));
// Color transform + video option round-trips.
assert_eq!(
unsafe { oakengine_encoding_params_set_color_transform(p, c"ACEScg".as_ptr()) },
0
);
let len = unsafe { oakengine_encoding_params_color_transform_output(p, buf.as_mut_ptr(), 64) };
assert_eq!(len, 6);
assert_eq!(
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap(),
"ACEScg"
);
assert_eq!(
unsafe { oakengine_encoding_params_set_video_option(p, c"crf".as_ptr(), c"18".as_ptr()) },
0
);
let len =
unsafe { oakengine_encoding_params_video_option(p, c"crf".as_ptr(), buf.as_mut_ptr(), 64) };
assert_eq!(len, 2);
assert_eq!(
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap(),
"18"
);
assert_eq!(
unsafe {
oakengine_encoding_params_video_option(p, c"missing".as_ptr(), buf.as_mut_ptr(), 64)
},
-4
);
unsafe { oakengine_encoding_params_destroy(p) };
// NULL destroy is a no-op.
unsafe { oakengine_encoding_params_destroy(std::ptr::null_mut()) };
}
/// Audio recording without a running audio manager fails with E_STATE.
#[test]
fn start_audio_recording_no_manager() {
// The audio manager is a process-wide singleton shared with it_audio;
// serialize and normalize it (no instance) before asserting E_STATE.
common::with_manager(|| unsafe {
let _ = oakengine_audio_destroy_instance();
let p = oakengine_encoding_params_create();
assert!(!p.is_null());
let rc = oakengine_encoding_start_audio_recording(p, std::ptr::null_mut(), 0);
assert_eq!(rc, -2); // OAKENGINE_E_STATE
oakengine_encoding_params_destroy(p);
});
}
@@ -1,117 +0,0 @@
// 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/>.
//! Shared test support, included from every integration test via
//! `#[path = "common/mod.rs"] mod common;`.
//!
//! Two jobs:
//!
//! 1. **Force rustc to link every module crate's rlib** into the test
//! binary ([`force_link`]). The test-support files import the module
//! crates directly (single-lib; the deleted `src/bridge` no longer
//! exists), and the array doubles as a compile-time proof that the
//! anchor paths in `crates/oakengine/src/linkage.rs` match the current
//! module layouts.
//!
//! 2. **Re-export the folded-in `oakcore_audioparams_*` accessors** for
//! the former mock call sites (`common::oakcore_audioparams_*`). The
//! facade used to leave those symbols as runtime lookups for a C++
//! liboakcore host, and the tests defined in-memory mocks; M12 P5
//! implemented them inside the dylib (crates/oakengine/src/stubs.rs,
//! module `audio`), so the tests just use those implementations.
#![allow(dead_code, unused_variables)]
use std::sync::Mutex;
/// One public direct-Rust symbol per module crate (the module C ABIs are
/// deleted; this mirrors the anchors in `crates/oakengine/src/linkage.rs`).
/// Under unit tests the crates are real dependencies of the lib target and
/// are linked regardless; the array doubles as a compile-time proof that
/// the anchor paths match the current module layouts.
#[allow(unused)]
pub fn force_link() -> usize {
let fns: [usize; 12] = [
oakundo::undostack::undostack_init as usize,
oakcodec::exportformat::Format::get_name as usize,
oakaudio::processor::Processor::init as usize,
oakrender::manager::RenderManager::init as usize,
oakcommon::configstore::ConfigStore::instance as usize,
oakplugin::host::Host::global as usize,
oaknode::project::Project::new as usize,
oaktimeline::marker::TimelineMarkerList::new as usize,
oaktask::manager::TaskManager::init as usize,
// oakundo/oakcommon no longer export a C ABI; their handle-level
// Rust API functions anchor the rlibs into every test binary (the
// same pattern as `crates/oakengine/src/linkage.rs`).
oakcommon::xmlutils::XmlWriter::new as usize,
oakcommon::xmlutils::XmlReader::new as usize,
oakundo::undocommand::command_init as usize,
];
fns.iter().sum()
}
/// Serialize every test that touches the process-wide AudioManager
/// singleton. The former integration tests were separate processes; as
/// unit tests they share one process (and one singleton), so the manager
/// tests must take a shared lock instead of relying on process isolation.
pub fn with_manager(f: impl FnOnce()) {
static LOCK: Mutex<()> = Mutex::new(());
let _g = LOCK.lock().unwrap_or_else(|e| e.into_inner());
f()
}
/// Serializes every test that reads or writes the `Storage` config group
/// (the facade's write-through library selection — see src/storage.rs).
/// The config store is process-global, so the write-through tests and the
/// tests that disable the backend must take this lock for their whole
/// body instead of racing on the shared store.
pub static STORAGE_CONFIG_LOCK: Mutex<()> = Mutex::new(());
/// Run `f` with the write-through storage backend disabled
/// (`Storage/Backend = "off"`). Tests that push undo commands on real
/// projects (e.g. `oakengine_project_add_node`) would otherwise bind them
/// to the default user library and write there; disabling the backend
/// keeps them side-effect-free. The value intentionally persists — every
/// storage test sets its own backend explicitly under
/// [`STORAGE_CONFIG_LOCK`].
pub fn with_storage_off<R>(f: impl FnOnce() -> R) -> R {
let _g = storage_off_guard();
f()
}
/// Take the storage-config lock AND disable the write-through backend,
/// returning the guard: a test that pushes undo commands throughout its
/// body holds the guard (and thus the lock) for its whole lifetime, so a
/// concurrently running storage test cannot flip the backend mid-test.
pub fn storage_off_guard() -> std::sync::MutexGuard<'static, ()> {
let g = STORAGE_CONFIG_LOCK.lock().unwrap_or_else(|e| e.into_inner());
oakcommon::configstore::ConfigStore::instance().set(Some("Storage"), "Backend", "off");
g
}
// ---------------------------------------------------------------------------
// oakcore_audioparams_* (see module docs)
// ---------------------------------------------------------------------------
/// The facade's in-dylib `oakcore_audioparams_*` C ABI (see
/// `crate::stubs::audio`): the accessors were host-provided mocks until
/// M12 P5 folded them into the engine, so the tests now share the real
/// implementations instead of defining per-binary duplicates. Only the
/// two entry points the test files call (`create`/`free`) are re-exported;
/// the read accessors are reached through `crate::stubs::audio` where the
/// tests need them.
pub use crate::stubs::audio::{oakcore_audioparams_create, oakcore_audioparams_free};

Some files were not shown because too many files have changed in this diff Show More