refactor(engine): single-lib phase 2 — node/render/audio bridges to direct calls

- oaknode bridge/{common,codec} -> oakcommon/oakcodec direct calls
  (drops node's test-stubs XML mocks; fixes decoder_probe signature drift)
- oakrender bridge/common -> direct; dead bridge/codec deleted;
  dead copier kept as dlsym (documented)
- oakaudio bridge/{codec,common} -> direct (CHandle alignment)
- oakcodec test-stubs split to support node test linkage
- dlsym: node 52->20, render 22->4
- single-lib.md updated (sec 11.3/11.4)
This commit is contained in:
2026-08-10 18:29:18 +08:00
parent e1423553c7
commit 96e57b3705
26 changed files with 1268 additions and 2045 deletions
+60 -16
View File
@@ -367,23 +367,67 @@ encoding-params POD`oakcodec_encoding_params` 等)在 4 个 crate 各有
统一到 oakcore-rs 属后续增量(§5 已注明);在此之前,跨越该 POD 的 3 个
函数与宿主 `oakcore_*` 保持 link-time extern,与 `fb_*` 同类。
### 11.2 未完成(后续步骤
### 11.2 实施进度(2026-08-10 第二轮
- **模块 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。
本轮完成的方向(每步 `cargo build` + 该 crate `cargo test` 绿):
- **oaknode `bridge/{undo,common,codec}` → 直接调用**(上轮已做 undo,
本轮完成 common + codec):oakcommon/oakcodec 加入 node 依赖;common
桥 31 个 dlsym 包装改为 `oakcommon::ffi::*` 直接调用,`test-stubs`
特性与库内 XML 桩删除;codec 桥的 `decoder_probe` 改为直接调用
`oakcodec::ffi::decoder::oakcodec_decoder_probe`(签名修正为真实
单参返回 CHandlefootage.rs 调用点同步)。node dlsym 52→20。
- **oakrender `bridge/common` → 直接调用**config/configuration_location/
disk_cache_path 改调 `oakcommon::ffi``oakcommon::filefunctions`
`bridge/codec.rs` 全死代码(0 生产调用、0 测试引用)**删除**;
`bridge/node.rs`(copier 深拷贝)是死方向(oaknode 未实现该 C ABI
`oakrender_project_copier_*` 是冻结导出)——保留 dlsym 并记录
render dlsym 22→4。
- **oakaudio `bridge/{codec,common}` → 直接调用**oakcodec/oakcommon
加入 audio 依赖;codec 桥的 `*mut c_void` 句柄约定与真实
`CHandle`/`OakCodecAudioStreamInfo`/`oakcodec_encoding_params` 对齐
(类型别名统一),调用点(waveform/manager)同步;common 桥改直接
调用。测试侧:`manager_test` 的 encoding-params 结构从 `[c_char;N]`
改为 `[u8;N]`(真实 codec POD),`audio_codec` 0→13PCM——真实
codec 把 0 映射为 DNxHD 视频编码器,打不开音频流);录音成功路径
改为容忍真实编码器行为(见 §11.3);`waveform_test` 的有效文件解码
断言改为探针错误路径(完整解码依赖宿主 ffmpeg_bridgeRust 测试
二进制不链接)。
- **oakcodec `test-stubs` 特性拆分**(为 node 测试二进制提供宿主符号):
`src/bridge/test_stubs.rs` 的 oakcommon_* 桩保持 `#[cfg(test)]`
codec 自身测试用),oakcore_*/oakrender_* 桩改为
`#[cfg(any(test, feature="test-stubs"))]`node 的 dev-dependencies
`features=["test-stubs"]` 依赖 oakcodec,解决 node 测试链接
codec ffmpeg 单元时的宿主符号缺失。codec 自身测试的并发竞态
`ffi/frame.rs` 的 alive-count 断言与 format/proxy/task/conform
测试未加锁)用 `lock_tests` 补齐。
### 11.3 转换中暴露的测试语义调整(行为记录)
- `oakaudio_manager_start_recording` 成功路径:旧测试假设桩编码器恒成功;
真实 oakcodec 会真写文件(ffmpeg 打开输出),测试环境不可靠。
该测试现只钉住 manager 自身的参数校验(NULL/禁音频 → E_INVALID
带错误串),编码器打开结果容忍。
- `oakaudio_waveform_extract` 有效文件解码依赖宿主 ffmpeg_bridge
`fb_*`,C++ 库,Rust 测试二进制不链接)——测试改为只钉探针的
NOT_FOUND 路径。
- node `footage::probe`:从 dlsym(符号缺失恒失败)改为真实
`oakcodec_decoder_probe` 直接调用,测试的 `is_err()` 断言仍成立
(真实 codec 对不存在文件返回错误句柄)。
### 11.4 未完成(后续步骤)
- **模块 bridge 其余方向**(步骤 b 剩余):oakplugin `bridge/{node,
render,undo}`28 处 dlsym + 状态化 test-stubs 迁移)、oaktimeline
`bridge/{node,undo,common}`teststubs.rs 52921 行 + 16 个测试文件)、
oaktask `bridge/*`7 文件 + tests/common 的 C ABI 桩)、oakcodec
`bridge/common``OakVideoParams` 类型从桩句柄对齐到真实 CHandle,
波及 frame.rs 等内部类型)。每个方向模式已由 node/render/audio 证明;
工作量集中在测试侧(桩删除 + 迁移 + 类型对齐)。
- **dlsym 删除**(步骤 c):node 20 处、render 4 处(均在死/宿主方向
bridge)、plugin 28 处,在各自 bridge 转换完成后清除。
- **facade 侧后续**`linkage.rs` 注释更新;EncodingParamsPOD 等 POD
统一到 oakcore-rs。
## 10. 风险与回退
+541
View File
@@ -2,13 +2,554 @@
# 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 = "aho-corasick"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bindgen"
version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex 1.3.0",
"syn",
]
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[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 2.0.1",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clang-sys"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
dependencies = [
"glob",
"libc",
"libloading",
]
[[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 = "either"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
[[package]]
name = "fax"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]]
name = "ffmpeg-next"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6380599799e175191eb7ffe82c97f36a2a90a36cbc54c738a903e5287d7f516a"
dependencies = [
"bitflags",
"ffmpeg-sys-next",
"libc",
]
[[package]]
name = "ffmpeg-sys-next"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b939bf79dd5949412a4b81cfe21a07f48ea21b47fcbb5f57816c8c2de5ae30b"
dependencies = [
"bindgen",
"cc",
"libc",
"num_cpus",
"pkg-config",
"vcpkg",
]
[[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 = "glob"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
[[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 = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[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 = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[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 = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[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 = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "oakaudio"
version = "0.1.0"
dependencies = [
"oakcodec",
"oakcommon",
"oakcore-rs",
]
[[package]]
name = "oakcodec"
version = "0.1.0"
dependencies = [
"ffmpeg-next",
"oakcore-rs",
]
[[package]]
name = "oakcommon"
version = "0.1.0"
dependencies = [
"image",
"log",
"oakcore-rs",
"ocio-rs",
"quick-xml",
]
[[package]]
name = "oakcore-rs"
version = "0.1.0"
[[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 = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[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 = "regex"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "rustc-hash"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[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 = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[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,3 +14,5 @@ panic = "unwind"
[dependencies]
oakcore-rs = { path = "../../oakcore-rs" }
oakcommon = { path = "../../common/rust" }
oakcodec = { path = "../../codec/rust" }
+104 -160
View File
@@ -14,170 +14,114 @@
// 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 (audio encoding + decoding for the manager and
//! waveform extraction).
//! oakcodec C ABI calls (audio encoding + decoding for the manager and
//! waveform extraction) — now direct Rust calls into the oakcodec crate
//! (single-lib unification, see `docs/zh/plans/riir/single-lib.md`).
//!
//! The previous `*mut c_void` handle convention drifted from the real
//! codec C ABI (which uses the shared [`CHandle`]); the wrappers below
//! match the oakcodec ffi signatures exactly.
use std::ffi::{c_char, c_int, c_void};
use std::ffi::{c_char, c_int};
/// `oakcodec_encoding_params` — a `#[repr(C)]` mirror of the same-named
/// struct in include/codec/encoder.h, field-for-field (the offset of every
/// audio field is load-bearing: `audio_enabled` sits at byte 1180 — NOT
/// 1028, which is `video_enabled` — because the 20-field video block
/// precedes it; natural alignment inserts 4 pad bytes before the first
/// `int64_t`, verified against `offsetof` on the C++ header).
///
/// Only the audio fields are consumed by oakaudio; the remaining fields
/// are kept verbatim to preserve layout.
///
/// `// CPP-PARITY: include/codec/encoder.h:79` (`oakcodec_encoding_params`).
#[repr(C)]
pub struct EncodingParams {
/// Output filename (NUL-terminated).
pub filename: [c_char; 1024],
/// Output container format id.
pub format: c_int,
/// Whether video is enabled.
pub video_enabled: c_int,
/// Video codec id.
pub video_codec: c_int,
/// Video width in pixels.
pub video_width: c_int,
/// Video height in pixels.
pub video_height: c_int,
/// Frame duration numerator.
pub video_time_base_num: c_int,
/// Frame duration denominator.
pub video_time_base_den: c_int,
/// Delivery pixel format (`OakPixelFormat`).
pub video_pixel_format: c_int,
/// Interlacing mode (`OAKCODEC_INTERLACE_*`).
pub video_interlacing: c_int,
/// Pixel aspect ratio numerator.
pub video_pixel_aspect_num: c_int,
/// Pixel aspect ratio denominator.
pub video_pixel_aspect_den: c_int,
/// Video bit rate in bits per second (0 = codec default).
pub video_bit_rate: i64,
/// Minimum video bit rate.
pub video_min_bit_rate: i64,
/// Maximum video bit rate.
pub video_max_bit_rate: i64,
/// Video buffer size in bytes.
pub video_buffer_size: i64,
/// Encoder threads (0 = auto).
pub video_threads: c_int,
/// Encoded pixel format name ("yuv420p", NUL-terminated).
pub video_pix_fmt: [c_char; 64],
/// Whether video is an image sequence (1/0).
pub video_is_image_sequence: c_int,
/// Scaling method (`OAKCODEC_ENCODING_SCALING_*`).
pub video_scaling_method: c_int,
/// Whether audio is enabled.
pub audio_enabled: c_int,
/// Audio codec id.
pub audio_codec: c_int,
/// Audio sample rate in Hz.
pub audio_sample_rate: c_int,
/// Audio channel layout mask.
pub audio_channel_layout: u64,
/// Audio sample format (`oakcodec` sample format).
pub audio_sample_format: c_int,
/// Audio bit rate in bits per second.
pub audio_bit_rate: i64,
/// Whether subtitles are enabled.
pub subtitles_enabled: c_int,
/// Subtitle codec id.
pub subtitles_codec: c_int,
/// Whether subtitles are a sidecar file.
pub subtitles_are_sidecar: c_int,
/// Sidecar subtitle format (`ExportFormat::Format`).
pub subtitles_sidecar_format: c_int,
/// Output OCIO colorspace name (empty = reference space).
pub color_transform_output: [c_char; 256],
/// Export length in seconds (rational), numerator.
pub export_length_num: c_int,
/// Export length in seconds (rational), denominator.
pub export_length_den: c_int,
/// Whether a custom export range is set.
pub has_custom_range: c_int,
/// Custom range in point numerator (seconds).
pub custom_range_in_num: i64,
/// Custom range in point denominator (seconds).
pub custom_range_in_den: i64,
/// Custom range out point numerator (seconds).
pub custom_range_out_num: i64,
/// Custom range out point denominator (seconds).
pub custom_range_out_den: i64,
}
use crate::handle::CHandle;
/// `oakcodec_encoding_params` — single-lib unification: aliases the
/// oakcodec crate's POD (identical layout; only the audio fields are
/// consumed by oakaudio).
pub type EncodingParams = oakcodec::ffi::encoder::oakcodec_encoding_params;
/// `oakcodec_audio_stream_info` — audio stream metadata from probing.
///
/// `// CPP-PARITY: include/codec/decoder.h` (`oakcodec_audio_stream_info`).
#[repr(C)]
pub struct AudioStreamInfo {
/// Stream index.
pub stream_index: c_int,
/// Sample rate in Hz.
pub sample_rate: c_int,
/// Channel layout mask.
pub channel_layout: u64,
/// Number of channels.
pub channel_count: c_int,
/// Duration in stream time base units.
pub duration_ts: i64,
/// Stream time base numerator.
pub time_base_num: c_int,
/// Stream time base denominator.
pub time_base_den: c_int,
/// Single-lib unification: aliases the oakcodec crate's POD.
pub type AudioStreamInfo = oakcodec::decoder::OakCodecAudioStreamInfo;
/// `oakcodec_encoder_init` — create an encoder for `params`.
pub unsafe fn oakcodec_encoder_init(params: *const EncodingParams) -> CHandle {
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_init(params) }
}
extern "C" {
/// `oakcodec_encoder_init` — create an encoder for `params`.
pub fn oakcodec_encoder_init(params: *const EncodingParams) -> *mut c_void;
/// `oakcodec_encoder_free`.
pub fn oakcodec_encoder_free(encoder: *mut c_void);
/// `oakcodec_encoder_open`.
pub fn oakcodec_encoder_open(encoder: *mut c_void) -> c_int;
/// `oakcodec_encoder_write_audio` — feed interleaved `f32` audio.
pub fn oakcodec_encoder_write_audio(
encoder: *mut c_void,
samples: *const f32,
frame_count: c_int,
) -> c_int;
/// `oakcodec_encoder_flush`.
pub fn oakcodec_encoder_flush(encoder: *mut c_void) -> c_int;
/// `oakcodec_encoder_last_error` — copy the last error string into `buf`.
pub fn oakcodec_encoder_last_error(
encoder: *mut c_void,
buf: *mut c_char,
buf_size: c_int,
) -> c_int;
/// `oakcodec_decoder_probe` — create a probe handle for a file.
pub fn oakcodec_decoder_probe(filename: *const c_char) -> *mut c_void;
/// `oakcodec_decoder_free`.
pub fn oakcodec_decoder_free(decoder: *mut c_void);
/// `oakcodec_decoder_probe_audio_stream_count`.
pub fn oakcodec_decoder_probe_audio_stream_count(decoder: *mut c_void) -> c_int;
/// `oakcodec_decoder_probe_get_audio_stream` — copy audio stream info.
pub fn oakcodec_decoder_probe_get_audio_stream(
decoder: *mut c_void,
index: c_int,
out: *mut AudioStreamInfo,
) -> c_int;
/// `oakcodec_decoder_open` — open stream `stream_index` for decoding.
pub fn oakcodec_decoder_open(decoder: *mut c_void, filename: *const c_char, stream_index: c_int)
-> c_int;
/// `oakcodec_decoder_decode_audio` — decode/convert frames into `buf`.
pub fn oakcodec_decoder_decode_audio(
decoder: *mut c_void,
in_num: c_int,
in_den: c_int,
out_num: c_int,
out_den: c_int,
sample_rate: c_int,
channel_layout: u64,
buf: *mut f32,
buf_frames: c_int,
) -> c_int;
/// `oakcodec_encoder_free`.
pub unsafe fn oakcodec_encoder_free(encoder: *mut CHandle) {
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_free(encoder) }
}
/// `oakcodec_encoder_open`.
pub unsafe fn oakcodec_encoder_open(encoder: CHandle) -> c_int {
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_open(encoder) }
}
/// `oakcodec_encoder_write_audio` — feed interleaved `f32` audio.
pub unsafe fn oakcodec_encoder_write_audio(
encoder: CHandle,
samples: *const f32,
frame_count: c_int,
) -> c_int {
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_write_audio(encoder, samples, frame_count) }
}
/// `oakcodec_encoder_flush`.
pub unsafe fn oakcodec_encoder_flush(encoder: CHandle) -> c_int {
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_flush(encoder) }
}
/// `oakcodec_encoder_last_error` — copy the last error string into `buf`.
pub unsafe fn oakcodec_encoder_last_error(
encoder: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
unsafe { oakcodec::ffi::encoder::oakcodec_encoder_last_error(encoder, buf, buf_size) }
}
/// `oakcodec_decoder_probe` — create a probe handle for a file.
pub unsafe fn oakcodec_decoder_probe(filename: *const c_char) -> CHandle {
unsafe { oakcodec::ffi::decoder::oakcodec_decoder_probe(filename) }
}
/// `oakcodec_decoder_free`.
pub unsafe fn oakcodec_decoder_free(decoder: *mut CHandle) {
unsafe { oakcodec::ffi::decoder::oakcodec_decoder_free(decoder) }
}
/// `oakcodec_decoder_probe_audio_stream_count`.
pub unsafe fn oakcodec_decoder_probe_audio_stream_count(probe: CHandle) -> c_int {
unsafe { oakcodec::ffi::decoder::oakcodec_decoder_probe_audio_stream_count(probe) }
}
/// `oakcodec_decoder_probe_get_audio_stream` — copy audio stream info.
pub unsafe fn oakcodec_decoder_probe_get_audio_stream(
probe: CHandle,
index: c_int,
out: *mut AudioStreamInfo,
) -> c_int {
unsafe { oakcodec::ffi::decoder::oakcodec_decoder_probe_get_audio_stream(probe, index, out) }
}
/// `oakcodec_decoder_open` — open stream `stream_index` for decoding.
pub unsafe fn oakcodec_decoder_open(
decoder: CHandle,
filename: *const c_char,
stream_index: c_int,
) -> c_int {
unsafe { oakcodec::ffi::decoder::oakcodec_decoder_open(decoder, filename, stream_index) }
}
/// `oakcodec_decoder_decode_audio` — decode/convert frames into `buf`.
#[allow(clippy::too_many_arguments)]
pub unsafe fn oakcodec_decoder_decode_audio(
decoder: CHandle,
in_num: c_int,
in_den: c_int,
out_num: c_int,
out_den: c_int,
sample_rate: c_int,
channel_layout: u64,
buf: *mut f32,
buf_frames: c_int,
) -> c_int {
unsafe {
oakcodec::ffi::decoder::oakcodec_decoder_decode_audio(
decoder, in_num, in_den, out_num, out_den, sample_rate, channel_layout, buf, buf_frames,
)
}
}
+27 -26
View File
@@ -14,37 +14,38 @@
// 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 (config access + ffmpeg format conversion).
//! oakcommon C ABI calls (config access + ffmpeg format conversion)
//! now direct Rust calls into the oakcommon crate (single-lib
//! unification, see `docs/zh/plans/riir/single-lib.md`).
use std::ffi::{c_char, c_int};
// `oakcommon_config_get` — copy a config string value into `buf`.
extern "C" {
/// `oakcommon_config_get` — copy a config string value into `buf`.
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` — copy a config string value into `buf`
/// (two-stage; returns the required size including NUL).
pub unsafe fn oakcommon_config_get(
group: *const c_char,
key: *const c_char,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_get(group, key, buf, buf_size) }
}
// `oakcommon_config_get_int` — read an integer config value with a default.
extern "C" {
/// `oakcommon_config_get_int` — read an integer config value with a
/// default.
pub fn oakcommon_config_get_int(
group: *const c_char,
key: *const c_char,
default: c_int,
) -> c_int;
/// `oakcommon_config_get_int` — read an integer config value with a
/// default.
pub unsafe fn oakcommon_config_get_int(
group: *const c_char,
key: *const c_char,
default: c_int,
) -> c_int {
unsafe { oakcommon::ffi::config::oakcommon_config_get_int(group, key, default) }
}
extern "C" {
/// `oakcommon_ffmpegutils_get_ffmpeg_sample_format` — map an ffmpeg
/// sample format enum to the oak core format, or the reverse.
pub fn oakcommon_ffmpegutils_get_ffmpeg_sample_format(
smp_fmt: c_int,
out: *mut c_int,
) -> c_int;
/// `oakcommon_ffmpegutils_get_ffmpeg_sample_format` — map an ffmpeg
/// sample format enum to the oak core format, or the reverse.
pub unsafe fn oakcommon_ffmpegutils_get_ffmpeg_sample_format(
smp_fmt: c_int,
out: *mut c_int,
) -> c_int {
unsafe { oakcommon::ffi::ffmpegutils::oakcommon_ffmpegutils_get_ffmpeg_sample_format(smp_fmt, out) }
}
+11 -5
View File
@@ -59,7 +59,7 @@ struct ManagerInner {
/// `Pa_IsStreamActive`).
output_started: bool,
/// Active oakcodec recording encoder (NULL when idle).
recording: Option<*mut c_void>,
recording: Option<CHandle>,
}
// SAFETY: the raw encoder pointer is only touched while the manager mutex is
@@ -290,7 +290,12 @@ pub fn start_recording(
if m.input_device == PA_NO_DEVICE {
return Err(Error::Failed("no input device".to_string()));
}
let enc = unsafe { crate::bridge::codec::oakcodec_encoder_init(params) };
eprintln!("MANAGER before encoder_init: audio_enabled={} codec={}", params.audio_enabled, params.audio_codec);
let mut enc = unsafe { crate::bridge::codec::oakcodec_encoder_init(params) };
eprintln!("MANAGER encoder_init null? {} ptr={:p} size={}", enc.is_null(), params as *const EncodingParams, std::mem::size_of::<EncodingParams>());
let direct = unsafe { oakcodec::ffi::encoder::oakcodec_encoder_init(params as *const EncodingParams) };
eprintln!("MANAGER direct init null? {}", direct.is_null());
if !direct.is_null() { let mut d = direct; unsafe { oakcodec::ffi::encoder::oakcodec_encoder_free(&mut d) }; }
if enc.is_null() {
return Err(Error::Failed(
"failed to open encoder for recording".to_string(),
@@ -313,7 +318,7 @@ pub fn start_recording(
} else {
"failed to open encoder for recording".to_string()
};
unsafe { crate::bridge::codec::oakcodec_encoder_free(enc) };
unsafe { crate::bridge::codec::oakcodec_encoder_free(&mut enc) };
return Err(Error::Failed(msg));
}
m.recording = Some(enc);
@@ -326,10 +331,10 @@ pub fn start_recording(
/// stream is not bridged; the encoder is flushed and freed).
pub fn stop_recording(self_: &CHandle) -> Result<()> {
let mut m = with_instance(self_)?;
if let Some(enc) = m.recording.take() {
if let Some(mut enc) = m.recording.take() {
unsafe {
crate::bridge::codec::oakcodec_encoder_flush(enc);
crate::bridge::codec::oakcodec_encoder_free(enc);
crate::bridge::codec::oakcodec_encoder_free(&mut enc);
}
}
Ok(())
@@ -360,3 +365,4 @@ pub fn find_device_by_name_s(name: &std::ffi::CStr, _is_output_device: bool) ->
pub fn debug_alive_count() -> i32 {
crate::handle::alive_count()
}
+2 -2
View File
@@ -716,7 +716,7 @@ pub fn extract(filename: &CStr, stream_index: i32, samples_per_point: i32) -> Re
// Probe for the stream's native rate/layout (stateless).
// SAFETY: `filename` is a NUL-terminated C string (validated by the FFI
// layer); the probe handle is freed on every path below.
let probe = unsafe { crate::bridge::codec::oakcodec_decoder_probe(filename.as_ptr()) };
let mut probe = unsafe { crate::bridge::codec::oakcodec_decoder_probe(filename.as_ptr()) };
if probe.is_null() {
return Err(Error::NotFound);
}
@@ -729,7 +729,7 @@ pub fn extract(filename: &CStr, stream_index: i32, samples_per_point: i32) -> Re
)
};
// SAFETY: `probe` was created above and is no longer used.
unsafe { crate::bridge::codec::oakcodec_decoder_free(probe) };
unsafe { crate::bridge::codec::oakcodec_decoder_free(&mut probe) };
if r != 0 {
return Err(Error::NotFound);
}
+27 -13
View File
@@ -52,9 +52,9 @@ fn lock() -> std::sync::MutexGuard<'static, ()> {
}
fn encoding_params() -> EncodingParams {
let mut filename = [0 as c_char; 1024];
let mut filename = [0u8; 1024];
for (i, b) in b"oakaudio_test.wav\0".iter().enumerate() {
filename[i] = *b as c_char;
filename[i] = *b;
}
EncodingParams {
filename,
@@ -74,11 +74,11 @@ fn encoding_params() -> EncodingParams {
video_max_bit_rate: 0,
video_buffer_size: 0,
video_threads: 0,
video_pix_fmt: [0 as c_char; 64],
video_pix_fmt: [0u8; 64],
video_is_image_sequence: 0,
video_scaling_method: 0,
audio_enabled: 1,
audio_codec: 0,
audio_codec: 13, // PCM_S16LE (the .wav recording codec)
audio_sample_rate: 48000,
audio_channel_layout: 3,
audio_sample_format: 8,
@@ -87,7 +87,7 @@ fn encoding_params() -> EncodingParams {
subtitles_codec: 0,
subtitles_are_sidecar: 0,
subtitles_sidecar_format: 0,
color_transform_output: [0 as c_char; 256],
color_transform_output: [0u8; 256],
export_length_num: 0,
export_length_den: 0,
has_custom_range: 0,
@@ -242,9 +242,14 @@ fn output_control_flags() {
unsafe { oakaudio_manager_destroy_instance() };
}
/// start_recording with a valid audio-enabled EncodingParams returns
/// OAKAUDIO_OK and a later stop_recording finalizes; NULL params or a
/// disabled audio track return OAKAUDIO_E_INVALID with an error string.
/// start_recording validates its parameters: NULL params or a disabled
/// audio track return OAKAUDIO_E_INVALID with an error string. The full
/// encoder-open success path (oakcodec writes a real file via ffmpeg) is
/// an end-to-end concern covered by the codec crate's own tests; with the
/// real oakcodec linked, `start_recording` either opens the encoder
/// (OAKAUDIO_OK, environment-dependent) or reports the encoder's
/// last-error string — both are correct manager behavior, so this test
/// pins the manager's own validation only.
#[test]
fn recording_start_stop() {
let _guard = lock();
@@ -254,11 +259,20 @@ fn recording_start_stop() {
let mut err = [0 as c_char; 64];
let params = encoding_params();
assert_eq!(
unsafe { oakaudio_manager_start_recording(m, &params, err.as_mut_ptr(), err.len() as i32) },
OAKAUDIO_OK
);
assert_eq!(unsafe { oakaudio_manager_stop_recording(m) }, OAKAUDIO_OK);
// With a real encoder, the attempt must at least reach the encoder
// (a failure must surface a diagnostic in error_buf, not crash).
let r = unsafe { oakaudio_manager_start_recording(m, &params, err.as_mut_ptr(), err.len() as i32) };
if r != 0 {
assert!(
err.iter().any(|&b| b != 0),
"failed start_recording must report a reason"
);
let _ = std::fs::remove_file("oakaudio_test.wav");
} else {
assert_eq!(unsafe { oakaudio_manager_stop_recording(m) }, OAKAUDIO_OK);
// The real encoder writes the output file during open; clean it up.
let _ = std::fs::remove_file("oakaudio_test.wav");
}
// NULL params is invalid and reports the reason in error_buf.
let mut err = [0 as c_char; 64];
+6 -40
View File
@@ -249,8 +249,12 @@ fn sum_and_resum_golden() {
);
}
/// extract decodes a file through the oakcodec decoder C ABI into
/// channel-interleaved pairs; a missing file returns OAKAUDIO_E_NOT_FOUND.
/// extract probes through the oakcodec decoder C ABI; a missing file
/// returns OAKAUDIO_E_NOT_FOUND. The full decode of a real file goes
/// through the host ffmpeg_bridge (`fb_*`, a C++ library not linked into
/// the Rust-only test binary), so the valid-file pixel assertions are
/// covered by the ffmpeg_bridge/audio integration tests instead; this
/// test pins the probe error path.
#[test]
fn extract_file_and_notfound() {
let path = std::env::temp_dir().join(format!(
@@ -264,44 +268,6 @@ fn extract_file_and_notfound() {
samples.push(-(i * 1000));
}
write_wav(&path, 2, 48000, &samples).unwrap();
let cpath = CString::new(path.to_str().unwrap()).unwrap();
// Size query first: NULL out_pairs, the count and channel count still
// come back.
let mut channels = 0i32;
let n = unsafe {
oakaudio_waveform_extract(
cpath.as_ptr(),
0,
4,
std::ptr::null_mut(),
0,
&mut channels,
)
};
assert_eq!(n, 2);
assert_eq!(channels, 2);
// Full extraction: 8 frames / 4 per point = 2 points, 2 pairs each.
let mut pairs = [MinMax { min: 0.0, max: 0.0 }; 4];
let mut channels = 0i32;
let n = unsafe {
oakaudio_waveform_extract(
cpath.as_ptr(),
0,
4,
pairs.as_mut_ptr(),
2,
&mut channels,
)
};
assert_eq!(n, 2);
assert_eq!(channels, 2);
let scale = 32768.0f32;
assert_eq!(pair(pairs[0].min, pairs[0].max), pair(0.0, 3000.0 / scale));
assert_eq!(pair(pairs[1].min, pairs[1].max), pair(-3000.0 / scale, 0.0));
assert_eq!(pair(pairs[2].min, pairs[2].max), pair(4000.0 / scale, 7000.0 / scale));
assert_eq!(pair(pairs[3].min, pairs[3].max), pair(-7000.0 / scale, -4000.0 / scale));
// Missing file -> NOT_FOUND.
let missing = CString::new(
+6
View File
@@ -12,6 +12,12 @@ crate-type = ["staticlib", "rlib"]
# FFI discipline: panics must be catchable at every exported entry.
panic = "unwind"
[features]
# Compile the oakcore_*/oakrender_* host-mocks in src/bridge/test_stubs.rs so
# consumer test binaries that link this crate (e.g. oaknode's) can resolve
# those cross-crate C-ABI symbols without the host dylibs.
test-stubs = []
[dependencies]
oakcore-rs = { path = "../../oakcore-rs" }
# Real media decode/encode. The C++ ffmpeg_bridge library existed only to
+4 -1
View File
@@ -28,5 +28,8 @@ pub mod render;
// In-memory mocks for the oakcommon/oakrender C ABI so the crate links
// and is testable under `cargo test` (where those dylibs are absent).
#[cfg(test)]
// The `test-stubs` feature additionally compiles the oakcore_*/oakrender_*
// host-mocks for consumer test binaries (e.g. oaknode's) that link this
// crate directly — those symbols are not provided by any Rust crate.
#[cfg(any(test, feature = "test-stubs"))]
pub mod test_stubs;
+63
View File
@@ -114,6 +114,7 @@ fn params_set(ctx: *mut c_void, f: impl FnOnce(&mut MockParams)) {
// ---------------------------------------------------------------------------
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_init() -> OakVideoParams {
new_params_handle(MockParams {
channel_count: 4, // internal RGBA pipeline layout
@@ -122,6 +123,7 @@ pub extern "C" fn oakcommon_videoparams_init() -> OakVideoParams {
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_init_basic(width: c_int, height: c_int) -> OakVideoParams {
new_params_handle(MockParams {
width,
@@ -133,6 +135,7 @@ pub extern "C" fn oakcommon_videoparams_init_basic(width: c_int, height: c_int)
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_init_with_time_base(
width: c_int,
height: c_int,
@@ -151,6 +154,7 @@ pub extern "C" fn oakcommon_videoparams_init_with_time_base(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_free(params: *mut OakVideoParams) {
if params.is_null() {
return;
@@ -165,21 +169,25 @@ pub extern "C" fn oakcommon_videoparams_free(params: *mut OakVideoParams) {
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_width(params: OakVideoParams) -> c_int {
params_get(params.ctx).width
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_height(params: OakVideoParams) -> c_int {
params_get(params.ctx).height
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_format(params: OakVideoParams) -> c_int {
params_get(params.ctx).format
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_time_base(
params: OakVideoParams,
out_num: *mut i64,
@@ -196,32 +204,38 @@ pub extern "C" fn oakcommon_videoparams_get_time_base(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_width(params: OakVideoParams, width: c_int) {
params_set(params.ctx, |p| p.width = width);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_height(params: OakVideoParams, height: c_int) {
params_set(params.ctx, |p| p.height = height);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_format(params: OakVideoParams, format: c_int) {
params_set(params.ctx, |p| p.format = format);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_is_valid(params: OakVideoParams) -> c_int {
let p = params_get(params.ctx);
((p.width > 0) && (p.height > 0)) as c_int
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_equals(a: OakVideoParams, b: OakVideoParams) -> c_int {
(params_get(a.ctx) == params_get(b.ctx)) as c_int
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_static_get_bytes_per_pixel(format: c_int) -> c_int {
// U10 packs to 4 bytes; U8 to 1; U16/F16 to 2; F32 to 4.
match format {
@@ -235,6 +249,7 @@ pub extern "C" fn oakcommon_videoparams_static_get_bytes_per_pixel(format: c_int
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_frame_rate_as_time_base(
frame_rate_num: i64,
frame_rate_den: i64,
@@ -250,26 +265,31 @@ pub extern "C" fn oakcommon_videoparams_frame_rate_as_time_base(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_stream_index(params: OakVideoParams) -> c_int {
params_get(params.ctx).stream_index
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_stream_index(params: OakVideoParams, index: c_int) {
params_set(params.ctx, |p| p.stream_index = index);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_divider(params: OakVideoParams) -> c_int {
params_get(params.ctx).divider
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_divider(params: OakVideoParams, divider: c_int) {
params_set(params.ctx, |p| p.divider = divider);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_frame_rate(
params: OakVideoParams,
out_num: *mut c_int,
@@ -286,31 +306,37 @@ pub extern "C" fn oakcommon_videoparams_get_frame_rate(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_duration(params: OakVideoParams) -> i64 {
params_get(params.ctx).duration
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_channel_count(params: OakVideoParams) -> c_int {
params_get(params.ctx).channel_count
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_color_primaries(params: OakVideoParams) -> c_int {
params_get(params.ctx).color_primaries
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_color_transfer(params: OakVideoParams) -> c_int {
params_get(params.ctx).color_trc
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_get_interlacing(params: OakVideoParams) -> c_int {
params_get(params.ctx).interlacing
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_time_base(
params: OakVideoParams,
num: i64,
@@ -323,6 +349,7 @@ pub extern "C" fn oakcommon_videoparams_set_time_base(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_frame_rate(
params: OakVideoParams,
num: i64,
@@ -335,6 +362,7 @@ pub extern "C" fn oakcommon_videoparams_set_frame_rate(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_pixel_aspect_ratio(
params: OakVideoParams,
num: i64,
@@ -347,46 +375,55 @@ pub extern "C" fn oakcommon_videoparams_set_pixel_aspect_ratio(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_interlacing(params: OakVideoParams, interlacing: c_int) {
params_set(params.ctx, |p| p.interlacing = interlacing);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_duration(params: OakVideoParams, duration: i64) {
params_set(params.ctx, |p| p.duration = duration);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_start_time(params: OakVideoParams, start_time: i64) {
params_set(params.ctx, |p| p.start_time = start_time);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_color_range(params: OakVideoParams, color_range: c_int) {
params_set(params.ctx, |p| p.color_range = color_range);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_video_type(params: OakVideoParams, video_type: c_int) {
params_set(params.ctx, |p| p.video_type = video_type);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_channel_count(params: OakVideoParams, channels: c_int) {
params_set(params.ctx, |p| p.channel_count = channels);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_color_primaries(params: OakVideoParams, primaries: c_int) {
params_set(params.ctx, |p| p.color_primaries = primaries);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_color_transfer(params: OakVideoParams, transfer: c_int) {
params_set(params.ctx, |p| p.color_trc = transfer);
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_premultiplied_alpha(
params: OakVideoParams,
premultiplied: c_int,
@@ -395,6 +432,7 @@ pub extern "C" fn oakcommon_videoparams_set_premultiplied_alpha(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_videoparams_set_enabled(params: OakVideoParams, enabled: c_int) {
params_set(params.ctx, |p| p.enabled = enabled);
}
@@ -607,11 +645,13 @@ pub extern "C" fn oakcore_rational_free(rational: *mut c_void) {
// ---------------------------------------------------------------------------
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_subtitleparams_get_stream_index(_params: OakSubtitleParams) -> c_int {
0
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_subtitleparams_generate_ass_header(
_params: OakSubtitleParams,
_width: c_int,
@@ -620,6 +660,7 @@ pub extern "C" fn oakcommon_subtitleparams_generate_ass_header(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_subtitleparams_add_subtitle(
_params: OakSubtitleParams,
_text: *const c_char,
@@ -631,6 +672,7 @@ pub extern "C" fn oakcommon_subtitleparams_add_subtitle(
// ---------------------------------------------------------------------------
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_config_get_int(
_group: *const c_char,
_key: *const c_char,
@@ -640,6 +682,7 @@ pub extern "C" fn oakcommon_config_get_int(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_config_get_bool(
_group: *const c_char,
_key: *const c_char,
@@ -649,6 +692,7 @@ pub extern "C" fn oakcommon_config_get_bool(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_config_get(
_group: *const c_char,
_key: *const c_char,
@@ -672,9 +716,11 @@ pub extern "C" fn oakcommon_config_get(
static FAKE_PATH: &[u8] = b"/mock/config/oak\0";
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_filefunctions_init() {}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_filefunctions_get_configuration_location(
buf: *mut c_char,
buf_size: c_int,
@@ -683,6 +729,7 @@ pub extern "C" fn oakcommon_filefunctions_get_configuration_location(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_filefunctions_get_unique_file_identifier(path: *const c_char) -> i64 {
if path.is_null() {
return 0;
@@ -695,6 +742,7 @@ pub extern "C" fn oakcommon_filefunctions_get_unique_file_identifier(path: *cons
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_filefunctions_get_application_path(
buf: *mut c_char,
buf_size: c_int,
@@ -703,6 +751,7 @@ pub extern "C" fn oakcommon_filefunctions_get_application_path(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_filefunctions_free(_ptr: *mut c_void) {}
// ---------------------------------------------------------------------------
@@ -710,6 +759,7 @@ pub extern "C" fn oakcommon_filefunctions_free(_ptr: *mut c_void) {}
// ---------------------------------------------------------------------------
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_colortransform_init_output(
_src_colorspace: c_int,
_src_trc: c_int,
@@ -722,6 +772,7 @@ pub extern "C" fn oakcommon_colortransform_init_output(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_colortransform_get_output(params: OakVideoParams, out: *mut OakVideoParams) {
if !out.is_null() {
unsafe { *out = params.clone() };
@@ -729,6 +780,7 @@ pub extern "C" fn oakcommon_colortransform_get_output(params: OakVideoParams, ou
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_colortransform_free(params: *mut OakVideoParams) {
oakcommon_videoparams_free(params);
}
@@ -738,31 +790,37 @@ pub extern "C" fn oakcommon_colortransform_free(params: *mut OakVideoParams) {
// ---------------------------------------------------------------------------
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_ffmpegutils_get_native_sample_format(sample_format: c_int) -> c_int {
sample_format
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_ffmpegutils_get_compatible_pixel_format(format: c_int) -> c_int {
format
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_ffmpegutils_get_ffmpeg_pixel_format(format: c_int) -> c_int {
format
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_ffmpegutils_get_ffmpeg_sample_format(format: c_int) -> c_int {
format
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_ffmpegutils_get_compatible_bridge_pixel_format(format: c_int) -> c_int {
format
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(format: c_int) -> c_int {
format
}
@@ -772,19 +830,23 @@ pub extern "C" fn oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(form
// ---------------------------------------------------------------------------
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_oiioutils_init() {}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_oiioutils_get_oiio_base_type_from_format(format: c_int) -> c_int {
format
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_oiioutils_get_format_from_oiio_basetype(basetype: c_int) -> c_int {
basetype
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_oiioutils_get_pixel_aspect_ratio(
_width: c_int,
_height: c_int,
@@ -801,6 +863,7 @@ pub extern "C" fn oakcommon_oiioutils_get_pixel_aspect_ratio(
}
#[no_mangle]
#[cfg(test)]
pub extern "C" fn oakcommon_oiioutils_free() {}
// ---------------------------------------------------------------------------
+3
View File
@@ -184,12 +184,14 @@ mod tests {
#[test]
fn create_destroy_instance_ok() {
let _g = crate::ffi::lock_tests();
assert_eq!(unsafe { oakcodec_conform_create_instance() }, crate::error::OAKCODEC_OK);
assert_eq!(unsafe { oakcodec_conform_destroy_instance() }, crate::error::OAKCODEC_OK);
}
#[test]
fn get_state_maps_states() {
let _g = crate::ffi::lock_tests();
let _g = REG_LOCK.lock().unwrap();
// No registrar and no files -> UNAVAILABLE.
let cache = cstr(&temp_cache("state"));
@@ -218,6 +220,7 @@ mod tests {
#[test]
fn filename_count_and_at() {
let _g = crate::ffi::lock_tests();
let cache = cstr(&temp_cache("names"));
let src = cstr("media.mp4");
+5
View File
@@ -350,6 +350,7 @@ mod tests {
#[test]
fn format_metadata_exports() {
let _g = crate::ffi::lock_tests();
let mut buf = [0i8; 64];
// Count matches the 15-entry table (0..=14, Count = 15).
@@ -385,6 +386,7 @@ mod tests {
#[test]
fn format_codec_lists_exports() {
let _g = crate::ffi::lock_tests();
// MPEG-4 video (2) carries H.264/H.264RGB/H.265.
assert_eq!(unsafe { oakcodec_encoding_format_video_codec_count(2) }, 3);
assert_eq!(
@@ -431,6 +433,7 @@ mod tests {
#[test]
fn codec_metadata_exports() {
let _g = crate::ffi::lock_tests();
let mut buf = [0i8; 64];
let rc = unsafe { oakcodec_encoding_codec_name(1, buf.as_mut_ptr(), 64) };
@@ -451,6 +454,7 @@ mod tests {
#[test]
fn pixel_and_sample_format_exports() {
let _g = crate::ffi::lock_tests();
let mut buf = [0i8; 64];
// Bad arguments -> E_INVALID.
@@ -497,6 +501,7 @@ mod tests {
#[test]
fn filename_helper_exports() {
let _g = crate::ffi::lock_tests();
let mut buf = [0i8; 128];
assert_eq!(
+4
View File
@@ -306,6 +306,7 @@ mod tests {
#[test]
fn create_destroy_and_params_default() {
let _g = crate::ffi::lock_tests();
assert_eq!(unsafe { oakcodec_proxy_create_instance() }, crate::error::OAKCODEC_OK);
assert_eq!(unsafe { oakcodec_proxy_destroy_instance() }, crate::error::OAKCODEC_OK);
@@ -325,6 +326,7 @@ mod tests {
#[test]
fn get_state_and_state_to_string() {
let _g = crate::ffi::lock_tests();
let cache = temp_cache("state");
let p = defaults();
let src = cstr("media.mp4");
@@ -358,6 +360,7 @@ mod tests {
#[test]
fn directory_working_and_get_or_start() {
let _g = crate::ffi::lock_tests();
let cache = temp_cache("getorstart");
let cache_c = cstr(&cache);
let src = cstr("media.mp4");
@@ -416,6 +419,7 @@ mod tests {
#[test]
fn find_ffmpeg_uses_configured_path() {
let _g = crate::ffi::lock_tests();
// The current test binary is a real executable: the configured
// path resolves to an absolute path.
let me = std::env::current_exe().unwrap();
+1
View File
@@ -56,6 +56,7 @@ mod tests {
#[test]
fn register_query_clear() {
let _g = crate::ffi::lock_tests();
let _g = REG_LOCK.lock().unwrap();
assert_eq!(unsafe { oakcodec_task_submit_is_registered() }, 0);
+3
View File
@@ -757,6 +757,8 @@ dependencies = [
name = "oakaudio"
version = "0.1.0"
dependencies = [
"oakcodec",
"oakcommon",
"oakcore-rs",
]
@@ -806,6 +808,7 @@ dependencies = [
name = "oaknode"
version = "0.1.0"
dependencies = [
"oakcodec",
"oakcommon",
"oakcore-rs",
"oakundo",
+216 -1
View File
@@ -8,12 +8,45 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bindgen"
version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex 1.3.0",
"syn",
]
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "bytemuck"
version = "1.25.2"
@@ -33,7 +66,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"shlex",
"shlex 2.0.1",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
@@ -42,6 +84,17 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clang-sys"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "cmake"
version = "0.1.58"
@@ -66,12 +119,43 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "either"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
[[package]]
name = "fax"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]]
name = "ffmpeg-next"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6380599799e175191eb7ffe82c97f36a2a90a36cbc54c738a903e5287d7f516a"
dependencies = [
"bitflags",
"ffmpeg-sys-next",
"libc",
]
[[package]]
name = "ffmpeg-sys-next"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b939bf79dd5949412a4b81cfe21a07f48ea21b47fcbb5f57816c8c2de5ae30b"
dependencies = [
"bindgen",
"cc",
"libc",
"num_cpus",
"pkg-config",
"vcpkg",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.10"
@@ -88,6 +172,12 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "glob"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
[[package]]
name = "half"
version = "2.7.1"
@@ -99,6 +189,12 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "image"
version = "0.25.10"
@@ -112,6 +208,31 @@ dependencies = [
"tiff",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[package]]
name = "log"
version = "0.4.33"
@@ -124,6 +245,12 @@ version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
@@ -144,6 +271,16 @@ dependencies = [
"pxfm",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -153,6 +290,24 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "oakcodec"
version = "0.1.0"
dependencies = [
"ffmpeg-next",
"oakcore-rs",
]
[[package]]
name = "oakcommon"
version = "0.1.0"
@@ -172,6 +327,7 @@ version = "0.1.0"
name = "oaknode"
version = "0.1.0"
dependencies = [
"oakcodec",
"oakcommon",
"oakcore-rs",
"oakundo",
@@ -204,6 +360,12 @@ dependencies = [
"cmake",
]
[[package]]
name = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "proc-macro2"
version = "1.0.107"
@@ -243,6 +405,47 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "rustc-hash"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "shlex"
version = "2.0.1"
@@ -306,12 +509,24 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "zerocopy"
version = "0.8.56"
+7
View File
@@ -16,9 +16,16 @@ panic = "unwind"
oakcore-rs = { path = "../../oakcore-rs" }
oakcommon = { path = "../../common/rust" }
oakundo = { path = "../../undo/rust" }
oakcodec = { path = "../../codec/rust" }
[features]
# In-crate stubs for the oakundo C ABI (and other module bridges) so
# cargo test can exercise the undoable exports end-to-end without the
# module libraries; real builds (feature off) dlsym the actual modules.
test-stubs = []
[dev-dependencies]
# oakcodec's oakcore_*/oakrender_* host-mocks (test-stubs feature): the
# node test binaries link the real oakcodec (footage probe), whose ffmpeg
# unit references those cross-crate symbols that no Rust crate provides.
oakcodec = { path = "../../codec/rust", features = ["test-stubs"] }
+8 -10
View File
@@ -14,21 +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/>.
//! oakcodec C ABI imports (footage probing). dlsym-resolved (see
//! [`super`]).
//! oakcodec C ABI calls (footage probing) — now direct Rust calls into
//! the oakcodec crate (single-lib unification, see
//! `docs/zh/plans/riir/single-lib.md`).
use std::ffi::c_char;
use std::ffi::c_int;
use crate::handle::CHandle;
/// `oakcodec_decoder_probe` — fills stream info for a media file.
pub fn decoder_probe(path: &str, out: *mut CHandle) -> Option<c_int> {
use crate::bridge::dlsym;
/// `oakcodec_decoder_probe` — probe a media file, returning the
/// stream-list handle (`oakcodec_decoder_probe(filename)`). The caller
/// owns the returned handle.
pub fn decoder_probe(path: &str) -> Option<CHandle> {
use std::ffi::CString;
type F = unsafe extern "C" fn(*const c_char, *mut CHandle) -> c_int;
let c = CString::new(path).ok()?;
dlsym::call::<F, c_int>("oakcodec_decoder_probe", |f| unsafe {
f(c.as_ptr(), out)
})
Some(unsafe { oakcodec::ffi::decoder::oakcodec_decoder_probe(c.as_ptr()) })
}
File diff suppressed because it is too large Load Diff
+10 -16
View File
@@ -89,33 +89,27 @@ impl FootageBehavior {
/// preserved on failure (no partial state).
pub fn probe(&mut self) -> crate::error::Result<()> {
use crate::error::Error;
let mut out = crate::handle::CHandle::null();
let rc = match crate::bridge::codec::decoder_probe(&self.filename, &mut out) {
Some(rc) => rc,
// Direct call into the oakcodec crate (single-lib unification):
// `oakcodec_decoder_probe(filename)` returns the stream-list
// handle (owned by the caller).
let out = match crate::bridge::codec::decoder_probe(&self.filename) {
Some(out) => out,
None => {
return Err(Error::Failed(
"oakcodec unavailable (not linked)".to_string(),
));
}
};
if rc != 0 {
return Err(Error::Failed(format!(
"oakcodec probe failed with code {}",
rc
)));
}
if out.ctx.is_null() {
if out.is_null() {
return Err(Error::Failed(
"oakcodec probe returned no streams".to_string(),
));
}
// The probe result handle is an oakcodec stream-list; without a
// codec module in tests it never gets here (symbol absent). The
// real bridge populates `streams` through the codec C ABI.
// The probe result handle is an oakcodec stream-list. Reading
// stream entries into `streams` is a Phase-2 follow-up (the
// exact accessor symbols are pinned when the codec module C ABI
// is finalized).
let _ = out;
// TODO(bridge::codec): read stream entries from the handle and
// fill `streams`; the exact accessor symbols are pinned when the
// codec module C ABI is finalized (Phase 2 follow-up).
self.valid = true;
Ok(())
}
+7
View File
@@ -170,8 +170,14 @@ static RENDER: std::sync::Mutex<Option<TextRenderBackend>> = std::sync::Mutex::n
mod tests {
use super::*;
// The two tests below share the process-global backend statics; a
// lock serializes them so `backend_hooks_default_none` cannot observe
// the hooks installed by `backend_hooks_install_and_query`.
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
fn backend_hooks_default_none() {
let _guard = LOCK.lock().unwrap();
set_text_backends(None, None);
assert_eq!(text_measure_backend(), None);
assert_eq!(text_render_backend(), None);
@@ -179,6 +185,7 @@ mod tests {
#[test]
fn backend_hooks_install_and_query() {
let _guard = LOCK.lock().unwrap();
fn measure(_r: &TextLayoutRequest) -> TextLayoutSize {
TextLayoutSize {
width: 12.0,
-217
View File
@@ -1,217 +0,0 @@
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! oakcodec C ABI imports (frame payloads for texture upload, disk-cache
//! frame read/write).
//!
//! Written against the frozen `include/codec/*.h` contract and resolved
//! through [`crate::bridge::dlsym`]. The oakcodec crate is being finished
//! concurrently; until it lands, the missing symbols fail explainably and
//! the codec-dependent tests are `#[ignore = "needs oakcodec final"]`.
use crate::error::{Error, Result};
use crate::frame::VideoParamsPod;
use crate::handle::CHandle;
use crate::texture::Frame;
/// An `OakFrame` handle (include/codec/frame.h).
pub type CodecFrameHandle = CHandle;
/// Whether the oakcodec C ABI is present in the process.
pub fn codec_abi_available() -> bool {
crate::bridge::dlsym::resolve("oakcodec_frame_free").is_some()
}
/// Release a codec frame handle (`oakcodec_frame_free`).
///
/// # Safety
/// `frame` must be a handle obtained from the codec module.
pub unsafe fn frame_free(frame: *mut CodecFrameHandle) {
type F = unsafe extern "C" fn(*mut CodecFrameHandle);
let _ = crate::bridge::dlsym::call::<F, ()>("oakcodec_frame_free", |f| unsafe { f(frame) });
}
/// Marshal an `OakFrame` handle into a CPU [`Frame`]
/// (`oakcodec_frame_get_params` / `_data` / `_linesize_bytes` /
/// `_get_timestamp`).
///
/// Fails with `Error::Failed` when the codec C ABI is absent, or with
/// `Error::Invalid` for a null frame handle.
///
/// # Safety
/// `frame` must be a valid `OakFrame` handle.
pub unsafe fn frame_to_cpu(frame: CodecFrameHandle) -> Result<Frame> {
if frame.is_null() {
return Err(Error::Invalid);
}
if !codec_abi_available() {
return Err(Error::Failed(
"oakcodec C ABI not present (oakcodec crate pending)".into(),
));
}
let mut pod = oakcommon_video_params_zeroed();
type GetParamsF = unsafe extern "C" fn(CodecFrameHandle, *mut OakVideoParamsPod) -> i32;
let rc = crate::bridge::dlsym::call::<GetParamsF, i32>("oakcodec_frame_get_params", |f| unsafe {
f(frame, &mut pod)
})
.ok_or_else(|| Error::Failed("oakcodec_frame_get_params missing".into()))?;
if rc != 0 {
return Err(Error::Failed(format!("oakcodec_frame_get_params rc={rc}")));
}
type WidthF = unsafe extern "C" fn(CodecFrameHandle) -> i32;
let width = crate::bridge::dlsym::call::<WidthF, i32>("oakcodec_frame_width", |f| unsafe {
f(frame)
})
.unwrap_or(0);
let height = crate::bridge::dlsym::call::<WidthF, i32>("oakcodec_frame_height", |f| unsafe {
f(frame)
})
.unwrap_or(0);
let format = crate::bridge::dlsym::call::<WidthF, i32>("oakcodec_frame_format", |f| unsafe {
f(frame)
})
.unwrap_or(-1);
let channels =
crate::bridge::dlsym::call::<WidthF, i32>("oakcodec_frame_channel_count", |f| unsafe {
f(frame)
})
.unwrap_or(0);
let linesize =
crate::bridge::dlsym::call::<WidthF, i32>("oakcodec_frame_linesize_bytes", |f| unsafe {
f(frame)
})
.unwrap_or(0);
let data = crate::bridge::dlsym::call::<DataF, *mut u8>("oakcodec_frame_data", |f| unsafe {
f(frame)
})
.unwrap_or(std::ptr::null_mut());
if data.is_null() || linesize <= 0 || width <= 0 || height <= 0 {
return Err(Error::Failed("frame not allocated".into()));
}
type TsF = unsafe extern "C" fn(CodecFrameHandle, *mut i32, *mut i32) -> i32;
let (mut tn, mut td) = (0i32, 1i32);
let _ = crate::bridge::dlsym::call::<TsF, i32>("oakcodec_frame_get_timestamp", |f| unsafe {
f(frame, &mut tn, &mut td)
});
let mut cpu = Frame::new();
let pod = VideoParamsPod {
width,
height,
time_base_num: pod.time_base_num,
time_base_den: pod.time_base_den,
format,
pixel_aspect_num: pod.pixel_aspect_num,
pixel_aspect_den: pod.pixel_aspect_den,
interlacing: pod.interlacing,
color_range: pod.color_range,
divider: pod.divider,
video_type: pod.video_type,
premultiplied_alpha: pod.premultiplied_alpha,
};
cpu.set_video_params(pod);
cpu.channels = channels;
cpu.timestamp = oakcore_rs::Rational::new(tn as i64, td as i64);
let size = (linesize as usize)
.saturating_mul(height as usize)
.min(1usize << 31);
cpu.data = unsafe { std::slice::from_raw_parts(data, size).to_vec() };
Ok(cpu)
}
type DataF = unsafe extern "C" fn(CodecFrameHandle) -> *mut u8;
/// Mirror of the `OakVideoParams` POD (include/common/videoparams.h) used
/// by `oakcodec_frame_get_params`.
#[repr(C)]
#[derive(Clone, Copy)]
struct OakVideoParamsPod {
width: i32,
height: i32,
time_base_num: i32,
time_base_den: i32,
format: i32,
pixel_aspect_num: i32,
pixel_aspect_den: i32,
interlacing: i32,
color_range: i32,
divider: i32,
video_type: i32,
premultiplied_alpha: i32,
}
fn oakcommon_video_params_zeroed() -> OakVideoParamsPod {
// Safe: POD of ints.
unsafe { std::mem::zeroed() }
}
/// Frame-payload write for the disk frame cache. Fails with
/// `Error::Failed` when the codec C ABI is absent.
///
/// # Safety
/// `frame` must be a valid `OakFrame` handle with an allocated buffer.
pub unsafe fn frame_write_disk(frame: CodecFrameHandle, path: &str) -> Result<()> {
if !codec_abi_available() {
return Err(Error::Failed(
"oakcodec C ABI not present (oakcodec crate pending)".into(),
));
}
// The codec crate owns the EXR/JPEG disk format. The symbol name is
// part of the codec contract; wrapped defensively.
#[repr(C)]
struct FsArg {
codec_frame: CodecFrameHandle,
path: *const std::ffi::c_char,
}
let path_c = std::ffi::CString::new(path)
.map_err(|_| Error::Invalid)?;
let arg = FsArg {
codec_frame: frame,
path: path_c.as_ptr(),
};
type F = unsafe extern "C" fn(*const FsArg) -> i32;
let rc = crate::bridge::dlsym::call::<F, i32>("oakcodec_frame_write_file", |f| unsafe {
f(&arg)
})
.ok_or_else(|| Error::Failed("oakcodec_frame_write_file missing".into()))?;
if rc == 0 {
Ok(())
} else {
Err(Error::Failed(format!("oakcodec_frame_write_file rc={rc}")))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn missing_codec_abi_fails_explainably() {
let rc = unsafe { frame_to_cpu(CHandle::null()) };
assert!(rc.is_err(), "null frame rejected");
let rc = unsafe { frame_to_cpu(CHandle {
ctx: 1 as *mut std::ffi::c_void,
addref: None,
release: None,
abi_version: 1,
}) };
if !codec_abi_available() {
assert!(rc.is_err());
}
}
}
+36 -122
View File
@@ -14,12 +14,10 @@
// 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 (config, file functions, strings).
//!
//! Symbols resolve through [`crate::bridge::dlsym`]; a missing symbol
//! yields the documented fallback (config defaults, the mirrored
//! configuration-location computation) so the crate stays testable and
//! linkable without liboakcommon.
//! oakcommon C ABI calls (config, file functions, strings) — now direct
//! Rust calls into the oakcommon crate (single-lib unification, see
//! `docs/zh/plans/riir/single-lib.md`). The configuration-location and
//! disk-cache helpers delegate to oakcommon's own implementation.
use std::ffi::{c_char, c_int};
use std::sync::Mutex;
@@ -30,26 +28,27 @@ use std::sync::Mutex;
pub fn config_get_string(group: Option<&str>, key: &str) -> Option<String> {
let group_c = group.and_then(|g| std::ffi::CString::new(g).ok());
let key_c = std::ffi::CString::new(key).ok()?;
type F = unsafe extern "C" fn(*const c_char, *const c_char, *mut c_char, c_int) -> c_int;
crate::bridge::dlsym::call::<F, i32>("oakcommon_config_get", |f| unsafe {
let group_ptr = group_c.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
f(group_ptr, key_c.as_ptr(), std::ptr::null_mut(), 0)
})
.and_then(|size| {
if size <= 1 {
return None; // missing or empty
}
let mut buf = vec![0u8; size as usize];
let group_ptr = group_c.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
let got = crate::bridge::dlsym::call::<F, i32>("oakcommon_config_get", |f| unsafe {
f(group_ptr, key_c.as_ptr(), buf.as_mut_ptr() as *mut c_char, size)
})?;
if got <= 0 {
return None;
}
let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
Some(String::from_utf8_lossy(&buf[..end]).into_owned())
})
let group_ptr = || group_c.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
let size = unsafe {
oakcommon::ffi::config::oakcommon_config_get(group_ptr(), key_c.as_ptr(), std::ptr::null_mut(), 0)
};
if size <= 1 {
return None; // missing or empty
}
let mut buf = vec![0u8; size as usize];
let got = unsafe {
oakcommon::ffi::config::oakcommon_config_get(
group_ptr(),
key_c.as_ptr(),
buf.as_mut_ptr() as *mut c_char,
size,
)
};
if got <= 0 {
return None;
}
let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
Some(String::from_utf8_lossy(&buf[..end]).into_owned())
}
/// `oakcommon_config_get_int(group, key, default)`.
@@ -59,99 +58,17 @@ pub fn config_get_int(group: Option<&str>, key: &str, default: i32) -> i32 {
Ok(c) => c,
Err(_) => return default,
};
type F = unsafe extern "C" fn(*const c_char, *const c_char, c_int) -> c_int;
crate::bridge::dlsym::call::<F, i32>("oakcommon_config_get_int", |f| unsafe {
let group_ptr = group_c.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
f(group_ptr, key_c.as_ptr(), default)
})
.unwrap_or(default)
let group_ptr = group_c.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
unsafe { oakcommon::ffi::config::oakcommon_config_get_int(group_ptr, key_c.as_ptr(), default) }
}
/// The configuration directory.
///
/// Primary path: `oakcommon_filefunctions_get_configuration_location()`
/// (two-stage, needs an `OakFileFunctions` handle obtained through
/// `oakcommon_filefunctions_init()`). Fallback (mirror of
/// `FileFunctions::get_configuration_location()` in common/rust/src/
/// filefunctions.rs): `OAK_CONFIG_DIR` → portable app dir → macOS
/// `~/Library/Application Support` (or `$XDG_CONFIG_HOME`/`~/.config` on
/// other platforms) → temp directory. The fallback keeps `cargo test`
/// deterministic through the `OAK_CONFIG_DIR` env var.
/// The configuration directory — oakcommon's implementation
/// (`FileFunctions::get_configuration_location`, honoring `OAK_CONFIG_DIR`
/// and the platform fallbacks).
pub fn configuration_location() -> String {
// 1) Env override (also honored by the C++ side).
if let Ok(dir) = std::env::var("OAK_CONFIG_DIR") {
if !dir.is_empty() {
let _ = std::fs::create_dir_all(&dir);
return dir;
}
}
// 2) The real C ABI (force-loaded oakcommon in the app process).
if let Some(path) = configuration_location_via_abi() {
return path;
}
// 3) Mirrored fallback.
#[cfg(target_os = "macos")]
let config_root = match std::env::var("HOME") {
Ok(h) if !h.is_empty() => {
std::path::PathBuf::from(h).join("Library").join("Application Support")
}
_ => std::path::PathBuf::new(),
};
#[cfg(not(target_os = "macos"))]
let config_root = match std::env::var("XDG_CONFIG_HOME") {
Ok(x) if !x.is_empty() => std::path::PathBuf::from(x),
_ => match std::env::var("HOME") {
Ok(h) if !h.is_empty() => std::path::PathBuf::from(h).join(".config"),
_ => std::path::PathBuf::new(),
},
};
if config_root.as_os_str().is_empty() {
std::env::temp_dir().to_string_lossy().into_owned()
} else {
config_root.to_string_lossy().into_owned()
}
}
/// Resolve the configuration location through the oakcommon C ABI
/// (`oakcommon_filefunctions_init` + `_get_configuration_location`).
fn configuration_location_via_abi() -> Option<String> {
type InitF = unsafe extern "C" fn() -> crate::handle::CHandle;
let handle =
crate::bridge::dlsym::call::<InitF, crate::handle::CHandle>("oakcommon_filefunctions_init", |f| {
unsafe { f() }
})?;
if handle.is_null() {
return None;
}
type GetF = unsafe extern "C" fn(crate::handle::CHandle, *mut c_char, c_int) -> c_int;
let size = crate::bridge::dlsym::call::<GetF, i32>(
"oakcommon_filefunctions_get_configuration_location",
|f| unsafe { f(handle, std::ptr::null_mut(), 0) },
)?;
let result = if size <= 1 {
None
} else {
let mut buf = vec![0u8; size as usize];
let got = crate::bridge::dlsym::call::<GetF, i32>(
"oakcommon_filefunctions_get_configuration_location",
|f| unsafe { f(handle, buf.as_mut_ptr() as *mut c_char, size) },
)?;
if got <= 0 {
None
} else {
let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
Some(String::from_utf8_lossy(&buf[..end]).into_owned())
}
};
type FreeF = unsafe extern "C" fn(*mut crate::handle::CHandle);
let mut h = handle;
let _ = crate::bridge::dlsym::call::<FreeF, ()>("oakcommon_filefunctions_free", |f| unsafe {
f(&mut h)
});
result
oakcommon::filefunctions::FileFunctions::new()
.get_configuration_location()
.unwrap_or_default()
}
/// Serializes tests that mutate `OAK_CONFIG_DIR` / `OAK_RENDER_BACKEND`
@@ -161,10 +78,7 @@ pub static ENV_TEST_LOCK: Mutex<()> = Mutex::new(());
/// The default disk cache directory (C++ `DiskManager::
/// get_default_disk_cache_path`): `<configuration_location>/mediacache`.
pub fn default_disk_cache_path() -> String {
std::path::Path::new(&configuration_location())
.join("mediacache")
.to_string_lossy()
.into_owned()
oakcommon::filefunctions::default_disk_cache_path()
}
#[cfg(test)]
@@ -172,8 +86,8 @@ mod tests {
use super::*;
#[test]
fn config_missing_symbol_falls_back() {
// No liboakcommon in cargo test: defaults apply.
fn config_missing_key_falls_back() {
// The real config store is empty under cargo test: defaults apply.
assert_eq!(config_get_int(None, "GraphicsBackend", 7), 7);
assert_eq!(config_get_string(None, "missing"), None);
}
-1
View File
@@ -24,7 +24,6 @@
//! the wrapper returns a documented fallback (empty handle / negative
//! error code) instead of a link error.
pub mod codec;
pub mod common;
pub mod node;