Fake tests rewritten to assert real behavior:
- audio_smoke: conversion tests now actually Convert() samples and verify
output; waveform length/summary assertions tightened to exact values
- plugin_format_conversion: RowBytes/U8ToU16/LoadImageFile now call real
production code (VideoParams::GetBytesPerPixel, sws scaler, OIIO decode
of tests/img.png with known pixel values)
- core_color: HSV round trip now verifies fromHsv(toHsv(c)) == c instead
of comparing toHsv against its own accessors
- core_bezier/node_inputimmediate: expected values replaced with
independently derived constants instead of re-running the code under test
- common_commandlineparser/common_debug/common_jobtime: capture
stdout/stderr/qDebug and assert actual output content
- proxy_manager: ProxyFinished test now drives a real proxy job instead of
emitting the signal itself; proxy_dialog/panel/proxy/preferences/timeruler
tests assert real widget state
- viewer_smoke/preview_autocacher/render_misc: zero-assertion tests given
observable-state assertions or removed where nothing is observable
Duplicates removed:
- plugin_smoke_test.cpp: 18 tests duplicated from plugin_paraminstance /
plugin_support_* / plugin_renderer_readback (751 -> 180 lines)
- module_smoke HumanStrings tests covered precisely by ui_humanstrings_test
- render_misc duplicate kDefaultInterpolation constant check
Removed by policy (skip allowed, never disabled):
- all DISABLED_ prefixes: re-enabled as real offscreen tests or deleted
- ffmpeg_decoder_hw: hardcoded personal path replaced with
OAK_TEST_HW_DECODE_FILE env var, GTEST_SKIP when unset
Also:
- config_test: restore Config defaults after run (cross-test pollution)
- render_worker_footage: drop /tmp debug-output scaffolding
- previewaudiodevice construction test asserts the real bugfix
- OpenGLRenderer: hold the viewer-owned QOpenGLContext in a QPointer so
DestroyInternal() safely skips it when the context has already been
destroyed by Qt's shared-context lifecycle. Fixes a SIGSEGV when the
full gtest suite ran MainWindow.ConstructsOffscreenWithPanelsAndMenus
after earlier viewer tests.
- PreviewAudioDevice: add SetParams() deriving bytes_per_frame from the
audio format (bytes per sample * channel count) instead of staying 0.
Enzo GD, administrator of the Olive Facebook user group, gave this
project generous promotional support in its early days. The shared body
text now carries a special-thanks line in both the About dialog and the
first-run Welcome dialog; zh_CN translation included.
- Runners: ubuntu-24.04, macos-15, windows-latest (free tier) instead
of WarpBuild 8x/12x instances
- openfx-misc parallelism adapts to nproc (GitHub runners have fewer
vCPUs than the WarpBuild instances)
- Schedule a daily 03:17 UTC run so ccache entries never hit GitHub's
7-day cache expiry and system dependencies stay current
WarpBuild's cache service is billed; GitHub's cache is free (10 GB
repo limit with LRU eviction, ample for ccache entries). All three
cache sites — ccache for the matrix and dynamic-backend jobs, plus the
macOS OpenTimelineIO build — now use actions/cache@v4, which also
removes the Windows-specific fallback step.
Point CCACHE_DIR and the cache action at a fixed workspace directory
instead; checkout cleans untracked files before the restore, so the
ordering is safe.
- Restore ccache with WarpBuilds/cache@v1 on all three OS and the
dynamic-backend job (per-OS keys with branch fallback), and wire it
up through CMAKE_C/CXX_COMPILER_LAUNCHER; builds print ccache -s so
hit rates are visible in CI logs
- Cache the macOS OpenTimelineIO build (keyed on v0.16.0) and skip the
rebuild on cache hit
- Install ccache via apt/brew/MSYS2 as needed; CCACHE_MAXSIZE=1G
- Runner sizes 16x/32x -> 8x (macOS 12x kept); openfx-misc make -j8 to
match
The test assumed msleep(100) measures close to 100 ms (expecting ~2
frames at 24fps vs ~6 at 60fps). On the loaded macOS CI runner the
24fps leg overslept ~2.5x, producing 6 frames at both rates. Measure
the actual interval per leg and compare against the expected frame
count with a 1-frame tolerance, and use highly distinct timebases
(1fps vs 240fps) for the rate-ordering check.
- Linux CD builds no longer disable Vulkan: the AppImage, deb, rpm and
Arch packages now build and ship liboakvulkan (the AppImage deploys
it via linuxdeploy --library so libvulkan is bundled too, and the
verify step checks both backend libraries); deb/rpm dependencies gain
libvulkan1/vulkan-loader; the Arch PKGBUILD gains vulkan-headers
- macOS CD installs vulkan-loader and exports VULKAN_SDK so
find_package(Vulkan) locates the Homebrew loader (previously the
Vulkan backend silently never built there)
- ffmpeg_bridge now installs to the standard lib directory with
/../lib RPATH instead of the non-standard ffmpeg_bridge/bin
layout (verified: editor, worker, oakgl and oakvulkan all resolve it)
- build guides (EN/ZH) document the macOS Vulkan backend dependencies
and the VULKAN_SDK variable
Version is no longer hardcoded: at configure time CMake uses the tag
name when HEAD is exactly on a tag (leading "v" stripped), otherwise
the first 8 hex digits of the commit hash. When git is unavailable
(e.g. source tarball) it falls back to the contents of version.txt,
which is now the single place to bump the release version.
Also remove the temporary qDebug() flood in the audio playback path
(ViewerWidget::QueueNextAudioBuffer / ReceivedAudioBufferForPlayback,
AudioManager::PushToOutput).
DirectoryIsValid() ignored try_to_create_if_not_exists and always
called mkpath(). On the Windows CI runner (which may create dirs at
the drive root) PathWidget validation both created bogus directories
and never flagged invalid paths; on Linux the mkpath just failed.
- filefunctions: only mkpath when try_to_create_if_not_exists is set
- TaskProjectLoadTest: Project::set_filename() stores native
separators on Windows, normalize before comparing paths
- DialogProjectProperties: use a nonexistent file inside a temp dir
instead of a fixed /definitely/... path that prior tests may have
partially created on a writable drive
QIcon::addFile() differs between Qt versions in whether entries for
nonexistent files keep the icon non-null (Ubuntu CI failure). Assert
on what matters: no usable pixmap or size is produced.
ColorManager::Init() (run by every Project construction) dereferenced
GetDefaultConfig() unconditionally. Any Project created before
SetUpDefaultConfig() crashed inside OCIO getCanonicalName on a null
config — the Windows CI SEGFAULT, where the suite order runs a
Project-creating test first. Reproduced locally by running
MainWindowLayoutInfo.AccessorsStoreAndRetrieve as the first suite.
The test forces GraphicsBackend=opengl and builds a full MainWindow
with QOpenGLWidget-based viewer panels. On headless platforms whose
QPA cannot provide a GL context (Ubuntu/Windows CI on offscreen), the
app's GL calls crash on a non-current context (SEGFAULT in CI on
both). Probe with QOpenGLContext+QOffscreenSurface and GTEST_SKIP when
GL is unusable; the test still runs on platforms with real GL.
- RenderManager: GPU-side members (context_, decoder_cache_,
shader_cache_, auto_cacher_, worker_pool_, decoder_clear_timer_) were
left uninitialized when the configured graphics backend is unknown
(e.g. dummy); ViewerWidget then dereferenced garbage and crashed.
Initialize them at declaration
- CurveView::SelectKeyframesOfInput ignored its reference parameter and
selected keyframes of every connected track; select only the
requested track's keyframes
- SeekableWidget::SeekToScenePoint dereferenced GetViewerNode()
unconditionally; skip the playhead update when no viewer is connected
- LoadOTIOTask: unknown root schema leaked the freshly allocated
project_ (delete + reset; OTIO is not enabled in local builds so this
file is compile-verified by inspection only)
Locked by new tests: RenderManagerDummyBackend,
TimeRuler.SeekToScenePointWithoutViewerIsNoOp,
CurveViewTest.SelectKeyframesOfInputSelectsOnlyRequestedTrack
format_, video_codec_, audio_codec_, subtitle_sidecar_fmt_ and
subtitles_codec_ were left indeterminate by the constructor; reading
them before the corresponding Enable* call was UB. Initialize to the
kFormatCount/kCodecCount invalid sentinels.
- all 19 panel classes: construction, titles, context/signal wiring,
save/load data round-trips
- MainWindowLayoutInfo XML round-trip, MainStatusBar, offscreen
MainWindow construction with standard panels and menus
- RatioDialog parsing (decimal and : / ; separators), validation
- MainWindowLayoutInfo: toXml() iterated open_sequences_ for the
<viewers> section and fromXml() never parsed it, so open footage
viewers were lost on layout save/load
- PanelWidget never set QObject::objectName, so
PanelManager::GetPanelWithName() always returned nullptr and every
caller (layout restore, NodeView param panel lookup) was dead code
- PanelManager::DestroyInstance() left instance_ dangling (UAF on any
later RegisterPanel), unlike the other singletons
- FrameRateComboBox: RepopulateList() left current index at -1 on first
fill, so GetFrameRate() returned 0/1 instead of the displayed first
entry until something called SetFrameRate()
- HandMovableView: default_drag_mode_ was never initialized; an early
Core::ToolChanged signal would setDragMode() with it (UB)
- SequencePreset::Save() wrote element "interlacing_" while Load() read
"interlacing", losing the interlacing mode on preset round-trips;
Save() now writes "interlacing" and Load() accepts both for backward
compatibility with existing preset files
- ExportFormatComboBox: current_ was never initialized (UB on GetFormat
before first user selection)
- ExportSubtitlesTab::SetSidecarEnabled() called setEnabled instead of
setChecked, so restored export params could never re-enable sidecar
subtitles
- h264section.h: static const int constants were odr-use unsafe (link
error when referenced); changed to static constexpr
- html.cpp: rgba() colors parsed with setRedF/GreenF/BlueF (0-1) while the
writer emits 0-255 integers, so semi-transparent text colors lost their
RGB on round-trip; parse with integer setters instead
- CLIProgressDialog: percentage padding compared normalized progress
(0.0-1.0) against 10/100, so padding was always fully applied; compute
the percentage first
- TimelineUndoPointer BlockTrimCommand: remove_block_from_graph_ was
never initialized (UB on redo)
- TimelineUndoGeneral TransitionRemoveCommand: track_ was never
initialized; GetRelevantProject() could dereference it before redo()
- ProjectLoadTask::Run(): failure path deleted project_ without
resetting it, leaving GetLoadedProject() dangling
- ChromaKey tolerance inputs are now spelled "upper/lower_tolerance_in";
old projects with the misspelled "tolerence" IDs keep loading (values
and connections) via a new Node::GetInputIDForLegacyID() hook applied
in LoadInput and connection deserialization
- flip/ripple/swirl/tile/wave node IDs moved to the consistent
org.olivevideoeditor.Olive.* domain; NodeFactory::CreateFromID maps
the old org.oliveeditor.* IDs so old projects still resolve
- Regression tests: legacy ChromaKey IDs (values + connections) and
legacy factory IDs
Node core:
- MathNode/TrigonometryNode combo strings realigned with Operation enums
- mathbase scalar/vector operand pick no longer uses bitwise type checks
- NodeSetPositionAndDependenciesRecursively moves dependencies again
- RemoveAllKeyframes undo actually restores keyframes
- NodeGroup GetInputName null-deref guard, passthrough ids use input id
- NodeValueTable::Has is an exact type match; tag fallback only for
empty tags; kStrCombo/kPushButton get data type names
- delete_all_keyframes no longer loops forever on unparented keyframes;
keyframe-load failures propagate; rational interpolation falls back to
double; OpacityEffect no longer leaks its internal MathNode
Audio/footage:
- AudioVisualWaveform: GetSummaryFromTime underflow OOB read, TrimIn
prepend length bookkeeping, OverwriteSums source channel indexing
- PanNode inserts the pan value into the sample job (keyframed pan
works); OutputParamsChanged is emitted on device change; PortAudio
device indices are validated before Pa_GetDeviceInfo
- Footage: AdjustTimeByLoopMode no longer hangs/UBs on degenerate
lengths, GetStreamIndex bounds-checked, CheckFootage clears stale
state on missing files, failed probes are not cached,
FootageDescription::Load requires its own root element
Render/track:
- ViewerOutput pushes the tagged samples value; TrackList disconnects
the track-height lambda; GetTrackFromReference validity check
- RenderManager dummy backend: null-initialized threads, guarded
decoder-cache/timer paths; Renderer::Destroy releases color cache
shaders/textures; unknown dynamic backends no longer alias to oakgl
- SharedMemoryRegion POSIX attach validates segment size; ReadMessage
skips blank lines instead of failing; GC counter clamped;
IsRenderingCustomRange implemented; TimeOffsetNode gets a true
inverse OutputTimeAdjustment; zero-speed clips return the held frame
Plugin/nodes:
- OliveClip: stored default region of definition is honored, on-demand
images are cached; OliveHost sets host identity properties and logs
instead of showing modal dialogs offscreen; Plugin.h dead decls gone
- DespillNode guards graph-less use with Rec.709 fallback; description
typos fixed (despill, swirl); mosaic applies when only one axis
matches; Windows-only Project filename separator test fixed
- node_save_load_test: Node Save/Load round trips for values, keyframes,
hints, caches, connections, positions
- node_polygon_folder_test: Folder child management, PolygonGenerator
rasterization/gizmos, TextGeneratorV1/V2
- footage_probe_test: real FFmpeg/OIIO probing of demo.mp4/img.png,
metadata cache, footage state transitions
- render_tail_test: DynamicRenderer, color-context shader plumbing,
PreviewAutoCacher pause/clear paths, DiskManager edges,
AudioPlaybackCache segment I/O
Also fixes AudioPlaybackCache::WritePartOfSampleBuffer, found by the new
tests: zero padding was written via QFile::write(const char*) which
treats the buffer as a NUL-terminated string, so no padding bytes were
ever written; the write length was also computed from the segment end
instead of the range end, making WriteSilence() spin forever on an
empty buffer
The unquoted set() turned any user-supplied C flags into a broken
semicolon list in generated compile rules (mirroring the quoted CXX
line); this also made --coverage configure fail.
- Linux: olive-render-worker had no install rule, so it was missing
from the AppImage, deb, rpm and Arch packages even though
RenderWorkerPool spawns it from applicationDirPath(); install it next
to oak-editor (verified: RPATH resolution of libffmpeg_bridge works
for editor, worker and liboakgl from the installed layout)
- Windows: ffmpeg_bridge.dll is built outside app/ and is invisible to
the ntldd dependency walk, so the NSIS installer shipped without it
(and without the av*.dll it pulls in); copy it explicitly and include
it in the dependency loop
- AppImage verification now also checks oak-render-worker, liboakgl.so,
libffmpeg_bridge.so and ldd resolution of all shipped binaries
- File inputs marked with the 'lut_library' property (currently the
OCIO LUT node) now show a combo box populated from the global LUT
library above the path field: pick a library LUT directly, or use
'Other (Custom File)...' with the regular path field; selections go
through the standard undoable input-change path
- Refresh zh_CN translations with lupdate and translate all strings
introduced by the proxy dialog, LUT library, LUT picker, waveform
sync and footage start time work
- Document the new per-footage custom <proxy> attributes and the
'manual' source-start-time origin in the project file reference
- Add LutFileField UI tests
- Waveform sync no longer treats uncached waveform regions as silence:
the envelope extraction now reports a per-window validity mask and the
correlation skips invalid windows on either side, improving accuracy
for partially cached clips
- Add stretch/speed sync: AudioWaveformSync::EstimateStretchAndOffset
searches a playback-rate range plus offset, and a new timeline
context action 'Synchronize by Waveform (Adjust Speed)' applies the
estimated rate as a clip speed change (with undo) when plain offset
alignment is inconclusive
- Footage properties dialog gains a Source Start Time field so the
value used by source-time sync can be viewed and edited manually
instead of relying solely on auto-detected metadata; applied via an
undo command, with Footage::ClearSourceStartTime() for removal
- Regression tests for masked correlation, stretch estimation, the
envelope validity mask, and source-start-time set/clear
- Add a global LUT library: user-configurable directories (new
Preferences > LUT tab) scanned recursively for .cube/.3dl files; LUT
node file pickers offer the library dirs as sidebar shortcuts via a
'lut_library' input property handled by the param view bridge
- OCIOLutNode no longer fails silently: missing files, unsupported
extensions and OCIO load errors are recorded in last_error() and
surfaced in the status bar (input still passes through for rendering
safety)
- ColorDialog: re-enable the display -> reference conversion using
ColorProcessor::kInverse with a validity guard, and re-enable the
Display tab in ColorValuesWidget; covered by a round-trip regression
test proving the old OCIO inverse crash no longer occurs
- OCIOGradingTransformLinearNode: enforce the OCIO clampWhite >
clampBlack invariant per frame in Value() so keyframed/connected
values cannot produce invalid grading transforms, and constrain the
white clamp UI minimum whenever the black clamp is static
- Regression tests for LUT extension checks, direction switching, node
error reporting, LUT library scanning, display inverse round-trip and
clamp enforcement
- New ProxyDialog (Tools > Proxy Settings..., plus 'Proxy Settings...'
in the project panel and timeline Proxy submenus) unifying global
proxy settings, per-footage custom presets, generation and deletion
in one place instead of three scattered entry points; this also fixes
the Tools menu action opening the wrong preferences tab
- Preferences Disk tab gains an 'include audio in proxies' checkbox and
an ffmpeg executable path field (blank = auto-detect)
- Add ProxyDialog smoke tests
- Footage can store custom proxy parameters (width/height/crf/preset/
extension/audio) that override the global settings; they are
serialized with the project and used by every generation entry point
via Footage::GetEffectiveProxyParams()
- Proxies now include the source audio streams (AAC) unless disabled;
the proxy filename records the audio flag and offline audio rendering
decodes from the proxy when present
- ProxyTask resolves ffmpeg from the new FFmpegPath config key first,
then PATH, then common install locations (e.g. Homebrew on macOS),
instead of relying on PATH only; the error message points at the
preferences when no executable is found
- ProxyTask::BuildArguments() is extracted for testability
- Add ProxyIncludeAudio and FFmpegPath config defaults
- Add regression tests for the filename audio marker, config-backed
params, ffmpeg resolution, argument building, and custom-param
persistence
On macOS Qt's text-heuristic menu role relocates any action whose text
starts with "Preferences" into the application menu. With the English
translation the Tools > Preferences action matched the heuristic and
disappeared from the Tools menu. Pin the action to QAction::NoRole so it
stays in the Tools menu on every platform and language.
The CI runner has no Vulkan runtime (liboakvulkan.so fails to load) and
no display (offscreen QOpenGLWidget cannot create a context). The
viewer display tests only checked that the parameter was "vulkan", so
they ran on the OpenGL fallback and segfaulted with no usable GL
context. Probe the backend the same way the other render test fixtures
do (load, verify the backend kind actually matches the request, init)
and skip when it is unavailable. Also replace the stale "temporary
scratch test" file header now that this is a committed regression test.
Ubuntu (FFmpeg 7.0) build failure: the public FB_PIX_FMT_* values were
hardcoded to AVPixelFormat enum values, but those shift between FFmpeg
releases (new formats are inserted mid-enum: X2RGB10 in 7.0,
RGBF16/GRAYF16 later), so the static_asserts in internal.h failed and
RGBF16LE/GRAYF16LE did not exist at all.
The FB pixel format values are now fixed identifiers translated inside
the library by pixel format name (stable across releases):
- fb::PixFmtToAV/PixFmtFromAV resolve the static table by name;
formats a decoder produces that have no static identifier (hardware
downloads like nv12/p010le/p210le) receive process-local dynamic ids
>= 1000 so they keep round-tripping through the API (scaler etc.)
instead of collapsing to NONE and crashing sws.
- All crossing points translated: scaler, frame get/set format,
decoder/probe stream info, encoder config and write_video_frame,
fb_pix_fmt_* utilities, fb_find_best_pix_fmt_of_list (which also
reinterpret_cast the caller's list; now translated element-wise and
properly NONE-terminated).
- nv12 and p010le get static identifiers as the common hardware
download formats.
- Hardware frame detection no longer compares pixel formats (breaks
across format spaces); it uses the existing fb_frame_is_hw().
macOS test failures (FFmpegBridgeEncoder.WritePngVideoAndProbeBack,
WritePcmAudioAndProbeBack): config.filename held a dangling pointer to
a temporary QByteArray (UB; happened to work on Linux). Keep the
QByteArray alive for the encoder's lifetime.
Windows (MSYS2 UCRT64) build failure: timecodefunctions.h used int64_t
without including <cstdint> (previously pulled in transitively).
Full gtest suite: 584 passed.
ClipBlock::kBufferIn is declared as NodeValue::kNone with no value
hint, so when a footage node is connected straight to a clip's buffer
input (no effect node in between), the traverser has no type to look
up in the footage's value table and falls back to its last entry. A
footage pushes its video texture first and its audio samples last, so
a video clip ended up fed with audio samples, produced no texture and
the preview went silently black. With any node in between, the table
only carries the passthrough texture, which is why the bug only showed
on direct connections.
Make Node::GetValueHintForInput virtual and override it in ClipBlock
to prefer the value type matching the clip's track (kTexture on video
tracks, kSamples on audio tracks).
Regression tests cover the exact application render path
(PreviewAutoCacher -> RenderManager -> RenderWorkerPool ->
oak-render-worker) for both the direct and indirect cases, the hint
following the track type, and viewer display widgets. The direct-case
test reproduces the black frame without the fix and passes with it.
Full suite: 584 passed.
- Replace direct FFmpeg usage in the test suite (channel layout masks,
pixel/sample format constants, AVFrame field access, sws_scale) with
the bridge equivalents and the olive::AVFrame adapter
- Drop CoreRational.ToAVRational (API removed with core's FFmpeg
dependency) and the GetSwsColorspaceFromAVColorSpace tests (helper
moved inside the bridge)
- New ffmpeg_bridge_test.cpp exercises the C API directly: constants vs
core, error strings, pixel format utilities, frames/packets, scaler,
resampler, audio graph tempo processing, probe/decoder round-trip on
tests/demo.mp4 (including hw-frame transfer), SRT subtitle reading,
and encoder end-to-end tests (PNG video and PCM audio probed back)
Replace every direct FFmpeg call in the editor and the render worker
with the pure C ffmpeg_bridge API, wrapped in thin C++ adapters that
preserve the original interfaces:
- avframeptr.h: olive::AVFrame adapter around FBFrame handles
- ffmpegutils: format conversion helpers on FB_* constants; the int
overload is renamed GetCompatibleBridgePixelFormat to avoid a silent
overload-resolution trap with the PixelFormat enum
- ffmpegdecoder/ffmpegencoder: rewritten as handle-based adapters over
FBDecoder/FBProbe/FBEncoder/FBScaler/FBResampler
- audioprocessor: FBAudioGraph push/pull adapter
- pluginrenderer/OliveClip: sws/pixdesc usage converted to FBScaler and
fb_pix_fmt_* queries
- AudioParams/channel layouts are plain uint64_t masks everywhere
Build integration: the root project no longer links FFMPEG directly;
only ffmpeg_bridge does. Binaries resolve the bridge library at runtime
via @loader_path inside the macOS app bundle (copied there post-build)
and via $ORIGIN/../ffmpeg_bridge/bin on Linux; on Windows the DLL is
installed next to the executables, so packages on all three platforms
ship the bridge library.
ffmpeg_bridge gains the extra API the adapters need:
fb_frame_make_writable, fb_decoder_get_format_duration,
fb_resampler_convert_frame, FB_PIX_FMT_YUV440P, SRT validation in
fb_probe_read_subtitle_stream, packed/planar fixes in
fb_encoder_write_audio, and a component-size fix in
fb_pix_fmt_component_size.
The library lives outside of app/ and is the only component that includes
FFmpeg headers or links FFmpeg libraries. All objects (frames, packets,
decoder/encoder instances, scalers, resamplers, audio filter graphs) are
identified by opaque handles and never leave the library.
Public surface (include/ffmpeg_bridge/ffmpeg_bridge.h):
- FBFrame/FBPacket: AVFrame/AVPacket wrappers with field accessors
- FBDecoder: demux+decode instance with hwaccel fallback logic
- FBProbe: file probing, per-stream details, subtitle reading
- FBScaler/FBResampler/FBAudioGraph: swscale/swresample/avfilter wrappers
- FBEncoder: full export encoder (video filter graph, audio resampling,
subtitles) ported from FFmpegEncoder
- FB_* constants mirror AV_* values, verified by static_asserts against
the real FFmpeg headers inside the library
Two small bugs in the ported encoder were fixed: the resampler is now
properly freed with swr_free() (was re-initialized with swr_init() and
leaked), and codec option dictionaries are freed after avcodec_open2().
- rational: store num/den natively instead of AVRational; math operators
re-implemented natively (ported av_reduce/av_d2q/av_cmp_q semantics,
verified bit-exact against FFmpeg)
- AudioParams: replace AVChannelLayout member with a plain uint64_t mask
(new render/channellayout.h constants mirror AV_CH_LAYOUT_* values)
- Timecode: native rescale (av_rescale_q/av_rescale_q_rnd equivalents)
with 128-bit intermediate precision
- core no longer finds or links FFMPEG::avutil
Part of the FFmpeg isolation effort: all FFmpeg access is being moved
behind a dedicated shared library (ffmpeg_bridge).
- On Linux, automatically prefer PipeWire/JACK/PulseAudio over ALSA
even when a saved ALSA device name exists in config.
- Restore Footage length after deserialization so worker snapshots
have valid stream lengths and video playback can advance.
- Guard ResolveDecoderFromInput and ProcessAudioFootage against a
null decoder cache to prevent worker crashes on direct Footage ->
ViewerOutput connections.
- Keep Linux signal backtrace handler in the render worker.
- Add Oak project SVG logos.
- Strip the dense RenderWorkerPool debug messages about cached/stale graph
snapshots and cleanup to reduce log spam.
- Add qDebug logging and status-bar feedback to
TimelineWidget::SynchronizeSelectedClipsByWaveform() so we can see why the
sync action does nothing (e.g. not enough cached clips or no usable offset).
- Add FixChannelLayout() helper to AudioProcessor::Open() to fall back to a
default native channel layout when the input/output layout is unspecified,
custom, or has a zero mask. This prevents FFmpeg's abuffer/aformat filters
from rejecting 'channel_layouts=0x0' on Linux.
- Log audio processor open parameters, viewer audio queue state, and
AudioManager::PushToOutput device/stream status to help diagnose silent
playback on Linux.
- Add explicit QTabBar::tab styles to olive-dark/style.css so inactive
KDDockWidgets tabs render with a dark background and white text instead
of falling back to the native macOS light tab appearance.
- Update macOS CD to stage Oak.app alongside an Applications symlink
before creating the DMG, matching standard macOS installer disk images.
The custom Python bundling script was copying Qt framework binaries as
flat dylibs (e.g. Contents/Frameworks/QtCore) on top of the frameworks
already deployed by macdeployqt (Contents/Frameworks/QtCore.framework).
This caused duplicate Objective-C class definitions and crashes in
QAction construction.
- Skip any dependency whose basename starts with 'Qt' or 'libQt'.
- Process every binary in Contents/MacOS (main app, worker, backends).
- Fix YAML duplicate 'run:' key from the previous commit.
- Add -verbose=2 to macdeployqt so deployment issues are visible in CI logs.
- Pass -executable for oak-render-worker so its rpaths are also fixed.
- Add a verification step that fails the build if platform plugins are missing.
- Print otool -L for both main binary and worker to aid debugging.
Oak only uses KDDockWidgets' QtWidgets frontend. Building the QtQuick
frontend caused duplicate QML module registration when QtQuick.framework
initialized, resulting in a SIGABRT in QQmlMetaType::qmlInsertModuleRegistration.
- Set KDDockWidgets_FRONTENDS to qtwidgets in ext/CMakeLists.txt.
- Remove the -qmldir flags from macOS CD deploy since QML plugins are no
longer needed.
The .ts files had been corrupted by an earlier formatting run that
inserted spaces into XML tags (e.g. '<?xml version = "1.0" ... ?>'),
breaking lrelease parsing. Restore all files under app/ts/ from the last
good revision before the corruption.
Run clang-format over all project C/C++ source and header files
(.c, .cpp, .cc, .h, .hpp, .hh, .m, .mm) under app/ and tests/.
Non-source files (svg, qm, qrc, etc.) were not touched.
- Reference Linux Kernel Coding Style instead of Google C++ Style Guide.
- Require tabs for indentation.
- Keep Javadoc-style documentation comments.
- Preserve the original naming rules section.
- Update project name to Oak Video Editor.
- Rename macOS bundle output from Olive.app to Oak.app.
- Rename editor binary to oak-editor and render worker to oak-render-worker.
- Rename crash handler output to oak-crashhandler.
- Update worker lookup, NSIS installer, Linux desktop/AppRun, crashhandler
symbol paths, and documentation for the new binary names.
- Update CD workflow paths and add -qmldir flags to macdeployqt so QtQuick
/ QML plugins required by KDDockWidgets are bundled, fixing the launch
crash on macOS.
- Update user-facing GitHub URLs to OakVideoEditorCommunity/oak.
MinGW CMake defaults to prefixing shared libraries with 'lib', producing liboakgl.dll which the dynamic backend loader cannot find. Set PREFIX empty on Windows so the output names match the runtime names oakgl.dll / oakvulkan.dll.
Install git in the Fedora container before actions/checkout so the checkout step can clone the repository normally instead of falling back to the REST API.
- Arch: create the source tarball in /tmp to avoid file changed as we read it and exclude existing tarballs from the archive.
- RPM: run the Fedora build inside docker instead of a job container so actions/checkout is not hampered by a missing Git in the base image.
- Add CPack configuration in CMakeLists.txt for .deb and .rpm generation
with declared runtime dependencies.
- Add Arch Linux PKGBUILD template under app/packaging/arch/ and build it
inside the official archlinux:base-devel Docker container.
- Extend CD workflow with deb, rpm and archlinux jobs and attach the
resulting packages to the draft release.
- English and Chinese build.md now list macOS as a fully supported
platform and point to the dedicated macOS build guide.
- Chinese build.md Fedora dependency list synced with English: use
ffmpeg-free-devel and add bzip2-devel.
- RenderWorkerFootageTest: skip when the dynamic render backend is not
built; we cannot verify backend availability without it, and the tests
fail on headless/GPU-less CI runners.
- NodeProject.FilenameAndNameUpdate: use a plain filename instead of
/tmp/... so the expected name does not depend on POSIX temp paths or
Windows backslash normalization.
Proxy generation modifies the project, causing graph snapshots to be
rewritten. The old implementation deleted the previous snapshot
immediately, even though queued render jobs still held its path. The
worker then failed to load the deleted file.
Add per-path reference counting:
- Increment when a job is created with a snapshot path.
- Decrement when the job is processed, cancelled or removed.
- Only delete a snapshot when its reference count reaches zero and it
is no longer the cached snapshot for the project.
Also keep snapshots in the system temp directory as before, since the
location itself was not the bug.
The worker process on macOS could not see the graph snapshot when it
was placed in /var/folders/.../T (likely sandbox/working-directory
visibility). Write snapshots to a 'render-graphs' subdirectory of the
application's persistent data location instead, which both the editor
and the worker can access on all platforms.
- Report QFile::errorString() when ProjectSerializer::Load() fails to open
the graph file, so the worker log shows why instead of an empty detail.
- In olive-render-worker LoadGraph(), check file existence, size and
readability before delegating to the serializer and log the attempt.
- Update CI badge URL to OakVideoEditorCommunity in README.
- Remove outdated TODO files, vcpkg.json, patch file and stale log.
- Clean up obsolete docs and move ofx-pluginrenderer-functions-zh.md
into docs/zh/.
- Make olive-editor depend on olive-render-worker on all platforms so the
worker is always built with the main app.
- Copy olive-render-worker and dynamic render backends (oakgl, oakvulkan)
into the macOS app bundle next to the executable.
- Include oakgl.dll and oakvulkan.dll in the Windows NSIS installer.
- Fix PluginSmokeThread.ConcurrentParamAccess CI failure by adding a mutex
around IntegerInstance's no-node fallback storage and serializing the
test's set/get pair.
- Hide the render worker's Dock icon on macOS via
NSApplicationActivationPolicyProhibited.