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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Generated
-1745
File diff suppressed because it is too large
Load Diff
@@ -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"] }
|
||||
@@ -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.
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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 §4(GL 路径 + 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
|
||||
* (OFX:render 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
@@ -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).
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
})
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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};
|
||||
@@ -1,241 +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 common family (`engine/include/oakengine/config.h`
|
||||
//! and `videoparams.h`). The oakcommon config store is a process-wide
|
||||
//! singleton, so config tests are serialized inside single test
|
||||
//! functions.
|
||||
|
||||
use super::common;
|
||||
|
||||
use std::ffi::{c_char, c_int};
|
||||
|
||||
use crate::common::{
|
||||
oakengine_config_get_int, oakengine_config_get_string, oakengine_config_load,
|
||||
oakengine_config_save, oakengine_config_set_error_handler, oakengine_config_set_int,
|
||||
oakengine_config_set_string, oakengine_video_params_bytes_per_pixel,
|
||||
oakengine_video_params_effective_size, oakengine_video_params_equal,
|
||||
oakengine_video_params_format_is_float, oakengine_video_params_internal_channel_count,
|
||||
oakengine_video_params_is_valid, oakengine_video_params_make,
|
||||
oakengine_video_params_standard_pixel_aspect_at,
|
||||
oakengine_video_params_standard_pixel_aspect_count,
|
||||
oakengine_video_params_supported_divider_at, oakengine_video_params_supported_divider_count,
|
||||
oakengine_video_params_supported_frame_rate_at,
|
||||
oakengine_video_params_supported_frame_rate_count, OakVideoParamsPod,
|
||||
};
|
||||
|
||||
/// Config: load/save, string and int round-trips, missing-key behavior.
|
||||
///
|
||||
/// Serialized with the write-through tests (the config store is
|
||||
/// process-global) and redirected to a temp `OAK_CONFIG_DIR` — without the
|
||||
/// redirect, `oakengine_config_save` would write the real `config.ini`.
|
||||
#[test]
|
||||
fn config_round_trip() {
|
||||
let _config = common::STORAGE_CONFIG_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"oakengine_common_smoke_config_{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = std::fs::create_dir_all(&dir);
|
||||
std::env::set_var("OAK_CONFIG_DIR", &dir);
|
||||
assert_eq!(unsafe { oakengine_config_load() }, 0);
|
||||
|
||||
// Missing key reads as 0 / empty.
|
||||
let mut buf = [0 as c_char; 64];
|
||||
assert_eq!(
|
||||
unsafe { oakengine_config_get_string(c"no/such/key".as_ptr(), buf.as_mut_ptr(), 64) },
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oakengine_config_get_int(c"no/such/key".as_ptr(), 7) },
|
||||
7
|
||||
);
|
||||
|
||||
// String round-trip.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_config_set_string(c"facade/test".as_ptr(), c"hello".as_ptr()) },
|
||||
0
|
||||
);
|
||||
let len = unsafe { oakengine_config_get_string(c"facade/test".as_ptr(), buf.as_mut_ptr(), 64) };
|
||||
assert_eq!(len, 5);
|
||||
assert_eq!(
|
||||
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"hello"
|
||||
);
|
||||
|
||||
// A too-small buffer is not written (the two-stage convention is:
|
||||
// query the required size, allocate, copy) — the module reports the
|
||||
// full length and leaves the buffer untouched.
|
||||
let mut small = [0 as c_char; 3];
|
||||
let len =
|
||||
unsafe { oakengine_config_get_string(c"facade/test".as_ptr(), small.as_mut_ptr(), 3) };
|
||||
assert_eq!(len, 5); // reported full length
|
||||
assert_eq!(
|
||||
unsafe { std::ffi::CStr::from_ptr(small.as_ptr()) }
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
""
|
||||
);
|
||||
|
||||
// Int round-trip.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_config_set_int(c"facade/n".as_ptr(), 1234) },
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oakengine_config_get_int(c"facade/n".as_ptr(), 0) },
|
||||
1234
|
||||
);
|
||||
|
||||
assert_eq!(unsafe { oakengine_config_save() }, 0);
|
||||
assert!(dir.join("config.ini").exists());
|
||||
|
||||
std::env::remove_var("OAK_CONFIG_DIR");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// Config error handler: registered, then invoked via report_error.
|
||||
#[test]
|
||||
fn config_error_handler() {
|
||||
static CALLED: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
|
||||
unsafe extern "C" fn handler(
|
||||
_title: *const c_char,
|
||||
_message: *const c_char,
|
||||
_userdata: *mut std::ffi::c_void,
|
||||
) {
|
||||
CALLED.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
assert_eq!(
|
||||
unsafe { oakengine_config_set_error_handler(Some(handler), std::ptr::null_mut()) },
|
||||
0
|
||||
);
|
||||
// Report an error through the handler.
|
||||
assert_eq!(
|
||||
unsafe { crate::common::oakengine_config_report_error(c"t".as_ptr(), c"m".as_ptr()) },
|
||||
0
|
||||
);
|
||||
assert_eq!(CALLED.load(std::sync::atomic::Ordering::SeqCst), 1);
|
||||
// NULL handler clears; reporting then does not invoke.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_config_set_error_handler(None, std::ptr::null_mut()) },
|
||||
0
|
||||
);
|
||||
unsafe { crate::common::oakengine_config_report_error(c"t".as_ptr(), c"m".as_ptr()) };
|
||||
assert_eq!(CALLED.load(std::sync::atomic::Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
/// Videoparams static tables.
|
||||
#[test]
|
||||
fn videoparams_static_tables() {
|
||||
// 12 standard frame rates; the 23.976 entry is 24000/1001.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_supported_frame_rate_count() },
|
||||
12
|
||||
);
|
||||
let (mut num, mut den) = (0, 0);
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_supported_frame_rate_at(2, &mut num, &mut den) },
|
||||
0
|
||||
);
|
||||
assert_eq!((num, den), (24000, 1001));
|
||||
// Out of range → E_INVALID.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_supported_frame_rate_at(99, &mut num, &mut den) },
|
||||
-1
|
||||
);
|
||||
|
||||
// 6 standard pixel aspects; index 4 is PAL widescreen 64/45.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_standard_pixel_aspect_count() },
|
||||
6
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_standard_pixel_aspect_at(4, &mut num, &mut den) },
|
||||
0
|
||||
);
|
||||
assert_eq!((num, den), (64, 45));
|
||||
|
||||
// Dividers 1..=8; out of range → -1.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_supported_divider_count() },
|
||||
8
|
||||
);
|
||||
assert_eq!(unsafe { oakengine_video_params_supported_divider_at(5) }, 8);
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_supported_divider_at(99) },
|
||||
-1
|
||||
);
|
||||
|
||||
// Format helpers (PixelFormat codes: F16 = 3, F32 = 4).
|
||||
assert_eq!(unsafe { oakengine_video_params_format_is_float(4) }, 1); // F32
|
||||
assert_eq!(unsafe { oakengine_video_params_format_is_float(3) }, 1); // F16
|
||||
assert_eq!(unsafe { oakengine_video_params_format_is_float(0) }, 0); // U8
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_internal_channel_count() },
|
||||
4
|
||||
);
|
||||
assert!(unsafe { oakengine_video_params_bytes_per_pixel(1, 4) } > 0);
|
||||
}
|
||||
|
||||
/// Videoparams POD: make/equal/valid + effective size.
|
||||
#[test]
|
||||
fn videoparams_pod() {
|
||||
let mut a: OakVideoParamsPod = unsafe { std::mem::zeroed() };
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_make(&mut a, 1920, 1080, 1001, 30000, 16, 1, 1, 0, 1, 1,) },
|
||||
0
|
||||
);
|
||||
assert_eq!(a.width, 1920);
|
||||
assert_eq!(a.height, 1080);
|
||||
assert_eq!(a.time_base_num, 1001);
|
||||
|
||||
// A valid POD is valid.
|
||||
assert_eq!(unsafe { oakengine_video_params_is_valid(&a) }, 1);
|
||||
// Zero dimensions are not.
|
||||
let mut bad = a;
|
||||
bad.width = 0;
|
||||
assert_eq!(unsafe { oakengine_video_params_is_valid(&bad) }, 0);
|
||||
// NULL is invalid.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_is_valid(std::ptr::null()) },
|
||||
0
|
||||
);
|
||||
|
||||
// Equality: identical PODs equal; differing field not.
|
||||
let mut b = a;
|
||||
assert_eq!(unsafe { oakengine_video_params_equal(&a, &b) }, 1);
|
||||
b.divider = 2;
|
||||
assert_eq!(unsafe { oakengine_video_params_equal(&a, &b) }, 0);
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_equal(std::ptr::null(), &a) },
|
||||
0
|
||||
);
|
||||
|
||||
// Effective size halves at divider 2.
|
||||
let (mut w, mut h) = (0, 0);
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_effective_size(1920, 1080, 2, &mut w, &mut h) },
|
||||
0
|
||||
);
|
||||
assert_eq!((w, h), (960, 540));
|
||||
// Invalid divider.
|
||||
assert_eq!(
|
||||
unsafe { oakengine_video_params_effective_size(1920, 1080, 0, &mut w, &mut h) },
|
||||
-1
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user