Files
oak-editor/docs/zh/plans/r8-app-pure-abi-cleanup.md
T
Mike-Solar 66d761b4b7 R8: finish app/ pure C ABI migration (P3-P9) and make OTIO required
- app/ no longer includes engine C++ headers nor holds engine C++ types:
  engine access goes through the oakengine C ABI plus C++ wrappers
  (oakutil/oaknode.h, oakutil/oakvideo.h) and app-local mirror types
  (tooltypes, trackreferencehandle, timelinecommonapp, keyframetypes,
  subtitleapp, serializedlayoutinfoapp, nodevaluehandle, sliderdisplaytypeapp)
- engine: new C ABI functions for block/track/clip/transition navigation
  and predicates, links, caches, waveform/playback, disk folder,
  sequence_track_list, node_free, footage_is_valid, block_get_track,
  get_brush; loadotio/saveotio ported to the current engine API
- OTIO is now a required dependency: CI and CD build it on every
  platform, FindOpenTimelineIO fixed for OTIO 0.16/0.19 (the old deps
  include requirement silently disabled OTIO everywhere), runtime
  libraries are bundled into packages and copied next to macOS binaries
  (oak_copy_otio_runtime)
- fix ProjectViewModel drag&drop mime read/write size mismatch (segfault)
- unify color label naming (k_olive -> "Oak") in the app-side mirror
- docs: OTIO required, FFmpeg minimum corrected to 6.0 (en/zh)
- gtest suite: 1925 passed, 0 failed
2026-07-31 22:46:52 +08:00

