Module-internal object references are Rust types now (values, Arc,
Mutex); CHandle remains only at the oakengine C-ABI boundary:
- oakundo: the global stack holds UndoStack/UndoCommand values
directly (stack token is the static's address)
- oaktimeline: marker/workarea boxes carry Arc<Mutex<T>>; commands
share the same allocation through Arc clones (readers in oakengine
stubs and the app's graphops updated to lock)
- oaktask/oakstorage: sessions, write-through bindings and the
database backend pass ProjectArc; the Session drops its manual
release bookkeeping; nodeutil keeps the CHandle<->Arc boundary
conversion (release_project restored for the app)
- oakcodec: handle.rs deleted outright (no facade entry needed it);
texture/block placeholders are unit structs
- oakrender: copier's project handle is an identity u64; alive-count
machinery removed; handle.rs is make_owned/get/get_mut only
- oakplugin: the instance registry is gone (its unregister key never
matched, leaking weak entries); handle.rs is the RefBox boundary type
- oaknode/oakcommon: only dead guard/borrow helpers removed; external
payload handles (texture/processor) documented as the boundary
Flake hunts landed along the way: the audio recording test serializes
on the shared manager lock with a normalized state; the autocacher
cancel test uses a slow producer so cancellation is deterministic.
- viewers show the 480px proxy immediately and a background thread
fills the sequence-resolution frame (per-monitor in-flight job,
generation-based staleness, playback skips full-res)
- preferences dialog complete: cache dir (now consumed by
default_disk_cache_path), proxy policy/divider, snapshot interval
(write-through era autosave), default transition length, audio
in/out devices (new facade device-enumeration exports; audio init
from config — playback was never creating the audio instance),
language/theme/renderer backend, all persisted via config
- shortcut map (src/shortcuts.rs): space/J/K/L, I/O, S split, A/^A,
⌘Z/⌘⇧Z, ⌘N/⌘O/⌘S/⌘E/⌘Q, frame step, Home, track zoom; dispatch
shares the menu action path and stays silent over modals
- screenshots: preferences dialog zh/en captured and reviewed
D3: oakdb+pg:// fully wired (shared sea-orm entities, BIGSERIAL DDL,
connect-probe instead of pool retry on dead servers); Storage/Backend=pg
+ Storage/PgUrl config; 13 OAK_TEST_PG_URL-gated PG tests (verified
against a Docker postgres:16), always-on clean-error tests otherwise.
D4: DaVinci-style project manager — list with derived stats, create/
rename/duplicate/delete (confirm)/import/export (native dialogs,
ove/otio/fcpxml), shown at startup and from the file menu; facade
oakengine_library_* exports (list/create/delete/rename/duplicate/
import/export + project_load_library that binds write-through);
save/save-as menu becomes 'export project file', open splits into
from-library/from-file; status bar shows library write state; storage
activates on app start and flushes on exit; spawn_modal reentrancy
fixed (window-callback path) with a doc note.
Also: the P1 audio test's environment probe was lost in the ffi purge;
restored on cpal (the output device is cpal now).
Single-lib cleanup: the per-crate src/bridge/ and src/ffi.rs layers are
gone (oakundo/oakcommon/oaknode/oaktimeline/oakcodec/oakaudio/
oakrender/oaktask/oakplugin/oakstorage); cross-crate calls are plain
Rust, CHandle marshalling shrinks to the oakengine boundary, and tests
call the Rust APIs directly (pure C-ABI wrapper tests removed where
the domain layer already covers the behavior).
exporter.h family implemented: oakengine_export_render (CLI contract),
oakengine_export_render_with_params (was a stub), last_error and
progress callback; synchronous path reuses task_create_export +
start_sync. Fixes on the way: oaktask video ticket self-deadlock,
audio params dropped on the export path, codec encoder AAC slicing and
H.264 time base. Real-mp4 tests cover both entry points, progress and
the illegal-argument matrix.
Also: oakstorage session maps null project handles to None (version-
info path), configstore test double literal 3.14 -> 3.15 (clippy PI
lint), oakaudio output callback scratch buffer + env-aware P1 test,
cli media round-trip test uses a generated 16-frame clip (no more
minute-long debug runs).
- output callback reuses a scratch buffer instead of allocating per
call (real-time rule)
- P1 consumption test probes real callback delivery and skips on
headless/background sessions (CoreAudio starts the stream but never
runs it outside the GUI session), with a 30s poll for slow HAL
startup; restores the manager singleton state afterwards
- waveform: drop leftover DBG-WF debug prints
engine:
- oakrender eval footage hook decodes via oakcodec (JobSpec::Footage
carries filename/stream); ticket/ffi/manager wiring, real-media
decode test with programmatically generated MPEG-2
- oakrender bridge/codec.rs + node.rs: direct oakcodec/oaknode calls;
the crate's dlsym module is gone (project_deep_copy/sync_copy remain
documented always-fail stubs — never implemented in oaknode)
- oakaudio waveform/decoder path adjustments for the decode hook
app (gpui + gpui_widgets):
- menu bar scrubbing: hovering another top-level title while a menu is
open switches to it; popup width is content-aware (CJK-aware) instead
of fixed 160px
- density pass: window rem 16 -> 14px, menu rows 26 -> 22px, dock tabs
32 -> 26px, viewer transport tightened
- open/import/save-as use the native platform file dialogs
(prompt_for_paths / prompt_for_new_path; multi-select import);
MockEngine records imported footage for tests
- project explorer Tree/Icons toggle is localized (explorer.tree /
explorer.icons widget keys)
source monitor:
- new facade exports oakengine_renderer_create_for_node (render any
node, not just sequences) and oakengine_project_footage_at (fetch a
footage node by index); it_render covers the e2e render plus the
NULL/illegal matrix
- RealEngine renders the selected footage's real frames to the source
viewer (per-node renderer slot, frame cache invalidated on selection
change, synthetic fallback kept)
- known gap (documented): pixels stay transparent black until
oakrender's footage decode hook lands — the render surface itself is
real end to end
audio meter:
- oakaudio manager can now report per-channel linear peaks of the
buffered output (PreviewAudioDevice::peek_tail + levelmeter analysis
of the newest 8192 frames, packed/planar F32)
- new facade export oakengine_audio_output_levels (negative codes pass
through); RealEngine's AudioMeterDataSource reads it instead of
returning hardcoded silence
- tests: module peak readback + facade validation/readback matrix
- oakengine_sequence_move_clip implemented for real (oaktimeline
TrackMoveBlockCommand; fixes the graph-ownership/gap-anchor/ripple
trim bugs the stub was hiding); same-track via the frozen C ABI,
cross-track supported by the module command
- oaknode clip blocks now declare a tex_in texture input and set
effect_input to it, so timeline clips can host effect chains; facade
test covers effect insert/remove on a real clip
- oakffmpeg-link: FFMPEG_DIR is now mandatory with a clear panic (a
Homebrew upgrade left the system ffmpeg .pc pointing at a deleted
dav1d Cellar path, breaking links); reads a git-ignored workspace
.env for IDEs that cannot inject env vars (RustRover); links the C++
stdlib for C++ codec libs (svt-av1)
- oakengine re-exports oaknode so tests share one crate instance;
it_node uses the direct instance's value type where it calls the
module FFI (the --workspace dev-dependency feature split builds
oaknode twice)
- tooling/ffmpeg/build-ffmpeg.sh builds release/8.0 static+PIC into
.cache/ffmpeg: GPL/version3, every free-license external codec lib
probed via pkg-config (enabled when present), per-OS hardware
acceleration (VideoToolbox/AudioToolbox, VAAPI/VDPAU/libdrm,
D3D11VA/DXVA2/MediaFoundation, nvenc when ffnvcodec exists)
- tooling/install-deps.sh installs those libraries on Homebrew / MSYS2
UCRT64 / Debian-Ubuntu / Fedora / Arch; nothing in the build sudo's
- ffmpeg-next's own build feature is unusable (every crate-version to
FFmpeg-release pairing is broken upstream: 9.0.0->FF9 AVCodec fields,
8.1.0->FF8.1 new enum variants, 8.0.0->FF8 FF_PROFILE rename), so
ffmpeg-next 9 + FFmpeg 8.x headers via FFMPEG_DIR it is
- new links-crate oakffmpeg-link emits the static FFmpeg's transitive
link flags from its .pc files (cargo only propagates them from links
crates, and rustc prunes the flags unless the rlib is referenced —
hence the force_link statics)
- docs/build.md updated for the Rust workspace flow
- processor.rs: resample/channel-convert/time-stretch now runs an
in-process FFmpeg filter graph (abuffer -> atempo -> aformat ->
abuffersink) via ffmpeg-next
- waveform.rs: extraction decodes through oakcodec's FFmpegDecoder
(interleaved f32) instead of the fb_decoder/fb_audio_graph pair
- bridge/ffmpeg.rs and the null fb_* test stubs deleted;
oakcommon::ffmpegutils bridge constants become plain ints
- liboakengine.dylib no longer imports any fb_* symbol (nm -u clean);
the remaining runtime imports are the host-provided oakcore_* symbols
- real decode/resample tests added (generated PCM input, no network)