refactor(engine): single-lib phase 1 — direct Rust calls across the facade

- unify CHandle in oakcore-rs (all crates re-export it)
- facade bridge: 618/624 externs converted to direct module calls
- signature-drift fixes surfaced by direct calls (videoparams,
  seconds, sync estimate, POD unification)
- dead node<->render call sites removed; cancelatom -> oakcommon
- node bridge/undo -> oakundo direct calls
- docs/zh/plans/riir/single-lib.md: design + live status
This commit is contained in:
2026-08-10 17:48:13 +08:00
parent f0d551d4a8
commit e1423553c7
49 changed files with 5636 additions and 2282 deletions
+400
View File
@@ -0,0 +1,400 @@
# 单库化(single-lib)· 模块间调用改回直接 Rust 调用
> 2026-08-10。本计划是 RIIR 拆分计划的**反向修正**:模块拆分阶段为了
> 让各 crate 独立链接,把模块间调用压成了 C ABI`bridge/` + dlsym /
> link-time extern)。现在模块已经全部 Rust 化,owner 要求模块间调用
> **改回直接 Rust 调用**(一个 `liboakengine` dylib 内联全部模块;
> unsafe 只允许留在 OFX 插件边界、引擎对外 C API、以及 shm/wgpu/libc
> 等不可避免的位置)。
>
> 约束(本次任务硬性要求):
> 1. **不改变** `oakengine_*` 外部签名与各模块 C ABI 签名
> `app`/`worker`/`cli` 继续工作;`src/plugin` 的 C-ABI 冻结)。
> 2. **不触碰** OFX 插件边界(oakplugin 的 suites 保持 C)。
> 3. 每个 crate 的 `cargo test`、`src/engine/rust` 的 `cargo test`
> 全程保持绿(每步闭环)。
> 4. Oak GPL 头、tab 缩进、不做 git 变更。
## 0. 目标与判据
终态:
```
liboakengine(一个 dylibcrate 图是 DAG
├── oakcore-rs ← 共享 ABI 类型(CHandle + POD),无依赖的叶子
├── oakcommon → oakcore-rs
├── oakundo → oakcore-rs
├── oaknode → oakcore-rs, oakcommon, oakundo, oakcodec
├── oaktimeline → oakcore-rs, oakundo, oaknode
├── oakcodec → oakcore-rs, oakrender codec→render 直接调)
├── oakaudio → oakcore-rs, oakcommon, oakcodec
├── oakrender → oakcore-rs, oakcommon, oaknode render→node 直接调)
├── oaktask → oakcore-rs, oakcommon, oakundo, oakcodec, oaknode, oakrender
├── oakplugin → oakcore-rs, oakcommon, oakundo, oaknode, oakrendersuites 仍 C
└── oakengine → 全部(facade
```
完成判据(每条命令可验证):
1. 模块间、facade→模块的调用 **100% 是编译期 Rust 函数调用**
`grep -rn "extern \"C\"" --include="*.rs" src/{undo,common,timeline,codec,audio,render,task,plugin,node}/rust/src/bridge/` = 0
`grep -rn "dlsym" --include="*.rs"` 只剩 facade 的 `linkage.rs`/`tests/` 注释与
OFX hostoakplugin suites)里的必要解析。
2. 模块 C ABI`oakundo_*``oaknode_*`)与 `oakengine_*` 依旧从
`liboakengine` dylib 导出(`nm -g liboakengine.dylib` 抽查)——
外部的 `oaknode_project_init` 等签名一行不改。
3. 每个 crate `cargo build` 0 error;每个 crate `cargo test` 绿;
`src/engine/rust` `cargo test` 绿。
4. unsafe 面收敛:模块 src 中,除
`{ffmpeg, ocio, wgpu, libc, shm} → unsafe` 包裹与各自 `ffi.rs` 导出外,
无残留;`bridge/` 目录从模块 crate 中消失(或只余测试桩,见 §7)。
## 1. 现状盘点(2026-08-10 实测)
### 1.1 crate 依赖现状
模块 crate 之间**没有 path 依赖**;跨模块调用走各自的 `bridge/`
| crate | 跨界机制 | bridge/ 规模(行) | dlsym::call 数 |
|---|---|---|---|
| oaknode | dlsym(RTLD_DEFAULT) | codec 1270 / common 50340 / core 5298 / render 5134 / timeline 2161 / undo 12964 | 59 |
| oakrender | dlsym | codec 6904 / common 7419 / node 5750 | 20 |
| oakplugin | dlsym | node 12124 / render 30454 / undo 10943 | 28 |
| oaktask | extern "C"link-time | codec 9909 / common 5980 / node 26044 / render 12340 / timeline 1291 / undo 2528 | 0 |
| oakcodec | extern "C" | common 15657 / render 7262+test_stubs 27176 | 0 |
| oakaudio | extern "C" | codec 6683 / common 1665 / ffmpeg 7812 | 0 |
| oaktimeline | extern "C" | common / node 11742 / undo 2780+teststubs 52921 | 0 |
| oakundo / oakcommon | — | 无 bridge | — |
facade `src/engine/rust/src/bridge/`9 个文件 2466 行、**624 个
`extern "C"` 导入**node 348 / common 91 / render 36 / timeline 34 /
task 33 / audio 31 / codec 24 / undo 22 / plugin 5),由
`linkage.rs` 锚定 13 个 `#[no_mangle]` 符号把模块 rlib 拉进 dylib。
### 1.2 真实调用方向(生产代码,不含 bridge 定义与测试桩)
按 "`crate::bridge::<b>``<a>` 生产代码中的出现" 统计(类型别名引用
与文档注释已剔除,函数调用计数):
| 方向 | 生产调用 | 说明 |
|---|---|---|
| node → render | **3** | `disk_cache_path`(project.rs)、`color_config_create_default`/`color_config_load`(colormanager.rs);其余是 `TextureHandle`/`ColorProcessorHandle`/`CacheHandle` 等**不透明 CHandle 类型别名** |
| render → node | **6** | `copier.rs``project_deep_copy` / `project_sync_copy` / `ChangeRecord` / `node_abi_available` |
| node → codec | 1 | — |
| node → timeline | **0** | bridge 是死代码(仅测试用) |
| timeline → node | 7 | — |
| node → undo | 15 | — |
| node → common | 21 | — |
| codec → render | 8 | cancelatom`heard_cancel` 生产、`init`/`cancel` 测试)+ `OakCancelAtom`/`OakRenderTexture` 类型 |
| render → codec | **1** | `ffi.rs:931``codec_abi_available()`,位于必然 Err 的 deferred stub 路径 |
| task → node/render/codec | 1 / 6 / 3 | timeline / undo 为 0 |
| plugin → node/render/undo | 14 / 38 / 13 | suites 层保持 C(冻结) |
| audio → codec/common | 12 / 4 | — |
| timeline → undo | 5 | — |
| **facade → 各模块** | **393 处调用** | 见 §5 |
### 1.3 环(Rust crate 禁止循环 path 依赖)
| 环 | 边 | 处理 |
|---|---|---|
| **node ↔ render** | node→render 3 fn + render→node copier | §4.1render→node 保留直接依赖;node→render 的 3 个函数下沉 oakcommon(实现本就在 oakcommon 侧:`default_disk_cache_path` 已是 render/bridge/common.rs 内的纯 Rust 实现;OCIO config 由 oakcommon `ocioutils::OcioConfig` 提供) |
| **codec ↔ render** | codec→render cancelatom + render→codec stub | §4.2cancelatom 下沉 oakcommon(原子取消旗标,纯 Rust);render→codec 的 stub 检查直接删除(路径必然 Err) |
其余方向均无环:node→timeline 0 调用、task→* 单向、plugin→* 单向、
audio→* 单向、timeline→node/undo 单向。
## 2. 目标架构:直接 Rust 调用 + 单一 dylib
### 2.1 公共面分层(每个模块 crate)
每个模块 crate 的公共面分两层,对应"机械落地"与"类型化"两档:
- **Layer 1(本次任务落地):`ffi` 即公共面,调用方直接 Rust 调用。**
模块 C ABI 函数是 `pub unsafe extern "C" fn``#[no_mangle]` 保持导出)。
引擎内部调用不再走 `extern "C"` 导入、不走 dlsym,而是
`oaknode::ffi::project::oaknode_project_init()` 这样的**普通 Rust
函数调用**——编译期类型检查、链接期直接解析到同 dylib 内的 rlib。
调用方在**包装层**(各 crate 的 bridge 改为安全包装 fn)一次性
`unsafe {}` 包住,业务代码保持 safe。
- **Layer 2(增量、后续):类型化 API。** 各 crate 已有的非 ffi 模块
`project::Project``undostack::UndoStack``graph::Graph`
`marker::Marker``manager::Manager`…)即"types, not CHandle"方向;
目前只覆盖部分操作,其余操作仍实现于 `ffi.rs`CHandle 上)。新代码
优先写类型层;把 `ffi.rs` 里的逻辑逐步提取成类型方法属于后续增量,
**不在本任务范围内**(本任务只改调用机制,不改实现)。
### 2.2 类型同一性:共享 ABI 类型下沉 oakcore-rs(本任务的地基)
直接 Rust 调用要求调用方与被调方**类型同一**。现状是每个 crate 各有一份
layout 相同但 Rust 类型不同的 `CHandle`、POD`OakVideoParams`
`OakAudioParams``OakRational``OakNodeValue`…)。必须统一:
- `oakcore-rs` 新增(或接收):
- `handle::CHandle``{ctx, addref, release, abi_version}``#[repr(C)]`
`null()`/`is_null()``Send + Sync``abi_version` 不校验,
只作标签——统一后 `null()` 盖 0,各 crate 的 `make_owned` 仍盖各自
`OAKXXX_ABI_VERSION`);
- 跨界 POD`params::OakVideoParams``params::OakAudioParams`
`rational::Rational`(已有)、`value::OakNodeValue` 等。
- 各模块 crate`pub use oakcore_rs::handle::CHandle;``handle.rs` 保留
`RefBox`/`make_owned`/`guard*`/ABI 版本常量);POD 同理 re-export。
各 crate 的 `ffi.rs` **签名不改**(类型名不变,只是指向共享类型,
C ABI 层面 layout 不变)。
- facade`pub use oakcore_rs::handle::CHandle;``OakVideoParamsPod`
改为 `pub type OakVideoParamsPod = oakcore_rs::params::OakVideoParams;`
> 这是"机械"的底气:统一后 `oaknode::ffi::project::oaknode_project_init()`
> 的返回类型就是 facade `OakEngineProject.handle` 的类型,调用点
> **一行不用改**。§7 会说明 facade `bridge/` 保留同名安全包装,因此
> facade 21k 行业务代码零改动。
## 3. 每 crate 的 bridge/ 去向
原则:**生产代码不再经过任何 `bridge/`bridge/ 从生产路径删除。**
| crate | 处理 |
|---|---|
| facade `engine/rust/src/bridge/` | §5`extern "C"` 块 → 直接调用模块 crate `ffi` 的安全包装(同名同签名) |
| oaknode `bridge/{undo,common,codec}` | 直接调用 oakundo/oakcommon/oakcodec 的 ffi`bridge/` 删除 |
| oaknode `bridge/{render,timeline,core}` | render/timeline 见 §4(环/死代码);core → oakcore-rs / 宿主 liboakcore(保持 link-time extern,见 §4.3 |
| oaktask `bridge/*` | 直接调用各目标 crate ffi`bridge/` 删除 |
| oakrender `bridge/node` | 直接调用 oaknode ffi(§4.1 |
| oakrender `bridge/{codec,common}` | codec → §4.2stub 删除);common → 直接调用 oakcommon |
| oakplugin `bridge/{node,render,undo}` | 直接调用各目标 crate ffisuites 保持 C |
| oakcodec `bridge/{common,render}` | common → oakcommonrender → oakrendercancelatom 移 oakcommon 后改调 oakcommon,见 §4.2 |
| oakaudio `bridge/{codec,common,ffmpeg}` | codec/common → 直接调用;ffmpeg → ffmpeg_bridgeC++,保持 extern "C" |
| oaktimeline `bridge/{node,undo,common}` | node/undo → 直接调用;common 0 调用 → 删除;`teststubs.rs` 见 §7 |
**同名 `#[no_mangle]` 冲突**:两个 crate 导出同名 `#[no_mangle]` 符号
在单个 dylib 里本来就不允许(重复符号)。现状不会冲突(每个 C ABI 符号
唯一归属一个 crate)。内部调用改成直接 Rust 调用后,**外部 C ABI 符号
原样保留**`oaknode_*` 仍由 oaknode crate 导出,供 `app`/`worker`/
`cli` 与各 crate 的 C ABI 测试使用);内部调用**绕过**这些符号,走
`pub` Rust fn。任何"同符号双实现"都不引入——机制上天然避免。
## 4. 依赖环处理
### 4.1 node ↔ render(实施结论:两侧调用均为死代码,删除即破环)
实施时发现:**两侧的跨模块调用都是死代码**——`oaknode` 的 dlsym 目标
`oakrender_color_config_*`)与 `oakrender` 的 dlsym 目标
`oaknode_project_deep_copy`/`project_sync_copy`**在对方 crate 中从未
实现**2026-08-10 全量 grep 确认),所以运行时必然 dlsym 失败,走
"缺失"分支。破环 = 删除死调用:
1. `disk_cache_path()`:实现**下沉 oakcommon**(新增
`filefunctions::default_disk_cache_path()`,基于既有的
`FileFunctions::get_configuration_location()`)。oaknode
`project.rs` 与 oakrender `manager.rs` 都直接调 oakcommon。
2. `color_config_create_default()`/`color_config_load()`oakrender 从未
实现这两个符号 → node `colormanager.rs` 的调用恒为 `None`(走
"标记已加载/保持原状" 分支)。删除调用、保留确定性等价逻辑(行为不变)。
3. render 的 `copier.rs``project_deep_copy` 等)同样恒失败且无生产
调用方——保持现状(dlsym 运行时失败),不引入 crate 依赖。
**类型别名**node 里存的 `TextureHandle`/`ColorProcessorHandle`/
`CacheHandle` 本就是 `pub type X = CHandle`(统一后 =
`oakcore_rs::handle::CHandle`),零依赖。
结果:node 与 render 之间无任何 crate 依赖、无任何有效调用,环消失。
### 4.2 codec ↔ render(实施结论:cancelatom 保持 C 边界,render 的 stub 删除)
- **render → codec**`ffi.rs:931``codec_abi_available()` 位于
deferred stub`Err(...)` 恒返回),**已删除**(保留原 Err 文案)。
render→codec = 0。
- **codec → render**:只有 cancelatom。实施时发现 codec 收到的 atom 句柄
是 oakrender `make_owned` 按 **oakrender 的 `RefBox` 布局**装箱的,
codec 直接解引用会在不同 crate 的 `RefBox` 布局间读内存(oakcommon
value-first、其余 refs-first),**不安全**。因此 cancelatom 的读取保持
经 oakrender 的 C 导出(codec `bridge::render` 的 link-time extern
`oakcore_*`/`fb_*` 同类,属"不可避免的 C 边界");实现体下沉
oakcommon`cancelatom::CancelAtom`oakrender 的
`oakrender_cancelatom_*` 导出改包 oakcommon`render/cancelatom.rs`
变为 re-export)。
结果:codec 与 render 之间无 crate 依赖;codec→render 仅剩 cancelatom
一个 link-time C 边界。
### 4.3 oakcore_*(宿主 liboakcore)与 fb_*ffmpeg_bridge
`oakcore_audioparams_*` / `oakcore_rational_*`(宿主 C++ liboakcore)与
`fb_*`ffmpeg_bridge C++**不是 Rust crate**,无法变成直接 Rust 调用:
保持现状(dylib 链接期 runtime lookup`build.rs`
`-Wl,-undefined,dynamic_lookup`)。`bridge/core.rs` 之类保留为 link-time
extern(或就地声明),属于"不可避免的 C 边界"。
## 5. facade bridge → 直接调用(步骤 a,最大但机械)
`src/engine/rust/src/bridge/*.rs``extern "C" { ... }` 导入改为
**同名同签名的安全包装 fn**
```rust
// src/bridge/node.rs(改写后)
use oakcore_rs::handle::CHandle;
/// `oaknode_project_init` — 直接调用 oaknode crate(同 dylib 内 rlib)。
pub fn oaknode_project_init() -> CHandle {
unsafe { oaknode::ffi::project::oaknode_project_init() }
}
```
- 每个 ffi fn 的路径:`oak<mod>::ffi::<子模块>::<fn>`(如
`oaknode::ffi::project``oaknode::ffi::folder``oakcommon::ffi::config`…);
实现时以模块 crate 实际 `pub mod` 布局为准(已核对:oaknode ffi 有
`project/node/keyframe/…` 子模块)。
- **facade 业务代码(node.rs/timeline.rs/… 共 393 处调用)零改动**:
它们只 `use crate::bridge::node as n;` 然后 `n::oaknode_project_init()`
包装 fn 同名同签名,链接目标从"extern 导入"变成"直接调用"。
- unsafe 收口在包装层:模块 ffi 是 `pub unsafe extern "C" fn`,包装层
一处 `unsafe {}`。facade 其余代码保持 safe(现存的散落 `unsafe {}`
调用点一并消除)。
- `linkage.rs` 保留(锚定符号导出,供外部 C ABI),注释更新。
- 现 facade 里 `crate::bridge::common` 的 POD 镜像(`OakVideoParamsPod`
改为 `type` 别名共享类型(§2.2)。
## 6. 模块 bridge → 直接调用(步骤 b)
逐方向(每步一个 crate 一个方向,`cargo build`+`cargo test` 闭环):
1. oaknode`bridge/undo.rs``bridge/common.rs``bridge/codec.rs`
(→ oakundo/oakcommon/oakcodec 直接调,同 §5 的包装写法);
2. oaktimeline`bridge/node.rs``bridge/undo.rs`(→ oaknode/oakundo);
3. oakrender`bridge/node.rs`(→ oaknode,§4.1)、`bridge/common.rs`
(→ oakcommon);
4. oaktask`bridge/{node,render,codec,undo,common}.rs`
5. oakplugin`bridge/{node,render,undo}.rs`suites 不动);
6. oakcodec`bridge/common.rs`(→ oakcommon)、`bridge/render.rs`
(→ oakcommon cancelatom,§4.2);
7. oakaudio`bridge/{codec,common}.rs`
8. oaknode`bridge/render.rs`(→ oakcommon,§4.1)、`bridge/timeline.rs`
(死代码删除,测试迁移见 §7)、`bridge/core.rs`(§4.3 保留 extern)。
被调方需要被加为 path 依赖的 crateoaknode 加 `oakundo`/`oakcommon`/
`oakcodec`oaktimeline 加 `oakundo`/`oaknode`oakrender 加
`oakcommon`/`oaknode`oaktask 加 `oakundo`/`oakcommon`/`oakcodec`/
`oaknode`/`oakrender`oakplugin 加 `oakcore-rs`/`oakcommon`/`oakundo`/
`oaknode`/`oakrender`oakcodec 加 `oakcommon`oakaudio 加
`oakcommon`/`oakcodec`。**不产生任何环**(§1.3 已消除)。
## 7. 测试策略(每 crate 测试保持绿)
- **每 crate 测试沿用 C ABI 测试为主**(`tests/*.rs` 走 crate 自己的
`oak<mod>_*` 导出,`ffi.rs` 不变),**内部 Rust API 测试增量补充**。
- **用 `bridge/` 的测试必须迁移**(桥层从生产路径删除后不能留在测试里):
- 目标 crate 已 path 依赖的:`oaknode::bridge::undo::…`
`oakundo::ffi::…`(或 oakundo 类型层)直接调;`bridge::common::videoparams_*`
`oakcommon::…``bridge::core::audioparams_*``oakcore_rs::…`
/ 宿主 liboakcore extern
- 依赖不存在(环/死代码)的:`node::bridge::timeline` 的测试用
oaktimeline 自身导出或改写;`node::bridge::render` 的测试改调
oakcommon 下沉后的函数;
- 专门测 dlsym 机制的测试(如 `node/rust/tests/dbg2_test.rs`
`bridge::dlsym::resolve`)随机制删除而删除。
- **`test-stubs` featureoakplugin/oakcommon/oaktimeline/oaknode**
语义变为"编译测试桩 C ABI(替代尚未存在的宿主/兄弟模块实现)"。
直接调用落地后,同一二进制内不再同时出现"真实现 + 桩"冲突:
facade `cargo test` 的 dev-dependency feature union 按新依赖图调整
(凡直接依赖的 crate 用真实现,不再叠 test-stubs)。
- facade `tests/`10 个集成测试)走 `oakengine_*` C ABI,不受影响;
`tests/common/mod.rs``force_link` 更新锚点。
## 8. unsafe 清单(before / after
| 位置 | 现状(实测计数) | 之后 |
|---|---|---|
| 模块 `bridge/`dlsym + extern 包装) | node 298 / plugin 167 / timeline 137 / render 45 / codec 50 / task 15 处 `unsafe` 行 | **删除**(或仅测试桩) |
| 模块 `ffi.rs` 导出(`unsafe extern "C"`,内含句柄解包) | 各 crate 均有 | **保留**(= 模块 C ABI,外部契约;内部调用在包装层包 unsafe) |
| 模块业务代码中散落的 `unsafe` | node ~1020 / plugin ~476 / codec ~533 / render ~277 / task ~306 / timeline ~271 / audio ~165 / common ~230 / undo ~80 行 | 只保留**包裹外部库**ffmpeg/ocio/wgpu/libc)的调用与必要的句柄解包;bridge 调用产生的 unsafe 随桥层消失 |
| oakplugin suitesOFX host | C 套件 trampoline | **保持 C**(冻结,任务红线) |
| facade `engine/rust/src` | 1642 行 `unsafe`(含 ipc/shm 1120+ 行、worker、handle guard、桥层 10 行) | 桥层包装 unsafe 保留;`handle.rs` guard/unbox、`ipc.rs` shm、`worker.rs` 保留(外部 C API 与 shm,属允许项) |
| `linkage.rs` / `build.rs` | — | 保留(符号锚定 + `-Wl,-undefined,dynamic_lookup`,宿主 C++ 符号必需) |
判定:模块 crate 中"非外部库包裹"的 unsafe 清零;facade 中 unsafe 只留
{导出 guard、shm/ipc、worker 会话}。
## 9. 实施顺序(一次一个边界,每步构建+测试闭环)
1. **地基**`oakcore-rs` 新增 `handle::CHandle`;各 crate
`handle.rs` 改 re-exportfacade 改 re-export。逐 crate 验证
`cargo build` + `cargo test`(这一步后直接调用才类型同一)。
2. **facade bridge → 直接调用**(§5,步骤 a)。
3. **环处理**(§4):删除死调用;disk_cache_path/cancelatom 下沉
oakcommonrender 的 `codec_abi_available()` stub 检查删除。
4. **模块 bridge 逐方向 → 直接调用**(§6,步骤 b)。
5. **dlsym 删除**(步骤 c):node/render/plugin 的 `bridge/mod.rs::dlsym`
与残留调用清除;`linkage.rs` 注释更新。
6. **测试迁移**(§7)与全量验证:每 crate `cargo test` +
`src/engine/rust` `cargo test` + 抽查 dylib 导出。
## 11. 实施状态(2026-08-10 实测)
已落地(全部构建 0 error、全量 `cargo test` 绿):
- **地基**`oakcore_rs::handle::CHandle``Clone+Copy+Debug+Default`
`Send+Sync``null()` 盖 0)。10 个 crate + facade 全部 re-export
各 crate 的 `impl CHandle``unsafe impl Send/Sync` 删除。受影响断言
common/codec/audio/task 的 null-handle 版本/函数指针测试)已同步更新。
- **facade bridge**(步骤 a):9 个文件 624 个 `extern "C"` 导入中
**618 个改为直接 Rust 调用**`unsafe { oak<mod>::ffi::<sub>::<fn>(...) }`
安全包装,名字签名不变,facade 业务代码 393 处调用点零改动)。
保留 8 个 link-time extern`oakcore_audioparams_*` ×5(宿主 C++)、
`oakaudio_manager_start_recording`/`oakcodec_encoder_init`/
`oaktask_create_export`encoding-params C ABI PODfacade 保留自己的
POD 镜像,见 §11.1)。
- **签名漂移修复**(直接调用把 facade 旧 extern 声明与模块真实签名之间的
漂移暴露为编译错误):`videoparams_init_basic` 4→8 参、
`init_with_time_base` 4→10 参、`get_bytes_per_channel_for_format`
2→1 参、`oakaudio_manager_seconds` 1→2 参、
`oakaudio_sync_estimate_stretch_and_offset` 9→12 参(facade 导出现在
把引擎的 `min_rate/max_rate/rate_step` 透传给模块,消除了原先
"模块自搜默认范围"的 documented deviation)、`get_buffer_size`/
`get_time_in_timebase_units`/`get_bytes_per_pixel_for_format` 参数
类型修正。POD 统一:`OakNodeValue``oaknode::value::OakNodeValue`
`type_` 字段改名 `kind`,1 读 3 构 + 1 测试更新)、
`OakUndoCommandVtable`/`OakVideoTicketParams`/`OakRenderVideoParams`/
`OffsetResult`/`StretchOffsetResult`/`SourceClip` → 各模块 crate 类型。
- **环处理**(§4 实施结论):node↔render 死调用删除;render→codec stub
删除;`oakcommon::{filefunctions::default_disk_cache_path, cancelatom}`
render 的 `cancelatom.rs` 改为 re-export。
- **模块 bridge**(步骤 b,模式已证明):oaknode `bridge/undo.rs`
→ oakundo 直接调用(oakundo 加入 node 依赖;bridge 函数名/签名不变,
node 生产代码与测试零改动,全部绿)。
### 11.1 保留的 C 边界(facade bridge 中 8 个 extern 声明)
encoding-params POD`oakcodec_encoding_params` 等)在 4 个 crate 各有
镜像(codec/audio/task/facade),字段仅 `[c_char;N]` vs `[u8;N]` 差异,
统一到 oakcore-rs 属后续增量(§5 已注明);在此之前,跨越该 POD 的 3 个
函数与宿主 `oakcore_*` 保持 link-time extern,与 `fb_*` 同类。
### 11.2 未完成(后续步骤)
- **模块 bridge 其余方向**(步骤 b 剩余):oaknode `bridge/{common,codec,
core,render,timeline}`、oaktimeline `bridge/{node,undo,common}`、
oakcodec `bridge/{common,render}`、oakaudio `bridge/{codec,common,ffmpeg}`、
oakrender `bridge/{codec,common,node}`、oaktask `bridge/*`、
oakplugin `bridge/*`。每个方向模式与已完成的 node→oakundo 相同
(桥函数名不变、函数体改直接调用、目标 crate 加 path 依赖);但
各 crate 的 `tests/` 大量直接使用 `bridge::*`timeline 16 个文件、
task 7 个、plugin 8 个、node 6 个),且有 `test-stubs`/`teststubs`
桩与真实 crate 的 `#[no_mangle]` 冲突问题——每方向需同步迁移测试并
删除对应桩,工作量集中在测试侧。
- **dlsym 删除**(步骤 c):node/render/plugin 的 `bridge/mod.rs::dlsym`
与残留调用(node 52 处、render 22 处、plugin 28 处)在各自 bridge
转换完成后清除;`dbg2_test.rs` 等专测 dlsym 的测试随之删除。
- **facade 侧后续**`linkage.rs` 的 13 个锚定符号注释更新(锚定仍需要);
EncodingParamsPOD 等 POD 统一到 oakcore-rs。
## 10. 风险与回退
- **共享类型统一**是全局改动:改完一个 crate 编译它,逐 crate 推进;
若某 crate 的 `CHandle` 有独有 impl 方法,移入共享 impl 或改为扩展 fn。
`abi_version` 统一为 0 标签后,逐 crate 测试确认无断言依赖。
- **模块 ffi fn 路径**`oaknode::ffi::project` 等)以实际 `pub mod`
布局为准;包装层用编译错误驱动修正路径。
- **宿主符号**`oakcore_*`/`fb_*`)与 `build.rs` 的
`-Wl,-undefined,dynamic_lookup` 不动;这些不是 Rust crate,是允许保留
的 C 边界。
- **行为零变更**:本计划只改调用机制(extern/dlsym → Rust fn),不改
实现;任何"顺带重构"禁止。回退 = 保留 `bridge/` 目录(生产已不引用),
不影响外部契约。
+7 -41
View File
@@ -54,47 +54,13 @@ pub struct RefBox<T: ?Sized> {
pub value: T,
}
/// `#[repr(C)]` mirror of the public handle structs
/// (`{ctx, addref, release, abi_version}`).
///
/// Handles cross the C ABI by value (the C caller copies the 4-field
/// struct), so the type is `Copy`/`Clone` — the oaknode crate only derives
/// `Clone`, but `Copy` matches the C semantics exactly and lets exports and
/// tests pass the same handle around freely. No `Drop`: releasing goes
/// through the explicit `free_*`/`release` path, never on scope exit.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
// Handles are passed by value across the C ABI and used from the caller's
// thread; the objects behind them synchronize their own state.
unsafe impl Send for CHandle {}
impl CHandle {
/// The empty handle.
pub fn null() -> Self {
CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: OAKAUDIO_ABI_VERSION,
}
}
/// True when the handle carries no object.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible. It is `Clone + Copy` (handles cross the C ABI by
/// value) and `Send + Sync` (refcounted, shared across threads).
pub use oakcore_rs::handle::CHandle;
/// `// CPP-PARITY: src/audio/c_api/refcounted.h` (`ref_counted_addref`).
unsafe extern "C" fn owned_addref<T: Send + 'static>(ctx: *mut std::ffi::c_void) {
+2 -1
View File
@@ -93,7 +93,8 @@ fn manager_singleton_and_alive_count() {
unsafe { oakaudio_manager_destroy_instance() };
let none = unsafe { oakaudio_manager_instance() };
assert!(none.ctx.is_null());
assert_eq!(none.abi_version, oakaudio::handle::OAKAUDIO_ABI_VERSION);
// The empty instance handle is the shared `null()` (no ABI version).
assert_eq!(none.abi_version, 0);
unsafe { oakaudio_manager_create_instance() };
let m1 = unsafe { oakaudio_manager_instance() };
+2 -1
View File
@@ -79,7 +79,8 @@ fn borrowed_release() {
fn null_and_guard_ok() {
let null = CHandle::null();
assert!(null.is_null());
assert_eq!(null.abi_version, oakaudio::handle::OAKAUDIO_ABI_VERSION);
// The shared `null()` stamps no ABI version (single-lib unification).
assert_eq!(null.abi_version, 0);
assert_eq!(guard(|| Ok(())), OAKAUDIO_OK);
+9 -33
View File
@@ -46,38 +46,12 @@ pub struct RefBox<T: ?Sized> {
/// `#[repr(C)]` mirror of the public handle structs
/// (`{ctx, addref, release, abi_version}`).
///
/// Handles are `Copy`: passing one by value copies the struct, not the
/// reference count — exactly the by-value convention the C ABI documents.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
impl CHandle {
/// The empty handle.
pub fn null() -> Self {
CHandle {
ctx: ptr::null_mut(),
addref: None,
release: None,
abi_version: OAKCODEC_ABI_VERSION,
}
}
/// Whether this is an empty (null) handle.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible.
pub use oakcore_rs::handle::CHandle;
/// Increment the reference count of a boxed `RefBox<T>`.
///
@@ -293,6 +267,8 @@ mod tests {
fn null_handle_helpers() {
let h = CHandle::null();
assert!(h.is_null());
assert_eq!(h.abi_version, OAKCODEC_ABI_VERSION);
// The shared `null()` stamps no ABI version (single-lib
// unification; `make_owned` stamps the crate version).
assert_eq!(h.abi_version, 0);
}
}
+98
View File
@@ -0,0 +1,98 @@
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// 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 cancellation primitive (`olive::CancelAtom`): a thread-safe cancel
//! flag shared between a render/encode caller and its worker.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): moved
//! here from the oakrender crate so the oakcodec crate can use it without
//! depending on oakrender (the oaknode/oakcodec/oakrender call cycle).
use std::sync::{Mutex, MutexGuard};
/// A cancellation atom (C++ `CancelAtom`). Reading a set flag also records
/// that a consumer heard the cancellation.
#[derive(Default)]
pub struct CancelAtom {
state: Mutex<AtomState>,
}
#[derive(Default)]
struct AtomState {
cancelled: bool,
heard: bool,
}
fn lock(m: &Mutex<AtomState>) -> MutexGuard<'_, AtomState> {
m.lock().unwrap_or_else(|e| e.into_inner())
}
impl CancelAtom {
/// A not-cancelled atom.
pub fn new() -> Self {
Self::default()
}
/// Set the cancel flag (C++ `CancelAtom::cancel()`).
pub fn cancel(&self) {
lock(&self.state).cancelled = true;
}
/// Read the cancel flag; reading a set flag records that the
/// cancellation was heard (C++ `is_cancelled()`).
pub fn is_cancelled(&self) -> bool {
let mut s = lock(&self.state);
if s.cancelled {
s.heard = true;
}
s.cancelled
}
/// Whether any consumer has observed the cancel flag through
/// [`CancelAtom::is_cancelled`] (C++ `heard_cancel()`).
pub fn heard_cancel(&self) -> bool {
lock(&self.state).heard
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_atom_is_not_cancelled() {
let a = CancelAtom::new();
assert!(!a.is_cancelled());
assert!(!a.heard_cancel());
}
#[test]
fn cancel_sets_flag_and_reading_marks_heard() {
let a = CancelAtom::new();
a.cancel();
assert!(!a.heard_cancel(), "not heard until read");
assert!(a.is_cancelled());
assert!(a.heard_cancel(), "reading a set flag marks it heard");
}
#[test]
fn repeated_cancel_is_idempotent() {
let a = CancelAtom::new();
a.cancel();
a.cancel();
assert!(a.is_cancelled());
}
}
+13
View File
@@ -920,3 +920,16 @@ mod tests {
let _ = std::fs::remove_dir_all(&b);
}
}
/// The default disk cache directory (C++ `DiskManager::
/// get_default_disk_cache_path`): `<configuration location>/mediacache`.
///
/// Single-lib unification: this used to live in the oakrender crate's
/// `bridge::common` fallback (see `docs/zh/plans/riir/single-lib.md`);
/// oaknode and oakrender both call it directly now.
pub fn default_disk_cache_path() -> String {
Path::new(&FileFunctions::new().get_configuration_location().unwrap_or_default())
.join("mediacache")
.to_string_lossy()
.into_owned()
}
+9 -31
View File
@@ -59,36 +59,12 @@ pub struct RefBox<T: Sized> {
pub refs: AtomicU32,
}
/// `#[repr(C)]` mirror of the public handle structs
/// (`{ctx, addref, release, abi_version}`).
#[repr(C)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
impl CHandle {
/// The empty handle.
pub fn null() -> Self {
Self {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: OAKCOMMON_ABI_VERSION,
}
}
/// Whether this is an empty handle (`ctx == NULL`).
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible.
pub use oakcore_rs::handle::CHandle;
/// addref thunk: atomically increments the count. Shared by owned and
/// borrowed boxes — for a borrowed handle addref only extends the life of
@@ -260,7 +236,9 @@ mod tests {
assert!(h.ctx.is_null());
assert!(h.addref.is_none());
assert!(h.release.is_none());
assert_eq!(h.abi_version, OAKCOMMON_ABI_VERSION);
// The shared `null()` stamps no ABI version (single-lib
// unification; `make_owned` stamps the crate version).
assert_eq!(h.abi_version, 0);
}
#[test]
+1
View File
@@ -23,6 +23,7 @@
pub mod colortransform;
pub mod commandlineparser;
pub mod cancelatom;
pub mod configstore;
pub mod debug;
pub mod error;
+4 -2
View File
@@ -126,7 +126,8 @@ fn init_output_null_yields_null_handle() {
assert!(h.ctx.is_null());
assert!(h.addref.is_none());
assert!(h.release.is_none());
assert_eq!(h.abi_version, OAKCOMMON_ABI_VERSION);
// The shared `null()` stamps no ABI version (single-lib unification).
assert_eq!(h.abi_version, 0);
}
/// An empty output name is a valid value: the transform is still an output
@@ -189,7 +190,8 @@ fn init_display_null_any_arg_yields_null_handle() {
assert!(h.ctx.is_null());
assert!(h.addref.is_none());
assert!(h.release.is_none());
assert_eq!(h.abi_version, OAKCOMMON_ABI_VERSION);
// The shared `null()` stamps no ABI version (single-lib unification).
assert_eq!(h.abi_version, 0);
}
}
+4
View File
@@ -806,7 +806,9 @@ dependencies = [
name = "oaknode"
version = "0.1.0"
dependencies = [
"oakcommon",
"oakcore-rs",
"oakundo",
]
[[package]]
@@ -824,12 +826,14 @@ name = "oakplugin"
version = "0.1.0"
dependencies = [
"cc",
"oakcore-rs",
]
[[package]]
name = "oakrender"
version = "0.1.0"
dependencies = [
"oakcommon",
"oakcore-rs",
"ocio-rs",
"wgpu",
+3 -4
View File
@@ -327,10 +327,6 @@ pub unsafe extern "C" fn oakengine_audio_estimate_stretch_and_offset(
confidence: 0.0,
valid: 0,
};
// The module's rate search is fixed-step; the engine's range
// parameters are not part of its C ABI (documented deviation:
// the module searches its own default range).
let _ = (min_rate, max_rate, rate_step);
Error::from_module(a::oakaudio_sync_estimate_stretch_and_offset(
reference,
reference_len,
@@ -340,6 +336,9 @@ pub unsafe extern "C" fn oakengine_audio_estimate_stretch_and_offset(
candidate_valid,
window_samples,
max_offset_windows,
min_rate,
max_rate,
rate_step,
&mut result,
))?;
(*out).rate = result.rate;
+277 -170
View File
@@ -14,6 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakaudio C ABI bridge: direct Rust calls into the `oakaudio` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oakaudio`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakaudio C ABI imports, mirroring the oakaudio crate's exports
//! (`src/audio/rust/src/ffi.rs`; headers `include/audio/*.h`), plus the
//! liboakcore `oakcore_audioparams_*` readers used to convert the
@@ -23,182 +50,21 @@ use std::ffi::{c_char, c_double, c_int, c_void};
use crate::handle::CHandle;
/// `oakaudio_sync_offset_result` mirror — `oak_audio_waveform_offset`.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OffsetResult {
/// Offset of the candidate relative to the reference, in samples.
pub offset_samples: i64,
/// Normalized correlation confidence in `[0, 1]`.
pub confidence: c_double,
/// Whether an offset could be determined.
pub valid: c_int,
}
/// `oakaudio_offsetresult` C ABI POD. Single-lib unification: aliases
/// the oakaudio crate's struct (identical layout).
pub type OffsetResult = oakaudio::ffi::sync::OffsetResult;
/// `oakaudio_stretch_offset_result` mirror — `oak_audio_waveform_stretch_offset`.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct StretchOffsetResult {
/// Playback rate aligning the candidate (`> 1` = speed up).
pub rate: c_double,
/// Offset in samples.
pub offset_samples: i64,
/// Normalized correlation confidence in `[0, 1]`.
pub confidence: c_double,
/// Whether a rate+offset could be determined.
pub valid: c_int,
}
/// `oakaudio_sync_source_clip` mirror — `oak_audio_sync_source_clip`.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SourceClip {
/// Source start time numerator (seconds).
pub source_start_time_num: i64,
/// Source start time denominator (seconds).
pub source_start_time_den: i64,
/// Media in point numerator (seconds).
pub media_in_num: i64,
/// Media in point denominator (seconds).
pub media_in_den: i64,
/// Whether `source_start_time` is set.
pub has_source_start_time: c_int,
}
/// `oakaudio_stretchoffsetresult` C ABI POD. Single-lib unification: aliases
/// the oakaudio crate's struct (identical layout).
pub type StretchOffsetResult = oakaudio::ffi::sync::StretchOffsetResult;
extern "C" {
// ---- manager.h --------------------------------------------------------
/// `oakaudio_manager_create_instance` — create the singleton.
pub fn oakaudio_manager_create_instance() -> c_int;
/// `oakaudio_manager_destroy_instance` — destroy the singleton.
pub fn oakaudio_manager_destroy_instance();
/// `oakaudio_manager_instance` — borrowed handle (empty when none).
pub fn oakaudio_manager_instance() -> CHandle;
/// `oakaudio_manager_free` — release a manager handle.
pub fn oakaudio_manager_free(_self: *mut CHandle);
/// `oakaudio_manager_set_output_notify_interval`.
pub fn oakaudio_manager_set_output_notify_interval(_self: CHandle, bytes: i64) -> c_int;
/// `oakaudio_manager_push_to_output`.
pub fn oakaudio_manager_push_to_output(
_self: CHandle,
rate: c_int,
layout: u64,
format: c_int,
samples: *const c_char,
samples_size: i64,
error_buf: *mut c_char,
error_buf_size: c_int,
) -> c_int;
/// `oakaudio_manager_clear_buffered_output`.
pub fn oakaudio_manager_clear_buffered_output(_self: CHandle) -> c_int;
/// `oakaudio_manager_stop_output`.
pub fn oakaudio_manager_stop_output(_self: CHandle) -> c_int;
/// `oakaudio_manager_reset_output_clock`.
pub fn oakaudio_manager_reset_output_clock(_self: CHandle) -> c_int;
/// `oakaudio_manager_get_output_device` — module returns the device as
/// `c_int`; the facade widens to the engine's int64_t.
pub fn oakaudio_manager_get_output_device(_self: CHandle) -> c_int;
/// `oakaudio_manager_set_output_device`.
pub fn oakaudio_manager_set_output_device(_self: CHandle, device: c_int) -> c_int;
/// `oakaudio_manager_get_input_device`.
pub fn oakaudio_manager_get_input_device(_self: CHandle) -> c_int;
/// `oakaudio_manager_set_input_device`.
pub fn oakaudio_manager_set_input_device(_self: CHandle, device: c_int) -> c_int;
/// `oakaudio_manager_hard_reset`.
pub fn oakaudio_manager_hard_reset(_self: CHandle) -> c_int;
/// `oakaudio_manager_start_recording`.
pub fn oakaudio_manager_start_recording(
_self: CHandle,
params: *const EncodingParams,
error_buf: *mut c_char,
error_buf_size: c_int,
) -> c_int;
/// `oakaudio_manager_stop_recording`.
pub fn oakaudio_manager_stop_recording(_self: CHandle) -> c_int;
/// `oakaudio_manager_seconds`.
pub fn oakaudio_manager_seconds(_self: CHandle) -> i64;
// ---- processor.h ------------------------------------------------------
/// `oakaudio_processor_init` — new processor, refcount 1.
pub fn oakaudio_processor_init() -> CHandle;
/// `oakaudio_processor_free` — NULL/empty no-op.
pub fn oakaudio_processor_free(_self: *mut CHandle);
/// `oakaudio_processor_open`.
pub fn oakaudio_processor_open(
_self: CHandle,
in_rate: c_int,
in_layout: u64,
in_format: c_int,
out_rate: c_int,
out_layout: u64,
out_format: c_int,
speed: c_double,
) -> c_int;
/// `oakaudio_processor_close`.
pub fn oakaudio_processor_close(_self: CHandle) -> c_int;
/// `oakaudio_processor_is_open`.
pub fn oakaudio_processor_is_open(_self: CHandle) -> c_int;
/// `oakaudio_sourceclip` C ABI POD. Single-lib unification: aliases
/// the oakaudio crate's struct (identical layout).
pub type SourceClip = oakaudio::ffi::sync::SourceClip;
// ---- sync.h -----------------------------------------------------------
/// `oakaudio_sync_estimate_envelope_offset`.
pub fn oakaudio_sync_estimate_envelope_offset(
reference: *const c_double,
reference_len: c_int,
candidate: *const c_double,
candidate_len: c_int,
reference_valid: *const u8,
candidate_valid: *const u8,
window_samples: u64,
max_offset_windows: i64,
out: *mut OffsetResult,
) -> c_int;
/// `oakaudio_sync_estimate_stretch_and_offset`.
pub fn oakaudio_sync_estimate_stretch_and_offset(
reference: *const c_double,
reference_len: c_int,
candidate: *const c_double,
candidate_len: c_int,
reference_valid: *const u8,
candidate_valid: *const u8,
window_samples: u64,
max_offset_windows: i64,
out: *mut StretchOffsetResult,
) -> c_int;
/// `oakaudio_sync_place_by_source_time`.
pub fn oakaudio_sync_place_by_source_time(
reference: *const SourceClip,
candidate: *const SourceClip,
reference_timeline_in_num: i64,
reference_timeline_in_den: i64,
out_num: *mut i64,
out_den: *mut i64,
out_valid: *mut c_int,
) -> c_int;
/// `oakaudio_sync_place_by_waveform_offset`.
pub fn oakaudio_sync_place_by_waveform_offset(
reference_timeline_in_num: i64,
reference_timeline_in_den: i64,
candidate_offset_samples: i64,
sample_rate: c_int,
out_num: *mut i64,
out_den: *mut i64,
out_valid: *mut c_int,
) -> c_int;
// ---- liboakcore (oakcore_audioparams_*) --------------------------------
/// `oakcore_audioparams_create` — new owned params (release with
/// [`oakcore_audioparams_free`]).
pub fn oakcore_audioparams_create(sample_rate: c_int, channel_layout: u64, format: c_int) -> *mut c_void;
/// `oakcore_audioparams_free` — release params created by
/// [`oakcore_audioparams_create`] (or returned by the oaknode sequence
/// audio-params getter).
pub fn oakcore_audioparams_free(params: *mut c_void);
/// `oakcore_audioparams_sample_rate` — borrowed params reader.
pub fn oakcore_audioparams_sample_rate(params: *const c_void) -> c_int;
/// `oakcore_audioparams_channel_layout` — borrowed params reader.
pub fn oakcore_audioparams_channel_layout(params: *const c_void) -> u64;
/// `oakcore_audioparams_format` — borrowed params reader.
pub fn oakcore_audioparams_format(params: *const c_void) -> c_int;
}
/// Opaque mirror of the oakaudio recording-params POD
/// (`include/audio/manager.h`). The facade passes the engine's
@@ -208,3 +74,244 @@ extern "C" {
pub struct EncodingParams {
_opaque: [u8; 0],
}
extern "C" {
pub fn oakcore_audioparams_create(sample_rate: c_int, channel_layout: u64, format: c_int) -> *mut c_void;
pub fn oakcore_audioparams_free(params: *mut c_void);
pub fn oakcore_audioparams_sample_rate(params: *const c_void) -> c_int;
pub fn oakcore_audioparams_channel_layout(params: *const c_void) -> u64;
pub fn oakcore_audioparams_format(params: *const c_void) -> c_int;
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_create_instance() -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_create_instance() }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_destroy_instance() {
unsafe { oakaudio::ffi::manager::oakaudio_manager_destroy_instance() }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_instance() -> CHandle {
unsafe { oakaudio::ffi::manager::oakaudio_manager_instance() }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_free(_self: *mut CHandle) {
unsafe { oakaudio::ffi::manager::oakaudio_manager_free(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_set_output_notify_interval(_self: CHandle, bytes: i64) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_set_output_notify_interval(_self, bytes) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_push_to_output(
_self: CHandle,
rate: c_int,
layout: u64,
format: c_int,
samples: *const c_char,
samples_size: i64,
error_buf: *mut c_char,
error_buf_size: c_int,
) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_push_to_output(_self, rate, layout, format, samples, samples_size, error_buf, error_buf_size) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_clear_buffered_output(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_clear_buffered_output(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_stop_output(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_stop_output(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_reset_output_clock(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_reset_output_clock(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_get_output_device(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_get_output_device(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_set_output_device(_self: CHandle, device: c_int) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_set_output_device(_self, device) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_get_input_device(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_get_input_device(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_set_input_device(_self: CHandle, device: c_int) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_set_input_device(_self, device) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_hard_reset(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_hard_reset(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
/// `oakaudio_manager_start_recording` — crosses the encoding-params C ABI
/// POD (the facade keeps its own opaque mirror; the module's
/// `oakaudio::bridge::codec::EncodingParams`). Kept as a link-time
/// `extern "C"` declaration against the frozen module C ABI.
pub fn oakaudio_manager_start_recording(
_self: CHandle,
params: *const EncodingParams,
error_buf: *mut c_char,
error_buf_size: c_int,
) -> c_int {
unsafe { oakaudio_start_recording_extern(_self, params, error_buf, error_buf_size) }
}
extern "C" {
#[link_name = "oakaudio_manager_start_recording"]
pub fn oakaudio_start_recording_extern(
_self: CHandle,
params: *const EncodingParams,
error_buf: *mut c_char,
error_buf_size: c_int,
) -> c_int;
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_stop_recording(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_stop_recording(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_manager_seconds(_self: CHandle, out: *mut c_double) -> c_int {
unsafe { oakaudio::ffi::manager::oakaudio_manager_seconds(_self, out) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_processor_init() -> CHandle {
unsafe { oakaudio::ffi::processor::oakaudio_processor_init() }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_processor_free(_self: *mut CHandle) {
unsafe { oakaudio::ffi::processor::oakaudio_processor_free(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_processor_open(
_self: CHandle,
in_rate: c_int,
in_layout: u64,
in_format: c_int,
out_rate: c_int,
out_layout: u64,
out_format: c_int,
speed: c_double,
) -> c_int {
unsafe { oakaudio::ffi::processor::oakaudio_processor_open(_self, in_rate, in_layout, in_format, out_rate, out_layout, out_format, speed) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_processor_close(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::processor::oakaudio_processor_close(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_processor_is_open(_self: CHandle) -> c_int {
unsafe { oakaudio::ffi::processor::oakaudio_processor_is_open(_self) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_sync_estimate_envelope_offset(
reference: *const c_double,
reference_len: c_int,
candidate: *const c_double,
candidate_len: c_int,
reference_valid: *const u8,
candidate_valid: *const u8,
window_samples: u64,
max_offset_windows: i64,
out: *mut OffsetResult,
) -> c_int {
unsafe { oakaudio::ffi::sync::oakaudio_sync_estimate_envelope_offset(reference, reference_len, candidate, candidate_len, reference_valid, candidate_valid, window_samples, max_offset_windows, out) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_sync_estimate_stretch_and_offset(
reference: *const c_double,
reference_len: c_int,
candidate: *const c_double,
candidate_len: c_int,
reference_valid: *const u8,
candidate_valid: *const u8,
window_samples: u64,
max_offset_windows: i64,
min_rate: c_double,
max_rate: c_double,
rate_step: c_double,
out: *mut StretchOffsetResult,
) -> c_int {
unsafe { oakaudio::ffi::sync::oakaudio_sync_estimate_stretch_and_offset(reference, reference_len, candidate, candidate_len, reference_valid, candidate_valid, window_samples, max_offset_windows, min_rate, max_rate, rate_step, out) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_sync_place_by_source_time(
reference: *const SourceClip,
candidate: *const SourceClip,
reference_timeline_in_num: i64,
reference_timeline_in_den: i64,
out_num: *mut i64,
out_den: *mut i64,
out_valid: *mut c_int,
) -> c_int {
unsafe { oakaudio::ffi::sync::oakaudio_sync_place_by_source_time(reference, candidate, reference_timeline_in_num, reference_timeline_in_den, out_num, out_den, out_valid) }
}
/// Direct call into the `oakaudio` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakaudio_sync_place_by_waveform_offset(
reference_timeline_in_num: i64,
reference_timeline_in_den: i64,
candidate_offset_samples: i64,
sample_rate: c_int,
out_num: *mut i64,
out_den: *mut i64,
out_valid: *mut c_int,
) -> c_int {
unsafe { oakaudio::ffi::sync::oakaudio_sync_place_by_waveform_offset(reference_timeline_in_num, reference_timeline_in_den, candidate_offset_samples, sample_rate, out_num, out_den, out_valid) }
}
+177 -55
View File
@@ -14,6 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakcodec C ABI bridge: direct Rust calls into the `oakcodec` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oakcodec`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakcodec C ABI imports, mirroring the oakcodec crate's exports
//! (`src/codec/rust/src/ffi/{format,encoder}.rs`; headers
//! `include/codec/{format,encoder}.h`). Also carries the
@@ -117,76 +144,171 @@ impl EncodingParamsPOD {
}
}
extern "C" {
// ---- include/codec/format.h (encoding metadata) ------------------------
/// `oakcodec_encoding_format_count`.
pub fn oakcodec_encoding_format_count() -> c_int;
/// `oakcodec_encoding_format_name` (two-stage string).
pub fn oakcodec_encoding_format_name(format: c_int, buf: *mut c_char, buf_size: c_int) -> c_int;
/// `oakcodec_encoding_format_extension` (two-stage string).
pub fn oakcodec_encoding_format_extension(
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_count() -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_count() }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_name(format: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_name(format, buf, buf_size) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_extension(
format: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcodec_encoding_format_video_codec_count`.
pub fn oakcodec_encoding_format_video_codec_count(format: c_int) -> c_int;
/// `oakcodec_encoding_format_video_codec_at`.
pub fn oakcodec_encoding_format_video_codec_at(format: c_int, index: c_int) -> c_int;
/// `oakcodec_encoding_format_audio_codec_count`.
pub fn oakcodec_encoding_format_audio_codec_count(format: c_int) -> c_int;
/// `oakcodec_encoding_format_audio_codec_at`.
pub fn oakcodec_encoding_format_audio_codec_at(format: c_int, index: c_int) -> c_int;
/// `oakcodec_encoding_format_subtitle_codec_count`.
pub fn oakcodec_encoding_format_subtitle_codec_count(format: c_int) -> c_int;
/// `oakcodec_encoding_format_subtitle_codec_at`.
pub fn oakcodec_encoding_format_subtitle_codec_at(format: c_int, index: c_int) -> c_int;
/// `oakcodec_encoding_codec_name` (two-stage string).
pub fn oakcodec_encoding_codec_name(codec: c_int, buf: *mut c_char, buf_size: c_int) -> c_int;
/// `oakcodec_encoding_codec_is_still_image`.
pub fn oakcodec_encoding_codec_is_still_image(codec: c_int) -> c_int;
/// `oakcodec_encoding_codec_is_lossless`.
pub fn oakcodec_encoding_codec_is_lossless(codec: c_int) -> c_int;
/// `oakcodec_encoding_pix_fmt_count`.
pub fn oakcodec_encoding_pix_fmt_count(format: c_int, codec: c_int) -> c_int;
/// `oakcodec_encoding_pix_fmt_at` (two-stage string).
pub fn oakcodec_encoding_pix_fmt_at(
) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_extension(format, buf, buf_size) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_video_codec_count(format: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_video_codec_count(format) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_video_codec_at(format: c_int, index: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_video_codec_at(format, index) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_audio_codec_count(format: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_audio_codec_count(format) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_audio_codec_at(format: c_int, index: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_audio_codec_at(format, index) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_subtitle_codec_count(format: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_subtitle_codec_count(format) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_format_subtitle_codec_at(format: c_int, index: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_format_subtitle_codec_at(format, index) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_codec_name(codec: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_codec_name(codec, buf, buf_size) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_codec_is_still_image(codec: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_codec_is_still_image(codec) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_codec_is_lossless(codec: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_codec_is_lossless(codec) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_pix_fmt_count(format: c_int, codec: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_pix_fmt_count(format, codec) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_pix_fmt_at(
format: c_int,
codec: c_int,
index: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcodec_encoding_pix_fmt_index`.
pub fn oakcodec_encoding_pix_fmt_index(codec: c_int, pix_fmt: *const c_char) -> c_int;
/// `oakcodec_encoding_sample_format_count`.
pub fn oakcodec_encoding_sample_format_count(format: c_int, codec: c_int) -> c_int;
/// `oakcodec_encoding_sample_format_at`.
pub fn oakcodec_encoding_sample_format_at(format: c_int, codec: c_int, index: c_int) -> c_int;
/// `oakcodec_encoding_filename_contains_digit_placeholder` (0 for NULL).
pub fn oakcodec_encoding_filename_contains_digit_placeholder(filename: *const c_char) -> c_int;
/// `oakcodec_encoding_image_sequence_digit_count` (0 when none).
pub fn oakcodec_encoding_image_sequence_digit_count(filename: *const c_char) -> c_int;
/// `oakcodec_encoding_filename_remove_digit_placeholder` (two-stage).
pub fn oakcodec_encoding_filename_remove_digit_placeholder(
) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_pix_fmt_at(format, codec, index, buf, buf_size) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_pix_fmt_index(codec: c_int, pix_fmt: *const c_char) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_pix_fmt_index(codec, pix_fmt) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_sample_format_count(format: c_int, codec: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_sample_format_count(format, codec) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_sample_format_at(format: c_int, codec: c_int, index: c_int) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_sample_format_at(format, codec, index) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_filename_contains_digit_placeholder(filename: *const c_char) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_filename_contains_digit_placeholder(filename) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_image_sequence_digit_count(filename: *const c_char) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_image_sequence_digit_count(filename) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_filename_remove_digit_placeholder(
filename: *const c_char,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcodec_encoding_generate_matrix` — writes 16 `f64` (the engine
/// surface uses `f32`; the facade converts).
pub fn oakcodec_encoding_generate_matrix(
) -> c_int {
unsafe { oakcodec::ffi::format::oakcodec_encoding_filename_remove_digit_placeholder(filename, buf, buf_size) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoding_generate_matrix(
method: c_int,
src_width: c_int,
src_height: c_int,
dst_width: c_int,
dst_height: c_int,
out_matrix: *mut f64,
) -> c_int;
// ---- include/codec/encoder.h -------------------------------------------
/// `oakcodec_encoder_init` — encoder over a params POD (refcount 1).
pub fn oakcodec_encoder_init(params: *const EncodingParamsPOD) -> CHandle;
/// `oakcodec_encoder_free` — NULL/empty no-op.
pub fn oakcodec_encoder_free(encoder: *mut CHandle);
) -> c_int {
unsafe { oakcodec::ffi::encoder::oakcodec_encoding_generate_matrix(method, src_width, src_height, dst_width, dst_height, out_matrix) }
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
/// `oakcodec_encoder_init` — crosses the encoding-params C ABI POD
/// (`oakcodec_encoding_params`; the facade keeps its own POD mirror).
/// Kept as a link-time `extern "C"` declaration against the frozen module
/// C ABI.
pub fn oakcodec_encoder_init(params: *const EncodingParamsPOD) -> CHandle {
unsafe { oakcodec_encoder_init_extern(params) }
}
extern "C" {
#[link_name = "oakcodec_encoder_init"]
pub fn oakcodec_encoder_init_extern(params: *const EncodingParamsPOD) -> CHandle;
}
/// Direct call into the `oakcodec` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcodec_encoder_free(encoder: *mut CHandle) {
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_free(encoder) }
}
+643 -246
View File
@@ -14,6 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakcommon C ABI bridge: direct Rust calls into the `oakcommon` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oakcommon`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakcommon C ABI imports, mirroring the oakcommon crate's exports
//! (`src/common/rust/src/ffi.rs`; headers `include/common/*.h`). Only the
//! families the facade wraps: config, videoparams, colortransform, xml
@@ -27,316 +54,686 @@ use crate::handle::CHandle;
pub type ConfigErrorHandler =
Option<unsafe extern "C" fn(title: *const c_char, message: *const c_char, userdata: *mut c_void)>;
extern "C" {
// ---- config.h -------------------------------------------------------
/// `oakcommon_config_load` — reset to defaults, then read config.ini.
pub fn oakcommon_config_load() -> c_int;
/// `oakcommon_config_save` — write config.ini (temp file + rename).
pub fn oakcommon_config_save() -> c_int;
/// `oakcommon_config_reset_defaults` — drop custom keys.
pub fn oakcommon_config_reset_defaults() -> c_int;
/// `oakcommon_config_set` — set a string entry.
pub fn oakcommon_config_set(group: *const c_char, key: *const c_char, value: *const c_char);
/// `oakcommon_config_get` — read an entry as string (two-stage).
pub fn oakcommon_config_get(
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_load() -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_load() }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_save() -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_save() }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_reset_defaults() -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_reset_defaults() }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_set(group: *const c_char, key: *const c_char, value: *const c_char) {
unsafe { oakcommon::ffi::config::oakcommon_config_set(group, key, value) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_get(
group: *const c_char,
key: *const c_char,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_config_get_int` — INT entry with fallback.
pub fn oakcommon_config_get_int(group: *const c_char, key: *const c_char, fallback: c_int) -> c_int;
/// `oakcommon_config_get_int64` — INT64 entry with fallback.
pub fn oakcommon_config_get_int64(
) -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_get(group, key, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_get_int(group: *const c_char, key: *const c_char, fallback: c_int) -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_get_int(group, key, fallback) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_get_int64(
group: *const c_char,
key: *const c_char,
fallback: i64,
) -> i64;
/// `oakcommon_config_get_double` — DOUBLE entry with fallback.
pub fn oakcommon_config_get_double(group: *const c_char, key: *const c_char, fallback: f64) -> f64;
/// `oakcommon_config_get_bool` — BOOL entry with fallback.
pub fn oakcommon_config_get_bool(group: *const c_char, key: *const c_char, fallback: c_int) -> c_int;
/// `oakcommon_config_set_int` — set an INT entry.
pub fn oakcommon_config_set_int(group: *const c_char, key: *const c_char, value: c_int);
/// `oakcommon_config_set_int64` — set an INT64 entry.
pub fn oakcommon_config_set_int64(group: *const c_char, key: *const c_char, value: i64);
/// `oakcommon_config_set_double` — set a DOUBLE entry.
pub fn oakcommon_config_set_double(group: *const c_char, key: *const c_char, value: f64);
/// `oakcommon_config_set_bool` — set a BOOL entry.
pub fn oakcommon_config_set_bool(group: *const c_char, key: *const c_char, value: c_int);
/// `oakcommon_config_entry_type` — the entry's declared type.
pub fn oakcommon_config_entry_type(group: *const c_char, key: *const c_char) -> c_int;
/// `oakcommon_config_set_error_handler` — install the UI error handler.
pub fn oakcommon_config_set_error_handler(handler: ConfigErrorHandler, userdata: *mut c_void) -> c_int;
) -> i64 {
unsafe { oakcommon::ffi::config::oakcommon_config_get_int64(group, key, fallback) }
}
// ---- videoparams.h --------------------------------------------------
/// `oakcommon_videoparams_init` — default video params, refcount 1.
pub fn oakcommon_videoparams_init() -> CHandle;
/// `oakcommon_videoparams_init_basic` — width/height/frame-rate params.
pub fn oakcommon_videoparams_init_basic(
width: c_int,
height: c_int,
time_base_num: c_int,
time_base_den: c_int,
) -> CHandle;
/// `oakcommon_videoparams_init_with_time_base` — params + time base.
pub fn oakcommon_videoparams_init_with_time_base(
width: c_int,
height: c_int,
time_base_num: c_int,
time_base_den: c_int,
) -> CHandle;
/// `oakcommon_videoparams_free` — NULL/empty no-op; clears `params->ctx`.
pub fn oakcommon_videoparams_free(params: *mut CHandle);
/// `oakcommon_videoparams_get_width`.
pub fn oakcommon_videoparams_get_width(params: CHandle, width: *mut c_int) -> c_int;
/// `oakcommon_videoparams_set_width`.
pub fn oakcommon_videoparams_set_width(params: CHandle, width: c_int) -> c_int;
/// `oakcommon_videoparams_get_height`.
pub fn oakcommon_videoparams_get_height(params: CHandle, height: *mut c_int) -> c_int;
/// `oakcommon_videoparams_set_height`.
pub fn oakcommon_videoparams_set_height(params: CHandle, height: c_int) -> c_int;
/// `oakcommon_videoparams_get_time_base`.
pub fn oakcommon_videoparams_get_time_base(
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_get_double(group: *const c_char, key: *const c_char, fallback: f64) -> f64 {
unsafe { oakcommon::ffi::config::oakcommon_config_get_double(group, key, fallback) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_get_bool(group: *const c_char, key: *const c_char, fallback: c_int) -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_get_bool(group, key, fallback) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_set_int(group: *const c_char, key: *const c_char, value: c_int) {
unsafe { oakcommon::ffi::config::oakcommon_config_set_int(group, key, value) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_set_int64(group: *const c_char, key: *const c_char, value: i64) {
unsafe { oakcommon::ffi::config::oakcommon_config_set_int64(group, key, value) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_set_double(group: *const c_char, key: *const c_char, value: f64) {
unsafe { oakcommon::ffi::config::oakcommon_config_set_double(group, key, value) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_set_bool(group: *const c_char, key: *const c_char, value: c_int) {
unsafe { oakcommon::ffi::config::oakcommon_config_set_bool(group, key, value) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_entry_type(group: *const c_char, key: *const c_char) -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_entry_type(group, key) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_config_set_error_handler(handler: ConfigErrorHandler, userdata: *mut c_void) -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_set_error_handler(handler, userdata) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_init() -> CHandle {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_init() }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_init_basic(
width: c_int,
height: c_int,
pixel_format: c_int,
nb_channels: c_int,
pixel_aspect_num: c_int,
pixel_aspect_den: c_int,
interlacing: c_int,
divider: c_int,
) -> CHandle {
unsafe {
oakcommon::ffi::videoparams::oakcommon_videoparams_init_basic(
width, height, pixel_format, nb_channels, pixel_aspect_num, pixel_aspect_den,
interlacing, divider,
)
}
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_init_with_time_base(
width: c_int,
height: c_int,
time_base_num: c_int,
time_base_den: c_int,
pixel_format: c_int,
nb_channels: c_int,
pixel_aspect_num: c_int,
pixel_aspect_den: c_int,
interlacing: c_int,
divider: c_int,
) -> CHandle {
unsafe {
oakcommon::ffi::videoparams::oakcommon_videoparams_init_with_time_base(
width, height, time_base_num, time_base_den, pixel_format, nb_channels,
pixel_aspect_num, pixel_aspect_den, interlacing, divider,
)
}
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_free(params: *mut CHandle) {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_free(params) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_width(params: CHandle, width: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_width(params, width) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_width(params: CHandle, width: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_width(params, width) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_height(params: CHandle, height: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_height(params, height) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_height(params: CHandle, height: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_height(params, height) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_time_base(
params: CHandle,
numerator: *mut c_int,
denominator: *mut c_int,
) -> c_int;
/// `oakcommon_videoparams_set_time_base`.
pub fn oakcommon_videoparams_set_time_base(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_time_base(params, numerator, denominator) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_time_base(
params: CHandle,
numerator: c_int,
denominator: c_int,
) -> c_int;
/// `oakcommon_videoparams_get_frame_rate`.
pub fn oakcommon_videoparams_get_frame_rate(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_time_base(params, numerator, denominator) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_frame_rate(
params: CHandle,
numerator: *mut c_int,
denominator: *mut c_int,
) -> c_int;
/// `oakcommon_videoparams_set_frame_rate`.
pub fn oakcommon_videoparams_set_frame_rate(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_frame_rate(params, numerator, denominator) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_frame_rate(
params: CHandle,
numerator: c_int,
denominator: c_int,
) -> c_int;
/// `oakcommon_videoparams_get_pixel_aspect_ratio`.
pub fn oakcommon_videoparams_get_pixel_aspect_ratio(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_frame_rate(params, numerator, denominator) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_pixel_aspect_ratio(
params: CHandle,
numerator: *mut c_int,
denominator: *mut c_int,
) -> c_int;
/// `oakcommon_videoparams_set_pixel_aspect_ratio`.
pub fn oakcommon_videoparams_set_pixel_aspect_ratio(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_pixel_aspect_ratio(params, numerator, denominator) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_pixel_aspect_ratio(
params: CHandle,
numerator: c_int,
denominator: c_int,
) -> c_int;
/// `oakcommon_videoparams_get_format`.
pub fn oakcommon_videoparams_get_format(params: CHandle, format: *mut c_int) -> c_int;
/// `oakcommon_videoparams_set_format`.
pub fn oakcommon_videoparams_set_format(params: CHandle, format: c_int) -> c_int;
/// `oakcommon_videoparams_get_interlacing`.
pub fn oakcommon_videoparams_get_interlacing(params: CHandle, interlacing: *mut c_int) -> c_int;
/// `oakcommon_videoparams_set_interlacing`.
pub fn oakcommon_videoparams_set_interlacing(params: CHandle, interlacing: c_int) -> c_int;
/// `oakcommon_videoparams_get_divider`.
pub fn oakcommon_videoparams_get_divider(params: CHandle, divider: *mut c_int) -> c_int;
/// `oakcommon_videoparams_set_divider`.
pub fn oakcommon_videoparams_set_divider(params: CHandle, divider: c_int) -> c_int;
/// `oakcommon_videoparams_get_video_type`.
pub fn oakcommon_videoparams_get_video_type(params: CHandle, type_: *mut c_int) -> c_int;
/// `oakcommon_videoparams_set_video_type`.
pub fn oakcommon_videoparams_set_video_type(params: CHandle, type_: c_int) -> c_int;
/// `oakcommon_videoparams_get_premultiplied_alpha`.
pub fn oakcommon_videoparams_get_premultiplied_alpha(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_pixel_aspect_ratio(params, numerator, denominator) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_format(params: CHandle, format: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_format(params, format) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_format(params: CHandle, format: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_format(params, format) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_interlacing(params: CHandle, interlacing: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_interlacing(params, interlacing) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_interlacing(params: CHandle, interlacing: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_interlacing(params, interlacing) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_divider(params: CHandle, divider: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_divider(params, divider) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_divider(params: CHandle, divider: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_divider(params, divider) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_video_type(params: CHandle, type_: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_video_type(params, type_) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_video_type(params: CHandle, type_: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_video_type(params, type_) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_premultiplied_alpha(
params: CHandle,
premultiplied: *mut c_int,
) -> c_int;
/// `oakcommon_videoparams_set_premultiplied_alpha`.
pub fn oakcommon_videoparams_set_premultiplied_alpha(params: CHandle, premultiplied: c_int) -> c_int;
/// `oakcommon_videoparams_get_color_range`.
pub fn oakcommon_videoparams_get_color_range(params: CHandle, color_range: *mut c_int) -> c_int;
/// `oakcommon_videoparams_set_color_range`.
pub fn oakcommon_videoparams_set_color_range(params: CHandle, color_range: c_int) -> c_int;
/// `oakcommon_videoparams_get_is_valid`.
pub fn oakcommon_videoparams_get_is_valid(params: CHandle, valid: *mut c_int) -> c_int;
/// `oakcommon_videoparams_get_effective_width` — divider-scaled width.
pub fn oakcommon_videoparams_get_effective_width(params: CHandle, width: *mut c_int) -> c_int;
/// `oakcommon_videoparams_get_effective_height` — divider-scaled height.
pub fn oakcommon_videoparams_get_effective_height(params: CHandle, height: *mut c_int) -> c_int;
/// `oakcommon_videoparams_get_bytes_per_pixel` — with the params' format.
pub fn oakcommon_videoparams_get_bytes_per_pixel(params: CHandle, bytes: *mut c_int) -> c_int;
/// `oakcommon_videoparams_equals` — user-facing field equality.
pub fn oakcommon_videoparams_equals(a: CHandle, b: CHandle, out_equal: *mut c_int) -> c_int;
/// `oakcommon_videoparams_format_is_float` — 1 when the format is float.
pub fn oakcommon_videoparams_format_is_float(pixel_format: c_int) -> c_int;
/// `oakcommon_videoparams_get_format_name` — display name (two-stage).
pub fn oakcommon_videoparams_get_format_name(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_premultiplied_alpha(params, premultiplied) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_premultiplied_alpha(params: CHandle, premultiplied: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_premultiplied_alpha(params, premultiplied) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_color_range(params: CHandle, color_range: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_color_range(params, color_range) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_set_color_range(params: CHandle, color_range: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_color_range(params, color_range) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_is_valid(params: CHandle, valid: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_is_valid(params, valid) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_effective_width(params: CHandle, width: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_effective_width(params, width) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_effective_height(params: CHandle, height: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_effective_height(params, height) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_bytes_per_pixel(params: CHandle, bytes: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_bytes_per_pixel(params, bytes) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_equals(a: CHandle, b: CHandle, out_equal: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_equals(a, b, out_equal) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_format_is_float(pixel_format: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_format_is_float(pixel_format) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_format_name(
pixel_format: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_videoparams_frame_rate_to_string` — label (two-stage).
pub fn oakcommon_videoparams_frame_rate_to_string(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_format_name(pixel_format, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_frame_rate_to_string(
numerator: c_int,
denominator: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_videoparams_get_name_for_divider` — label (two-stage).
pub fn oakcommon_videoparams_get_name_for_divider(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_frame_rate_to_string(numerator, denominator, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_name_for_divider(
divider: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_videoparams_get_scaled_dimension` — size at a divider.
pub fn oakcommon_videoparams_get_scaled_dimension(dimension: c_int, divider: c_int) -> c_int;
/// `oakcommon_videoparams_generate_auto_divider` — best divider for size.
pub fn oakcommon_videoparams_generate_auto_divider(width: i64, height: i64) -> c_int;
/// `oakcommon_videoparams_get_divider_for_target_resolution`.
pub fn oakcommon_videoparams_get_divider_for_target_resolution(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_name_for_divider(divider, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_scaled_dimension(dimension: c_int, divider: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_scaled_dimension(dimension, divider) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_generate_auto_divider(width: i64, height: i64) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_generate_auto_divider(width, height) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_divider_for_target_resolution(
src_width: c_int,
src_height: c_int,
target_width: c_int,
target_height: c_int,
) -> c_int;
/// `oakcommon_videoparams_get_bytes_per_channel_for_format`.
pub fn oakcommon_videoparams_get_bytes_per_channel_for_format(
pixel_format: c_int,
bytes: *mut c_int,
) -> c_int;
/// `oakcommon_videoparams_get_bytes_per_pixel_for_format`.
pub fn oakcommon_videoparams_get_bytes_per_pixel_for_format(
pixel_format: c_int,
bytes: *mut c_int,
) -> c_int;
/// `oakcommon_videoparams_static_get_bytes_per_pixel`.
pub fn oakcommon_videoparams_static_get_bytes_per_pixel(
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_divider_for_target_resolution(src_width, src_height, target_width, target_height) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_bytes_per_channel_for_format(pixel_format: c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_bytes_per_channel_for_format(pixel_format) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_bytes_per_pixel_for_format(
pixel_format: c_int,
channels: c_int,
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_bytes_per_pixel_for_format(pixel_format, channels) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_static_get_bytes_per_pixel(
pixel_format: c_int,
channels: c_int,
) -> c_int;
/// `oakcommon_videoparams_get_buffer_size`.
pub fn oakcommon_videoparams_get_buffer_size(params: CHandle, bytes: *mut i64) -> c_int;
/// `oakcommon_videoparams_get_time_in_timebase_units`.
pub fn oakcommon_videoparams_get_time_in_timebase_units(
params: CHandle,
time_num: i64,
time_den: i64,
out: *mut i64,
) -> c_int;
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_static_get_bytes_per_pixel(pixel_format, channels) }
}
// ---- colortransform.h -----------------------------------------------
/// `oakcommon_colortransform_init_output` — output color space transform.
pub fn oakcommon_colortransform_init_output(output: *const c_char) -> CHandle;
/// `oakcommon_colortransform_init_display` — display/view/look transform.
pub fn oakcommon_colortransform_init_display(
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_buffer_size(params: CHandle, size: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_buffer_size(params, size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_videoparams_get_time_in_timebase_units(
params: CHandle,
time_num: c_int,
time_den: c_int,
timestamp: *mut i64,
) -> c_int {
unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_time_in_timebase_units(params, time_num, time_den, timestamp) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_init_output(output: *const c_char) -> CHandle {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_init_output(output) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_init_display(
display: *const c_char,
view: *const c_char,
look: *const c_char,
) -> CHandle;
/// `oakcommon_colortransform_free` — NULL/empty no-op.
pub fn oakcommon_colortransform_free(transform: *mut CHandle);
/// `oakcommon_colortransform_is_display` — 1 for a display transform.
pub fn oakcommon_colortransform_is_display(transform: CHandle) -> c_int;
/// `oakcommon_colortransform_get_display` (two-stage string).
pub fn oakcommon_colortransform_get_display(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_colortransform_get_output` (two-stage string).
pub fn oakcommon_colortransform_get_output(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_colortransform_get_view` (two-stage string).
pub fn oakcommon_colortransform_get_view(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_colortransform_get_look` (two-stage string).
pub fn oakcommon_colortransform_get_look(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
) -> CHandle {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_init_display(display, view, look) }
}
// ---- xmlutils.h -----------------------------------------------------
/// `oakcommon_xml_reader_init` — reader over a NUL-terminated document.
pub fn oakcommon_xml_reader_init(data: *const c_char) -> CHandle;
/// `oakcommon_xml_reader_free` — NULL/empty no-op.
pub fn oakcommon_xml_reader_free(reader: *mut CHandle);
/// `oakcommon_xml_reader_read_next_start_element` — found 1/0 in `found`.
pub fn oakcommon_xml_reader_read_next_start_element(reader: CHandle, found: *mut c_int) -> c_int;
/// `oakcommon_xml_reader_name` (two-stage string).
pub fn oakcommon_xml_reader_name(reader: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int;
/// `oakcommon_xml_reader_read_element_text` (two-stage string).
pub fn oakcommon_xml_reader_read_element_text(
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_free(transform: *mut CHandle) {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_free(transform) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_is_display(transform: CHandle) -> c_int {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_is_display(transform) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_get_display(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_display(transform, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_get_output(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_output(transform, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_get_view(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_view(transform, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_colortransform_get_look(
transform: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_get_look(transform, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_init(data: *const c_char) -> CHandle {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_init(data) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_free(reader: *mut CHandle) {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_free(reader) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_read_next_start_element(reader: CHandle, found: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_read_next_start_element(reader, found) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_name(reader: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_name(reader, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_read_element_text(
reader: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_xml_reader_skip_current_element`.
pub fn oakcommon_xml_reader_skip_current_element(reader: CHandle) -> c_int;
/// `oakcommon_xml_reader_attribute_count`.
pub fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: *mut c_int) -> c_int;
/// `oakcommon_xml_reader_attribute_name` (two-stage string).
pub fn oakcommon_xml_reader_attribute_name(
) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_read_element_text(reader, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_skip_current_element(reader: CHandle) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_skip_current_element(reader) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_count(reader, count) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_attribute_name(
reader: CHandle,
index: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_xml_reader_attribute_value` (two-stage string).
pub fn oakcommon_xml_reader_attribute_value(
) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_name(reader, index, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_attribute_value(
reader: CHandle,
index: c_int,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcommon_xml_reader_has_error`.
pub fn oakcommon_xml_reader_has_error(reader: CHandle, has_error: *mut c_int) -> c_int;
/// `oakcommon_xml_writer_init` — fresh writer.
pub fn oakcommon_xml_writer_init() -> CHandle;
/// `oakcommon_xml_writer_free` — NULL/empty no-op.
pub fn oakcommon_xml_writer_free(writer: *mut CHandle);
/// `oakcommon_xml_writer_write_start_element`.
pub fn oakcommon_xml_writer_write_start_element(writer: CHandle, name: *const c_char) -> c_int;
/// `oakcommon_xml_writer_write_attribute`.
pub fn oakcommon_xml_writer_write_attribute(
) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_value(reader, index, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_reader_has_error(reader: CHandle, has_error: *mut c_int) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_has_error(reader, has_error) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_init() -> CHandle {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_init() }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_free(writer: *mut CHandle) {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_free(writer) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_write_start_element(writer: CHandle, name: *const c_char) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_start_element(writer, name) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_write_attribute(
writer: CHandle,
name: *const c_char,
value: *const c_char,
) -> c_int;
/// `oakcommon_xml_writer_write_characters`.
pub fn oakcommon_xml_writer_write_characters(writer: CHandle, text: *const c_char) -> c_int;
/// `oakcommon_xml_writer_write_text_element`.
pub fn oakcommon_xml_writer_write_text_element(
) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_attribute(writer, name, value) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_write_characters(writer: CHandle, text: *const c_char) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_characters(writer, text) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_write_text_element(
writer: CHandle,
name: *const c_char,
text: *const c_char,
) -> c_int;
/// `oakcommon_xml_writer_write_end_element`.
pub fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_int;
/// `oakcommon_xml_writer_write_end_document`.
pub fn oakcommon_xml_writer_write_end_document(writer: CHandle) -> c_int;
/// `oakcommon_xml_writer_output` (two-stage string).
pub fn oakcommon_xml_writer_output(writer: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int;
// ---- decibel helpers -------------------------------------------------
/// `oakcommon_decibel_from_linear` — linear amplitude to decibels.
pub fn oakcommon_decibel_from_linear(linear: f64, out_db: *mut f64) -> c_int;
/// `oakcommon_decibel_to_linear` — decibels to linear amplitude.
pub fn oakcommon_decibel_to_linear(db: f64, out_linear: *mut f64) -> c_int;
/// `oakcommon_decibel_from_logarithmic` — slider position to decibels.
pub fn oakcommon_decibel_from_logarithmic(logarithmic: f64, out_db: *mut f64) -> c_int;
/// `oakcommon_decibel_to_logarithmic` — decibels to slider position.
pub fn oakcommon_decibel_to_logarithmic(db: f64, out_logarithmic: *mut f64) -> c_int;
/// `oakcommon_decibel_linear_to_logarithmic`.
pub fn oakcommon_decibel_linear_to_logarithmic(linear: f64, out_logarithmic: *mut f64) -> c_int;
/// `oakcommon_decibel_logarithmic_to_linear`.
pub fn oakcommon_decibel_logarithmic_to_linear(logarithmic: f64, out_linear: *mut f64) -> c_int;
) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_text_element(writer, name, text) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_end_element(writer) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_write_end_document(writer: CHandle) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_end_document(writer) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_xml_writer_output(writer: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_output(writer, buf, buf_size) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_decibel_from_linear(linear: f64, out_db: *mut f64) -> c_int {
unsafe { oakcommon::ffi::misc::oakcommon_decibel_from_linear(linear, out_db) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_decibel_to_linear(db: f64, out_linear: *mut f64) -> c_int {
unsafe { oakcommon::ffi::misc::oakcommon_decibel_to_linear(db, out_linear) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_decibel_from_logarithmic(logarithmic: f64, out_db: *mut f64) -> c_int {
unsafe { oakcommon::ffi::misc::oakcommon_decibel_from_logarithmic(logarithmic, out_db) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_decibel_to_logarithmic(db: f64, out_logarithmic: *mut f64) -> c_int {
unsafe { oakcommon::ffi::misc::oakcommon_decibel_to_logarithmic(db, out_logarithmic) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_decibel_linear_to_logarithmic(linear: f64, out_logarithmic: *mut f64) -> c_int {
unsafe { oakcommon::ffi::misc::oakcommon_decibel_linear_to_logarithmic(linear, out_logarithmic) }
}
/// Direct call into the `oakcommon` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakcommon_decibel_logarithmic_to_linear(logarithmic: f64, out_linear: *mut f64) -> c_int {
unsafe { oakcommon::ffi::misc::oakcommon_decibel_logarithmic_to_linear(logarithmic, out_linear) }
}
File diff suppressed because it is too large Load Diff
+56 -12
View File
@@ -14,21 +14,65 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakplugin C ABI bridge: direct Rust calls into the `oakplugin` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oakplugin`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakplugin C ABI imports, mirroring the oakplugin crate's exports
//! (`src/plugin/rust/src/ffi.rs`; headers `include/plugin/*.h`).
use std::ffi::{c_char, c_int};
extern "C" {
/// `oakplugin_host_scan` — scan bundle directories (NULL/0 uses the
/// default path set).
pub fn oakplugin_host_scan(bundle_dirs: *const *const c_char, dir_count: c_int) -> c_int;
/// `oakplugin_host_init` — initialize the OFX host (idempotent).
pub fn oakplugin_host_init() -> c_int;
/// `oakplugin_host_plugin_count` — number of discovered plugins.
pub fn oakplugin_host_plugin_count() -> c_int;
/// `oakplugin_host_plugin_id_at` — plugin id at index (two-stage).
pub fn oakplugin_host_plugin_id_at(index: c_int, buf: *mut c_char, buf_size: c_int) -> c_int;
/// `oakplugin_host_plugin_label` — label for an id (two-stage).
pub fn oakplugin_host_plugin_label(plugin_id: *const c_char, buf: *mut c_char, buf_size: c_int) -> c_int;
/// Direct call into the `oakplugin` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakplugin_host_scan(bundle_dirs: *const *const c_char, dir_count: c_int) -> c_int {
unsafe { oakplugin::ffi::oakplugin_host_scan(bundle_dirs, dir_count) }
}
/// Direct call into the `oakplugin` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakplugin_host_init() -> c_int {
unsafe { oakplugin::ffi::oakplugin_host_init() }
}
/// Direct call into the `oakplugin` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakplugin_host_plugin_count() -> c_int {
unsafe { oakplugin::ffi::oakplugin_host_plugin_count() }
}
/// Direct call into the `oakplugin` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakplugin_host_plugin_id_at(index: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oakplugin::ffi::oakplugin_host_plugin_id_at(index, buf, buf_size) }
}
/// Direct call into the `oakplugin` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakplugin_host_plugin_label(plugin_id: *const c_char, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oakplugin::ffi::oakplugin_host_plugin_label(plugin_id, buf, buf_size) }
}
+255 -156
View File
@@ -14,6 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakrender C ABI bridge: direct Rust calls into the `oakrender` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oakrender`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakrender C ABI imports, mirroring the oakrender crate's exports
//! (`src/render/rust/src/ffi.rs`; headers `include/render/*.h`).
@@ -21,110 +48,84 @@ use std::ffi::{c_char, c_double, c_int, c_void};
use crate::handle::CHandle;
/// `include/render/ticket.h` — video render ticket params mirror. All
/// handle fields are [`CHandle`] (borrowed unless noted).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakVideoTicketParams {
/// Connected texture output node (borrowed).
pub output_node: CHandle,
/// By-value oakcommon video-params handle.
pub video_params: CHandle,
/// Borrowed oakcore audio-params handle, may be null.
pub audio_params: *const c_void,
/// Frame timestamp numerator.
pub time_num: i64,
/// Frame timestamp denominator.
pub time_den: i64,
/// Borrowed color manager, empty ctx = null.
pub color_manager: 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: [c_double; 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: CHandle,
/// By value; empty ctx = default.
pub force_color_transform: CHandle,
/// Borrowed frame cache; empty ctx = none.
pub cache: CHandle,
}
/// `include/render/ticket.h` — video render ticket params. Single-lib
/// unification: aliases the oakrender crate's POD (same `repr(C)`
/// layout; all handle fields are the shared [`CHandle`]).
pub type OakVideoTicketParams = oakrender::ffi::OakVideoTicketParams;
/// `include/render/renderer.h` — frame video-params POD returned by
/// `oakrender_codec_frame_get_params`.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakRenderVideoParams {
/// Width.
pub width: c_int,
/// Height.
pub height: c_int,
/// Frame duration numerator.
pub time_base_num: c_int,
/// Frame duration denominator.
pub time_base_den: c_int,
/// PixelFormat as int.
pub format: c_int,
/// Pixel aspect numerator.
pub pixel_aspect_num: c_int,
/// Pixel aspect denominator.
pub pixel_aspect_den: c_int,
/// Interlacing as int.
pub interlacing: c_int,
/// Color range as int.
pub color_range: c_int,
/// Preview divider.
pub divider: c_int,
/// Video type.
pub video_type: c_int,
/// Premultiplied alpha 0/1.
pub premultiplied_alpha: c_int,
/// `oakrender_codec_frame_get_params`. Single-lib unification: aliases
/// the oakrender crate's POD.
pub type OakRenderVideoParams = oakrender::ffi::OakRenderVideoParams;
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_display_renderer_create_dynamic(backend_id: *const c_char) -> CHandle {
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_create_dynamic(backend_id) }
}
extern "C" {
// ---- display renderer --------------------------------------------------
/// `oakrender_display_renderer_create_dynamic` — named backend
/// ("opengl", "vulkan", "metal", "auto", ...).
pub fn oakrender_display_renderer_create_dynamic(backend_id: *const c_char) -> CHandle;
/// `oakrender_display_renderer_create_opengl` — direct OpenGL renderer.
pub fn oakrender_display_renderer_create_opengl() -> CHandle;
/// `oakrender_display_renderer_init` — NULL gl_context uses the
/// backend's default device/context path.
pub fn oakrender_display_renderer_init(renderer: CHandle, gl_context: *mut c_void) -> c_int;
/// `oakrender_display_renderer_destroy` — NULL/empty no-op.
pub fn oakrender_display_renderer_destroy(renderer: *mut CHandle);
/// `oakrender_display_renderer_is_open_gl` — 1/0.
pub fn oakrender_display_renderer_is_open_gl(renderer: CHandle) -> c_int;
/// `oakrender_display_renderer_is_vulkan` — 1/0.
pub fn oakrender_display_renderer_is_vulkan(renderer: CHandle) -> c_int;
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_display_renderer_create_opengl() -> CHandle {
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_create_opengl() }
}
// ---- render manager -----------------------------------------------------
/// `oakrender_manager_set_aggressive_gc`.
pub fn oakrender_manager_set_aggressive_gc(enabled: c_int) -> c_int;
/// `oakrender_set_cacher_multicam` — NULL clears.
pub fn oakrender_set_cacher_multicam(multicam_or_null: CHandle) -> c_int;
/// `oakrender_set_display_color_processor` — NULL clears.
pub fn oakrender_set_display_color_processor(p_or_null: CHandle) -> c_int;
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_display_renderer_init(renderer: CHandle, gl_context: *mut c_void) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_init(renderer, gl_context) }
}
// ---- render tickets -----------------------------------------------------
/// `oakrender_ticket_render_frame`.
pub fn oakrender_ticket_render_frame(
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_display_renderer_destroy(renderer: *mut CHandle) {
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_destroy(renderer) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_display_renderer_is_open_gl(renderer: CHandle) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_is_open_gl(renderer) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_display_renderer_is_vulkan(renderer: CHandle) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_display_renderer_is_vulkan(renderer) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_manager_set_aggressive_gc(enabled: c_int) -> c_int {
unsafe { oakrender::ffi::ticket::oakrender_manager_set_aggressive_gc(enabled) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_set_cacher_multicam(multicam_or_null: CHandle) -> c_int {
unsafe { oakrender::ffi::manager::oakrender_set_cacher_multicam(multicam_or_null) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_set_display_color_processor(p_or_null: CHandle) -> c_int {
unsafe { oakrender::ffi::manager::oakrender_set_display_color_processor(p_or_null) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_ticket_render_frame(
params: *const OakVideoTicketParams,
cb: Option<unsafe extern "C" fn(CHandle, *mut c_void)>,
userdata: *mut c_void,
) -> CHandle;
/// `oakrender_ticket_render_audio`.
pub fn oakrender_ticket_render_audio(
) -> CHandle {
unsafe { oakrender::ffi::ticket::oakrender_ticket_render_frame(params, cb, userdata) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_ticket_render_audio(
output_node: CHandle,
in_num: i64,
in_den: i64,
@@ -134,61 +135,141 @@ extern "C" {
mode: c_int,
cb: Option<unsafe extern "C" fn(CHandle, *mut c_void)>,
userdata: *mut c_void,
) -> CHandle;
/// `oakrender_ticket_wait` — block until finished.
pub fn oakrender_ticket_wait(ticket: CHandle) -> c_int;
/// `oakrender_ticket_cancel`.
pub fn oakrender_ticket_cancel(ticket: CHandle) -> c_int;
/// `oakrender_ticket_get_frame` — `*out` receives an owned copy.
pub fn oakrender_ticket_get_frame(ticket: CHandle, out: *mut CHandle) -> c_int;
/// `oakrender_ticket_get_samples` — audio not implemented in the crate.
pub fn oakrender_ticket_get_samples(ticket: CHandle, out: *mut *mut c_void) -> c_int;
/// `oakrender_ticket_free` — NULL/empty no-op.
pub fn oakrender_ticket_free(ticket: *mut CHandle);
) -> CHandle {
unsafe {
oakrender::ffi::ticket::oakrender_ticket_render_audio(
output_node, in_num, in_den, out_num, out_den, params as *const CHandle, mode, cb,
userdata,
)
}
}
// ---- codec frame --------------------------------------------------------
/// `oakrender_codec_frame_create` — refcount 1.
pub fn oakrender_codec_frame_create() -> CHandle;
/// `oakrender_codec_frame_retain` — addref, return copy.
pub fn oakrender_codec_frame_retain(frame: CHandle) -> CHandle;
/// `oakrender_codec_frame_free` — NULL/empty no-op.
pub fn oakrender_codec_frame_free(frame: *mut CHandle);
/// `oakrender_codec_frame_width`.
pub fn oakrender_codec_frame_width(frame: CHandle) -> c_int;
/// `oakrender_codec_frame_height`.
pub fn oakrender_codec_frame_height(frame: CHandle) -> c_int;
/// `oakrender_codec_frame_linesize_bytes`.
pub fn oakrender_codec_frame_linesize_bytes(frame: CHandle) -> c_int;
/// `oakrender_codec_frame_data` — borrowed.
pub fn oakrender_codec_frame_data(frame: CHandle) -> *mut c_void;
/// `oakrender_codec_frame_const_data` — borrowed.
pub fn oakrender_codec_frame_const_data(frame: CHandle) -> *const c_void;
/// `oakrender_codec_frame_is_allocated`.
pub fn oakrender_codec_frame_is_allocated(frame: CHandle) -> c_int;
/// `oakrender_codec_frame_get_params` — fills the params POD.
pub fn oakrender_codec_frame_get_params(frame: CHandle, out: *mut OakRenderVideoParams) -> c_int;
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_ticket_wait(ticket: CHandle) -> c_int {
unsafe { oakrender::ffi::ticket::oakrender_ticket_wait(ticket) }
}
// ---- color processor ----------------------------------------------------
/// `oakrender_color_processor_create`.
pub fn oakrender_color_processor_create(
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_ticket_cancel(ticket: CHandle) -> c_int {
unsafe { oakrender::ffi::ticket::oakrender_ticket_cancel(ticket) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_ticket_get_frame(ticket: CHandle, out: *mut CHandle) -> c_int {
unsafe { oakrender::ffi::ticket::oakrender_ticket_get_frame(ticket, out) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_ticket_get_samples(ticket: CHandle, out: *mut *mut c_void) -> c_int {
unsafe { oakrender::ffi::ticket::oakrender_ticket_get_samples(ticket, out) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_ticket_free(ticket: *mut CHandle) {
unsafe { oakrender::ffi::ticket::oakrender_ticket_free(ticket) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_create() -> CHandle {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_create() }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_retain(frame: CHandle) -> CHandle {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_retain(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_free(frame: *mut CHandle) {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_free(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_width(frame: CHandle) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_width(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_height(frame: CHandle) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_height(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_linesize_bytes(frame: CHandle) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_linesize_bytes(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_data(frame: CHandle) -> *mut c_void {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_data(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_const_data(frame: CHandle) -> *const c_void {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_const_data(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_is_allocated(frame: CHandle) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_is_allocated(frame) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_codec_frame_get_params(frame: CHandle, out: *mut OakRenderVideoParams) -> c_int {
unsafe { oakrender::ffi::renderer::oakrender_codec_frame_get_params(frame, out) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_color_processor_create(
src_space: *const c_char,
dst_transform: *const c_char,
direction: c_int,
) -> CHandle;
/// `oakrender_color_processor_free` — NULL/empty no-op.
pub fn oakrender_color_processor_free(processor: *mut CHandle);
/// `oakrender_color_processor_is_valid`.
pub fn oakrender_color_processor_is_valid(processor: CHandle) -> c_int;
/// `oakrender_color_processor_create_transform` — manager + input +
/// oakcommon colortransform handle + direction.
pub fn oakrender_color_processor_create_transform(
) -> CHandle {
unsafe { oakrender::ffi::color::oakrender_color_processor_create(src_space, dst_transform, direction) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_color_processor_free(processor: *mut CHandle) {
unsafe { oakrender::ffi::color::oakrender_color_processor_free(processor) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_color_processor_is_valid(processor: CHandle) -> c_int {
unsafe { oakrender::ffi::color::oakrender_color_processor_is_valid(processor) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_color_processor_create_transform(
manager: CHandle,
input: *const c_char,
dest: CHandle,
direction: c_int,
) -> CHandle;
/// `oakrender_color_processor_convert` — single RGBA color.
pub fn oakrender_color_processor_convert(
) -> CHandle {
unsafe { oakrender::ffi::color::oakrender_color_processor_create_transform(manager, input, dest, direction) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_color_processor_convert(
processor: CHandle,
ir: c_double,
ig: c_double,
@@ -198,19 +279,37 @@ extern "C" {
out_g: *mut c_double,
out_b: *mut c_double,
out_a: *mut c_double,
) -> c_int;
// ---- color manager ------------------------------------------------------
/// `oakrender_color_manager_set_up_default_config`.
pub fn oakrender_color_manager_set_up_default_config() -> c_int;
/// `oakrender_color_manager_get_config` (two-stage).
pub fn oakrender_color_manager_get_config(buf: *mut c_char, n: c_int) -> c_int;
// ---- LUT extension enumeration ------------------------------------------
/// `oakrender_lut_is_supported_extension`.
pub fn oakrender_lut_is_supported_extension(extension: *const c_char) -> c_int;
/// `oakrender_lut_supported_extensions_count`.
pub fn oakrender_lut_supported_extensions_count() -> c_int;
/// `oakrender_lut_supported_extension_at` (two-stage).
pub fn oakrender_lut_supported_extension_at(i: c_int, buf: *mut c_char, n: c_int) -> c_int;
) -> c_int {
unsafe { oakrender::ffi::color::oakrender_color_processor_convert(processor, ir, ig, ib, ia, out_r, out_g, out_b, out_a) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_color_manager_set_up_default_config() -> c_int {
unsafe { oakrender::ffi::color::oakrender_color_manager_set_up_default_config() }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_color_manager_get_config(buf: *mut c_char, n: c_int) -> c_int {
unsafe { oakrender::ffi::color::oakrender_color_manager_get_config(buf, n) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_lut_is_supported_extension(extension: *const c_char) -> c_int {
unsafe { oakrender::ffi::color::oakrender_lut_is_supported_extension(extension) }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_lut_supported_extensions_count() -> c_int {
unsafe { oakrender::ffi::color::oakrender_lut_supported_extensions_count() }
}
/// Direct call into the `oakrender` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakrender_lut_supported_extension_at(i: c_int, buf: *mut c_char, n: c_int) -> c_int {
unsafe { oakrender::ffi::color::oakrender_lut_supported_extension_at(i, buf, n) }
}
+235 -72
View File
@@ -14,6 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oaktask C ABI bridge: direct Rust calls into the `oaktask` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oaktask`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oaktask C ABI imports, mirroring the oaktask crate's exports
//! (`src/task/rust/src/ffi/{manager,task,project}.rs`; headers
//! `include/task/*.h`).
@@ -41,81 +68,217 @@ pub type OakTaskOtioImportConfirmFn =
pub type OakTaskImageSequenceConfirmFn =
unsafe extern "C" fn(filename: *const c_char, userdata: *mut c_void) -> c_int;
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_manager_init() -> c_int {
unsafe { oaktask::ffi::manager::oaktask_manager_init() }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_manager_shutdown() {
unsafe { oaktask::ffi::manager::oaktask_manager_shutdown() }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_register_codec_submitter() -> c_int {
unsafe { oaktask::ffi::manager::oaktask_register_codec_submitter() }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_manager_count() -> c_int {
unsafe { oaktask::ffi::manager::oaktask_manager_count() }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_manager_at(i: c_int) -> CHandle {
unsafe { oaktask::ffi::manager::oaktask_manager_at(i) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_manager_delete_finished() {
unsafe { oaktask::ffi::manager::oaktask_manager_delete_finished() }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_free(t: *mut CHandle) {
unsafe { oaktask::ffi::task::oaktask_task_free(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_start_sync(t: CHandle) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_start_sync(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_start(t: CHandle) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_start(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_cancel(t: CHandle) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_cancel(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_wait(t: CHandle) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_wait(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_is_finished(t: CHandle) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_is_finished(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_succeeded(t: CHandle) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_succeeded(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_title(t: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_title(t, buf, buf_size) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_error(t: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oaktask::ffi::task::oaktask_task_error(t, buf, buf_size) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_task_subscribe(t: CHandle, cb: Option<OakTaskEventFn>, userdata: *mut c_void) -> i64 {
unsafe { oaktask::ffi::task::oaktask_task_subscribe(t, cb, userdata) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_debug_alive_count() -> c_int {
unsafe { oaktask::ffi::task::oaktask_debug_alive_count() }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_create_project_load(filename: *const c_char) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_create_project_load(filename) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_load_take_project(t: CHandle) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_load_take_project(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_create_project_save(project: CHandle, filename_or_null: *const c_char, use_compression: c_int) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_create_project_save(project, filename_or_null, use_compression) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_create_project_import(folder: CHandle, project: CHandle, urls: *const *const c_char, url_count: c_int) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_create_project_import(folder, project, urls, url_count) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_import_take_command(t: CHandle) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_import_take_command(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_import_footage_count(t: CHandle) -> c_int {
unsafe { oaktask::ffi::project::oaktask_import_footage_count(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_import_footage_at(t: CHandle, index: c_int) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_import_footage_at(t, index) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_import_invalid_count(t: CHandle) -> c_int {
unsafe { oaktask::ffi::project::oaktask_import_invalid_count(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_import_invalid_at(t: CHandle, index: c_int, buf: *mut c_char, buf_size: c_int) -> c_int {
unsafe { oaktask::ffi::project::oaktask_import_invalid_at(t, index, buf, buf_size) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_create_project_load_otio(filename: *const c_char) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_create_project_load_otio(filename) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_load_otio_take_project(t: CHandle) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_load_otio_take_project(t) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_create_project_save_otio(project: CHandle, filename: *const c_char) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_create_project_save_otio(project, filename) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_load_otio_set_confirm_cb(cb: Option<OakTaskOtioImportConfirmFn>, userdata: *mut c_void) {
unsafe { oaktask::ffi::project::oaktask_load_otio_set_confirm_cb(cb, userdata) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_create_precache(footage: CHandle, index: c_int, sequence: CHandle) -> CHandle {
unsafe { oaktask::ffi::project::oaktask_create_precache(footage, index, sequence) }
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
/// `oaktask_create_export` — crosses the encoding-params C ABI POD
/// (the facade keeps its own POD mirror). Kept as a link-time
/// `extern "C"` declaration against the frozen module C ABI.
pub fn oaktask_create_export(
viewer: CHandle,
color_manager: CHandle,
params: *const crate::bridge::codec::EncodingParamsPOD,
) -> CHandle {
unsafe { oaktask_create_export_extern(viewer, color_manager, params) }
}
extern "C" {
// ---- include/task/manager.h ---------------------------------------------
/// `oaktask_manager_init` — start the global task manager.
pub fn oaktask_manager_init() -> c_int;
/// `oaktask_manager_shutdown`.
pub fn oaktask_manager_shutdown();
/// `oaktask_register_codec_submitter`.
pub fn oaktask_register_codec_submitter() -> c_int;
/// `oaktask_manager_count` — running plus failed-but-kept tasks.
pub fn oaktask_manager_count() -> c_int;
/// `oaktask_manager_at` — borrowed task handle at index.
pub fn oaktask_manager_at(i: c_int) -> CHandle;
/// `oaktask_manager_delete_finished`.
pub fn oaktask_manager_delete_finished();
// ---- include/task/task.h -------------------------------------------------
/// `oaktask_task_free` — release one reference; NULL/empty no-op.
pub fn oaktask_task_free(t: *mut CHandle);
/// `oaktask_task_start_sync` — run in the calling thread; 1 = succeeded.
pub fn oaktask_task_start_sync(t: CHandle) -> c_int;
/// `oaktask_task_start` — run on the task manager (transfers ownership).
pub fn oaktask_task_start(t: CHandle) -> c_int;
/// `oaktask_task_cancel`.
pub fn oaktask_task_cancel(t: CHandle) -> c_int;
/// `oaktask_task_wait` — wait for an asynchronously started task.
pub fn oaktask_task_wait(t: CHandle) -> c_int;
/// `oaktask_task_is_finished`.
pub fn oaktask_task_is_finished(t: CHandle) -> c_int;
/// `oaktask_task_succeeded`.
pub fn oaktask_task_succeeded(t: CHandle) -> c_int;
/// `oaktask_task_title` (two-stage string).
pub fn oaktask_task_title(t: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int;
/// `oaktask_task_error` (two-stage string).
pub fn oaktask_task_error(t: CHandle, buf: *mut c_char, buf_size: c_int) -> c_int;
/// `oaktask_task_subscribe` — subscription id >= 0 or a negative code.
pub fn oaktask_task_subscribe(t: CHandle, cb: Option<OakTaskEventFn>, userdata: *mut c_void) -> i64;
/// `oaktask_debug_alive_count`.
pub fn oaktask_debug_alive_count() -> c_int;
// ---- include/task/project.h ----------------------------------------------
/// `oaktask_create_project_load` — owned `ProjectLoadTask`.
pub fn oaktask_create_project_load(filename: *const c_char) -> CHandle;
/// `oaktask_load_take_project` — ownership transfer of the loaded project.
pub fn oaktask_load_take_project(t: CHandle) -> CHandle;
/// `oaktask_create_project_save` — owned `ProjectSaveTask`.
pub fn oaktask_create_project_save(project: CHandle, filename_or_null: *const c_char, use_compression: c_int) -> CHandle;
/// `oaktask_create_project_import` — owned `ProjectImportTask`.
pub fn oaktask_create_project_import(folder: CHandle, project: CHandle, urls: *const *const c_char, url_count: c_int) -> CHandle;
/// `oaktask_import_take_command` — ownership transfer of the undo command.
pub fn oaktask_import_take_command(t: CHandle) -> CHandle;
/// `oaktask_import_footage_count`.
pub fn oaktask_import_footage_count(t: CHandle) -> c_int;
/// `oaktask_import_footage_at` — addref'd footage handle.
pub fn oaktask_import_footage_at(t: CHandle, index: c_int) -> CHandle;
/// `oaktask_import_invalid_count`.
pub fn oaktask_import_invalid_count(t: CHandle) -> c_int;
/// `oaktask_import_invalid_at` (two-stage string).
pub fn oaktask_import_invalid_at(t: CHandle, index: c_int, buf: *mut c_char, buf_size: c_int) -> c_int;
/// `oaktask_create_project_load_otio` — owned `LoadOTIOTask`.
pub fn oaktask_create_project_load_otio(filename: *const c_char) -> CHandle;
/// `oaktask_load_otio_take_project` — ownership transfer.
pub fn oaktask_load_otio_take_project(t: CHandle) -> CHandle;
/// `oaktask_create_project_save_otio` — owned `SaveOTIOTask`.
pub fn oaktask_create_project_save_otio(project: CHandle, filename: *const c_char) -> CHandle;
/// `oaktask_load_otio_set_confirm_cb` — install/clear the confirm callback.
pub fn oaktask_load_otio_set_confirm_cb(cb: Option<OakTaskOtioImportConfirmFn>, userdata: *mut c_void);
/// `oaktask_create_precache` — owned `PreCacheTask`.
pub fn oaktask_create_precache(footage: CHandle, index: c_int, sequence: CHandle) -> CHandle;
/// `oaktask_create_export` — owned `ExportTask`; `params` is the
/// encoding-params POD (facade's `EncodingParamsPOD`).
pub fn oaktask_create_export(
#[link_name = "oaktask_create_export"]
pub fn oaktask_create_export_extern(
viewer: CHandle,
color_manager: CHandle,
params: *const crate::bridge::codec::EncodingParamsPOD,
) -> CHandle;
/// `oaktask_import_set_image_sequence_confirm_cb` — install/clear.
pub fn oaktask_import_set_image_sequence_confirm_cb(cb: Option<OakTaskImageSequenceConfirmFn>, userdata: *mut c_void);
}
/// Direct call into the `oaktask` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktask_import_set_image_sequence_confirm_cb(cb: Option<OakTaskImageSequenceConfirmFn>, userdata: *mut c_void) {
unsafe { oaktask::ffi::project::oaktask_import_set_image_sequence_confirm_cb(cb, userdata) }
}
+262 -106
View File
@@ -14,6 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oaktimeline C ABI bridge: direct Rust calls into the `oaktimeline` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oaktimeline`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oaktimeline C ABI imports, mirroring the oaktimeline crate's exports
//! (`src/timeline/rust/src/ffi.rs`; headers `include/timeline/*.h`).
//!
@@ -30,15 +57,28 @@ use crate::handle::CHandle;
// oaktimeline_marker_list_create / free / of / add / count / at /
// add_command / remove_at_command / set_time_command /
// set_props_command / list_load / list_save.
extern "C" {
/// `oaktimeline_marker_list_create` — new owning list, refcount 1.
pub fn oaktimeline_marker_list_create() -> CHandle;
/// `oaktimeline_marker_list_of` — borrowed list of a viewer node.
pub fn oaktimeline_marker_list_of(owner: CHandle) -> CHandle;
/// `oaktimeline_marker_list_free` — NULL/empty no-op; clears `list->ctx`.
pub fn oaktimeline_marker_list_free(list: *mut CHandle);
/// `oaktimeline_marker_add` — append a marker directly (no command).
pub fn oaktimeline_marker_add(
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_list_create() -> CHandle {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_create() }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_list_of(owner: CHandle) -> CHandle {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_of(owner) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_list_free(list: *mut CHandle) {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_free(list) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_add(
list: CHandle,
in_num: c_int,
in_den: c_int,
@@ -46,11 +86,19 @@ extern "C" {
out_den: c_int,
name: *const c_char,
color: c_int,
) -> c_int;
/// `oaktimeline_marker_count` — number of markers.
pub fn oaktimeline_marker_count(list: CHandle, out_count: *mut c_int) -> c_int;
/// `oaktimeline_marker_at` — marker at index; name two-stage.
pub fn oaktimeline_marker_at(
) -> c_int {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_add(list, in_num, in_den, out_num, out_den, name, color) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_count(list: CHandle, out_count: *mut c_int) -> c_int {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_count(list, out_count) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_at(
list: CHandle,
index: c_int,
in_num: *mut c_int,
@@ -60,68 +108,118 @@ extern "C" {
color: *mut c_int,
name_buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oaktimeline_marker_add_command` — owned `MarkerAddCommand`.
pub fn oaktimeline_marker_add_command(
list: CHandle,
in_num: c_int,
in_den: c_int,
out_num: c_int,
out_den: c_int,
name: *const c_char,
color: c_int,
) -> CHandle;
/// `oaktimeline_marker_remove_at_command` — owned `MarkerRemoveCommand`.
pub fn oaktimeline_marker_remove_at_command(list: CHandle, index: c_int) -> CHandle;
/// `oaktimeline_marker_set_time_command` — owned `MarkerChangeTimeCommand`.
pub fn oaktimeline_marker_set_time_command(
list: CHandle,
index: c_int,
in_num: c_int,
in_den: c_int,
out_num: c_int,
out_den: c_int,
) -> CHandle;
/// `oaktimeline_marker_set_props_command` — owned color/name command.
pub fn oaktimeline_marker_set_props_command(
list: CHandle,
index: c_int,
color: c_int,
name: *const c_char,
) -> CHandle;
/// `oaktimeline_marker_list_load` — read from an oakcommon reader.
pub fn oaktimeline_marker_list_load(list: CHandle, reader: CHandle) -> c_int;
/// `oaktimeline_marker_list_save` — write to an oakcommon writer.
pub fn oaktimeline_marker_list_save(list: CHandle, writer: CHandle) -> c_int;
) -> c_int {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_at(list, index, in_num, in_den, out_num, out_den, color, name_buf, buf_size) }
}
// ---- include/timeline/workarea.h ----------------------------------------
/// `oaktimeline_workarea_create` — new owning work area, refcount 1.
pub fn oaktimeline_workarea_create() -> CHandle;
/// `oaktimeline_workarea_of` — borrowed work area of a viewer node.
pub fn oaktimeline_workarea_of(owner: CHandle) -> CHandle;
/// `oaktimeline_workarea_free` — NULL/empty no-op; clears `w->ctx`.
pub fn oaktimeline_workarea_free(w: *mut CHandle);
/// `oaktimeline_workarea_set_enabled` — live.
pub fn oaktimeline_workarea_set_enabled(w: CHandle, enabled: c_int) -> c_int;
/// `oaktimeline_workarea_get` — state out; params may be NULL.
pub fn oaktimeline_workarea_get(
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_add_command(
list: CHandle,
in_num: c_int,
in_den: c_int,
out_num: c_int,
out_den: c_int,
name: *const c_char,
color: c_int,
) -> CHandle {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_add_command(list, in_num, in_den, out_num, out_den, name, color) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_remove_at_command(list: CHandle, index: c_int) -> CHandle {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_remove_at_command(list, index) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_set_time_command(
list: CHandle,
index: c_int,
in_num: c_int,
in_den: c_int,
out_num: c_int,
out_den: c_int,
) -> CHandle {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_set_time_command(list, index, in_num, in_den, out_num, out_den) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_set_props_command(
list: CHandle,
index: c_int,
color: c_int,
name: *const c_char,
) -> CHandle {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_set_props_command(list, index, color, name) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_list_load(list: CHandle, reader: CHandle) -> c_int {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_load(list, reader) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_marker_list_save(list: CHandle, writer: CHandle) -> c_int {
unsafe { oaktimeline::ffi::marker::oaktimeline_marker_list_save(list, writer) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_create() -> CHandle {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_create() }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_of(owner: CHandle) -> CHandle {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_of(owner) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_free(w: *mut CHandle) {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_free(w) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_set_enabled(w: CHandle, enabled: c_int) -> c_int {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_enabled(w, enabled) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_get(
w: CHandle,
in_num: *mut c_int,
in_den: *mut c_int,
out_num: *mut c_int,
out_den: *mut c_int,
enabled: *mut c_int,
) -> c_int;
/// `oaktimeline_workarea_set_range` — live.
pub fn oaktimeline_workarea_set_range(
) -> c_int {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_get(w, in_num, in_den, out_num, out_den, enabled) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_set_range(
w: CHandle,
in_num: c_int,
in_den: c_int,
out_num: c_int,
out_den: c_int,
) -> c_int;
/// `oaktimeline_workarea_set_range_command` — owned command, old range from caller.
pub fn oaktimeline_workarea_set_range_command(
) -> c_int {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_range(w, in_num, in_den, out_num, out_den) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_set_range_command(
w: CHandle,
in_num: c_int,
in_den: c_int,
@@ -131,57 +229,102 @@ extern "C" {
old_in_den: c_int,
old_out_num: c_int,
old_out_den: c_int,
) -> CHandle;
/// `oaktimeline_workarea_set_enabled_command` — owned command.
pub fn oaktimeline_workarea_set_enabled_command(w: CHandle, enabled: c_int) -> CHandle;
/// `oaktimeline_workarea_reset` — the reset sentinel range.
pub fn oaktimeline_workarea_reset(
) -> CHandle {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_range_command(w, in_num, in_den, out_num, out_den, old_in_num, old_in_den, old_out_num, old_out_den) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_set_enabled_command(w: CHandle, enabled: c_int) -> CHandle {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_set_enabled_command(w, enabled) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_reset(
in_num: *mut c_int,
in_den: *mut c_int,
out_num: *mut c_int,
out_den: *mut c_int,
) -> c_int;
/// `oaktimeline_workarea_load` — read from an oakcommon reader.
pub fn oaktimeline_workarea_load(w: CHandle, reader: CHandle) -> c_int;
/// `oaktimeline_workarea_save` — write to an oakcommon writer.
pub fn oaktimeline_workarea_save(w: CHandle, writer: CHandle) -> c_int;
) -> c_int {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_reset(in_num, in_den, out_num, out_den) }
}
// ---- include/timeline/edit.h --------------------------------------------
/// `oaktimeline_add_track_command` — owned `TimelineAddTrackCommand`.
pub fn oaktimeline_add_track_command(list: CHandle) -> CHandle;
/// `oaktimeline_remove_track_command` — owned `TimelineRemoveTrackCommand`.
pub fn oaktimeline_remove_track_command(track: CHandle) -> CHandle;
/// `oaktimeline_place_block_command` — owned `TrackPlaceBlockCommand`.
pub fn oaktimeline_place_block_command(
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_load(w: CHandle, reader: CHandle) -> c_int {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_load(w, reader) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_workarea_save(w: CHandle, writer: CHandle) -> c_int {
unsafe { oaktimeline::ffi::workarea::oaktimeline_workarea_save(w, writer) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_add_track_command(list: CHandle) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_add_track_command(list) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_remove_track_command(track: CHandle) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_remove_track_command(track) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_place_block_command(
list: CHandle,
track_index: c_int,
block: CHandle,
in_num: i64,
in_den: i64,
) -> CHandle;
/// `oaktimeline_replace_block_with_gap_command` — owned command.
pub fn oaktimeline_replace_block_with_gap_command(track: CHandle, block: CHandle) -> CHandle;
/// `oaktimeline_trim_command` — owned `BlockTrimCommand`; `mode` is an
/// `OakTimelineMovementMode` value.
pub fn oaktimeline_trim_command(
) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_place_block_command(list, track_index, block, in_num, in_den) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_replace_block_with_gap_command(track: CHandle, block: CHandle) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_replace_block_with_gap_command(track, block) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_trim_command(
track: CHandle,
block: CHandle,
new_length_num: i64,
new_length_den: i64,
mode: c_int,
) -> CHandle;
/// `oaktimeline_split_command` — owned `BlockSplitCommand` (one point).
pub fn oaktimeline_split_command(blocks: *const CHandle, count: c_int, point_num: i64, point_den: i64) -> CHandle;
/// `oaktimeline_split_preserving_links_command` — owned command.
pub fn oaktimeline_split_preserving_links_command(
) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_trim_command(track, block, new_length_num, new_length_den, mode) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_split_command(blocks: *const CHandle, count: c_int, point_num: i64, point_den: i64) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_split_command(blocks, count, point_num, point_den) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_split_preserving_links_command(
blocks: *const CHandle,
count: c_int,
point_nums: *const i64,
point_dens: *const i64,
time_count: c_int,
) -> CHandle;
/// `oaktimeline_ripple_delete_gaps_command` — owned command.
pub fn oaktimeline_ripple_delete_gaps_command(
) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_split_preserving_links_command(blocks, count, point_nums, point_dens, time_count) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_ripple_delete_gaps_command(
sequence: CHandle,
in_nums: *const i64,
in_dens: *const i64,
@@ -189,9 +332,13 @@ extern "C" {
out_dens: *const i64,
tracks: *const CHandle,
range_count: c_int,
) -> CHandle;
/// `oaktimeline_slide_command` — owned `TrackSlideCommand`.
pub fn oaktimeline_slide_command(
) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_ripple_delete_gaps_command(sequence, in_nums, in_dens, out_nums, out_dens, tracks, range_count) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_slide_command(
track: CHandle,
blocks: *const CHandle,
block_count: c_int,
@@ -199,22 +346,31 @@ extern "C" {
out_adjacent: CHandle,
movement_num: i64,
movement_den: i64,
) -> CHandle;
/// `oaktimeline_ripple_remove_area_command` — owned
/// `TrackRippleRemoveAreaCommand`.
pub fn oaktimeline_ripple_remove_area_command(
) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_slide_command(track, blocks, block_count, in_adjacent, out_adjacent, movement_num, movement_den) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_ripple_remove_area_command(
track: CHandle,
in_num: i64,
in_den: i64,
out_num: i64,
out_den: i64,
) -> CHandle;
/// `oaktimeline_insert_gaps_command` — owned `TrackListInsertGaps`.
pub fn oaktimeline_insert_gaps_command(
) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_ripple_remove_area_command(track, in_num, in_den, out_num, out_den) }
}
/// Direct call into the `oaktimeline` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oaktimeline_insert_gaps_command(
list: CHandle,
point_num: i64,
point_den: i64,
length_num: i64,
length_den: i64,
) -> CHandle;
) -> CHandle {
unsafe { oaktimeline::ffi::edit::oaktimeline_insert_gaps_command(list, point_num, point_den, length_num, length_den) }
}
+168 -62
View File
@@ -14,6 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakundo C ABI bridge: direct Rust calls into the `oakundo` crate.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): every
//! call below is a compile-time Rust call into `oakundo`'s `ffi` (the
//! `#[no_mangle]` exports stay in the dylib for the external C ABI;
//! internal callers bypass them). Handles cross as the shared
//! [`crate::handle::CHandle`]. Exceptions that keep an `extern "C"`
//! declaration (resolved at link time against the sibling crate in the
//! same dylib) are the host `oakcore_*` symbols and the encoding-params
//! C ABI POD crossings (the facade keeps its own POD mirrors there).
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakundo C ABI imports, mirroring the oakundo crate's exports
//! (`src/undo/rust/src/ffi.rs`; headers `include/undo/*.h`).
@@ -21,82 +48,161 @@ use std::ffi::{c_char, c_int};
use crate::handle::CHandle;
extern "C" {
/// `oakundo_command_init` — vtable-backed command, refcount 1.
pub fn oakundo_command_init(
/// `include/undo/undocommand.h` — callback table backing a
/// caller-defined undo command. Single-lib unification: aliases the
/// oakundo crate's vtable POD.
pub type OakUndoCommandVtable = oakundo::undocommand::OakUndoCommandVtable;
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_init(
vtable: *const OakUndoCommandVtable,
userdata: *mut std::ffi::c_void,
) -> CHandle;
/// `oakundo_command_init_multi` — empty multi command, refcount 1.
pub fn oakundo_command_init_multi() -> CHandle;
/// `oakundo_command_multi_add_child` (stack takes one child ref).
pub fn oakundo_command_multi_add_child(multi: CHandle, child: CHandle) -> c_int;
/// `oakundo_command_multi_child_count`.
pub fn oakundo_command_multi_child_count(multi: CHandle, out_count: *mut c_int) -> c_int;
/// `oakundo_command_multi_child` (returned handle carries own ref).
pub fn oakundo_command_multi_child(
) -> CHandle {
unsafe { oakundo::ffi::command::oakundo_command_init(vtable, userdata) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_init_multi() -> CHandle {
unsafe { oakundo::ffi::command::oakundo_command_init_multi() }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_multi_add_child(multi: CHandle, child: CHandle) -> c_int {
unsafe { oakundo::ffi::command::oakundo_command_multi_add_child(multi, child) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_multi_child_count(multi: CHandle, out_count: *mut c_int) -> c_int {
unsafe { oakundo::ffi::command::oakundo_command_multi_child_count(multi, out_count) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_multi_child(
multi: CHandle,
index: c_int,
out_child: *mut CHandle,
) -> c_int;
/// `oakundo_command_redo_now`.
pub fn oakundo_command_redo_now(command: CHandle) -> c_int;
/// `oakundo_command_undo_now`.
pub fn oakundo_command_undo_now(command: CHandle) -> c_int;
/// `oakundo_command_free` — NULL/empty no-op; clears `command->ctx`.
pub fn oakundo_command_free(command: *mut CHandle);
/// `oakundo_undostack_init` — fresh stack, refcount 1.
pub fn oakundo_undostack_init() -> CHandle;
/// `oakundo_undostack_free` — NULL/empty no-op; clears `stack->ctx`.
pub fn oakundo_undostack_free(stack: *mut CHandle);
/// `oakundo_undostack_push` — redo then record; drops redoable tail.
pub fn oakundo_undostack_push(stack: CHandle, command: CHandle, name: *const c_char) -> c_int;
/// `oakundo_undostack_push_pre_executed` — record without redoing.
pub fn oakundo_undostack_push_pre_executed(
) -> c_int {
unsafe { oakundo::ffi::command::oakundo_command_multi_child(multi, index, out_child) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_redo_now(command: CHandle) -> c_int {
unsafe { oakundo::ffi::command::oakundo_command_redo_now(command) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_undo_now(command: CHandle) -> c_int {
unsafe { oakundo::ffi::command::oakundo_command_undo_now(command) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_command_free(command: *mut CHandle) {
unsafe { oakundo::ffi::command::oakundo_command_free(command) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_init() -> CHandle {
unsafe { oakundo::ffi::undostack::oakundo_undostack_init() }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_free(stack: *mut CHandle) {
unsafe { oakundo::ffi::undostack::oakundo_undostack_free(stack) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_push(stack: CHandle, command: CHandle, name: *const c_char) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_push(stack, command, name) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_push_pre_executed(
stack: CHandle,
command: CHandle,
name: *const c_char,
) -> c_int;
/// `oakundo_undostack_undo`.
pub fn oakundo_undostack_undo(stack: CHandle) -> c_int;
/// `oakundo_undostack_redo`.
pub fn oakundo_undostack_redo(stack: CHandle) -> c_int;
/// `oakundo_undostack_jump` (clamped to 0; `index` is i64).
pub fn oakundo_undostack_jump(stack: CHandle, index: i64) -> c_int;
/// `oakundo_undostack_clear`.
pub fn oakundo_undostack_clear(stack: CHandle) -> c_int;
/// `oakundo_undostack_can_undo`.
pub fn oakundo_undostack_can_undo(stack: CHandle, out_value: *mut c_int) -> c_int;
/// `oakundo_undostack_can_redo`.
pub fn oakundo_undostack_can_redo(stack: CHandle, out_value: *mut c_int) -> c_int;
/// `oakundo_undostack_count`.
pub fn oakundo_undostack_count(stack: CHandle, out_count: *mut i64) -> c_int;
/// `oakundo_undostack_index`.
pub fn oakundo_undostack_index(stack: CHandle, out_index: *mut i64) -> c_int;
/// `oakundo_undostack_command_text` (two-stage string getter).
pub fn oakundo_undostack_command_text(
) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_push_pre_executed(stack, command, name) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_undo(stack: CHandle) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_undo(stack) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_redo(stack: CHandle) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_redo(stack) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_jump(stack: CHandle, index: i64) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_jump(stack, index) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_clear(stack: CHandle) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_clear(stack) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_can_undo(stack: CHandle, out_value: *mut c_int) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_can_undo(stack, out_value) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_can_redo(stack: CHandle, out_value: *mut c_int) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_can_redo(stack, out_value) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_count(stack: CHandle, out_count: *mut i64) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_count(stack, out_count) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_index(stack: CHandle, out_index: *mut i64) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_index(stack, out_index) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_command_text(
stack: CHandle,
row: i64,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakundo_undostack_command_is_done`.
pub fn oakundo_undostack_command_is_done(
) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_command_text(stack, row, buf, buf_size) }
}
/// Direct call into the `oakundo` crate (single-lib unification; the
/// `#[no_mangle]` export stays for the external C ABI).
pub fn oakundo_undostack_command_is_done(
stack: CHandle,
row: i64,
out_value: *mut c_int,
) -> c_int;
) -> c_int {
unsafe { oakundo::ffi::undostack::oakundo_undostack_command_is_done(stack, row, out_value) }
}
/// `include/undo/undocommand.h` — callback table backing a
/// caller-defined undo command.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakUndoCommandVtable {
/// Redo callback (NULL = no-op).
pub redo: Option<unsafe extern "C" fn(userdata: *mut std::ffi::c_void)>,
/// Undo callback (NULL = no-op).
pub undo: Option<unsafe extern "C" fn(userdata: *mut std::ffi::c_void)>,
/// Destruction callback releasing `userdata`.
pub free_fn: Option<unsafe extern "C" fn(userdata: *mut std::ffi::c_void)>,
}
+7 -57
View File
@@ -40,63 +40,13 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::error::{Error, Result};
/// ABI-neutral mirror of the module handle struct
/// (`{ctx, addref, release, abi_version}`). Structurally identical to
/// every `Oak<Mod><Type>` value handle in `include/<mod>/*.h`, so the
/// facade can copy a handle across the module boundary without knowing
/// the module's concrete type names.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut c_void)>,
/// ABI version.
pub abi_version: u32,
}
impl CHandle {
/// The empty handle.
pub const fn null() -> Self {
CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: 0,
}
}
/// Whether this is the empty (zero) handle.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
/// Take an additional reference through the handle's `addref` and
/// return the (now refcount-incremented) copy.
///
/// # Safety
/// `self` must be a live handle returned by a module function.
pub unsafe fn addref(&self) -> Self {
let mut copy = *self;
if let Some(addref) = copy.addref {
let _ = &mut copy;
unsafe {
addref(copy.ctx);
}
}
copy
}
}
// Module handles follow the shared_ptr-like convention of
// `include/common/handle.h`: they are opaque, refcounted and safe to
// share across threads (the module crates themselves use them behind
// mutexes). The facade therefore declares them Send + Sync.
unsafe impl Send for CHandle {}
unsafe impl Sync for CHandle {}
/// The shared ABI value-handle type (single-lib unification, see
/// `docs/zh/plans/riir/single-lib.md`): one canonical
/// `{ctx, addref, release, abi_version}` type in `oakcore-rs`, re-exported
/// by every module crate, so the facade can pass a handle straight into a
/// module's `pub` Rust functions without an `extern "C"` declaration.
/// `Clone + Copy + Send + Sync` come from the shared type.
pub use oakcore_rs::handle::CHandle;
/// Engine opaque handle types, one per `typedef struct OakEngine*` in
/// `engine/include/oakengine/*.h`. All are thin newtype wrappers around a
+8 -17
View File
@@ -33,20 +33,11 @@ use crate::undo::push_or_run;
/// `engine/include/oakengine/node.h` — POD mirror of `oak_node_value`.
///
/// Layout-identical to the module's `oaknode_value`, so the facade hands
/// the engine POD straight to the oaknode bridge.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakNodeValue {
/// `oak_node_value_type`.
pub type_: c_int,
/// INT/COMBO value, BOOL 0/1, RATIONAL numerator.
pub num: i64,
/// RATIONAL denominator.
pub den: i64,
/// FLOAT f[0]; VEC2/3/4 f[0..n-1]; COLOR r,g,b,a.
pub f: [f64; 4],
}
/// Single-lib unification: aliases the oaknode crate's POD (the module
/// C ABI struct `oak_node_value`), so the facade can pass it straight
/// into `oaknode::ffi` functions without an `extern "C"` declaration.
/// The field is named `kind` on the shared type (was `type_`).
pub type OakNodeValue = oaknode::value::OakNodeValue;
/// `engine/include/oakengine/footage.h` — POD mirror of
/// `oak_footage_video_info` (olive::VideoParams stream description).
@@ -5584,7 +5575,7 @@ pub unsafe extern "C" fn oakengine_node_value_split_to_tracks(
let count = oakengine_node_value_keyframe_track_count(c_type).min(track_count);
for i in 0..count as usize {
let mut t = OakNodeValue {
type_: c_type,
kind: c_type,
num: 0,
den: 0,
f: [0.0; 4],
@@ -5604,7 +5595,7 @@ pub unsafe extern "C" fn oakengine_node_value_split_to_tracks(
t.f[0] = n.f[0];
}
_ => {
t.type_ = c_type;
t.kind = c_type;
t.f = n.f;
}
}
@@ -5627,7 +5618,7 @@ pub unsafe extern "C" fn oakengine_node_value_combine_tracks(
return Err(Error::Invalid);
}
let mut out = OakNodeValue {
type_: c_type,
kind: c_type,
num: 0,
den: 0,
f: [0.0; 4],
+1 -1
View File
@@ -4794,7 +4794,7 @@ pub unsafe extern "C" fn oakengine_track_block_at_time(
unsafe fn track_nearest_boxed(
track: *const OakEngineTrack,
timestamp: i64,
via: unsafe extern "C" fn(CHandle, c_int, c_int, *mut CHandle) -> c_int,
via: fn(CHandle, c_int, c_int, *mut CHandle) -> c_int,
) -> *mut OakEngineBlock {
guard_ptr(|| unsafe {
if track.is_null() {
+3 -3
View File
@@ -75,7 +75,7 @@ fn force_oakundo_command_link() -> usize {
/// A float POD value.
fn float_value(x: f64) -> OakNodeValue {
OakNodeValue {
type_: 2, // OAK_NODE_VALUE_FLOAT
kind: 2, // OAK_NODE_VALUE_FLOAT
num: 0,
den: 0,
f: [x, 0.0, 0.0, 0.0],
@@ -229,7 +229,7 @@ fn project_node_keyframe_lifecycle() {
unsafe { oakengine_node_get_input(value, c"value_in".as_ptr(), &mut out) },
0
);
assert_eq!(out.type_, 2);
assert_eq!(out.kind, 2);
assert!((out.f[0] - 3.5).abs() < 1e-6);
// ---- connect / disconnect -------------------------------------------
@@ -265,7 +265,7 @@ fn project_node_keyframe_lifecycle() {
unsafe { oakengine_node_get_input_at_time(value, c"value_in".as_ptr(), -1, -1, 0, 0, &mut at) },
0
);
assert_eq!(at.type_, 2);
assert_eq!(at.kind, 2);
assert!((at.f[0] - 0.5).abs() < 1e-6);
// ---- project save → fresh load round-trip ---------------------------
+334
View File
@@ -2,6 +2,168 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bytemuck"
version = "1.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "cc"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cmake"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
dependencies = [
"cc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "fax"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]]
name = "find-msvc-tools"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "half"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
"cfg-if",
"crunchy",
"zerocopy",
]
[[package]]
name = "image"
version = "0.25.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
dependencies = [
"bytemuck",
"byteorder-lite",
"moxcms",
"num-traits",
"tiff",
]
[[package]]
name = "log"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "moxcms"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "oakcommon"
version = "0.1.0"
dependencies = [
"image",
"log",
"oakcore-rs",
"ocio-rs",
"quick-xml",
]
[[package]]
name = "oakcore-rs"
version = "0.1.0"
@@ -9,6 +171,178 @@ version = "0.1.0"
[[package]]
name = "oaknode"
version = "0.1.0"
dependencies = [
"oakcommon",
"oakcore-rs",
"oakundo",
]
[[package]]
name = "oakundo"
version = "0.1.0"
dependencies = [
"oakcore-rs",
]
[[package]]
name = "ocio-rs"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3492534019b59e29dba06014f907dd12824537ed4d293d4108c4bfc669de7fd"
dependencies = [
"ocio-sys",
"thiserror",
]
[[package]]
name = "ocio-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63251d72d848de5eda39d59cd6490260cf031738ebd518ea37d76b5aae614ec"
dependencies = [
"cc",
"cmake",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pxfm"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tiff"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
dependencies = [
"fax",
"flate2",
"half",
"quick-error",
"weezl",
"zune-jpeg",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "zerocopy"
version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zune-core"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b"
[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
+2
View File
@@ -14,6 +14,8 @@ panic = "unwind"
[dependencies]
oakcore-rs = { path = "../../oakcore-rs" }
oakcommon = { path = "../../common/rust" }
oakundo = { path = "../../undo/rust" }
[features]
# In-crate stubs for the oakundo C ABI (and other module bridges) so
+37 -52
View File
@@ -14,19 +14,19 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakundo C ABI imports. Undo commands are created through the C ABI
//! vtable (`oakundo_command_init` with Rust closures as userdata) — no
//! C++ UndoCommand subclassing exists on this side. Symbols resolve via
//! `dlsym(RTLD_DEFAULT)` (see [`super`]).
//! oakundo C ABI calls — now direct Rust calls into the oakundo crate
//! (single-lib unification, see `docs/zh/plans/riir/single-lib.md`).
//! Undo commands are created through the C ABI vtable
//! (`oakundo_command_init` with Rust closures as userdata) — no C++
//! UndoCommand subclassing exists on this side.
//!
//! ## Test stubs (`--features test-stubs`)
//!
//! `cargo test` builds without liboakundo, so the feature compiles
//! in-crate `#[no_mangle]` implementations of the undo C ABI
//! (see [`stub`]) that run Rust closures directly. `dlsym` then resolves
//! the stub symbols from the test binary's global scope, so the undoable
//! exports run end-to-end in tests. Real module builds (feature off)
//! resolve the actual oakundo library.
//! With the direct dependency, the real oakundo rlib is linked into
//! every build and test; the `test-stubs` feature still compiles
//! in-crate `#[no_mangle]` implementations of the undo C ABI (see
//! [`stub`]) for environments that link without oakundo. Do not enable
//! the feature in a binary that also links oakundo (duplicate symbols).
use std::ffi::c_int;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -34,17 +34,9 @@ use std::sync::atomic::{AtomicBool, Ordering};
use crate::handle::CHandle;
/// `OakUndoCommandVtable` (include/undo/undocommand.h) — the callback
/// table backing a caller-defined undo command.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Vtable {
/// Execute the redo.
pub redo: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Execute the undo.
pub undo: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Release `userdata` (invoked when the command is destroyed).
pub free_fn: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
}
/// table backing a caller-defined undo command. Single-lib unification:
/// aliases the oakundo crate's vtable POD (identical layout).
pub type Vtable = oakundo::undocommand::OakUndoCommandVtable;
/// Rust closure state behind a vtable command's `userdata` pointer.
///
@@ -130,11 +122,13 @@ pub fn command_init(vtable: &Vtable, userdata: *mut std::ffi::c_void) -> Option<
/// `oakundo_command_init` (vtable command).
#[cfg(not(feature = "test-stubs"))]
pub fn command_init(vtable: &Vtable, userdata: *mut std::ffi::c_void) -> Option<CHandle> {
use crate::bridge::dlsym;
type F = unsafe extern "C" fn(*const Vtable, *mut std::ffi::c_void) -> CHandle;
dlsym::call::<F, CHandle>("oakundo_command_init", |f| unsafe {
f(vtable as *const Vtable, userdata)
})
// Direct call into the oakundo crate (single-lib unification).
let h = unsafe { oakundo::ffi::command::oakundo_command_init(vtable, userdata) };
if h.is_null() {
None
} else {
Some(h)
}
}
/// `oakundo_command_init_multi`.
@@ -146,9 +140,13 @@ pub fn command_init_multi() -> Option<CHandle> {
/// `oakundo_command_init_multi`.
#[cfg(not(feature = "test-stubs"))]
pub fn command_init_multi() -> Option<CHandle> {
use crate::bridge::dlsym;
type F = unsafe extern "C" fn() -> CHandle;
dlsym::call::<F, CHandle>("oakundo_command_init_multi", |f| unsafe { f() })
// Direct call into the oakundo crate (single-lib unification).
let h = unsafe { oakundo::ffi::command::oakundo_command_init_multi() };
if h.is_null() {
None
} else {
Some(h)
}
}
/// `oakundo_command_multi_add_child`.
@@ -160,11 +158,8 @@ pub fn command_multi_add_child(multi: CHandle, child: CHandle) -> Option<c_int>
/// `oakundo_command_multi_add_child`.
#[cfg(not(feature = "test-stubs"))]
pub fn command_multi_add_child(multi: CHandle, child: CHandle) -> Option<c_int> {
use crate::bridge::dlsym;
type F = unsafe extern "C" fn(CHandle, CHandle) -> c_int;
dlsym::call::<F, c_int>("oakundo_command_multi_add_child", |f| unsafe {
f(multi, child)
})
// Direct call into the oakundo crate (single-lib unification).
Some(unsafe { oakundo::ffi::command::oakundo_command_multi_add_child(multi, child) })
}
/// `oakundo_command_redo_now`.
@@ -176,9 +171,8 @@ pub fn command_redo_now(command: CHandle) -> Option<c_int> {
/// `oakundo_command_redo_now`.
#[cfg(not(feature = "test-stubs"))]
pub fn command_redo_now(command: CHandle) -> Option<c_int> {
use crate::bridge::dlsym;
type F = unsafe extern "C" fn(CHandle) -> c_int;
dlsym::call::<F, c_int>("oakundo_command_redo_now", |f| unsafe { f(command) })
// Direct call into the oakundo crate (single-lib unification).
Some(unsafe { oakundo::ffi::command::oakundo_command_redo_now(command) })
}
/// `oakundo_command_undo_now`.
@@ -190,9 +184,8 @@ pub fn command_undo_now(command: CHandle) -> Option<c_int> {
/// `oakundo_command_undo_now`.
#[cfg(not(feature = "test-stubs"))]
pub fn command_undo_now(command: CHandle) -> Option<c_int> {
use crate::bridge::dlsym;
type F = unsafe extern "C" fn(CHandle) -> c_int;
dlsym::call::<F, c_int>("oakundo_command_undo_now", |f| unsafe { f(command) })
// Direct call into the oakundo crate (single-lib unification).
Some(unsafe { oakundo::ffi::command::oakundo_command_undo_now(command) })
}
/// `oakundo_command_free`.
@@ -204,22 +197,14 @@ pub fn command_free(command: *mut CHandle) {
/// `oakundo_command_free`.
#[cfg(not(feature = "test-stubs"))]
pub fn command_free(command: *mut CHandle) {
use crate::bridge::dlsym;
type F = unsafe extern "C" fn(*mut CHandle);
if let Some(f) = dlsym::call::<F, ()>("oakundo_command_free", |f| unsafe {
f(command)
}) {
let _ = f;
}
// Direct call into the oakundo crate (single-lib unification).
unsafe { oakundo::ffi::command::oakundo_command_free(command) }
}
/// `oakundo_stack_push` (facade-owned stack).
pub fn stack_push(stack: CHandle, command: CHandle, text: *const std::ffi::c_char) -> Option<c_int> {
use crate::bridge::dlsym;
type F = unsafe extern "C" fn(CHandle, CHandle, *const std::ffi::c_char) -> c_int;
dlsym::call::<F, c_int>("oakundo_stack_push", |f| unsafe {
f(stack, command, text)
})
// Direct call into the oakundo crate (single-lib unification).
Some(unsafe { oakundo::ffi::undostack::oakundo_undostack_push(stack, command, text) })
}
/// In-crate implementations of the undo C ABI for `cargo test`
+9 -29
View File
@@ -55,19 +55,11 @@ impl ColorManager {
}
/// Load the built-in default config (C++ `ColorManager::init()`).
/// The config load itself goes through the oakrender color C ABI;
/// when oakrender is absent (cargo test) the manager still marks the
/// config loaded with the built-in defaults so pure-graph consumers
/// can query state.
/// The oakrender color C ABI never exported the config-loading
/// symbols, so this always took the "mark loaded with the built-in
/// defaults" path (single-lib: the render call is removed and the
/// deterministic equivalent kept).
pub fn initialize(&mut self) -> crate::error::Result<()> {
match crate::bridge::render::color_config_create_default() {
Some(Err(())) => {
return Err(crate::error::Error::Failed(
"OCIO config creation failed".to_string(),
));
}
_ => {}
}
self.config_loaded = true;
Ok(())
}
@@ -79,30 +71,18 @@ impl ColorManager {
Ok(())
}
/// (Re)load the config from `config_filename` via the oakrender
/// color C ABI (`oakrender_color_manager_*`); E_FAILED on OCIO
/// (Re)load the config from `config_filename`; E_FAILED on OCIO
/// errors. Missing/invalid files keep the previous config (C++
/// `update_config_from_filename()`).
/// `update_config_from_filename()`). The oakrender color C ABI never
/// implemented the load, so the file-loaded path always kept the
/// previous state (single-lib: the render call is removed).
pub fn update_config_from_filename(&mut self) -> crate::error::Result<()> {
if self.config_filename.is_empty() {
// Empty filename selects the bundled default.
self.config_loaded = true;
return Ok(());
}
match crate::bridge::render::color_config_load(&self.config_filename) {
Some(Ok(())) => {
self.config_loaded = true;
Ok(())
}
Some(Err(())) => {
// Invalid file: keep the previous config (C++ tolerance).
Ok(())
}
None => {
// oakrender absent (tests): keep the previous state.
Ok(())
}
}
Ok(())
}
/// Enumerate colorspaces of the active config (two-stage lists are
+6 -39
View File
@@ -37,45 +37,12 @@ pub struct RefBox<T: ?Sized> {
pub value: T,
}
/// `#[repr(C)]` mirror of the public handle structs
/// (`{ctx, addref, release, abi_version}`).
#[repr(C)]
#[derive(Clone, Debug)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
impl CHandle {
/// The empty handle.
pub fn null() -> Self {
Self {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: OAKNODE_ABI_VERSION,
}
}
/// True when `ctx` is null.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
// CHandle is the C-side value handle (passed by value, copyable across
// threads); `ctx` is an opaque box pointer. The addref/release functions
// are atomic, so sharing the box between threads is the host's
// dispatching contract (same rationale as the oakplugin crate's
// handle.rs).
unsafe impl Send for CHandle {}
unsafe impl Sync for CHandle {}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible.
pub use oakcore_rs::handle::CHandle;
/// addref 的实现:原子 +1。拥有型与借用型共用——借用型只延长盒子
/// 的寿命,不延长被借用对象。
+3 -5
View File
@@ -311,11 +311,9 @@ impl Project {
}
_ => {}
}
// Default location: ask oakrender via the C ABI.
match crate::bridge::render::disk_cache_path() {
Some(path) => path,
None => String::new(),
}
// Default location: the shared disk-cache directory (single-lib:
// lives in oakcommon, used by oaknode and oakrender alike).
oakcommon::filefunctions::default_disk_cache_path()
}
/// Copy all settings from `src` into `self` (C++
+95
View File
@@ -0,0 +1,95 @@
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// 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 shared ABI value-handle type (single-lib unification).
//!
//! Every module crate used to define its own `CHandle` with an identical
//! `{ctx, addref, release, abi_version}` layout but a distinct Rust type,
//! so cross-module calls had to cross an `extern "C"`/dlsym boundary.
//! With the module-to-module calls going back to direct Rust calls (see
//! `docs/zh/plans/riir/single-lib.md`), one canonical handle type lives
//! here in `oakcore-rs` (the crate every module already depends on) and
//! every crate re-exports it. The C layout is unchanged, so the frozen
//! module C ABIs (`Oak<Mod><Type>*` handles in `include/<mod>/*.h`) are
//! unaffected.
use std::ffi::c_void;
/// ABI value handle mirroring the C handle struct
/// (`{ctx, addref, release, abi_version}`) from `include/common/handle.h`.
///
/// `ctx` points to an opaque refcounted box; `addref`/`release` are the
/// box's atomic increment/decrement (release destroys at zero);
/// `abi_version` is a tag stamped by each crate's `make_owned` (0 for
/// empty handles). Structurally identical to every `Oak<Mod><Type>` value
/// handle, so a handle can cross any module boundary by value.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut c_void)>,
/// ABI version.
pub abi_version: u32,
}
impl CHandle {
/// The empty handle.
pub const fn null() -> Self {
CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: 0,
}
} /// Whether this is the empty (zero) handle.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
/// Take an additional reference through the handle's `addref` and
/// return the (now refcount-incremented) copy.
///
/// # Safety
/// `self` must be a live handle returned by a module function.
pub unsafe fn addref(&self) -> Self {
let mut copy = *self;
if let Some(addref) = copy.addref {
let _ = &mut copy;
unsafe {
addref(copy.ctx);
}
}
copy
}
}
/// The empty handle is the natural default (some crates derive `Default`
/// on structs holding a handle).
impl Default for CHandle {
fn default() -> Self {
Self::null()
}
}
// Handles follow the shared_ptr-like convention of
// `include/common/handle.h`: they are opaque, refcounted and safe to
// share across threads (the module crates use them behind mutexes).
unsafe impl Send for CHandle {}
unsafe impl Sync for CHandle {}
+4
View File
@@ -25,6 +25,10 @@ mod rational;
mod samplefmt;
mod timerange;
/// Shared ABI value-handle type (see [`handle::CHandle`]).
pub mod handle;
pub use handle::CHandle;
pub use rational::Rational;
pub use samplefmt::{PixelFormat, SampleFormat};
pub use timerange::{TimeRange, TimeRangeList};
+5
View File
@@ -18,11 +18,16 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "oakcore-rs"
version = "0.1.0"
[[package]]
name = "oakplugin"
version = "0.1.0"
dependencies = [
"cc",
"oakcore-rs",
]
[[package]]
+2
View File
@@ -17,7 +17,9 @@ crate-type = ["staticlib", "rlib"]
panic = "unwind"
[dependencies]
# 共享 ABI 类型(CHandle 等,单库化统一,见 docs/zh/plans/riir/single-lib.md)。
# 只允许零依赖起步。确需引入的 crate 必须在 README 登记理由。
oakcore-rs = { path = "../../oakcore-rs" }
[features]
# oakrender 测试桩(库内 no_mangle 符号 + 状态访问器):cargo test
+6 -38
View File
@@ -53,44 +53,12 @@ pub struct RefBox<T: ?Sized> {
/// C 句柄的 Rust 镜像。`#[repr(C)]`,与 C 头文件布局一致。
///
/// 生命周期:`*_init`/`*_create` 返回计数 1 的拥有型句柄;
/// `*_free(&h)` 释放一次并清空 `ctx``free(NULL)`/空句柄是 no-op。
/// 值型(Clone/CopyC 侧按值传句柄,复制后再 addref 是调用方
/// 契约)。
#[derive(Clone, Copy)]
#[repr(C)]
pub struct CHandle {
/// 不透明盒子指针(`RefBox<T>` 擦型后的 `*mut c_void`)。
pub ctx: *mut std::ffi::c_void,
/// 原子 +1。
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// 原子 -1,归零销毁。
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// [`OAKPLUGIN_ABI_VERSION`]。
pub abi_version: u32,
}
// CHandle 是 C 侧值型句柄(按值传、可跨线程复制);`ctx` 是
// 不透明盒子指针,跨线程搬运是宿主分发语义(multithread suite
// 允许插件线程回调任意 suite)。
unsafe impl Send for CHandle {}
unsafe impl Sync for CHandle {}
impl CHandle {
/// 空句柄(`ctx == NULL`)。
pub fn null() -> Self {
Self {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: OAKPLUGIN_ABI_VERSION,
}
}
/// 是否为空调用面(`ctx` 为空)。
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible. `Send + Sync` come from the shared type.
pub use oakcore_rs::handle::CHandle;
/// addref 的实现:原子 +1。拥有型与借用型共用——借用型只延长盒子
/// 的寿命,不延长被借用对象。
+150
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "android_system_properties"
version = "0.1.6"
@@ -94,6 +100,12 @@ dependencies = [
"syn 3.0.3",
]
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "cc"
version = "1.4.2"
@@ -163,6 +175,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crunchy"
version = "0.2.4"
@@ -184,12 +205,28 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fax"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]]
name = "find-msvc-tools"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "foldhash"
version = "0.1.5"
@@ -369,6 +406,19 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "image"
version = "0.25.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
dependencies = [
"bytemuck",
"byteorder-lite",
"moxcms",
"num-traits",
"tiff",
]
[[package]]
name = "indexmap"
version = "2.14.0"
@@ -487,6 +537,12 @@ dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "metal"
version = "0.31.0"
@@ -502,6 +558,26 @@ dependencies = [
"paste",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "moxcms"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "naga"
version = "25.0.1"
@@ -546,6 +622,17 @@ dependencies = [
"libm",
]
[[package]]
name = "oakcommon"
version = "0.1.0"
dependencies = [
"image",
"log",
"oakcore-rs",
"ocio-rs",
"quick-xml",
]
[[package]]
name = "oakcore-rs"
version = "0.1.0"
@@ -554,6 +641,7 @@ version = "0.1.0"
name = "oakrender"
version = "0.1.0"
dependencies = [
"oakcommon",
"oakcore-rs",
"ocio-rs",
"wgpu",
@@ -671,6 +759,27 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
[[package]]
name = "pxfm"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.47"
@@ -761,6 +870,12 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "slab"
version = "0.4.12"
@@ -890,6 +1005,20 @@ dependencies = [
"syn 3.0.3",
]
[[package]]
name = "tiff"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
dependencies = [
"fax",
"flate2",
"half",
"quick-error",
"weezl",
"zune-jpeg",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -973,6 +1102,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "wgpu"
version = "25.0.2"
@@ -1297,3 +1432,18 @@ dependencies = [
"quote",
"syn 2.0.119",
]
[[package]]
name = "zune-core"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b"
[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
+1
View File
@@ -15,6 +15,7 @@ panic = "unwind"
[dependencies]
oakcore-rs = { path = "../../oakcore-rs" }
oakcommon = { path = "../../common/rust" }
# wgpu: portable GPU backend (Metal/Vulkan/GL/DX12 in one safe API) — the
# direct replacement for the C++ liboakgl2/liboakvulkan backend plugins.
# Version 25 (2025 stable line); the only GPU dependency.
+7 -75
View File
@@ -16,79 +16,11 @@
//! The cancellation primitive (`olive::CancelAtom`): a thread-safe cancel
//! flag shared between a render/encode caller and its worker.
//!
//! Single-lib unification (see `docs/zh/plans/riir/single-lib.md`): the
//! implementation moved to oakcommon; this module re-exports it so the
//! render ffi's `oakrender_cancelatom_*` exports (and their C-ABI
//! consumers, e.g. oakcodec) keep working unchanged.
use std::sync::{Mutex, MutexGuard};
/// A cancellation atom (C++ `CancelAtom`). Reading a set flag also records
/// that a consumer heard the cancellation.
#[derive(Default)]
pub struct CancelAtom {
state: Mutex<AtomState>,
}
#[derive(Default)]
struct AtomState {
cancelled: bool,
heard: bool,
}
fn lock(m: &Mutex<AtomState>) -> MutexGuard<'_, AtomState> {
m.lock().unwrap_or_else(|e| e.into_inner())
}
impl CancelAtom {
/// A not-cancelled atom.
pub fn new() -> Self {
Self::default()
}
/// Set the cancel flag (C++ `CancelAtom::cancel()`).
pub fn cancel(&self) {
lock(&self.state).cancelled = true;
}
/// Read the cancel flag; reading a set flag records that the
/// cancellation was heard (C++ `is_cancelled()`).
pub fn is_cancelled(&self) -> bool {
let mut s = lock(&self.state);
if s.cancelled {
s.heard = true;
}
s.cancelled
}
/// Whether any consumer has observed the cancel flag through
/// [`CancelAtom::is_cancelled`] (C++ `heard_cancel()`).
pub fn heard_cancel(&self) -> bool {
lock(&self.state).heard
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_atom_is_not_cancelled() {
let a = CancelAtom::new();
assert!(!a.is_cancelled());
assert!(!a.heard_cancel());
}
#[test]
fn cancel_sets_flag_and_reading_marks_heard() {
let a = CancelAtom::new();
a.cancel();
assert!(!a.heard_cancel(), "not heard until read");
assert!(a.is_cancelled());
assert!(a.heard_cancel(), "reading a set flag marks it heard");
}
#[test]
fn repeated_cancel_is_idempotent() {
let a = CancelAtom::new();
a.cancel();
a.cancel();
assert!(a.is_cancelled());
}
}
/// Shared cancellation atom (oakcommon).
pub use oakcommon::cancelatom::CancelAtom;
+2 -7
View File
@@ -926,13 +926,8 @@ pub mod cache {
if !std::path::Path::new(&filename).exists() {
return Err(crate::error::Error::NotFound);
}
// Payload decode belongs to the oakcodec crate (EXR/JPEG).
// Without it the file exists but cannot be decoded.
if !crate::bridge::codec::codec_abi_available() {
return Err(crate::error::Error::Failed(
"oakcodec C ABI not present (frame decode pending)".into(),
));
}
// Payload decode belongs to the oakcodec crate (EXR/JPEG),
// deferred; without it the file exists but cannot be decoded.
Err(crate::error::Error::Failed(
"oakcodec frame decode bridge pending".into(),
))
+6 -36
View File
@@ -42,42 +42,12 @@ pub struct RefBox<T: ?Sized> {
pub value: T,
}
/// `#[repr(C)]` mirror of the public handle structs.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
// CHandle is a by-value handle (copied across threads per the shared_ptr
// semantics documented in the public headers); `ctx` is an opaque box
// pointer, and moving the struct itself never dereferences it.
unsafe impl Send for CHandle {}
unsafe impl Sync for CHandle {}
impl CHandle {
/// The empty handle.
pub fn null() -> Self {
Self {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: OAKRENDER_ABI_VERSION,
}
}
/// True when `ctx` is null.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible. `Send + Sync` come from the shared type.
pub use oakcore_rs::handle::CHandle;
/// Global live-object count (owned handles + cancel-atom boxes).
static ALIVE_COUNT: AtomicUsize = AtomicUsize::new(0);
+3 -2
View File
@@ -132,9 +132,10 @@ impl RenderManager {
}
/// The default disk cache directory (C++ `DiskManager::
/// get_default_disk_cache_path`).
/// get_default_disk_cache_path`); shared with oaknode via oakcommon
/// (single-lib unification).
pub fn disk_cache_path() -> String {
crate::bridge::common::default_disk_cache_path()
oakcommon::filefunctions::default_disk_cache_path()
}
/// Bytes consumed by the default disk cache folder (direct filesystem
+135
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "android_system_properties"
version = "0.1.6"
@@ -94,6 +100,12 @@ dependencies = [
"syn 3.0.3",
]
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "cc"
version = "1.4.2"
@@ -163,6 +175,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crunchy"
version = "0.2.4"
@@ -184,12 +205,28 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fax"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]]
name = "find-msvc-tools"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "foldhash"
version = "0.1.5"
@@ -369,6 +406,19 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "image"
version = "0.25.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
dependencies = [
"bytemuck",
"byteorder-lite",
"moxcms",
"num-traits",
"tiff",
]
[[package]]
name = "indexmap"
version = "2.14.0"
@@ -514,6 +564,26 @@ dependencies = [
"paste",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "moxcms"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "naga"
version = "25.0.1"
@@ -558,6 +628,17 @@ dependencies = [
"libm",
]
[[package]]
name = "oakcommon"
version = "0.1.0"
dependencies = [
"image",
"log",
"oakcore-rs",
"ocio-rs",
"quick-xml",
]
[[package]]
name = "oakcore-rs"
version = "0.1.0"
@@ -576,6 +657,7 @@ dependencies = [
name = "oakrender"
version = "0.1.0"
dependencies = [
"oakcommon",
"oakcore-rs",
"ocio-rs",
"wgpu",
@@ -702,6 +784,18 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
[[package]]
name = "pxfm"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.41.0"
@@ -815,6 +909,12 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "slab"
version = "0.4.12"
@@ -944,6 +1044,20 @@ dependencies = [
"syn 3.0.3",
]
[[package]]
name = "tiff"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
dependencies = [
"fax",
"flate2",
"half",
"quick-error",
"weezl",
"zune-jpeg",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -1027,6 +1141,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "wgpu"
version = "25.0.2"
@@ -1357,3 +1477,18 @@ name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
[[package]]
name = "zune-core"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b"
[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
+6 -41
View File
@@ -33,29 +33,12 @@ pub struct RefBox<T: ?Sized> {
pub value: T,
}
/// `#[repr(C)]` mirror of the public handle structs
/// (`{ctx, addref, release, abi_version}`).
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
// Handles are shared between the calling thread and the manager worker
// threads (same ownership model as a C++ shared_ptr). The raw pointer and
// function-pointer fields are inherently `!Send`/`!Sync`; the box behind
// `ctx` is either `Send + Sync` (task state, atomics) or a plain pointer
// (borrowed handles), both of which are safe to share through the
// refcounted handle.
unsafe impl Send for CHandle {}
unsafe impl Sync for CHandle {}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible. `Send + Sync` come from the shared type.
pub use oakcore_rs::handle::CHandle;
unsafe extern "C" fn noop_addref(_ctx: *mut std::ffi::c_void) {}
@@ -101,24 +84,6 @@ unsafe extern "C" fn borrowed_release<T: 'static>(ctx: *mut std::ffi::c_void) {
}
}
impl CHandle {
/// The empty handle. `ctx` is null but the fn pointers and ABI version
/// are stamped so callers can unconditionally call `release`.
pub fn null() -> Self {
CHandle {
ctx: std::ptr::null_mut(),
addref: Some(noop_addref),
release: Some(noop_release),
abi_version: OAKTASK_ABI_VERSION,
}
}
/// Whether the handle is empty (`ctx == NULL`).
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// Owned handle with count 1; empty on allocation failure.
pub fn make_owned<T: Send + 'static>(value: T) -> CHandle {
let b = Box::new(RefBox { refs: AtomicU32::new(1), value });
+11 -12
View File
@@ -53,20 +53,15 @@ fn abi_version_is_one() {
assert_eq!(OAKTASK_ABI_VERSION, 1);
}
/// Given a null handle, `CHandle::null()` has a null ctx, non-null
/// addref/release, and the stamped ABI version.
/// Given a null handle, `CHandle::null()` has a null ctx, no addref/
/// release fn pointers and no ABI version (single-lib unification).
#[test]
fn null_handle_contract() {
let h = CHandle::null();
assert!(h.ctx.is_null());
assert!(h.addref.is_some(), "null handle addref must be callable");
assert!(h.release.is_some(), "null handle release must be callable");
assert_eq!(h.abi_version, OAKTASK_ABI_VERSION);
// The empty handle is a no-op for both ops (never touches memory).
unsafe {
h.addref.unwrap()(h.ctx);
h.release.unwrap()(h.ctx);
}
assert!(h.addref.is_none(), "null handle addref is empty");
assert!(h.release.is_none(), "null handle release is empty");
assert_eq!(h.abi_version, 0);
// `is_null` reflects the ctx.
assert!(h.is_null());
}
@@ -91,7 +86,11 @@ fn handle_size_and_owned_release() {
}
// The boxed value was dropped; a null handle still works afterwards.
let null_h = CHandle::null();
unsafe {
null_h.release.unwrap()(null_h.ctx);
// The shared null() carries no fn pointers; releasing goes through
// the `if let Some` path (a no-op for empty handles).
if let Some(release) = null_h.release {
unsafe {
release(null_h.ctx);
}
}
}
+6 -31
View File
@@ -38,20 +38,12 @@ pub struct RefBox<T: ?Sized> {
pub value: T,
}
/// `#[repr(C)]` mirror of the public handle structs
/// (`{ctx, addref, release, abi_version}`).
#[repr(C)]
#[derive(Clone)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible.
pub use oakcore_rs::handle::CHandle;
/// Generic `addref` implementation: increments the box's reference count.
///
@@ -82,23 +74,6 @@ unsafe extern "C" fn release_box<T: 'static>(ptr: *mut c_void) {
}
}
impl CHandle {
/// The empty handle.
pub fn null() -> Self {
CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: 0,
}
}
/// Whether this is the empty (all-null) handle.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// Owned handle with count 1; empty on allocation failure.
pub fn make_owned<T: Send + 'static>(value: T) -> CHandle {
let boxed = Box::new(RefBox {
+15 -64
View File
@@ -48,63 +48,14 @@ use crate::undostack::UndoStack;
/// [`crate::handle`].
/// `include/undo/undocommand.h` handle type — `OakUndoCommand`
/// (`{ctx, addref, release, abi_version}`).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakUndoCommand {
/// Opaque box pointer.
pub ctx: *mut c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut c_void)>,
/// OAKUNDO_ABI_VERSION.
pub abi_version: u32,
}
/// (`{ctx, addref, release, abi_version}`). Single-lib unification:
/// aliases the shared [`CHandle`] (identical layout; the C ABI is
/// unchanged).
pub type OakUndoCommand = CHandle;
/// `include/undo/undostack.h` handle type — `OakUndoStack`.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct OakUndoStack {
/// Opaque box pointer.
pub ctx: *mut c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut c_void)>,
/// OAKUNDO_ABI_VERSION.
pub abi_version: u32,
}
/// Field-copy [`OakUndoStack`] into a [`CHandle`].
fn chandle_from_stack(stack: OakUndoStack) -> CHandle {
CHandle {
ctx: stack.ctx,
addref: stack.addref,
release: stack.release,
abi_version: stack.abi_version,
}
}
/// Field-copy a [`CHandle`] into an [`OakUndoStack`].
fn stack_from_chandle(handle: CHandle) -> OakUndoStack {
OakUndoStack {
ctx: handle.ctx,
addref: handle.addref,
release: handle.release,
abi_version: handle.abi_version,
}
}
/// Field-copy a [`CHandle`] into an [`OakUndoCommand`].
fn command_from_chandle(handle: CHandle) -> OakUndoCommand {
OakUndoCommand {
ctx: handle.ctx,
addref: handle.addref,
release: handle.release,
abi_version: handle.abi_version,
}
}
/// `include/undo/undostack.h` handle type — `OakUndoStack`. Single-lib
/// unification: aliases the shared [`CHandle`].
pub type OakUndoStack = CHandle;
/// Lock the stack behind `stack` and run `f` on it. `E_INVALID` for an
/// empty handle. A poisoned mutex is recovered (its inner value is
@@ -112,7 +63,7 @@ fn command_from_chandle(handle: CHandle) -> OakUndoCommand {
fn with_stack<R>(stack: OakUndoStack, f: impl FnOnce(&mut UndoStack) -> Result<R>) -> Result<R> {
// Keep a local handle so `get`'s returned reference lives for the
// whole call (its lifetime ties to the `&CHandle` argument).
let ch = chandle_from_stack(stack);
let ch = stack;
let m = unsafe { crate::handle::get::<Mutex<UndoStack>>(&ch) }.ok_or(Error::Invalid)?;
let mut guard = m.lock().unwrap_or_else(|e| e.into_inner());
f(&mut guard)
@@ -143,21 +94,21 @@ pub mod command {
vtable: *const OakUndoCommandVtable,
userdata: *mut c_void,
) -> OakUndoCommand {
command_from_chandle(guard_handle(|| unsafe {
guard_handle(|| unsafe {
if vtable.is_null() {
return Ok(CHandle::null());
}
let table = *vtable;
Ok(command_from_owned(UndoCommand::from_vtable(table, userdata)))
}))
})
}
/// `oakundo_command_init_multi`: empty multi command, refcount 1.
#[no_mangle]
pub unsafe extern "C" fn oakundo_command_init_multi() -> OakUndoCommand {
command_from_chandle(guard_handle(|| unsafe {
guard_handle(|| unsafe {
Ok(command_from_owned(UndoCommand::multi()))
}))
})
}
/// `oakundo_command_multi_add_child` (stack takes one child ref).
@@ -216,7 +167,7 @@ pub mod command {
}
let child = parent.multi_child(index as usize)?;
let ptr = child as *const UndoCommand as *mut UndoCommand;
*out_child = command_from_chandle(command_from_borrowed(ptr));
*out_child = command_from_borrowed(ptr);
Ok(())
})
}
@@ -272,9 +223,9 @@ pub mod undostack {
/// `oakundo_undostack_init`: fresh stack, refcount 1.
#[no_mangle]
pub unsafe extern "C" fn oakundo_undostack_init() -> OakUndoStack {
stack_from_chandle(guard_handle(|| {
guard_handle(|| {
Ok(make_owned(Mutex::new(UndoStack::new())))
}))
})
}
/// `oakundo_undostack_free`: NULL/empty no-op; clears `stack->ctx`.
+6 -30
View File
@@ -34,36 +34,12 @@ pub struct RefBox<T: ?Sized> {
pub value: T,
}
/// `#[repr(C)]` mirror of the public handle structs
/// (`{ctx, addref, release, abi_version}`).
#[repr(C)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut std::ffi::c_void,
/// Atomic increment.
pub addref: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// Atomic decrement; destroys at zero.
pub release: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
/// ABI version.
pub abi_version: u32,
}
impl CHandle {
/// The empty handle.
pub fn null() -> Self {
CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
abi_version: 0,
}
}
/// Whether this is the empty (zero) handle.
pub fn is_null(&self) -> bool {
self.ctx.is_null()
}
}
/// 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
/// here so the crate's `ffi.rs` signatures and handle scaffolding stay
/// source-compatible.
pub use oakcore_rs::handle::CHandle;
/// Owned handle with count 1; empty on allocation failure.
pub fn make_owned<T: Send + 'static>(value: T) -> CHandle {
+3 -2
View File
@@ -38,6 +38,7 @@ use oakundo::ffi::undostack::{
oakundo_undostack_redo, oakundo_undostack_undo,
};
use oakundo::ffi::{OakUndoCommand, OakUndoStack};
use oakundo::handle::CHandle;
use oakundo::handle::OAKUNDO_ABI_VERSION;
use oakundo::undocommand::OakUndoCommandVtable;
@@ -103,7 +104,7 @@ unsafe fn make_cmd(probe: *mut Probe) -> OakUndoCommand {
/// An empty (all-zero) command handle.
fn empty_cmd() -> OakUndoCommand {
OakUndoCommand {
CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,
@@ -113,7 +114,7 @@ fn empty_cmd() -> OakUndoCommand {
/// An empty (all-zero) stack handle.
fn empty_stack() -> OakUndoStack {
OakUndoStack {
CHandle {
ctx: std::ptr::null_mut(),
addref: None,
release: None,