160 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# R8 计划:app/ 纯 C ABI 头文件清理
> 目标:app/ 下的代码只 include engine/ 的 C 头文件(`oakengine/*.h`),
> 不再 include 任何 engine 内部 C++ 头。
> 前置:R7 已完成(nm U _ZN5olive = 0,liboakengine.so 导出收口)。
---
## 1. 纯头工具共享层(shared/)
### 1.1 新建目录
```
shared/
include/
oakutil/
define.h ← engine/common/define.h
lerp.h ← engine/common/lerp.h
decibel.h ← engine/common/decibel.h
digit.h ← engine/common/digit.h
range.h ← engine/common/range.h
crashpadutils.h ← engine/common/crashpadutils.h
qtutils.h ← engine/common/qtutils.h(声明)
filefunctions.h ← engine/common/filefunctions.h(声明)
xmlutils.h ← 精简版(XMLAttributeLoop 宏 + xml_read_next_start_element)
```
### 1.2 CMake 接入
顶层 CMakeLists.txt 添加:
```cmake
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/shared/include)
```
### 1.3 迁移规则
- engine/common/ 中被移出的 .h:原位置改为 `#include "oakutil/xxx.h"` 的转发头
(保持 engine 内部现有 include 路径不断)。
- app/ 中所有 `#include "common/xxx.h"`(指 engine 的)改为 `#include "oakutil/xxx.h"`。
- qtutils.h / filefunctions.h:声明移到 shared,.cpp 实现分别留在
engine/common/(engine 用)和 app/common/(app 用,已有)。
- xmlutils.h 精简版:只保留 XMLAttributeLoop 宏 + 无 CancelAtom 的函数声明;
engine 内部完整版保留在 engine/common/xmlutils.h(include 精简版 + 追加
CancelAtom 重载)。
### 1.4 验证
```bash
grep -rn '#include "common/' app/ | grep -vE '"common/(colorcodingapp|configwrapper|nodevaluehandle|oakvaluehelper|undowrapper|debugapp|filefunctionsapp|hashstreamapp|htmlapp|qtutilsapp|xmlutilsapp)'
# 期望:0 结果
```
---
## 2. engine/ui/colorcoding.h → app 本地化
engine/ui/colorcoding.h 是 C++ 类(ColorCoding),被 app 5 处引用。
方案:在 app/common/ 下新建 `colorcoding.h`,通过 C ABI 或本地静态表实现。
engine 内部保留原文件。
---
## 3. engine C++ 类 → C ABI 替换(按子系统)
按影响面排序,每批一个 PR:
| 批次 | 子系统 | 违规 include | 替换方案 |
|---|---|---|---|
| P2 | node/value.h + node/keyframe.h | ~10 处 | oakengine/node.h 的 oak_node_value_type 枚举 |
| P3 | node/node.h + node/param.h | ~20 处 | OakEngineNode* + oakengine_node_* 函数(执行手册:[r8-p3-node-param-abi.md](r8-p3-node-param-abi.md)) |
| P4 | node/project*.h | ~15 处 | OakEngineProject*/OakEngineSequence* |
| P5 | render/* | ~10 处 | oakengine/display.h + viewer.h |
| P6 | timeline/* | ~6 处 | oakengine/timeline.h |
| P7 | codec/* + audio/* | ~10 处 | oakengine/encoding.h + audio.h |
| P8 | tool/* + undo/* | ~20 处 | 新增 C ABI 或 app 本地枚举 |
| P9 | pluginSupport/* | ~6 处 | oakengine/plugin.h |
---
## 4. CMake 收口(最终步骤)
- 移除 `target_link_libraries(olive-editor PRIVATE oakengine-obj)`
- 从 `oakengine` 共享库 PUBLIC include 中移除 engine 根目录
- 只保留 `engine/include`(C ABI 头)
验证:
```bash
grep -rn '#include "' app/ | grep -E '"(node|render|timeline|codec|audio|task|undo|tool|pluginSupport|ui/colorcoding)/'
# 期望:0 结果
cmake --build build && ctest --test-dir build --output-on-failure
```
---
## 状态
- [x] Phase 1:shared/ 纯头工具层(define, lerp, decibel, digit, range, crashpadutils, qtutils, filefunctions, xmlutils, autoscroll)
- [x] Phase 2:node/value.h + node/keyframe.h 直接 include 已全部移除
- app/common/nodevaluehandle.h 提供 AppNodeValueType 本地枚举
- app/common/oakvaluehelper.h 提供 AppKeyframeType + 转换函数
- NodeValue::Type → int, NodeValue::k_* → AppNodeValueType 常量
- keyframeproperties.h 改为前置声明 NodeKeyframe
- [x] Phase 3:node/node.h + node/param.h 主体清理完成,**以双适配器(oak:: wrapper)形态落地**,
消费侧统一经 `shared/include/oakutil/oaknode.h` 访问 engine,AppNodeInput 方案已废弃
(落地细节与 WRAPPER-GAP 登记见 [r8-p3-node-param-abi.md](r8-p3-node-param-abi.md) 的"落地状态"一节)
- [x] Phase 4-9 + 补充批(2026-07-29 完成):node/project*、render/*、timeline/*、codec/*、audio/*、
tool/*、undo/*、pluginSupport/* 及 P3 遗留(block/track/clip/gizmo/factory 等)全部清理,
同样以双适配器形态落地:
- engine 侧新增 30 个 C ABI 函数(block/track/clip/transition 导航与谓词、链接、
thumbnail/waveform/frame cache、playback cache、disk folder、sequence_track_list、
visible_block_at_time、set_length_and_media_out、node_free、footage_is_valid、
block_get_track 等)
- 新增 app 侧构件:`shared/include/oakutil/oakvideo.h`(oak::VideoParams/ColorTransform)、
`app/common/`(tooltypes.h、trackreferencehandle.h、keyframetypes.h、subtitleapp.h、
serializedlayoutinfoapp.h、nodedatatypes.h、projecttypes.h、sliderdisplaytypeapp.h)、
`app/timeline/timelinecommonapp.h`(TimelineApp 枚举镜像)、
`app/widget/history/historywidget.{h,cpp}`(HistoryModel,C ABI 驱动)
- `app/core.h` 不再 include engine `coreengine.h`(Tool/Timecode/Color 改经镜像与 olive/core)
- [x] 类型清洗(2026-07-29,Wave4/5):app/ 内部接口不再持有 engine C++ 类型
(olive::Node*/Block*/ClipBlock*/Track*/Sequence*/ViewerOutput*/TimelineMarker* 等全部
换成 OakEngine* 句柄或 oak:: wrapper),~600 处双向 reinterpret_cast 消除;
engine 侧配套新增 `oakengine_node_get_brush`(Qt QBrush 越界,照 QPainter* 先例)。
全树 grep 终验:代码级 engine 类型使用仅剩 1 处论证例外(见下)。
全量链接构建 0 error。
- [ ] Phase 10:CMake 收口
### Phase 4-9 已知遗留
- `app/widget/timebased/timebasedwidget.h` 仍 include engine `node/output/viewer/viewer.h`
+ `timebasedwidget.cpp:104` 的 `ViewerOutput*` 桥接:
`QPointer<ViewerOutput>` 需要完整 QObject 类型,C ABI 无节点销毁事件与 QObject* 访问器,
无法等价替代(文件内已论证并标 WRAPPER-GAP;是 app/ 唯一保留的 engine 类型使用)。
- WRAPPER-GAP 登记(app 代码内注释,后续 facade 扩充时清理):
undo 命令装配、group passthrough、traverse、clipboard、NodeValueRow create/free
(viewerdisplay 的 gizmo_db_)、oak::Footage 的 proxy 家族方法、TrackList 枚举
(oakengine_sequence_track_list 暂无查询函数)、VideoParams::stream_index、
Node::get_context_positions、waveform validated ranges 专用 ABI、
playback_cache_draw 缺 y 偏移参数、泛型 QVariant input property 读取
(widgetbridge 以类型化 getter 重建)、engine keyframe has_sibling/closest 系
"整秒"契约与 engine 语义不符(app 侧已用精确 rational 路径规避)、
plugin CAdapter 的 is_cancelled 回调未接(取消不生效,engine 侧缺口)。
### Phase 1 已知遗留
- `app/widget/timebased/timebasedwidget.cpp` 仍引用 `common/current.h`(依赖 pluginSupport + render,需 Phase 2+ 处理)
- app/ 仍引用 3 个 engine 内部头(不在 Phase 1 清单内,待后续阶段处理):
`common/commandlineparser.h`(app/main.cpp)、`common/crashpadinterface.h`(app/main.cpp)、
`common/dropworkflowbehavior.h`(app/widget/timelinewidget/tool/import.h)
- `engine/node/project.h` 原先经 `common/xmlutils.h` 间接获得 `NodeGroup` 前置声明,
精简后已改为在 project.h 内显式前置声明
### Phase 2 已知遗留
- `node/node.h` 仍被 11 个 app/ 头文件直接引用(widget 层深度使用 NodeInput、
NodeKeyframeTrackReference、Node::Position、Node* 方法调用)—— 待 Phase 3 处理
- `node/param.h` 仍被 4 个 app/ 头文件直接引用(NodeInput 类型)
- 其他 engine 内部头引用(render/, timeline/, codec/, audio/, node/project*,
node/output/, pluginSupport/, tool/, undo/)—— 待 Phase 4-9