Expand docs/project-file-reference.md to cover the full .ove format implemented in the current codebase, including: - Root document and project container structure - Project data: uuid, plugins, nodes, settings - Detailed Node serialization (inputs, keyframes, connections, hints, context, caches) - Node-specific custom data: Footage proxy/sourcestarttime, ViewerOutput workarea/markers, Track height, NodeGroup passthroughs - VideoParams and AudioParams field lists - MainWindowLayoutInfo layout block - Partial documents (markers, keyframes, nodes/timeline) - Versioning and OpenFX notes All sections are provided in English with Chinese translations.
20 KiB
Oak Video Editor Project File Reference / Oak 视频编辑器项目文件格式参考
This document describes Oak Video Editor's XML project format (.ove) as implemented in the current codebase. It is intended to be detailed enough to implement a compatible reader/writer.
本文件描述 Oak 视频编辑器当前代码库实现的 XML 项目格式(.ove),详细程度足以支持兼容的读写实现。
Source of truth / 权威源码:
app/node/project.cpp,app/node/node.cpp,app/node/value.*,app/node/keyframe.*,app/node/project/serializer/*,app/node/project/footage/footage.cpp,app/node/output/viewer/viewer.cpp,app/node/output/track/track.cpp,app/node/group/group.cpp,app/window/mainwindow/mainwindowlayoutinfo.cpp.
1. Root Document / 根文档
<olive version="230220" url="/path/to/project.ove">
...
</olive>
version: serializer version inYYMMDDformat. The latest serializer is230220(ProjectSerializer230220).version:序列化器版本,格式为YYMMDD。当前最新为230220(ProjectSerializer230220)。
url: optional source path.url:可选的源文件路径。
2. Project Container / 项目容器
For full saves, the serializer writes a project container:
完整保存时,序列化器会写入一个项目容器:
<project>
<project>...</project> <!-- actual project data / 实际项目数据 -->
<layout>...</layout> <!-- UI layout / 用户界面布局 -->
</project>
See ProjectSerializer230220::Save() and Load() in app/node/project/serializer/serializer230220.cpp.
3. Project Data (Project::Save / Project::Load) / 项目数据
<project version="1">
<uuid>...</uuid>
<plugins>...</plugins>
<nodes>...</nodes>
<settings>...</settings>
</project>
3.1 uuid
- QUuid string identifying the project.
- 项目 UUID 字符串。
3.2 plugins / 插件列表
List of OpenFX plugins referenced by nodes. This block is loaded before <nodes> so that plugin nodes can be registered.
列出节点引用的 OpenFX 插件。该块在 <nodes> 之前加载,以便注册插件节点。
<plugins>
<plugin id="com.vendor.Plugin" major="1" minor="2"
bundle="/path/to/Plugin.ofx.bundle"
file="/path/to/Plugin.ofx.bundle/Contents/MacOS/Plugin" />
</plugins>
Attributes / 属性:
id: OFX plugin identifier (matches nodeid).- OFX 插件标识符(与节点
id一致)。
- OFX 插件标识符(与节点
major/minor: OFX plugin version.- OFX 插件版本。
bundle: bundle directory path (preferred).- 包目录路径(优先使用)。
file: plugin binary path (fallback).- 插件二进制路径(回退)。
Loading behavior / 加载行为:
- If
<plugins>exists, eachbundle(orfileifbundleis empty) is added to the OFX plugin path, the cache scans the paths, and plugin nodes are registered before<nodes>is parsed. - 如果存在
<plugins>,则把每个bundle(若bundle为空则使用file)加入 OFX 插件路径,扫描缓存,并在解析<nodes>前注册插件节点。
3.3 nodes / 节点图
The node graph. Each <node> is written by Node::Save() and read by Node::Load().
节点图。每个 <node> 由 Node::Save() 写入、Node::Load() 读取。
<nodes version="1">
<node version="1" id="node.id" ptr="123456">
<label>Optional Label</label>
<color>3</color>
<input>...</input>
<links>...</links>
<connections>...</connections>
<hints>...</hints>
<context>...</context>
<caches>...</caches>
<custom>...</custom>
</node>
</nodes>
Attributes / 属性:
id: node type identifier. For OpenFX nodes, this equals the OFX plugin identifier.- 节点类型标识符。对于 OpenFX 节点,等于 OFX 插件标识符。
ptr: numeric pointer ID used to resolve connections and context positions.- 数字指针 ID,用于解析连接和上下文位置。
version: currently1.- 当前为
1。
- 当前为
3.4 settings / 项目设置
Project settings stored as key/value text elements.
以键值文本元素保存的项目设置。
Known keys / 已知键:
cachesettingcustomcachepathcolorconfigfilenamedefaultinputcolorspacecolorreferencespaceroot— pointer id of the rootFoldernode, resolved after all nodes are loaded.root:根Folder节点的指针 ID,在所有节点加载完成后解析。
4. Node Serialization (Node::Save / Node::Load) / 节点序列化
4.1 label
User-visible node label. 用户可见的节点标签。
4.2 color
Override color index (integer), only written if not -1.
覆盖颜色索引(整数),只有不为 -1 时才写出。
4.3 input / 输入
Each input is serialized as:
每个输入序列化为:
<input id="InputId">
<primary>...</primary>
<subelements count="N">
<element>...</element>
</subelements>
</input>
primary: element-1(the main input).primary:元素-1(主输入)。
subelements: array elements (if the input is an array).countis the array size.subelements:数组元素(如果输入是数组)。count为数组大小。
4.3.1 Immediate Values (primary / element) / 立即值
Each immediate block contains:
每个立即值块包含:
<keyframing>0|1</keyframing>
<standard>
<track>...</track>
...
</standard>
<keyframes>
<track>
<key ...>...</key>
</track>
</keyframes>
<csinput>...</csinput>
<csdisplay>...</csdisplay>
<csview>...</csview>
<cslook>...</cslook>
keyframing: whether the input is keyframed (only written if the input is keyframable).keyframing:输入是否启用关键帧(仅当输入可关键帧化时写出)。
standard: default/static values (one<track>per keyframe track).standard:默认值/静态值(每个关键帧轨道一个<track>)。
keyframes: only written ifkeyframingis true.keyframes:仅在keyframing为 true 时写出。
cs*: only written forkColorinputs; stored as input propertiescol_input,col_display,col_view,col_look.cs*:仅对kColor输入写出;保存为输入属性col_input、col_display、col_view、col_look。
4.3.2 Track Count / 轨道数量
Track count is determined by NodeValue::get_number_of_keyframe_tracks():
轨道数量由 NodeValue::get_number_of_keyframe_tracks() 决定:
| Type / 类型 | Tracks / 轨道数 |
|---|---|
kVec2 |
2 |
kVec3 |
3 |
kVec4 |
4 |
kColor |
4 |
kBezier |
6 |
| other / 其他 | 1 |
4.3.3 Standard Value Encoding / 标准值编码
Values are written with NodeValue::ValueToString() and read with NodeValue::StringToValue().
值通过 NodeValue::ValueToString() 写入,通过 NodeValue::StringToValue() 读取。
String encodings / 字符串编码:
kVec2:x:ykVec3:x:y:zkVec4:x:y:z:wkColor:r:g:b:akBezier:x:y:cp1x:cp1y:cp2x:cp2ykRational:num/den(seerational::toString())kInt: integer as text / 整数字符串kBinary: Base64kText,kFont,kFile,kCombo,kStrCombo: stringkTexture,kSamples,kNone: no text / 无文本
Special cases / 特例:
kVideoParams/kAudioParamsare nested objects (see sections 5 and 6).kVideoParams/kAudioParams为嵌套对象(见第 5、6 节)。
kSubtitleParamsis skipped on load to avoid overwriting subtitle data.kSubtitleParams在加载时被跳过,以避免覆盖字幕数据。
4.3.4 Keyframes (NodeKeyframe::save / NodeKeyframe::load) / 关键帧
<key input="InputId" time="num/den" type="0"
inhandlex="0" inhandley="0"
outhandlex="0" outhandley="0">value</key>
Attributes / 属性:
input: input id.- 输入 ID。
time: rational time (rational::toString()).- 有理数时间(
rational::toString())。
- 有理数时间(
type: integer enumNodeKeyframe::Type(0= linear, etc.).- 整数枚举
NodeKeyframe::Type(0为线性等)。
- 整数枚举
inhandlex,inhandley,outhandlex,outhandley: bezier handle coordinates.- 贝塞尔手柄坐标。
Text value uses NodeValue::ValueToString(data_type, value, true).
文本值使用 NodeValue::ValueToString(data_type, value, true)。
4.4 links / 链接
Block-to-block link list (for timeline items):
块到块链接列表(用于时间线项目):
<links>
<link>ptr</link>
</links>
4.5 connections / 连接
Input/output connections:
输入/输出连接:
<connections>
<connection input="InputId" element="-1">
<output>ptr</output>
</connection>
</connections>
outputis the serialized pointer (ptr) of the output node.output为输出节点的序列化指针(ptr)。
4.6 hints (Value Hints) / 值提示
Value hints are per-input UI hints stored by Node::ValueHint::save() / load():
值提示是按输入保存的 UI 提示,由 Node::ValueHint::save() / load() 读写:
<hints>
<hint input="InputId" element="-1" version="1">
<types>
<type>0</type>
</types>
<index>0</index>
<tag>...</tag>
</hint>
</hints>
types: list of allowedNodeValue::Typeintegers.types:允许的NodeValue::Type整数列表。
index: hint index.index:提示索引。
tag: hint tag string.tag:提示标签字符串。
4.7 context (Node positions in contexts) / 上下文中的节点位置
<context>
<node ptr="other_node_ptr">
<x>0</x>
<y>0</y>
<expanded>0|1</expanded>
</node>
</context>
- Stores the position and expanded state of other nodes inside this node's context (e.g. inside a NodeGroup).
- 保存其他节点在该节点上下文(例如 NodeGroup 内部)中的位置和展开状态。
4.8 caches / 缓存 UUID
Node cache UUIDs:
节点缓存 UUID:
<caches>
<audio>uuid</audio>
<video>uuid</video>
<thumb>uuid</thumb>
<waveform>uuid</waveform>
</caches>
4.9 custom / 自定义节点数据
Default Node::SaveCustom() writes nothing. Specific node subclasses may override.
默认 Node::SaveCustom() 不写任何内容。具体子类可重写。
5. Node-specific Custom Data / 节点专属自定义数据
5.1 Footage (app/node/project/footage/footage.cpp)
<custom>
<timestamp>1740000000</timestamp>
<proxy enabled="1" state="ready" stream="0" preset="1">/path/to/proxy.mp4</proxy>
<sourcestarttime source="timecode">1/25</sourcestarttime>
<viewer>...</viewer>
</custom>
<timestamp>: media modification timestamp (epoch milliseconds).<timestamp>:媒体修改时间戳(Unix 毫秒)。
<proxy>: proxy media state.<proxy>:代理媒体状态。- Attributes / 属性:
enabled:0or1.enabled:0或1。state: string fromProxyManager::ProxyStateToString()(e.g.missing,ready,generating,failed).state:ProxyManager::ProxyStateToString()返回的字符串(如missing、ready、generating、failed)。stream: video stream index used for the proxy.stream:代理使用的视频流索引。preset: proxy preset version.preset:代理预设版本。
- Text content: proxy file path (may be empty if
enabledis true but proxy is not yet generated). - 文本内容:代理文件路径(如果
enabled为 true 但代理尚未生成,则可能为空)。
<sourcestarttime>: source start time offset.<sourcestarttime>:源起始时间偏移。sourceattribute: source identifier (e.g.timecode).source属性:源标识符(如timecode)。- Text: rational
numerator/denominator. - 文本:有理数
numerator/denominator。
<viewer>: seeViewerOutputbelow.<viewer>:见下文ViewerOutput。
5.2 ViewerOutput (app/node/output/viewer/viewer.cpp)
Used by Footage, Sequence, etc.
Footage、Sequence 等使用。
<viewer>
<workarea version="1">
<enabled>0|1</enabled>
<in>num/den</in>
<out>num/den</out>
</workarea>
<markers>
<marker name="Marker Name" in="num/den" out="num/den" color="0" />
</markers>
</viewer>
<workarea>: render/export work area.<workarea>:渲染/导出工作区。
<markers>: timeline markers.<markers>:时间线标记。- Marker attributes / 标记属性:
name: marker name.name:标记名称。in/out: rational time range.in/out:有理数时间范围。color: color index.color:颜色索引。
5.3 Track (app/node/output/track/track.cpp)
<custom>
<height>48</height>
</custom>
<height>: track height in the timeline.<height>:时间线中轨道的显示高度。
5.4 NodeGroup (app/node/group/group.cpp)
<custom>
<inputpassthroughs>
<inputpassthrough>
<node>ptr</node>
<input>InputId</input>
<element>0</element>
<id>PassthroughId</id>
<name>Display Name</name>
<flags>0</flags>
<type>float</type>
<default>...</default>
<properties>
<property>
<key>...</key>
<value>...</value>
</property>
</properties>
</inputpassthrough>
</inputpassthroughs>
<outputpassthrough>ptr</outputpassthrough>
</custom>
<inputpassthrough>: maps an inner node's input to a group-level passthrough input.<inputpassthrough>:将内部节点输入映射到组级别的透传输入。- Child elements / 子元素:
<node>: pointer id of the inner node.<node>:内部节点的指针 ID。
<input>: inner input id.<input>:内部输入 ID。
<element>: element index.<element>:元素索引。
<id>: passthrough input id on the group.<id>:组上的透传输入 ID。
<name>: display name.<name>:显示名称。
<flags>: input flags (integer bitmask).<flags>:输入标志(整数位掩码)。
<type>:NodeValuetype name (seeNodeValue::GetDataTypeName()).<type>:NodeValue类型名称(见NodeValue::GetDataTypeName())。
<default>: default value encoded withNodeValue::ValueToString(type, ..., false).<default>:使用NodeValue::ValueToString(type, ..., false)编码的默认值。
<properties>: arbitrary key/value property pairs.<properties>:任意键值属性对。
<outputpassthrough>: pointer id of the node that provides the group's output.<outputpassthrough>:提供组输出的节点指针 ID。
6. VideoParams / 视频参数
Serialized inside <standard><track> for inputs of type kVideoParams.
在 kVideoParams 类型输入的 <standard><track> 中序列化。
<width>...</width>
<height>...</height>
<depth>...</depth>
<timebase>num/den</timebase>
<format>int</format>
<channelcount>int</channelcount>
<pixelaspectratio>num/den</pixelaspectratio>
<interlacing>int</interlacing>
<divider>int</divider>
<enabled>0|1</enabled>
<x>float</x>
<y>float</y>
<streamindex>int</streamindex>
<videotype>int</videotype>
<framerate>num/den</framerate>
<starttime>int64</starttime>
<duration>int64</duration>
<premultipliedalpha>0|1</premultipliedalpha>
<colorspace>string</colorspace>
<colorrange>int</colorrange>
format:PixelFormat::Formatinteger (U8,U10,U16,F16,F32).format:PixelFormat::Format整数(U8、U10、U16、F16、F32)。
interlacing:VideoParams::Interlacinginteger.interlacing:VideoParams::Interlacing整数。
videotype:VideoParams::Typeinteger.videotype:VideoParams::Type整数。
colorrange:VideoParams::ColorRangeinteger (0= limited,1= full).colorrange:VideoParams::ColorRange整数(0为 limited,1为 full)。
7. AudioParams / 音频参数
Serialized inside <standard><track> for inputs of type kAudioParams.
在 kAudioParams 类型输入的 <standard><track> 中序列化。
<samplerate>int</samplerate>
<channellayout>uint64</channellayout>
<format>string</format>
<enabled>0|1</enabled>
<streamindex>int</streamindex>
<duration>int64</duration>
<timebase>num/den</timebase>
format:SampleFormat::to_string()value.format:SampleFormat::to_string()值。
8. Main Window Layout (MainWindowLayoutInfo) / 主窗口布局
The <layout> element stores the state of the main window (open folders, sequences, panel data, and Qt window state).
<layout> 元素保存主窗口状态(打开的文件夹、序列、面板数据和 Qt 窗口状态)。
<layout version="1">
<folders>
<folder>ptr</folder>
</folders>
<timeline>
<sequence>ptr</sequence>
</timeline>
<viewers>
<viewer>ptr</viewer>
</viewers>
<data>
<panel id="PanelId">
<option name="key">value</option>
</panel>
</data>
<state>base64</state>
</layout>
<folders>: pointer ids of open project folders.<folders>:打开的项目文件夹指针 ID。
<timeline>: pointer ids of open sequences.<timeline>:打开的序列指针 ID。
<viewers>: same as<timeline>in this implementation.<viewers>:当前实现与<timeline>相同。
<data>: per-panel persistent data.<data>:每个面板的持久化数据。
<state>: base64-encoded Qt window/toolbar/dock state.<state>:Base64 编码的 Qt 窗口/工具栏/停靠状态。
9. Partial Documents / 部分文档
ProjectSerializer230220 can emit three kinds of partial documents for copy/paste and interchange:
ProjectSerializer230220 可输出三种部分文档,用于复制/粘贴和交换:
9.1 markers
<markers version="1">
<marker name="..." in="num/den" out="num/den" color="0" />
</markers>
9.2 keyframes
<keyframes version="1">
<node id="NodeId">
<input id="InputId">
<element id="0">
<track id="0">
<key ...>...</key>
</track>
</element>
</input>
</node>
</keyframes>
9.3 nodes / timeline
For copy/paste of nodes (kOnlyNodes) the root is <nodes>; for clip paste (kOnlyClips) the root is <timeline>.
复制/粘贴节点(kOnlyNodes)时根为 <nodes>;剪辑粘贴(kOnlyClips)时根为 <timeline>。
<nodes version="1">
<node id="NodeId" ptr="..." items="ptr1,ptr2">
...
</node>
<properties>
<node ptr="...">
<key>value</key>
</node>
</properties>
</nodes>
items: comma-separated pointer ids of items that depend on this node; used to avoid duplicating shared dependencies.items:依赖该节点的项目指针 ID 逗号列表,用于避免重复共享依赖。
<properties>: opaque string key/value pairs attached to nodes during copy/paste.<properties>:复制/粘贴时附加到节点的任意字符串键值对。
10. Versioning / 版本管理
- Root
<olive version="...">selects the serializer.- 根元素
<olive version="...">选择序列化器。
- 根元素
- Newer files may be rejected with
kProjectTooNewif no matching serializer exists.- 如果没有匹配的序列化器,较新的文件可能以
kProjectTooNew拒绝加载。
- 如果没有匹配的序列化器,较新的文件可能以
- Each major block (
project,nodes,workarea, etc.) has its own internalversionattribute for future expansion.- 每个主要块(
project、nodes、workarea等)都有自己的内部version属性,便于未来扩展。
- 每个主要块(
11. OpenFX Node Compatibility Notes / OpenFX 节点兼容性说明
- OpenFX nodes are identified by the OFX plugin identifier (
Plugin::getIdentifier()).- OpenFX 节点由 OFX 插件标识符(
Plugin::getIdentifier())标识。
- OpenFX 节点由 OFX 插件标识符(
- The
<plugins>list ensures Oak Video Editor can locate external plugins before instantiating nodes.<plugins>列表确保 Oak 在实例化节点前能够定位外部插件。
- If a plugin cannot be found at load time, the node cannot be instantiated and will be skipped.
- 如果加载时找不到插件,则无法实例化该节点,会被跳过。