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.
- 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
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)
- NodeFactory's menu creation moves to UI-side widget/menu/factorymenu
(the factory only exposes its node library read-only now)
- DiskManager's cache-settings dialog is created through a registered
std::function handler (registered by Core at startup)
- OlivePluginInstance creates progress UIs through a
PluginProgressReporter interface (Null fallback headless) and queries
the active viewer through a provider callback, both registered by Core
- factory.h, diskmanager and pluginSupport no longer reference any
widget//dialog//panel//window headers or classes
EngineCore (new app/coreengine.{h,cpp}) owns every engine-safe part of
the old Core singleton: CoreParams, lifecycle of the engine managers,
UndoStack, tool/snapping/timecode state, locale, autorecovery, recent
projects, footage filters, clipboard, project registry, type
declarations, and the proxy toggle. UI dependencies are inverted
through hooks instead: status-bar/cache-full signals and std::function
handlers for image-sequence confirmation, footage relink, OTIO import,
project save/close and layout load (same pattern as
Config::ErrorHandler).
Core (app/) now derives from EngineCore and keeps only UI behavior:
the main window, dialogs, panel heuristics, import/export flows and
project lifecycle presentation. Its public API is unchanged (all
inherited), and Core::instance() covariantly static_casts the engine
singleton. The render worker constructs EngineCore directly, making it
the first binary that no longer needs the UI side of Core.
~25 engine call sites move from core.h to coreengine.h; a dozen more
drop a vestigial core.h include (gaining direct includes for symbols
they were borrowing transitively). Full gtest suite green (1986 tests,
0 failures).
- slider DisplayType enums sink to node/sliderdisplaytype.h (canonical
engine home); FloatSlider/RationalSlider alias them for compatibility
- DropWithoutSequenceBehavior enum sinks to common/dropworkflowbehavior.h
- Config errors now go through a registered ErrorHandler hook instead of
QMessageBox with a MainWindow parent; the style default no longer
depends on the UI style manager
- MainWindowLayoutInfo moves to node/project/serializer/ and its panel
dependency is reduced to a plain std::map alias (PanelLayoutInfo),
breaking the engine -> PanelWidget -> KDDockWidgets chain
- ProjectImportErrorDialog moves from task/ to dialog/projectimport/
- remove confirmed-redundant UI includes and give project.h/import.h/
project.cpp the direct includes they were borrowing transitively
- project.h includes folder/sequence headers directly (it used both
types in its own API all along)
liboakcore is now a shared library that exposes only a C ABI:
- every value class (Rational, TimeRange, Color, Bezier, AudioParams,
SampleBuffer) and the free-function groups (StringUtils, fraction
utils, Timecode) is wrapped in an opaque-handle C API under
core/include/olive/core/oakcore/ (init/copy/free + self-first
functions), implemented in core/src/capi/
- consumers keep the original C++ API unchanged through same-name
wrapper classes that hold the handle and forward across the C
boundary; original implementations moved to core/src/oliveimpl
(namespace olive::core::internal) and are hidden from export
- TimeRangeList/TimeRangeListFrameIterator are reimplemented inline
over the wrapper (iterators/containers don't cross C ABI)
- generic Value container stays internal (unused by consumers) and is
no longer part of the public umbrella header
- hidden visibility + OAKCORE_BUILD export macro; nm shows zero
olive::* symbols exported
- install into the platform's standard libdir (GNUInstallDirs);
Windows DLLs next to the executables, macOS into the app bundle
- TimelineWorkArea::in/out/length now return by value: the wrapped
TimeRange getters return values, and forwarding them through const
references dangled (found via RenderWorkerFootageTest crash)
- tests: 9 new pure C ABI test executables (oakcore_*_test) covering
every public C function; 4 stale legacy core tests removed (they
targeted a long-renamed API and were never built due to a malformed
option() that also kept OLIVECORE_BUILD_TESTS off)
- CI/CD: oakcore.dll staged for NSIS, liboakcore.so added to the
AppImage validation list, build-tree DLL copies on Windows
The load/save of the per-footage custom divider attribute was lost when
the proxy changes were re-applied during the three-way commit split;
ProxyManager.FootagePersistsCustomProxyParams caught it.
- oak-render-worker now builds from worker/ (own CMakeLists.txt) as a
peer of app/; RenderWorkerPool resolves the new build-tree location
- deduplicated the Linux install() rules for the worker
- worker-spawning tests resolve build/worker instead of build/app
- cd.yml: Windows staging copies the worker from its new output path
- media color primaries/transfer tags now flow from the FFmpeg probe
through VideoParams into Footage::get_colorspace_to_use(); precedence
is user override > media tags > project default
- export nclc tags derive from the output colorspace (PQ/HLG/BT.2020,
P3, sRGB, Rec.601, Rec.709) instead of hardcoded BT.709
- new OCIO Color Grading (Log) node (lift/gamma/gain) and White Balance
node (kelvin temperature + tint, HDR-safe)
- LUT whitelist extended to 9 OCIO-supported formats
- waveform scope gains an RGB parade mode (GPU and software paths)
- tests updated for the new colorspace precedence
- playback timer uses the audio output device as its master clock: the
PortAudio callback counts consumed frames (including underrun
zero-fill) so video cannot drift away from what is heard; wall clock
remains as fallback when no clocked output is running
- output clock compensates for device output latency; new Preferences >
Audio buffer size setting (0 = auto)
- SampleBuffer::speed() now uses linear interpolation instead of
nearest-neighbor sampling
- regression tests: audio-clock driven timer (fwd/rev/speed), wall
clock fallback, interpolation correctness
- FootageJob::should_use_proxy() centralizes the proxy decision; worker
pre-decode now honors the render mode so exports always decode the
original media (previously every frame was pre-decoded from proxies)
- global Tools > Use Proxy Media toggle with footage invalidation
- ffmpeg -progress parsing for real percentage feedback while generating
- divider mode (1/2, 1/4, 1/8 of source resolution) with UI, proxy
filename tags and per-footage persistence (pdivider)
- Media Offline warning slat rendered for missing footage
- regression tests: export isolation, relink invalidation, offline slat,
progress parsing, divider arguments
- 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.
- 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
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.
- 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.
- 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
- 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.
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.