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:
Generated
-24
@@ -4823,30 +4823,6 @@ dependencies = [
|
||||
name = "oakcore-rs"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "oakengine"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"oakaudio",
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakplugin",
|
||||
"oakrender",
|
||||
"oakstorage",
|
||||
"oaktask",
|
||||
"oaktimeline",
|
||||
"oakundo",
|
||||
"parking_lot",
|
||||
"sea-orm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.20",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakffmpeg-link"
|
||||
version = "0.1.0"
|
||||
|
||||
+6
-9
@@ -26,20 +26,17 @@
|
||||
# workspace root, exactly as before the monorepo workspace existed.
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
exclude = ["gpui"]
|
||||
exclude = ["gpui", "crates/oakengine.bk"]
|
||||
# NOTE: oakstorage (crates/oakstorage) is a workspace member but NOT a
|
||||
# default member (it stays out of the default-members test matrix to keep
|
||||
# `cargo test` at the root fast; the app links it as a normal path
|
||||
# dependency, so it builds with the app). Build/test it explicitly with
|
||||
# `cargo test -p oakstorage`.
|
||||
# NOTE: `crates/oakengine` is deliberately NOT a default member (it stays a
|
||||
# workspace member, so `cargo test -p oakengine` / `cargo test --workspace`
|
||||
# still run its tests): it is the plugin/external-consumer cdylib (M14 R4) —
|
||||
# no crate in the workspace links it (app/cli/worker call the module rlibs
|
||||
# directly), so default builds skip it. Its in-crate unit tests
|
||||
# (src/test_support/, the former tests/*.rs) share temp files and
|
||||
# process-global facade state, which makes the parallel default-members run
|
||||
# flaky.
|
||||
# NOTE: `crates/oakengine` (the frozen C-ABI facade cdylib) is retired:
|
||||
# every consumer (app/cli/worker/plugins) links the module rlibs
|
||||
# directly, so nothing in the workspace referenced it. The sources are
|
||||
# kept at crates/oakengine.bk (excluded from the workspace) as a
|
||||
# reference snapshot; git history is the authoritative backup.
|
||||
default-members = [".", "crates/oak-cli", "crates/oak-worker"]
|
||||
resolver = "2"
|
||||
|
||||
|
||||
@@ -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};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user