Files
oak-editor/docs/zh/plans/riir/M9-oakplugin.md
T
Mike-Solar fd2111d560 refactor(plugin): de-Qt oakplugin and wrap it in a pure C ABI
- de-Qt src/plugin/src (olivehost/oliveplugininstance/oliveclip/
  paraminstance/image/pluginprogressreporter); QMessageBox/
  QApplication replaced by facade callbacks
- new C ABI in include/plugin/{error,host,instance}.h with
  refcounted OakPluginInstance value handle
- paraminstance bridges via oaknode C ABI (OakNodeNode value handle,
  oaknode_node_identity registry); undo via oakundo C ABI
- oliveclip textures are OakRenderTexture value handles; oakrender
  gains texture/copier/ticket C API additions
- oaknode gains get_input_at_time/set_input_at_time_undoable/
  node_identity/sequence_from_node/sequence_set_default_parameters/
  find_input_footage
- move avframeptr.h to src/common/src (shared by codec and render)
- node transition pluginSupport stubs bridge the real oakplugin
  headers; all oaknode-building standalone trees add src/plugin and
  link oakplugin into their test binaries
- plugin tests self-sufficient (ipc shim, OfxHost force_load,
  PRE_TEST discovery, OCIO env); timeline standalone gains
  render/c_api
- restore ffmpegdecoder.cpp in src/codec/src/ffmpeg/CMakeLists.txt
  (dropped in 3d004c081, caused jump-to-0 in decoder/encoder tests)

All six standalone trees green: codec 22, audio 40, task 110,
render 49, timeline 121, plugin 100.
2026-08-07 22:18:36 +08:00

5.3 KiB
Raw Blame History

M9 · oakplugin 拆分手册 + facade 装配层裁决

内容:engine/pluginSupport/OpenFX hostOliveHost、 OlivePluginInstance、PluginNode、PluginProgressReporter)。 依赖:node 6、render 6、common 6、core 2、undo 2、coreengine 2。 拆分顺序第 9 位(最后拆的实体模块)。文末 §4 是 liboakengine 装配层的最终裁决。

1. 目标形态

oakplugin/
  include/oakplugin/{host.h, instance.h, progress.h, types.h, export.h}
  src/
  tests/  # oakplugin_gtest

2. 冻结 C API

2.1 oakplugin/host.h

OAKPL_API int oakplugin_host_init(void);
OAKPL_API void oakplugin_host_shutdown(void);
OAKPL_API int oakplugin_host_scan(const char *const *bundle_dirs,
	int dir_count);
OAKPL_API int oakplugin_host_plugin_count(void);
OAKPL_API int oakplugin_host_plugin_id_at(int i, char *buf, int n);
OAKPL_API int oakplugin_host_plugin_label(const char *plugin_id,
	char *buf, int n);

2.2 oakplugin/instance.h

typedef struct OakPluginInstance OakPluginInstance;
OAKPL_API OakPluginInstance *oakplugin_instance_create(
	const char *plugin_id);
OAKPL_API void oakplugin_instance_free(OakPluginInstance *i);
OAKPL_API int oakplugin_instance_set_param(OakPluginInstance *i,
	const char *param_id, const oak_node_value *v);
OAKPL_API int oakplugin_instance_get_param(OakPluginInstance *i,
	const char *param_id, oak_node_value *out);
OAKPL_API int oakplugin_instance_render(OakPluginInstance *i,
	OakCodecFrame *dst, const OakCodecFrame *src, int64_t ts);
/* 进度回调:instance_render 是异步命令(渲染中进行),进度/取消回调
 * 即其返回通道——04 §3 唯一例外情形(R6 已把 cancelled 信号改成 C
 * 回调,沿用该机制)。非异步接口一律不配回调。 */
OAKPL_API void oakplugin_instance_set_progress_cb(OakPluginInstance *i,
	oakplugin_progress_fn fn, void *userdata);
OAKPL_API void oakplugin_instance_cancel(OakPluginInstance *i);

2.3 oakplugin/progress.h

typedef struct OakPluginProgress OakPluginProgress; /* 报告器句柄 */
OAKPL_API OakPluginProgress *oakplugin_progress_create_dialog(
	const char *message, const char *title);  /* UI 侧实现 */
OAKPL_API void oakplugin_progress_free(OakPluginProgress *p);

3. 切割点

现状 处理
plugin → node/ 6plugins/plugin.h 3 等) 经 oaknode C ABIPluginNode 是节点,留在 oaknodeoakplugin 只经 factory/host 接口交互)——PluginNode 归属裁决:PluginNode 类随 oaknode 走(它是节点体系成员),oakplugin 提供 host/instance
plugin → render/ 6videoparams 3 等) videoparams 已下沉;其余经 oakrender C ABI
plugin → undo/ 2 oakundo 适配类
plugin → coreengine.h 2 插件注册点内聚进 oakplugin_host_init

4. facade 装配层最终裁决(liboakengine 的终态)

M1-M9 全部完成后,engine/src/capi + coreengine + tool/ + ui/ 残余构成 liboakengine。裁决(选定):

facade 直链各模块,不绕 C ABI 自调。即:facade 的 oakengine_* 实现可以继续直接调用各模块的 C++ 内部(链接 oaknode/oakrender 等 的静态或共享库),不要求 facade 经各模块的公共 C ABI 兜圈。 理由:facade 与 app 的边界(oakengine_*)已经纯 C 且 nm=0,模块间 边界是给"模块互相调用"用的;facade 是装配层,自家人不绕远路。 coreengine.h 被 node/render/task/plugin 引用的 5+2+2+2 处必须内聚(各模块的初始化改由各自的 oak<mod>_init 完成, coreengine 只做编排调用)。

终态验证:

nm -D --defined-only liboakengine.so | grep -c " T _Z"      # 0R7-B
nm -D --defined-only liboaknode.so | grep -c " T _Z"        # 0(每模块同查)
ldd liboakengine.so | grep oak                               # 只见 oak* 模块库
全量构建 0 error;全量 ctest 绿

5. 测试(映射 03 §2/§3

  • hostinit/scan/枚举(照现有 OliveHost 测试的 bundle fixture)。
  • instanceShadertoy 类插件(CI 里可用的)创建/参数/渲染一帧非空。
  • progress:回调触发与 cancel。
  • oakplugin_debug_alive_count() 泄漏断言。

6. 实施现状(2026-08-05

已完成。src/plugin/src/ 六个类(olivehost/oliveplugininstance/ oliveclip/paraminstance/image/pluginprogressreporter)全部去Qt QMessageBox/QApplication 依赖删除,改为 §4 裁决的 facade 回调 装配。C ABI 落在 include/plugin/{error,host,instance}.h OakPluginInstance 为引用计数值句柄(ctx/addref/release/ abi_version)。

偏离手册之处:

  • paraminstance 不直接持有 Node*,改持 OakNodeNode 值句柄,参数 读写全部经 oaknode C ABInode↔instance 的身份路由用新增的 oaknode_node_identity() 注册表实现。
  • oliveclip 的纹理为 OakRenderTexture 值句柄,oakrender 因此补了 texture/copier/ticket 一族 C API。
  • oaknode 的 transition stubsrc/node/transition/pluginSupport/ 桥接 oakplugin 真身头,liboaknode 运行期引用 oakplugin 符号 dynamic_lookup);所有构建 oaknode 的 standalone 树都要把 src/plugin 加进构建,测试二进制必须链接 oakplugin。

验证:build-oakplugin 100/100codec 22/22、audio 40/40、 task 110/110、render 49/49、timeline 121/121(均含 oakplugin 接入后的全量回归)。