- pointer.cpp/transition.cpp: same Qt 6.4 incompatibility as import.cpp
- Windows: DLLs cannot have undefined symbols, so the
executable-provides-k_app_version trick does not link; embed
olive-version-obj into oakengine on WIN32 (each module keeps its own
copy)
- import.cpp: pass the const char* input ids directly; the QLatin1String
roundtrip uses toUtf8(), unavailable in distro Qt 6.4
- sharedmemoryregion.cpp: the Windows branch still used the pre-refactor
API (Open/Close/kCreate); rewritten to the current lowercase
open/close/k_create interface
- resizabletimelinescrollbar instantiates QMetaType over OakEngineMarker
via timeline.h includes; the declaration belongs there, not serializer.h
- execinfo.h doesn't exist on Windows; the OAK_DEBUG_INVALID_INPUT
backtrace diagnostic is now Unix-only
The scripted Q_DECLARE_OPAQUE_POINTER sweep missed the QtCore/qmetatype.h
include (cascade of stdlib errors) and declared a few types in two headers
(redefinition of IsPointerDeclaredOpaque). Include qmetatype.h everywhere
and declare each handle in exactly one header.
- Linux GCC rejects QMetaType instantiation over incomplete types
(static_assert(sizeof(T))); Q_DECLARE_OPAQUE_POINTER every opaque
OakEngine* handle in the public headers so QList<OakEngineTask*> etc
compile
- Windows: gtest discovery ran the fresh exe before the DLLs were next
to it; use DISCOVERY_MODE PRE_TEST on WIN32 and copy the DLLs for
ctest time
- 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
renderer_generated_frame_for_queue pushed a raw void* QVariant into the
display queue, but on_paint only unwraps OakSharedBufferPtr; the unwrap
failed and the widget painted blank. Paused display worked because
set_display_image wraps with oak_make_shared_frame. Wrap the queued
frames the same way. Also adds env-gated OAK_DEBUG_PLAYBACK
diagnostics.
- capi: oakengine_node_output_connection_at/_at_ex returned the source
node as the connection destination; the actual destination is
conn.second.node(). Out-edge enumeration was useless, so the node
view could only draw in-edges and randomly lost whichever edges
needed the out-edge path (random per context build order).
Regression test in oakengine_node_test
- project teardown: Project::clear() pre-notifies node removal while
nodes are fully constructed (observers used to crash on
half-destroyed nodes); childEvent suppresses the removal dance while
clearing; Node::disconnect_all/disconnect_edge get a silent mode for
teardown so no invalidation/events touch dying members
(is_being_cleared); ClipBlock marker disconnect guarded against
dead viewer/markers; ProjectCopier and PreviewAutoCacher drop
project references on Project::destroyed instead of disconnecting
dead objects at shutdown
- preview: add oakengine_preview_request_get_audio_sample_count; the
viewer queried sample count by passing nullptr to get_audio_samples
which rejects it, so all playback audio was silently dropped
- app: fix unterminated input-id memcpy in ResolveGroupInput
(nodeparamviewitem, widgetbridge) that corrupted every parameter id
- app: unsubscribe raw C-API event subscriptions in destructors of
NodeParamViewKeyframeControl, NodeParamViewConnectedLabel and
ExportDialog; playhead events used to fire into dead widgets
(crash when dragging the playhead)
- tests: preview request roundtrip (video frame + audio range) and
free-while-active teardown coverage; env-gated OAK_DEBUG_EDGES /
OAK_DEBUG_INVALID_INPUT diagnostics
- docs: investigation notes in docs/zh/
- Track: new InputDisconnectedEvent trims blocks_/block_array_indexes_
when a block edge is removed outside the Track's own operations
(block deletion, undo commands detaching a whole track). Previously
blocks_ kept dangling pointers and Project teardown crashed in
track_length() (ASan heap-use-after-free in oakengine_timeline_edit /
oakengine_sync). The persistent array map is intentionally not
rewritten so undo of remove_track can re-attach the clips; replace_block
guards the handler with ignore_block_disconnect_
- filefunctions: honor OAK_CONFIG_DIR to redirect the configuration
root; QStandardPaths ignores XDG_* on macOS, so the engine tests read
the real user config (a stale 0.1.x file) and failed frame-rate
assertions. All engine tests now set OAK_CONFIG_DIR to their tmpdir
- init test: chdir to the fixture directory before loading
project_with_footage.ove so the engine's moved-project footage
relocation does not rewrite the stored relative filename
- renderworkerpool: do not let the render worker inherit
QT_QPA_PLATFORM=offscreen from a headless host process; the worker
needs a real platform GL context and exited immediately otherwise,
failing oak_cli_transcode
- nodeparamview: add visited set to get_distance_between_nodes, fixing
unbounded recursion (stack overflow) when the node graph has a cycle
- viewerdisplay: give texture_ a consistent owner via assign_texture();
borrowed queue textures are now retained, created ones freed, fixing
a dangling pointer that corrupted the heap and crashed in the GL driver
- playbackcache: resignal_requests() iterates a copy, handlers may
clear_request_range() while iterating (ASan container-overflow)
- preview C API: preview request ticket lambdas captured the request
state raw; after oakengine_preview_request_free the ticket outlived
the request and the finished callback wrote into freed memory
(heap-use-after-free). The finished flag is now a shared_ptr captured
weakly by the callbacks
- playback: oak_playback_frame regains a timestamp (num/den) filled
from olive::Frame; the viewer queue append no longer uses Rational()
for every frame, which made append_timewise drop all but the first
frame and froze the picture during playback
- mainwindow: open_node_in_viewer refuses sequence nodes; sequences
already have the Sequence Viewer, and saved layouts could otherwise
resurrect a redundant floating Viewer bound to the sequence
- 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.
Move the pure-header utilities shared by app/ and engine/ out of
engine/common/ into a new shared/include/oakutil/ layer (define, lerp,
decibel, digit, range, crashpadutils, autoscroll, qtutils, filefunctions
declarations, and a trimmed xmlutils exposing a CancelAtom-free void*
overload). engine/common/ keeps forwarding headers so internal include
paths are unchanged; app/ now includes oakutil/* directly.
engine/node/project.h gains an explicit NodeGroup forward declaration
previously obtained transitively through the old xmlutils.h.
Move the finished migration campaign docs (handoffs v3-v6, roadmap,
R5 guides, R6 cleanup, R7 pure-ABI) from docs/zh/ into
docs/zh/plans/completed/ with an archive README; fix all
cross-references; refresh plans/README.md index (active plans now
marked unlocked).
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 app/worker/cli now reach liboakengine exclusively through the
oakengine_* pure-C ABI (557 -> 0 undefined olive:: symbols in
oak-editor, 0 in oak-render-worker). Includes the full facade,
app-side migration, EngineEventBridge event mechanism, undo-group
semantics, and the campaign documentation (roadmap through R7 plan).
Verified: full build 0 errors, ctest 45/45 (oak_cli_transcode
intermittent SEGFAULT is pre-existing flaky).
oak-cli and oak-render-worker use the C ABI exclusively (0 olive::
symbols). Google Test suites updated for the migrated APIs, new engine
facade test coverage, CLI transcode verification.
Every app module now reaches liboakengine exclusively through
oakengine_* C calls, EngineEventBridge subscriptions and app-side
handle headers (cliphandle/keyframehandle/nodevaluehandle/oakvaluehelper).
Direct C++ command construction, engine signal connect()s, and engine
type usage in MOC-visible signatures are gone: 557 -> 0 undefined
olive:: symbols in oak-editor.
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.
Complete documentation set for the facade migration (B1-R6) and beyond:
facade-migration-roadmap, handoffs v3-v6, R5 guides, R6 cleanup plan,
R7 pure-ABI plan, long-term plans (riir/ai-agent/gtest/ui-redesign),
updated Chinese README draft, UI design mockups, and the Google Test +
struct-typedef rules in CONTRIBUTING.
- new primitives: clip_toggle_enabled (per-block flip), clip_set_linked,
sequence_add_default_transition (config-driven, sequence timebase),
node_set_label_many, node_set_color_label, plus observation getters
- toggle-links, default transitions, enable toggles, color labels, and
block renaming now go through the facade; the stray empty undo entry
from block renaming is gone along the way
- nest/multicam/waveform-sync stay as documented composites: they mix
redo_now intermediate state, graph surgery, and app-side computation
that a single primitive cannot express faithfully
- the timeline panel's command execution paths are now fully migrated
- new primitives: keyframes_set_time_many (conflict-safe batch time
move), keyframes_set_value_many (captured or explicit old values),
keyframes_set_bezier_many (double precision), and
keyframe_set_bezier_point (single handle with NaN-capture fallback)
- dialog and curveview drag finalization go through the facade; the
tests exercise the same global undo stack via oakengine_project_undo
- keyframeview/keyframeviewundo.{h,cpp} removed with zero remaining
references
- new facade API: set_input_at_time (element addressing, track=-1 for
all components at once), set_input_string_at_time, frame_time_base,
array_insert_at/remove_at, disconnect_ex (element-aware), and
keyframes_set_type_many (first cross-track keyframe op, addressed by
(time,track) pairs)
- the widget bridge's commit funnel, color path, array ops, label
disconnect, and keyframe set-type actions in keyframeview/curvewidget
now go through the facade; keyframeviewundo.h loses two consumers
- deliberate leftovers with rationale: keyframecontrol's multi-track
composite ops (documented track-0-only limitation of the keyframe
family), keyframeproperties dialog (needs a set_time primitive),
curveview's drag UX, and NodeInputDragger (already engine-side)
- new primitives: ripple_delete_in_to_out (ripple or gap fill plus
work-area state, one undo command), trim_clips_to (batch edge trim
returning a count), delete_empty_tracks (type-filtered batch), and
marker_remove_many (sparse marker deletion by timestamp array)
- delete-in-to-out, edit-to, delete-all-empty-tracks, and sequence
viewer marker deletion now go through the facade; empty operations
no longer push empty undo entries
- footage viewer marker deletion keeps its app path deliberately
(facade marker handles are Sequences, not generic viewers); the
tentative subtitle track and pointer drag chain stay as documented
leftovers
- new batch primitives: split_clips (link-preserving, single undo
command), delete_clips (gap replace + optional ripple with explicit
region support), ripple_delete_range, marker_add_ex with color
- razor/split-at-playhead, clip delete, ripple-to-point, track delete,
and the non-dialog marker path now issue facade commands instead of
the app's own undo command classes
- batch operations deliberately produce one undo command per user
action (deleting twenty clips is one entry, not twenty); selection
and transition removal stay UI-side as documented leftovers
- new facade API: video params ex (dimensions/rate/par/interlacing/
preview format/divider), audio params, preview divider, and
undoable-flagged setters mirroring the dialog's dual undo/no-undo
modes; label setting gains node_set_label_ex
- the dialog's own SequenceParamCommand is gone; accept now issues
facade calls (per-field commands, unchanged fields skipped)
- preset system stays UI-side by design: its flat XML schema never
touches engine objects
- the auto-cache checkbox maps to the engine's existing stub (no undo
noise)
- new facade API: video stream overrides (colorspace/range/interlacing/
premultiply), pixel aspect, image-sequence params, stream enable,
source start time, and colorspace candidates - all undoable
- project explorer proxy actions now run through FacadeProxyTask and
the facade media-management functions (ProxyManager references in
projectexplorer.cpp drop from 5 call sites to a comment)
- footage properties dialog reads/writes through the facade; its two
app-side undo command classes are gone
- handle-model fix: the footage handle is a heap state object, not a
plain pointer cast - oakengine_footage_borrow() wraps app-held
Footage nodes correctly (nine UB reinterpret_casts caught by the
DialogFootageProperties tests)
- oakengine_export_render_ex covers the dialog's entire option surface
(formats, codecs, pix fmts, audio params, ranges incl. still frame,
subtitles, scaling, threads, custom OCIO color transform names,
per-codec key/value options) - zero feature reduction
- the dialog's Start now creates a FacadeExportTask that drives
oakengine_export_render_ex instead of constructing ExportTask in the
UI; progress flows through the facade callback and cancel through
oakengine_export_cancel (OAKENGINE_E_CANCELLED preserves the
keep-dialog-open semantics)
- two real fixes: audio sample format 0 no longer means an
AAC-unsupported u8_p (default is f32_p), and image-sequence exports
use the engine's real [#####] placeholder instead of a made-up -%04d
Drives ConformManager for every audio-streaming footage with the same
AudioParams the export will use, before starting ExportTask - matching
the app's preview prewarm semantics. Root-cause analysis showed the
export's own wait-for-conform path already worked; this remains as a
first-export speedup and a belt-and-braces guarantee
- 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
- relink through Footage::set_filename with the engine's natural
clear+reprobe cascade; find_offline_footage recovers offline media
by exact filename match under a search directory
- synchronous proxy generate (event-loop driven like the export
family), state query, enable/disable, path, and delete, mirroring
the app's proxy dialog assembly
- documented as non-undoable, matching the app's existing semantics
- undoable track removal (content restored on undo) and true-move
track reordering assembled from the edge commands (the engine has
no move-track API)
- track height/mute/lock getters and setters, documented as
non-undoable to match the engine's current semantics
- undoable marker add/remove/rename; duplicate timestamps are
rejected with E_STATE instead of hitting the engine's debug assert