- Linux: Qt6 metatype static_assert requires complete types; declare
OakEngineBlock with Q_DECLARE_OPAQUE_POINTER in the public header
(outside extern "C")
- Windows: gtest discovery runs the freshly linked test exe at build
time and it cannot find the DLLs (0xc0000135); copy oakcore/oakengine/
ffmpeg_bridge DLLs next to the test binaries on WIN32
- macOS: Imath header mixing between otio-install's stale bundled Imath
and Homebrew's Imath broke ImathBox.h (V2h undeclared); bump the OTIO
cache to v2 and drop bundled Imath headers so the system one is the
only set in play
- OAK_ENABLE_DYNAMIC_RENDER_BACKEND now defaults ON; it was previously
undefined (OFF) unless passed, so CI configure failed on the
unconditional oakgl target references in app/worker/tests
- add git to the MSYS2 package list in ci.yml and cd.yml; the OTIO
build step runs git clone in the msys2 shell, where git is not
installed by default
- 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
- engine: liboakengine.dylib intentionally leaves olive::k_app_version
to the final executable (olive-version-obj); whitelist that symbol
with -U on Apple, where undefined symbols in dylibs are an error
(ELF allows them by default).
- app/common/configwrapper.h: only declare the int64_t/uint64_t
conversion/assignment overloads on Linux LP64, where they differ
from qint64/quint64; elsewhere both are long long and the extra
overloads are redeclarations.
R7-A (Qwen 3.8 Max): oakengine/display.h rewritten to the POD contract
from r7-pure-abi-plan.md - oak_video_params everywhere, opaque
texture/frame handles with retain/free protocol (engine-heap control
blocks), OakSharedBuffer refcounted wrapper for the QVariant playback
path. All TexturePtr/FramePtr gone from app (47 sites).
R7-B (Qwen 3.8 Max): liboakengine.so exports 3486 -> 19 C++ symbols
(version script oakengine.ver: oakengine_* plus the documented
oakgl/oakvulkan dlopen plugin ABI). oakengine-obj OBJECT library feeds
both the shared lib and the test binaries (-rdynamic so dlopen'd
backends resolve engine objects).
Fix (Kimi K3): producer/consumer type mismatch - viewerdisplay
unpacks OakSharedBufferPtr but viewer.cpp pushed raw void* handles,
so no frame ever reached the display widget (all 5 vulkan viewer
tests timed out with 'never received a texture'). Producers now wrap
with oak_make_shared_frame / oak_make_shared_texture(retain).
Verified: build 0 errors, ctest 45/45, nm U _ZN5olive = 0 in
oak-editor/oak-render-worker/oak-cli, 19 exported C++ symbols in
liboakengine.so (all documented plugin ABI).
The full pure-C facade used by the app: node/project/timeline/viewer/
undo/task/events/serializer/playback/preview/renderer/gizmo/color/
audio/footage/proxy/encoding/exporter/config/disk/ipc/plugin/worker
families, plus undo-group semantics, display renderer handles,
NodeFactory accessors, and per-family pure-C engine tests.
- clip-level loop mode (off/loop/clamp), undoable
- per-channel audio levels as one-frame RMS at a timestamp
- waveform min/max buckets over a footage range, rendered on demand
- two real fixes uncovered by this family: conform completion signals
were starved by msleep-only waits (audio renders always came back
empty), and incomplete tickets from conform-pending renders are now
retried until the conform is ready
- keyframed state, count, read (time in sequence timebase + mapped
values), add/remove, easing read/write (linear/bezier/hold with
control points), and clear - all undoable with full undo/redo
assertions
- easing set commands are minimal capi-local UndoCommands matching
the app-layer semantics (engine has none of its own); same-time
duplicates are rejected with E_STATE instead of hitting the engine's
debug assert
- project node enumeration, type/name/label metadata, input
introspection (id, mapped value type, connection state)
- typed parameter read/write for the eight common NodeValue kinds
(int/float/bool/rational/color/vec2-4/combo/string), undoable via
NodeParamSetSplitStandardValueCommand - the split-track value path,
chosen after proving the standard-value command stores whole
variants into a single component track
- graph operations (add/remove/connect/disconnect) reusing the
engine's undo commands, all covered by undo/redo assertions
- oakengine_export_render drives ExportTask synchronously (offline
render + encode) with a progress callback, codec probing, and a
thread-local error channel; exporter.h keeps clear of the visibility
macro header
- oak-cli transcode now defaults to mp4 (H.264/AAC) with --format ppm
keeping the raw output path
- two real concurrency bugs found by the facade's own test: ExportTask
deadlocks when start()ed synchronously (queued conform handshake
needs an event loop), and the progress callback must be captured by
value because it fires on the task thread
- oakengine_sequence_add_track and add_footage_clip are the facade's
first editing primitives: undoable track creation and clip placement
with full range validation, clip enumeration, and gap filtering
- oak-cli transcode closes the loop: media file -> import -> clip ->
render, producing scaled PPM frames and a WAV from just the C ABI
- engine fix uncovered by transcode: the render worker used an invalid
empty AudioParams for IPC render frames, crashing any sequence that
contains audio; it now derives them from the rendered node itself
- sequence_new hardens its defaults against missing audio config keys
- oakengine_footage_probe inspects media without a project (decoder
probe): stream counts, per-stream video info (dimensions, rate,
duration, color tags, interlacing), audio info, duration, decoder
name, source start time; thread_local last_error for the NULL-handle
failure paths
- oakengine_project_import_footage adds probed footage to a project
through the same undoable command path as the app
- oak-cli probe prints decoder/duration/per-stream details and runs as
a ctest everywhere (no GL)
- dual ownership documented: probe handles are owned, imported footage
is borrowed from its project
- oakengine_renderer_create/set_mode/last_error, render_frame (sync,
60s timeout, CPU frames via the existing worker pool),
render_audio (planar float), cancel; frames and audio buffers are
owned handles with borrowed data pointers
- output colorspace names map to OCIO display transforms, with
graceful fallback to reference-space output
- oakengine_renderer_test: parameter validation and error paths need
no GL and always run; render assertions gate on
DynamicRenderer backend availability and SKIP cleanly otherwise
- fix a facade bug found by its own test: wait_for_ticket leaked a
connected lambda capturing a stack reference, which a subsequent
cancelled ticket could fire into reused stack memory
- oakengine/export.h establishes the OAKENGINE_API visibility macros;
include/oakengine/ipc.h is the first pure-C surface (41 functions:
shm, frame slot pool, and the worker IPC messages as POD<->JSON
build/parse), implemented in engine/src/capi/
- the IPC implementations move to engine/src/oliveimpl (namespace
olive::engine::internal::ipc); engine/render/ipc/*.h are rebuilt as
same-name/same-API wrapper classes forwarding across the C boundary
- FrameSlotMeta is shared with the C header verbatim so the app/worker
wire format (v1) is bit-identical; static_asserts pin sizeof and
field offsets
- spscringbuffer.h moves to include/oakengine/ as an inline-only
header (no symbols, not ABI)
- new pure-C test oakengine_ipc_test (make_oakengine_test, no GL)
covers shm, frame pool, message round-trips and the layout asserts;
full gtest suite stays green (1986 tests)
Physical split: app/{audio,cli,codec,common,config,node,pluginSupport,
render,task,timeline,undo,tool,shaders} plus coreengine, version and
ui/icons+colorcoding move to a new top-level engine/ tree, built as
liboakengine.so (shared). The render backends (oakgl/oakvulkan) move
with it and link the engine library instead of embedding a static
render-core subset (libolive-rendercore is gone).
- oak-render-worker now links liboakengine instead of the whole
libolive-editor object set: 336MB -> 2.9MB, no Qt Widgets UI
- the editor links liboakengine for the engine and keeps only UI
objects in libolive-editor
- install/packaging: GNUInstallDirs libdir on Linux, bundle copy on
macOS, oakengine.dll staged for NSIS, AppImage validation entry
- fix backend lookup for the new layout: DynamicRenderer searched
../app but backends now live in engine/; a stale pre-split liboakgl
in the build tree got dlopened instead, re-initialized and later
destroyed the interposed engine statics (full-suite segfault at
DialogSequenceParameterTab, found via gdb watchpoint)