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
This commit is contained in:
+62
-27
@@ -7,7 +7,8 @@ This document describes how to build Oak Video Editor from source on Windows, Li
|
||||
- CMake 3.20+
|
||||
- Ninja (recommended)
|
||||
- Qt 6 (with private headers)
|
||||
- FFmpeg 8.0+ development libraries (distro packages on Ubuntu/Debian are often too old; see the Linux section below)
|
||||
- FFmpeg 6.0+ development libraries
|
||||
- OpenTimelineIO (0.16+, built from source below — no distro package on most platforms)
|
||||
- OpenImageIO
|
||||
- OpenColorIO (2.x)
|
||||
- OpenEXR
|
||||
@@ -50,7 +51,23 @@ pacman -S --needed \
|
||||
|
||||
> **Note:** Qt 6 private headers may require additional packages depending on the MSYS2 repository state. If CMake reports missing private headers, install `mingw-w64-ucrt-x86_64-qt6-base-private` if available.
|
||||
|
||||
### 3. Clone and Build
|
||||
### 3. Build OpenTimelineIO (required)
|
||||
|
||||
There is no MSYS2 package for OpenTimelineIO, so build it from source:
|
||||
|
||||
```bash
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
cmake --install OpenTimelineIO/build
|
||||
```
|
||||
|
||||
### 4. Clone and Build
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
@@ -60,13 +77,14 @@ cd oak
|
||||
# Configure
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DOTIO_LOCATION="/path/to/otio-install" \
|
||||
-DBUILD_QT6=ON
|
||||
|
||||
# Build
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
### 4. Run Tests (Optional)
|
||||
### 5. Run Tests (Optional)
|
||||
|
||||
```bash
|
||||
ctest --test-dir build --output-on-failure -C Release
|
||||
@@ -78,36 +96,30 @@ ctest --test-dir build --output-on-failure -C Release
|
||||
|
||||
### Debian / Ubuntu
|
||||
|
||||
Install dependencies (FFmpeg is built from source below because distro packages are often too old):
|
||||
Install dependencies (Ubuntu 24.04+ ships FFmpeg 6.1, which satisfies the 6.0 minimum; on older releases build FFmpeg from source as described in Troubleshooting):
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
cmake ninja-build pkg-config nasm \
|
||||
cmake ninja-build pkg-config \
|
||||
qt6-base-dev qt6-base-dev-tools qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \
|
||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev \
|
||||
libopencolorio-dev libopenimageio-dev libopenexr-dev libexpat1-dev \
|
||||
portaudio19-dev libgl1-mesa-dev libvulkan-dev libxkbcommon-dev
|
||||
```
|
||||
|
||||
Build FFmpeg 8.0+ from source:
|
||||
Build OpenTimelineIO (required, no distro package):
|
||||
|
||||
```bash
|
||||
git clone --branch n8.1.1 --depth 1 https://git.ffmpeg.org/ffmpeg.git ffmpeg-src
|
||||
cd ffmpeg-src
|
||||
./configure \
|
||||
--prefix="$PWD/../ffmpeg-install" \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--disable-doc \
|
||||
--disable-programs \
|
||||
--disable-avdevice \
|
||||
--disable-network \
|
||||
--enable-pic \
|
||||
--enable-gpl \
|
||||
--enable-version3
|
||||
make -j$(nproc)
|
||||
make install
|
||||
cd ..
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
cmake --install OpenTimelineIO/build
|
||||
```
|
||||
|
||||
Configure and build:
|
||||
@@ -115,7 +127,7 @@ Configure and build:
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DBUILD_TESTS=ON -DBUILD_QT6=ON \
|
||||
-DFFMPEG_ROOT="$PWD/ffmpeg-install"
|
||||
-DOTIO_LOCATION="$PWD/otio-install"
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
@@ -147,10 +159,26 @@ sudo dnf install -y \
|
||||
bzip2-devel
|
||||
```
|
||||
|
||||
Build OpenTimelineIO (required, no distro package):
|
||||
|
||||
```bash
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
cmake --install OpenTimelineIO/build
|
||||
```
|
||||
|
||||
Configure and build:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja -DBUILD_TESTS=ON -DBUILD_QT6=ON
|
||||
cmake -S . -B build -G Ninja -DBUILD_TESTS=ON -DBUILD_QT6=ON \
|
||||
-DOTIO_LOCATION="$PWD/otio-install"
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
@@ -175,6 +203,7 @@ sudo pacman -S --needed \
|
||||
openexr \
|
||||
expat \
|
||||
portaudio \
|
||||
opentimelineio \
|
||||
mesa \
|
||||
vulkan-headers \
|
||||
vulkan-icd-loader \
|
||||
@@ -211,13 +240,14 @@ brew update
|
||||
brew install cmake ninja pkg-config qt@6 ffmpeg openimageio opencolorio openexr portaudio expat molten-vk vulkan-headers vulkan-loader
|
||||
```
|
||||
|
||||
Build OpenTimelineIO (optional, required for OTIO support):
|
||||
Build OpenTimelineIO (required):
|
||||
|
||||
```bash
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
@@ -257,7 +287,8 @@ ctest --test-dir build --output-on-failure -C Release
|
||||
| `BUILD_DOXYGEN` | `OFF` | Build Doxygen documentation |
|
||||
| `USE_WERROR` | `OFF` | Treat warnings as errors |
|
||||
| `BUILD_QT6` | `ON` | Build with Qt 6 instead of Qt 5 |
|
||||
| `OTIO_LOCATION` | - | Path to OpenTimelineIO installation (optional) |
|
||||
| `OTIO_LOCATION` | - | Path to OpenTimelineIO installation (required) |
|
||||
| `OAK_BUNDLE_OTIO` | `ON` | Install OTIO runtime libraries alongside Oak (set `OFF` for distro-native packaging where `opentimelineio` is a package dependency, e.g. Arch) |
|
||||
| `OCIO_LOCATION` | - | Path to OpenColorIO installation |
|
||||
| `OAK_ENABLE_DYNAMIC_RENDER_BACKEND` | `ON` | Build dynamic render backend libraries (`liboakgl.so` / `liboakvulkan.so`) |
|
||||
|
||||
@@ -292,7 +323,7 @@ pkg-config --exists libavcodec && echo "Found" || echo "Not found"
|
||||
|
||||
### FFmpeg Version Too Old
|
||||
|
||||
If you encounter errors like `AV_PIX_FMT_GRAYF16 was not declared in this scope`, your FFmpeg is too old (Oak requires 8.0+). Build from source:
|
||||
Oak requires FFmpeg 6.0 or newer; CMake configure fails with `Could NOT find FFMPEG (missing: FFMPEG_VERSION) (Required is at least version "6.0")` on older versions. Ubuntu 24.04+ / Fedora / Arch / Homebrew / MSYS2 all ship new enough FFmpeg. If your distro is older, build from source:
|
||||
|
||||
```bash
|
||||
git clone --branch n8.1.1 --depth 1 https://git.ffmpeg.org/ffmpeg.git ffmpeg-src
|
||||
@@ -314,3 +345,7 @@ cd ..
|
||||
```
|
||||
|
||||
Then pass `-DFFMPEG_ROOT="$PWD/ffmpeg-install"` to CMake.
|
||||
|
||||
### OpenTimelineIO Not Found
|
||||
|
||||
OpenTimelineIO is a required dependency. Build it from source as shown in your platform's section above and pass `-DOTIO_LOCATION=/path/to/otio-install` to CMake. On Arch Linux the `opentimelineio` package provides it directly.
|
||||
|
||||
+4
-13
@@ -90,9 +90,9 @@ brew install googletest
|
||||
|
||||
---
|
||||
|
||||
## Build OpenTimelineIO (Optional)
|
||||
## Build OpenTimelineIO (Required)
|
||||
|
||||
OpenTimelineIO enables importing/exporting timeline data in OTIO format. If you don't need OTIO support, you can skip this step.
|
||||
OpenTimelineIO enables importing/exporting timeline data in OTIO format. It is a required dependency.
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
@@ -103,6 +103,7 @@ cd OpenTimelineIO
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/install"
|
||||
|
||||
@@ -127,16 +128,6 @@ cd oak
|
||||
|
||||
### 2. Configure with CMake
|
||||
|
||||
Basic configuration (without OTIO):
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DOCIO_LOCATION=$(brew --prefix opencolorio)
|
||||
```
|
||||
|
||||
Configuration with OTIO support:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -188,7 +179,7 @@ ctest --test-dir build --output-on-failure -C Release
|
||||
| `BUILD_TESTS` | `OFF` | Build unit tests |
|
||||
| `BUILD_DOXYGEN` | `OFF` | Build Doxygen documentation |
|
||||
| `USE_WERROR` | `OFF` | Treat warnings as errors |
|
||||
| `OTIO_LOCATION` | - | Path to OpenTimelineIO installation (optional) |
|
||||
| `OTIO_LOCATION` | - | Path to OpenTimelineIO installation (required) |
|
||||
| `OCIO_LOCATION` | - | Path to OpenColorIO installation |
|
||||
|
||||
---
|
||||
|
||||
+61
-27
@@ -7,7 +7,8 @@
|
||||
- CMake 3.20+
|
||||
- Ninja(推荐)
|
||||
- Qt 6(含私有头文件)
|
||||
- FFmpeg 8.0+ 开发库(Ubuntu/Debian 系统源里的版本通常太旧,见下文 Linux 章节)
|
||||
- FFmpeg 6.0+ 开发库
|
||||
- OpenTimelineIO(0.16+,按下文从源码构建——大多数平台没有发行版软件包)
|
||||
- OpenImageIO
|
||||
- OpenColorIO(2.x)
|
||||
- OpenEXR
|
||||
@@ -50,7 +51,23 @@ pacman -S --needed \
|
||||
|
||||
> **注意:** Qt 6 私有头文件可能需要额外安装。如果 CMake 报告找不到私有头文件,请尝试安装 `mingw-w64-ucrt-x86_64-qt6-base-private`(如果仓库中有)。
|
||||
|
||||
### 3. 克隆并构建
|
||||
### 3. 构建 OpenTimelineIO(必需)
|
||||
|
||||
MSYS2 仓库没有 OpenTimelineIO 软件包,需从源码构建:
|
||||
|
||||
```bash
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
cmake --install OpenTimelineIO/build
|
||||
```
|
||||
|
||||
### 4. 克隆并构建
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
@@ -60,13 +77,14 @@ cd oak
|
||||
# 配置
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DOTIO_LOCATION="/path/to/otio-install" \
|
||||
-DBUILD_QT6=ON
|
||||
|
||||
# 构建
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
### 4. 运行测试(可选)
|
||||
### 5. 运行测试(可选)
|
||||
|
||||
```bash
|
||||
ctest --test-dir build --output-on-failure -C Release
|
||||
@@ -78,36 +96,30 @@ ctest --test-dir build --output-on-failure -C Release
|
||||
|
||||
### Debian / Ubuntu
|
||||
|
||||
安装依赖:
|
||||
安装依赖(Ubuntu 24.04+ 自带的 FFmpeg 6.1 已满足 6.0 最低要求;更旧的发行版请按"故障排除"一节从源码编译 FFmpeg):
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
cmake ninja-build pkg-config nasm \
|
||||
cmake ninja-build pkg-config \
|
||||
qt6-base-dev qt6-base-dev-tools qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \
|
||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev \
|
||||
libopencolorio-dev libopenimageio-dev libopenexr-dev libexpat1-dev \
|
||||
portaudio19-dev libgl1-mesa-dev libvulkan-dev libxkbcommon-dev
|
||||
```
|
||||
|
||||
从源码编译 FFmpeg 8.0+:
|
||||
从源码构建 OpenTimelineIO(必需,无发行版软件包):
|
||||
|
||||
```bash
|
||||
git clone --branch n8.1.1 --depth 1 https://git.ffmpeg.org/ffmpeg.git ffmpeg-src
|
||||
cd ffmpeg-src
|
||||
./configure \
|
||||
--prefix="$PWD/../ffmpeg-install" \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--disable-doc \
|
||||
--disable-programs \
|
||||
--disable-avdevice \
|
||||
--disable-network \
|
||||
--enable-pic \
|
||||
--enable-gpl \
|
||||
--enable-version3
|
||||
make -j$(nproc)
|
||||
make install
|
||||
cd ..
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
cmake --install OpenTimelineIO/build
|
||||
```
|
||||
|
||||
配置并构建:
|
||||
@@ -115,7 +127,7 @@ cd ..
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DBUILD_TESTS=ON -DBUILD_QT6=ON \
|
||||
-DFFMPEG_ROOT="$PWD/ffmpeg-install"
|
||||
-DOTIO_LOCATION="$PWD/otio-install"
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
@@ -147,10 +159,25 @@ sudo dnf install -y \
|
||||
bzip2-devel
|
||||
```
|
||||
|
||||
从源码构建 OpenTimelineIO(必需,无发行版软件包):
|
||||
|
||||
```bash
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
cmake --install OpenTimelineIO/build
|
||||
```
|
||||
|
||||
配置并构建:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja -DBUILD_TESTS=ON -DBUILD_QT6=ON
|
||||
cmake -S . -B build -G Ninja -DBUILD_TESTS=ON -DBUILD_QT6=ON \
|
||||
-DOTIO_LOCATION="$PWD/otio-install"
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
@@ -175,6 +202,7 @@ sudo pacman -S --needed \
|
||||
openexr \
|
||||
expat \
|
||||
portaudio \
|
||||
opentimelineio \
|
||||
mesa \
|
||||
vulkan-headers \
|
||||
vulkan-icd-loader \
|
||||
@@ -211,13 +239,14 @@ brew update
|
||||
brew install cmake ninja pkg-config qt@6 ffmpeg openimageio opencolorio openexr portaudio expat molten-vk vulkan-headers vulkan-loader
|
||||
```
|
||||
|
||||
构建 OpenTimelineIO(可选,如需 OTIO 支持):
|
||||
构建 OpenTimelineIO(必需):
|
||||
|
||||
```bash
|
||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||
cmake --build OpenTimelineIO/build
|
||||
@@ -257,7 +286,8 @@ ctest --test-dir build --output-on-failure -C Release
|
||||
| `BUILD_DOXYGEN` | `OFF` | 构建 Doxygen 文档 |
|
||||
| `USE_WERROR` | `OFF` | 将警告视为错误 |
|
||||
| `BUILD_QT6` | `ON` | 使用 Qt 6 而非 Qt 5 |
|
||||
| `OTIO_LOCATION` | - | OpenTimelineIO 安装路径(可选) |
|
||||
| `OTIO_LOCATION` | - | OpenTimelineIO 安装路径(必需) |
|
||||
| `OAK_BUNDLE_OTIO` | `ON` | 随 Oak 一并安装 OTIO 运行库(发行版原生打包且 `opentimelineio` 是包依赖时设 `OFF`,如 Arch) |
|
||||
| `OCIO_LOCATION` | - | OpenColorIO 安装路径 |
|
||||
| `OAK_ENABLE_DYNAMIC_RENDER_BACKEND` | `ON` | 构建动态渲染后端库(`liboakgl.so` / `liboakvulkan.so`) |
|
||||
|
||||
@@ -284,7 +314,7 @@ export PATH="/ucrt64/bin:$PATH"
|
||||
|
||||
### FFmpeg 版本太旧
|
||||
|
||||
如果遇到 `AV_PIX_FMT_GRAYF16 was not declared in this scope` 之类的错误,说明你的 FFmpeg 版本太旧(Oak 需要 8.0+)。请从源码编译:
|
||||
Oak 要求 FFmpeg 6.0 或更新版本;版本不足时 CMake 配置阶段会报 `Could NOT find FFMPEG ... (Required is at least version "6.0")`。Ubuntu 24.04+ / Fedora / Arch / Homebrew / MSYS2 自带的版本都足够新。如果发行版过旧,请从源码编译:
|
||||
|
||||
```bash
|
||||
git clone --branch n8.1.1 --depth 1 https://git.ffmpeg.org/ffmpeg.git ffmpeg-src
|
||||
@@ -306,3 +336,7 @@ cd ..
|
||||
```
|
||||
|
||||
然后在 CMake 中加上 `-DFFMPEG_ROOT="$PWD/ffmpeg-install"`。
|
||||
|
||||
### 找不到 OpenTimelineIO
|
||||
|
||||
OpenTimelineIO 是必需依赖。按你所在平台章节的说明从源码构建,并在 CMake 中加上 `-DOTIO_LOCATION=/path/to/otio-install`。Arch Linux 可直接安装 `opentimelineio` 包。
|
||||
|
||||
@@ -90,9 +90,9 @@ brew install googletest
|
||||
|
||||
---
|
||||
|
||||
## 编译 OpenTimelineIO(可选)
|
||||
## 编译 OpenTimelineIO(必需)
|
||||
|
||||
OpenTimelineIO 支持以 OTIO 格式导入/导出时间线数据。如果你不需要 OTIO 支持,可以跳过此步骤。
|
||||
OpenTimelineIO 支持以 OTIO 格式导入/导出时间线数据,是必需依赖。
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
@@ -103,6 +103,7 @@ cd OpenTimelineIO
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DOTIO_SHARED_LIBS=ON \
|
||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||
-DOTIO_FIND_IMATH=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/install"
|
||||
|
||||
@@ -127,16 +128,6 @@ cd oak
|
||||
|
||||
### 2. 使用 CMake 配置
|
||||
|
||||
基础配置(不包含 OTIO):
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DOCIO_LOCATION=$(brew --prefix opencolorio)
|
||||
```
|
||||
|
||||
包含 OTIO 支持的配置:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -188,7 +179,7 @@ ctest --test-dir build --output-on-failure -C Release
|
||||
| `BUILD_TESTS` | `OFF` | 构建单元测试 |
|
||||
| `BUILD_DOXYGEN` | `OFF` | 构建 Doxygen 文档 |
|
||||
| `USE_WERROR` | `OFF` | 将警告视为错误 |
|
||||
| `OTIO_LOCATION` | - | OpenTimelineIO 安装路径(可选) |
|
||||
| `OTIO_LOCATION` | - | OpenTimelineIO 安装路径(必需) |
|
||||
| `OCIO_LOCATION` | - | OpenColorIO 安装路径 |
|
||||
|
||||
---
|
||||
|
||||
@@ -67,7 +67,7 @@ engine 内部保留原文件。
|
||||
| 批次 | 子系统 | 违规 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_* 函数 |
|
||||
| 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 |
|
||||
@@ -95,9 +95,52 @@ cmake --build build && ctest --test-dir build --output-on-failure
|
||||
## 状态
|
||||
|
||||
- [x] Phase 1:shared/ 纯头工具层(define, lerp, decibel, digit, range, crashpadutils, qtutils, filefunctions, xmlutils, autoscroll)
|
||||
- [ ] Phase 2-9:C++ 类替换(node/, render/, timeline/, codec/, audio/, task/, undo/, tool/, pluginSupport/)
|
||||
- [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+ 处理)
|
||||
@@ -106,3 +149,11 @@ cmake --build build && ctest --test-dir build --output-on-failure
|
||||
`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
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
# R8-P3 执行手册:清除 app/ 对 node/node.h + node/param.h 的直接引用
|
||||
|
||||
> 本手册是**纯执行指令**。所有设计决策已经做出,不要重新设计、不要引入
|
||||
> 本手册之外的改动。遇到与本手册矛盾的事实时停下来报告,不要自行变通。
|
||||
>
|
||||
> 目标:`grep -rn '#include "node/node.h"\|#include "node/param.h"' app/` 结果为 0。
|
||||
> 原则:**engine/ 侧零改动**。所需的全部 C ABI 函数已存在于
|
||||
> `engine/include/oakengine/node.h`(映射表见第 3 节),本阶段只在 app/ 内工作。
|
||||
|
||||
---
|
||||
|
||||
## 落地状态(2026-07-29 修订,取代 §2.1 的 AppNodeInput 方案)
|
||||
|
||||
本阶段最终按**双适配器**形态落地,与下文 §2.1 的原始决策不同:
|
||||
|
||||
- 消费侧不直接调 C ABI,统一经过 C++ wrapper 层
|
||||
`shared/include/oakutil/oaknode.h`(namespace `oak`):
|
||||
`Node`/`Project`/`Footage`/`Input`/`Keyframe`/`KeyframeTrackRef`/`InputPair`
|
||||
+ `NodeCategory` 枚举 + `NodeConnection`/`ContextNodeItem` 结构。
|
||||
wrapper 只做转发;owned/borrowed 语义见文件头注释。
|
||||
- `app/common/nodeinputhandle.h`(`AppNodeInput`/`AppNodeInputPair`/
|
||||
`AppNodeKeyframeTrackReference`/`AppNodeCategory` 方案)**已废弃并删除**,
|
||||
全部消费点迁到 `oak::` 类型。
|
||||
- app/ 中 node/param 相关裸 `oakengine_*` 调用已清零;保留的裸调用均属
|
||||
其它子系统,以 `// WRAPPER-GAP:` 注释登记(见下表),归后续批次建立
|
||||
对应 wrapper 时清理。
|
||||
|
||||
WRAPPER-GAP 登记(按归属批次分组):
|
||||
|
||||
| 归属 | 函数族 | 位置(示例) |
|
||||
|---|---|---|
|
||||
| undo 批次 | `oakengine_undo_*`、`oakengine_node_*_command` | nodeview.cpp、nodeparamviewcontext.cpp、mainwindowundo.cpp |
|
||||
| group 批次 | `oakengine_group_*`(passthrough/resolve/create) | nodeview.cpp、nodeparamview.cpp、nodeparamviewitem.cpp |
|
||||
| traverse 批次 | `oakengine_traverse_*`、`oakengine_node_set_value_hint` | nodevaluetree.cpp、nodetableview.cpp |
|
||||
| clipboard 批次 | `oakengine_clipboard_*`、`oakengine_nodes_delete_many` | nodeview.cpp |
|
||||
| keyframe 查询 | 最早/最晚/最近 keyframe、`get_split_*` 等 | nodeparamviewkeyframecontrol.cpp |
|
||||
| 杂项 | `Node::getPluginInstance`、`Node::get_input_property`、`oakengine_node_array_insert_at/remove_at`、`Node::brush()`、`Node::has_gizmos`、效果标志常量 | nodeparamviewitem.cpp、nodeparamview.cpp、nodeviewitem.cpp |
|
||||
|
||||
除原手册列出的文件外,本次一并完成了前半程未迁完的文件:
|
||||
`nodeparamviewitem.h/.cpp`、`nodeparamview.h/.cpp`、`nodeparamviewcontext.cpp`、
|
||||
`nodeparamviewwidgetbridge.cpp`、`nodevaluetree.h/.cpp`、`curvewidget.h/.cpp`、
|
||||
`curveview.cpp`、`timebasedwidget.cpp`、`nodeviewscene.cpp`、`nodewidget.h`、
|
||||
`panel/node/node.h`、`panel/param/param.*`、`toolbar.cpp`、`mainwindow.cpp`、
|
||||
`mainwindowundo.h/.cpp`、`timelinewidget/tool/import.cpp`(仅一处)、
|
||||
`hashstreamapp.cpp`、`tests/gtest/widget_panels_model_test.cpp`。
|
||||
|
||||
### 落地后已知遗留(2026-07-29 Wave3 收尾后)
|
||||
|
||||
- ~~`app/widget/viewer/viewerdisplay.h`、`app/widget/colorwheel/*.cpp` 仍直接
|
||||
include `node/node.h`~~(Wave2 已清)。
|
||||
- ~~`app/widget/nodeparamview/nodeparamviewwidgetbridge.h` 公开构造签名仍用
|
||||
engine `NodeInput`~~(Wave3 已收敛到 `oak::Input`)。
|
||||
- app/ 全树唯一保留的违规 include:`app/widget/timebased/timebasedwidget.h`
|
||||
的 engine `node/output/viewer/viewer.h`(`QPointer<ViewerOutput>` 需要完整
|
||||
QObject 类型,C ABI 无节点销毁事件,文件内有论证,待 facade 增加销毁通知后清理)。
|
||||
- `tests/gtest/` 5 个文件白盒引用 engine C++ 头(engine 级测试,不在 P3 范围)。
|
||||
- `nodeparamviewkeyframecontrol.cpp` 等仍经 `node/value.h` 传递使用 engine
|
||||
C++ keyframe 查询方法(见 WRAPPER-GAP 表 keyframe 查询行)。
|
||||
- `app/core.cpp` 与 `app/dialog/otioproperties/otiopropertiesdialog.cpp` 的
|
||||
OTIO 适配已在 OTIO 必需化后本机验证编译通过(/opt/otio 0.19 与
|
||||
otio-install 0.16 双版本)。
|
||||
- 全量对象编译 + 全量链接构建均 0 error(cmake-build-debug,含 OTIO)。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 0. 前置条件(先确认再动手)
|
||||
|
||||
1. 工作树中 P2 的改动(node/value.h + node/keyframe.h 清理)应先已提交。
|
||||
P3 必须基于干净工作树开始,结束时单独成一个 commit。
|
||||
2. 本机(macOS)验证环境说明,**不要被全量构建失败迷惑**:
|
||||
- `liboakengine.dylib` 链接依赖 `18aed979a` 的 `-U,__ZN5olive13k_app_versionE`
|
||||
修复,确认该 commit 已在历史中。
|
||||
- 本机未安装 OpenTimelineIO,`app/core.cpp` 与
|
||||
`app/dialog/otioproperties/otiopropertiesdialog.cpp` **永远无法在本机编译**,
|
||||
与本次改动无关,验证时跳过这两个文件。
|
||||
- 验证编译用对象级构建(绕过 dylib 依赖与 OTIO 文件):
|
||||
|
||||
```bash
|
||||
cd cmake-build-debug
|
||||
ninja -t targets all | grep -oE 'app/CMakeFiles/libolive-editor\.dir/[^:]+\.o' \
|
||||
| sort -u | grep -vE 'dir/core\.cpp\.o|otiopropertiesdialog\.cpp\.o' > /tmp/app_objs.txt
|
||||
xargs ninja -j"$(sysctl -n hw.ncpu)" < /tmp/app_objs.txt
|
||||
# engine 侧:
|
||||
ninja engine/CMakeFiles/oakengine-obj.dir/all 2>/dev/null || ninja $(ninja -t targets all | grep -oE 'engine/CMakeFiles/oakengine-obj\.dir/[^:]+\.o' | sort -u)
|
||||
```
|
||||
- 每完成下面一个步骤就跑一次上述 app 对象编译,保持可编译状态。
|
||||
|
||||
---
|
||||
|
||||
## 1. 现状清单(20 个文件,调查于 P2 完成后)
|
||||
|
||||
事实:`engine/node/node.h:40` 自己 include `node/param.h`,所以 include
|
||||
node/node.h 等于同时引入两个头。
|
||||
|
||||
| 分组 | 文件 | 直接 include | 关键用法 |
|
||||
|---|---|---|---|
|
||||
| A 琐碎 | `app/widget/nodecombobox/nodecombobox.h` | node/node.h | **完全未使用任何符号**,直接删 |
|
||||
| A 琐碎 | `app/dialog/preferences/tabs/preferencesappearancetab.cpp` | node/node.h | 仅用 `Node::k_category_count`(:72) |
|
||||
| A 琐碎 | `app/widget/menu/factorymenu.h` | node/node.h | `Node::CategoryID`/`k_category_unknown`(:44)、`Node*` 返回值(:52) |
|
||||
| B 指针替换 | `app/widget/nodeparamview/nodeparamviewitembase.h` | node/node.h | `get_title_bar_text_from_node(Node*)`(:52)一处 |
|
||||
| B 指针替换 | `app/widget/nodetableview/nodetableview.h` | node/node.h | `QVector<Node*>` 参数、`QMap<Node*, QTreeWidgetItem*>`(:44) |
|
||||
| B 指针替换 | `app/widget/nodevaluetree/nodevaluetree.h` | node/node.h | `set_node(const NodeInput&, Rational)`(:35)一处 |
|
||||
| C nodeview | `app/widget/nodeview/nodeviewedge.h` | node/node.h | `Node* output_`、`NodeInput input_` 值成员(:125/127) |
|
||||
| C nodeview | `app/widget/nodeview/nodeviewitem.h` | node/node.h | `Node::Position`(:60/63)、`get_input()` 按值返回 NodeInput(:75) |
|
||||
| C nodeview | `app/widget/nodeview/nodeviewcontext.h` | node/node.h | `Node*` 成员/参数、`NodeInput` const 引用参数 |
|
||||
| D TrackRef | `app/widget/nodetreeview/nodetreeview.h` | node/node.h | `NodeKeyframeTrackReference` 值存储、QHash key、signal 签名(:69-73) |
|
||||
| D TrackRef | `app/widget/keyframeview/keyframeviewinputconnection.h` | node/node.h + node/param.h | `NodeKeyframe*`、`Node::get_keyframe_tracks()`(:55-61) |
|
||||
| E paramview | `app/widget/nodeparamview/nodeparamview.h` | node/node.h | `QVector<Node*>`、`QHash<Node*,Node*>`、slot 签名带 NodeInput |
|
||||
| E paramview | `app/widget/nodeparamview/nodeparamviewarraywidget.h/.cpp` | param.h / node.h | `Node* node_`、`node_->input_array_size()`(cpp:53) |
|
||||
| E paramview | `app/widget/nodeparamview/nodeparamviewconnectedlabel.h/.cpp` | param.h / node.h | `NodeInput input_` 值成员、NodeInput 成员调用 |
|
||||
| E paramview | `app/widget/nodeparamview/nodeparamviewitem.h` | node/node.h | `QHash<NodeInput,…>`、`QHash<NodeInputPair,…>`、signal 签名 |
|
||||
| E paramview | `app/widget/nodeparamview/nodeparamviewkeyframecontrol.h` | node/param.h | `NodeInput input_` 值成员、slot 签名 |
|
||||
| F 特殊 | `app/common/hashstreamapp.cpp` | node/param.h | NodeInput/NodeInputPair/NodeKeyframeTrackReference 的 qHash |
|
||||
| F 特殊 | `app/widget/viewer/viewerdisplay.h` | node/node.h | `Node*` 参数/成员、`NodeValueRow`(P5 遗留见 4.8) |
|
||||
|
||||
`Node::Position`、`Node::get_keyframe_tracks` 的真实用法比预想少:
|
||||
`Node::Position` 仅 nodeviewitem.h 两处且**纯 UI 聚合,不触 engine**;
|
||||
`NodeKeyframeTrackReference` 仅 nodetreeview.h + keyframeviewinputconnection.h。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心决策(已定,照做即可)
|
||||
|
||||
### 2.1 新建 `app/common/nodeinputhandle.h`(本阶段唯一新增文件)
|
||||
|
||||
这是 P2 的 `nodevaluehandle.h` 的姊妹文件:app 侧值类型镜像 + 薄封装。
|
||||
**完整内容如下,直接照抄创建**(许可证头与 P2 文件保持一致):
|
||||
|
||||
```cpp
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_NODEINPUTHANDLE_H
|
||||
#define OAK_NODEINPUTHANDLE_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#include "oakengine/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-local mirror of engine's olive::NodeInput (node/param.h).
|
||||
*
|
||||
* Value type identifying one input (or array element) on a node. The C ABI
|
||||
* identifies inputs by (node, input_id, element), so this struct interoperates
|
||||
* with oakengine_node_input_*() directly. Semantics must stay identical to the
|
||||
* engine type: default-constructed is {nullptr, QString(), -1}.
|
||||
*/
|
||||
struct AppNodeInput {
|
||||
AppNodeInput() : node(nullptr), element(-1) {}
|
||||
AppNodeInput(OakEngineNode *n, const QString &i, int e = -1)
|
||||
: node(n), input(i), element(e)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const AppNodeInput &rhs) const
|
||||
{
|
||||
return node == rhs.node && input == rhs.input &&
|
||||
element == rhs.element;
|
||||
}
|
||||
bool operator!=(const AppNodeInput &rhs) const { return !(*this == rhs); }
|
||||
bool operator<(const AppNodeInput &rhs) const
|
||||
{
|
||||
if (node != rhs.node) return node < rhs.node;
|
||||
if (input != rhs.input) return input < rhs.input;
|
||||
return element < rhs.element;
|
||||
}
|
||||
|
||||
bool is_valid() const { return node != nullptr; }
|
||||
|
||||
OakEngineNode *node;
|
||||
QString input;
|
||||
int element;
|
||||
};
|
||||
|
||||
inline uint qHash(const AppNodeInput &i, uint seed = 0)
|
||||
{
|
||||
return ::qHash(i.node, seed) ^ ::qHash(i.input, seed) ^
|
||||
::qHash(i.element, seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief App-local mirror of engine's olive::NodeInputPair.
|
||||
*
|
||||
* NOTE: engine's NodeInputPair is {Node *node; QString input;} — NO element.
|
||||
* Keep the same shape; it is used as the array-widget key.
|
||||
*/
|
||||
struct AppNodeInputPair {
|
||||
bool operator==(const AppNodeInputPair &rhs) const
|
||||
{
|
||||
return node == rhs.node && input == rhs.input;
|
||||
}
|
||||
|
||||
OakEngineNode *node = nullptr;
|
||||
QString input;
|
||||
};
|
||||
|
||||
inline uint qHash(const AppNodeInputPair &p, uint seed = 0)
|
||||
{
|
||||
return ::qHash(p.node, seed) ^ ::qHash(p.input, seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief App-local mirror of engine's olive::NodeKeyframeTrackReference.
|
||||
*/
|
||||
struct AppNodeKeyframeTrackReference {
|
||||
AppNodeKeyframeTrackReference() : track(-1) {}
|
||||
AppNodeKeyframeTrackReference(const AppNodeInput &i, int t = 0)
|
||||
: input(i), track(t)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const AppNodeKeyframeTrackReference &rhs) const
|
||||
{
|
||||
return input == rhs.input && track == rhs.track;
|
||||
}
|
||||
bool operator!=(const AppNodeKeyframeTrackReference &rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
AppNodeInput input;
|
||||
int track;
|
||||
};
|
||||
|
||||
inline uint qHash(const AppNodeKeyframeTrackReference &r, uint seed = 0)
|
||||
{
|
||||
return qHash(r.input, seed) ^ ::qHash(r.track, seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief App-local mirror of engine's Node::CategoryID ordinals
|
||||
* (engine/node/node.h). Values must stay in sync with the engine enum;
|
||||
* oakengine_node_category_name() takes these ordinals directly.
|
||||
*/
|
||||
enum AppNodeCategory {
|
||||
k_app_category_unknown = -1,
|
||||
k_app_category_output,
|
||||
k_app_category_generator,
|
||||
k_app_category_math,
|
||||
k_app_category_keying,
|
||||
k_app_category_filter,
|
||||
k_app_category_color,
|
||||
k_app_category_time,
|
||||
k_app_category_timeline,
|
||||
k_app_category_transition,
|
||||
k_app_category_distort,
|
||||
k_app_category_project,
|
||||
k_app_category_open_fx,
|
||||
k_app_category_count
|
||||
};
|
||||
|
||||
/* Thin wrappers over the C ABI replacing the NodeInput member functions that
|
||||
* app code actually called. */
|
||||
|
||||
inline bool app_input_is_connected(const AppNodeInput &i)
|
||||
{
|
||||
return oakengine_node_input_is_connected(
|
||||
i.node, i.input.toUtf8().constData()) != 0;
|
||||
}
|
||||
|
||||
inline OakEngineNode *app_input_get_connected_node(const AppNodeInput &i)
|
||||
{
|
||||
return oakengine_node_input_get_connected_node(
|
||||
i.node, i.input.toUtf8().constData(), i.element);
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
Q_DECLARE_METATYPE(olive::AppNodeInput)
|
||||
Q_DECLARE_METATYPE(olive::AppNodeKeyframeTrackReference)
|
||||
|
||||
#endif // OAK_NODEINPUTHANDLE_H
|
||||
```
|
||||
|
||||
要点:
|
||||
- `AppNodeInput` 字段公开(node/input/element),替换代码里 `input_.node()`
|
||||
→ `input_.node`、`input_.input()` → `input_.input`、`input_.element()`
|
||||
→ `input_.element`。
|
||||
- `Q_DECLARE_METATYPE` 使这两个类型可以进 Qt signal/slot 签名和
|
||||
`QVariant::fromValue`(item data)。这与 `OakEngineNode*` 进 MOC 的既有
|
||||
先例一致;**不要**再把它们改成非 slot 规避。
|
||||
|
||||
### 2.2 `Node::Position` → nodeviewitem 本地纯 UI 结构
|
||||
|
||||
`NodeViewItem::get_node_position_data()` 只是把 item 自身的
|
||||
`pos()+is_expanded()` 打包(nodeviewitem.cpp:120-123),`set_node_position`
|
||||
只是解包(:137-141),完全不触 engine。决策:在 `nodeviewitem.h` 内定义
|
||||
局部结构,不进公共头:
|
||||
|
||||
```cpp
|
||||
struct NodeViewItemPosition {
|
||||
QPointF position;
|
||||
bool expanded = false;
|
||||
};
|
||||
```
|
||||
|
||||
签名改为 `NodeViewItemPosition get_node_position_data() const` /
|
||||
`void set_node_position(const NodeViewItemPosition &pos)`。调用点
|
||||
(nodeview 相关 .cpp)同名替换即可。
|
||||
|
||||
### 2.3 `Node*` → `OakEngineNode*`
|
||||
|
||||
所有成员变量、参数、返回值、容器 key 机械替换。两者是同一指针的不同
|
||||
opaque 类型,边界处用 `reinterpret_cast`(既有先例:
|
||||
nodeparamview.h:152、connectedlabel.cpp 等)。EngineEventBridge 的信号
|
||||
全部已经是 `OakEngineNode*` + `(QString input, int element)`,lambda 里
|
||||
把旧的 `NodeInput(reinterpret_cast<Node*>(source), input, element)` 改为
|
||||
`AppNodeInput(source, input, element)`。
|
||||
|
||||
### 2.4 `Node::CategoryID` → `AppNodeCategory`
|
||||
|
||||
`preferencesappearancetab.cpp:72` 的 `Node::k_category_count` →
|
||||
`k_app_category_count`;`factorymenu.h:44` 的参数类型与默认值
|
||||
`Node::k_category_unknown` → `AppNodeCategory` / `k_app_category_unknown`。
|
||||
`oakengine_node_category_name(i, …)` 的调用已经用 int 序数,不用动。
|
||||
|
||||
### 2.5 `NodeKeyframe*` / `Node::get_keyframe_tracks` → C ABI 循环
|
||||
|
||||
keyframeviewinputconnection.h:55-61 目前内联调用
|
||||
`input_.input().node()->get_keyframe_tracks(input_.input()).at(input_.track())`
|
||||
返回 `const QVector<NodeKeyframe*>&`。决策:
|
||||
- 成员类型改为 `AppNodeKeyframeTrackReference input_;`(含 track)。
|
||||
- `get_keyframes()` 改为**按值返回** `QVector<OakEngineKeyframe*>`,实现
|
||||
移到 .cpp,用 C ABI 拼装:
|
||||
|
||||
```cpp
|
||||
QVector<OakEngineKeyframe *> KeyframeViewInputConnection::get_keyframes() const
|
||||
{
|
||||
QVector<OakEngineKeyframe *> keys;
|
||||
const int n = oakengine_node_keyframe_count_on_track(
|
||||
input_.input.node, input_.input.input.toUtf8().constData(),
|
||||
input_.input.element, input_.track);
|
||||
keys.reserve(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
keys.append(oakengine_node_keyframe_handle_on_track(
|
||||
input_.input.node, input_.input.input.toUtf8().constData(),
|
||||
input_.input.element, input_.track, i));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
```
|
||||
|
||||
- 完成后 grep `get_keyframes()` 的全部调用方(keyframeview.cpp 等),
|
||||
把 `NodeKeyframe*` 改为 `OakEngineKeyframe*`,属性访问改走 P2 已建立的
|
||||
keyframe C ABI(`oakengine_node_keyframe_*` / `OakEngineKeyframe` 访问器)。
|
||||
若调用方用到某个没有 C ABI 对应的 keyframe 成员函数,**停下来报告**,
|
||||
不要自己在 engine 加函数。
|
||||
|
||||
### 2.6 hashstreamapp.cpp 瘦身
|
||||
|
||||
删除 :31-44 的三个 qHash(NodeInput/NodeInputPair/
|
||||
NodeKeyframeTrackReference)——App 类型的 qHash 已由新头内联提供。
|
||||
保留 Track::Reference 的 qHash 与 QDataStream 运算符(来自
|
||||
`node/output/track/track.h`,属 P6 范围),删除
|
||||
`#include "node/param.h"`,保留 `#include "node/output/track/track.h"`。
|
||||
|
||||
### 2.7 nodetreeview 顺带修一个既有 bug
|
||||
|
||||
nodetreeview.h:96-97 `k_item_input_reference` 与 `k_item_node_pointer`
|
||||
都定义为 `Qt::UserRole + 1`。改为 `Qt::UserRole + 1` / `Qt::UserRole + 2`。
|
||||
item data 中存的引用类型随之改为
|
||||
`QVariant::fromValue(AppNodeKeyframeTrackReference)`。
|
||||
|
||||
### 2.8 viewerdisplay.h 只做最小改动
|
||||
|
||||
该头还有 `node/gizmo/text.h`、`node/output/track/tracklist.h`、
|
||||
`node/color/colormanager/colormanager.h` 三处直接 include,属 P5/P6 范围,
|
||||
**本阶段不动**。本阶段只做:
|
||||
- 删除 `#include "node/node.h"`;
|
||||
- `set_gizmos(Node*)`、`generate_gizmo_transform(Node*, Node*, …)`、
|
||||
成员 `Node *gizmos_` 改为 `OakEngineNode*`;
|
||||
- `NodeValueRow`、`NodeGizmo*`、`TextGizmo*` 维持现状(它们经 gizmo/text.h
|
||||
链传递引入,P5 处理),.cpp 中把 `OakEngineNode*` 传给 gizmo C++ API 的
|
||||
边界处加 `reinterpret_cast<Node*>`。
|
||||
|
||||
---
|
||||
|
||||
## 3. C ABI 映射表(证明 engine 零改动)
|
||||
|
||||
| 旧调用 | 替换 |
|
||||
|---|---|
|
||||
| `input.is_connected()` | `app_input_is_connected()`(新头,包装 `oakengine_node_input_is_connected`) |
|
||||
| `input.get_connected_output()` | `app_input_get_connected_node()`(包装 `oakengine_node_input_get_connected_node`) |
|
||||
| `input.node()/.input()/.element()` | `AppNodeInput` 公开字段 |
|
||||
| `node->input_array_size(id)` | `oakengine_node_input_array_size` |
|
||||
| `node->name()` | `oakengine_node_get_name` |
|
||||
| `Node::disconnect` 等 | 已迁过(`oakengine_node_disconnect_ex` 等),不在本阶段 |
|
||||
| `Node::get_keyframe_tracks` | `oakengine_node_keyframe_count_on_track` + `oakengine_node_keyframe_handle_on_track`(见 2.5) |
|
||||
| `Node::k_category_count` / `CategoryID` | `AppNodeCategory`(2.4) |
|
||||
| `Node::Position` | 本地 `NodeViewItemPosition`(2.2) |
|
||||
| `NodeInputPair` | `AppNodeInputPair`(注意:无 element 字段) |
|
||||
| `NodeKeyframeTrackReference` | `AppNodeKeyframeTrackReference`(字段 input/track 公开) |
|
||||
|
||||
---
|
||||
|
||||
## 4. 执行步骤(按序,每步后可编译)
|
||||
|
||||
### 4.1 步骤 1:新建头 + hashstreamapp 瘦身
|
||||
- 按 2.1 创建 `app/common/nodeinputhandle.h`。
|
||||
- 按 2.6 改 `app/common/hashstreamapp.cpp`。
|
||||
- 编译验证(此时还没有使用方,只验证新头自身可编译:随便一个已改文件
|
||||
include 它即可,或等到步骤 2 一起验证)。
|
||||
|
||||
### 4.2 步骤 2:A 组(琐碎)
|
||||
- `nodecombobox.h`:删除 `#include "node/node.h"`。
|
||||
- `preferencesappearancetab.cpp`:include 换 `common/nodeinputhandle.h`,
|
||||
:72 `Node::k_category_count` → `k_app_category_count`。
|
||||
- `factorymenu.h`:include 换 `common/nodeinputhandle.h`;:44 参数类型
|
||||
`Node::CategoryID` → `AppNodeCategory`,默认值 → `k_app_category_unknown`;
|
||||
:52 返回值 `Node*` → `OakEngineNode*`。检查 factorymenu.cpp 及调用方
|
||||
(`create_node_from_menu_action` 的使用处)同步改类型。
|
||||
|
||||
### 4.3 步骤 3:B 组(纯指针/引用替换)
|
||||
- `nodeparamviewitembase.h`:`Node*` → `OakEngineNode*`;检查对应
|
||||
.cpp 实现内部(如用 `n->name()`/`n->GetLabel()` 改 C ABI
|
||||
`oakengine_node_get_name`/`oakengine_node_get_label`)。
|
||||
- `nodetableview.h`:`QVector<Node*>` → `QVector<OakEngineNode*>`,
|
||||
`QMap<Node*, QTreeWidgetItem*>` → `QMap<OakEngineNode*, …>`。
|
||||
该头用到的 `Rational` 来自 core(`olive/core/...`),确保 include
|
||||
core 头而不是靠 node.h 传递。
|
||||
- `nodevaluetree.h`:`set_node(const NodeInput&, const Rational&)` →
|
||||
`set_node(const AppNodeInput&, const Rational&)`;.cpp 内
|
||||
`input.node()/input()/element()` 改字段访问 + C ABI。
|
||||
|
||||
### 4.4 步骤 4:C 组(nodeview 簇)
|
||||
顺序:nodeviewedge.h → nodeviewitem.h → nodeviewcontext.h → 各自 .cpp。
|
||||
- `nodeviewedge.h`:`Node* output_` → `OakEngineNode*`;
|
||||
`NodeInput input_` → `AppNodeInput input_`;`output()`/`input()` 返回类型
|
||||
同步。构造函数参数同步。
|
||||
- `nodeviewitem.h`:按 2.2 加 `NodeViewItemPosition`;`Node*` →
|
||||
`OakEngineNode*`;`NodeInput get_input()` → `AppNodeInput get_input()`
|
||||
(返回 `AppNodeInput(node_, input_, element_)`);
|
||||
`get_item_for_input(NodeInput)` → `AppNodeInput`。
|
||||
- `nodeviewcontext.h`:`Node*` → `OakEngineNode*`(含 `QMap`、`QHash` key、
|
||||
`context_subs_`);`const NodeInput &` 参数 → `const AppNodeInput &`。
|
||||
- 各 .cpp:桥接 lambda 按 2.3 组装 `AppNodeInput`;其它 Node 成员调用改
|
||||
C ABI(绝大多数之前已迁,只剩类型改名)。
|
||||
|
||||
### 4.5 步骤 5:D 组(TrackRef)
|
||||
- `nodetreeview.h`:include 换 `common/nodeinputhandle.h`(保留已有的
|
||||
`oakengine/node.h`);`NodeKeyframeTrackReference` →
|
||||
`AppNodeKeyframeTrackReference`(signals、成员、QHash key 全部);
|
||||
按 2.7 修 UserRole bug;`Node*` → `OakEngineNode*`。
|
||||
- `nodetreeview.cpp`:`ref.input()` → `ref.input`,`ref.track()` →
|
||||
`ref.track`;item data 读写改 `QVariant::fromValue(...)`/`value<...>()`。
|
||||
- `keyframeviewinputconnection.h/.cpp`:按 2.5。注意 `get_reference()`
|
||||
返回类型改为 `AppNodeKeyframeTrackReference`。
|
||||
|
||||
### 4.6 步骤 6:E 组(nodeparamview 簇,最大)
|
||||
顺序:connectedlabel → keyframecontrol → arraywidget → item → view。
|
||||
- `nodeparamviewconnectedlabel.h/.cpp`:
|
||||
- 成员 `NodeInput input_` → `AppNodeInput input_`;
|
||||
`Node *connected_node_` → `OakEngineNode *connected_node_`;
|
||||
- slots `input_connected/input_disconnected(OakEngineNode*, const NodeInput&)`
|
||||
第二个参数改 `const AppNodeInput &`;
|
||||
- cpp:85-89 `input_.is_connected()`/`get_connected_output()` →
|
||||
`app_input_is_connected(input_)`/`app_input_get_connected_node(input_)`;
|
||||
- cpp:95-108 lambda 里 `NodeInput(reinterpret_cast<Node*>(source), …)` →
|
||||
`AppNodeInput(source, …)`;
|
||||
- cpp:180-181 的 `oakengine_node_disconnect_ex` 调用已合规,只需把
|
||||
`input_.node()`/`input_.input()`/`input_.element()` 改字段访问;
|
||||
- cpp:199 `connected_node_->name()` → `oakengine_node_get_name`;
|
||||
- `value_tree_->set_node(input_, …)` 随 4.3 的 nodevaluetree 改后自然兼容。
|
||||
- `nodeparamviewkeyframecontrol.h`:`NodeInput input_` → `AppNodeInput`;
|
||||
slot `keyframe_enable_changed(const NodeInput&, bool)` → `AppNodeInput`;
|
||||
`get_connected_input()` 返回类型同步。
|
||||
- `nodeparamviewarraywidget.h/.cpp`:`Node* node_` → `OakEngineNode*`;
|
||||
cpp:53 `node_->input_array_size(input_)` →
|
||||
`oakengine_node_input_array_size(node_, input_.toUtf8().constData())`;
|
||||
cpp:45 的 `reinterpret_cast<void*>(node_)` 订阅保持不变。
|
||||
- `nodeparamviewitem.h`:`QHash<NodeInput, InputUI>` →
|
||||
`QHash<AppNodeInput, InputUI>`;`NodeInputPair` → `AppNodeInputPair`;
|
||||
signal `input_checked_changed(const NodeInput&, bool)` 与
|
||||
slot `edge_changed(OakEngineNode*, const NodeInput&)` 改 `AppNodeInput`;
|
||||
`get_element_y(NodeInput)` → `AppNodeInput`;`Node*` → `OakEngineNode*`。
|
||||
对应 .cpp(nodeparamviewitem.cpp)内 NodeInput 构造/比较改 AppNodeInput。
|
||||
- `nodeparamview.h`:`QVector<Node*>` ×4 → `QVector<OakEngineNode*>`;
|
||||
`QHash<Node*, Node*>`(paste 映射、generate_existing_paste_map)→
|
||||
`QHash<OakEngineNode*, OakEngineNode*>`;
|
||||
`QHash<Node*, QPair<int64_t,int64_t>> context_subs_` → key 换类型;
|
||||
slots `input_check_box_changed / group_input_passthrough_added/removed`
|
||||
的 `const NodeInput &` → `const AppNodeInput &`;
|
||||
`get_snap_ignore_keyframes()` 返回 `std::vector<NodeKeyframe*>*` →
|
||||
`std::vector<OakEngineKeyframe*>*`(NodeKeyframe 经 node.h 传递而来,
|
||||
随 include 删除必须一并处理;调用点 grep `get_snap_ignore_keyframes`
|
||||
逐一适配,keyframe 属性访问走 P2 的 C ABI)。
|
||||
|
||||
### 4.7 步骤 7:F 组(viewerdisplay.h)
|
||||
按 2.8 做最小改动。
|
||||
|
||||
### 4.8 步骤 8:验证 + 提交
|
||||
```bash
|
||||
# 1. 直接 include 清零
|
||||
grep -rn '#include "node/node.h"\|#include "node/param.h"' app/
|
||||
# 期望:无输出
|
||||
|
||||
# 2. engine 符号泄漏检查(app 对象中不应出现 olive::Node/NodeInput 等未定义符号)
|
||||
# 对象编译全部通过后即视为通过
|
||||
|
||||
# 3. app 对象编译(命令见第 0 节)
|
||||
# 4. engine 未动:git diff --stat -- engine/ 应为空
|
||||
|
||||
# 5. 提交(单独一个 commit)
|
||||
git add app/ docs/
|
||||
git commit -m "R8 phase 3: replace node/node.h + node/param.h in app/ with C ABI and app-local value types"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 明令禁止
|
||||
|
||||
- 不要改 engine/ 下任何文件(包括 oakengine/node.h)。
|
||||
- 不要把 `NodeInput` 等 engine 类型换成 `using` 别名指回 engine 头。
|
||||
- 不要在本阶段处理 P4-P9 的范围(viewerdisplay.h 的 gizmo/tracklist/
|
||||
colormanager include、nodeviewcontext.cpp 的 block.h/track.h/project.h、
|
||||
hashstreamapp.cpp 的 track.h)。
|
||||
- 不要"顺手"重构无关代码;唯一允许的顺手修复是 2.7 的 UserRole bug。
|
||||
- 发现映射表覆盖不到的 engine 成员调用时,停下来报告,不要自行在
|
||||
engine 加 C ABI 函数。
|
||||
|
||||
## 6. 已知遗留(写进提交说明/计划文档状态)
|
||||
|
||||
- `app/widget/viewer/viewerdisplay.h/.cpp` 仍有 gizmo/text.h、tracklist.h、
|
||||
colormanager.h 直接 include(P5/P6)。
|
||||
- `app/widget/nodeview/nodeviewcontext.cpp` 有 block.h、track.h、project.h、
|
||||
sequence.h 直接 include(P3/P4 后续批)。
|
||||
- `app/common/hashstreamapp.cpp` 保留 track.h(P6)。
|
||||
Reference in New Issue
Block a user