Commit Graph
100 Commits
Author SHA1 Message Date
Mike-Solar 9957577cb9 ffmpeg_bridge: fix cross-platform CI failures (Ubuntu/macOS/Windows)
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.
2026-07-16 08:35:19 +08:00
Mike-Solar c203ce1dbf fix: black preview when footage connects directly to a clip
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.
2026-07-16 01:06:24 +08:00
Mike-Solar e1d3019659 tests: migrate gtest suite to the ffmpeg_bridge API and add bridge coverage
- 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)
2026-07-15 23:00:13 +08:00
Mike-Solar 3a3ead6fc0 app: route all FFmpeg access through the ffmpeg_bridge C API
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.
2026-07-15 23:00:06 +08:00
Mike-Solar ddb2b30cf4 ffmpeg_bridge: new shared library isolating all FFmpeg access behind a pure C API
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().
2026-07-15 22:07:57 +08:00
Mike-Solar 0f41620a0b core: remove direct FFmpeg dependency from libolivecore
- 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).
2026-07-15 21:55:11 +08:00
Mike-Solar 3be4294e15 Fix Linux audio backend preference and worker footage render crash
- 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.
2026-07-14 22:33:30 +08:00
Mike-Solar 5c8ce17c7e submodule: change core into nomarl folder, and move KDockWidgets into third_party. 2026-07-14 15:14:29 +08:00
Mike-Solar e37a87be67 docs: Update test plan. 2026-07-13 22:01:13 +08:00
Mike-Solar 93d92c93db Merge remote-tracking branch 'origin/main' 2026-07-13 18:47:33 +08:00
Mike-Solar 8b431c941e docs: Update README.md download link 2026-07-13 18:46:54 +08:00
Mike-Solar b4b52abbd4 Remove noisy graph-snapshot logs and add waveform-sync diagnostics
- 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).
2026-07-13 18:05:02 +08:00
Mike-Solar 7cde0b0f1a Fix invalid channel layout (0x0) in audio processor and add playback diagnostics
- 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.
2026-07-13 17:46:15 +08:00
Mike-Solar c6310d104d Prefer PipeWire/PulseAudio over raw ALSA/JACK default on Linux audio devices 2026-07-13 17:24:26 +08:00
Mike-Solar e484af9a1b Merge remote-tracking branch 'origin/main' 2026-07-13 17:05:22 +08:00
Mike-Solar 536d664cbb ui+ci: style KDDockWidgets tabs and add Applications symlink to DMG
- 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.
2026-07-13 16:39:49 +08:00
Mike-Solar 36f48a6a4b ci(macos): skip Qt libs in custom bundler and process all binaries
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.
2026-07-13 16:17:02 +08:00
Mike-Solar 035b460c2d ci(macos): make macdeployqt verbose and verify platform plugins
- 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.
2026-07-13 16:12:54 +08:00
Mike-Solar a4a932238c fix(macOS): disable KDDockWidgets QtQuick frontend to resolve launch crash
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.
2026-07-13 15:54:55 +08:00
Mike-Solar e16facf123 fix: restore Qt translation files to valid XML
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.
2026-07-13 15:35:36 +08:00
Mike-Solar f03b6b58ff style: reformat C/C++ sources and headers with clang-format
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.
2026-07-13 15:34:11 +08:00
Mike-Solar 6a696bc234 Revert "docs: update README binary link to v0.4.0-alpha"
This reverts commit 7522543c48.
2026-07-13 15:31:35 +08:00
Mike-Solar e5eeaddf2f format: reformatting files 2026-07-13 15:30:43 +08:00
Mike-Solar 7522543c48 docs: update README binary link to v0.4.0-alpha 2026-07-13 15:29:13 +08:00
Mike-Solar 64abc9cc76 docs: update CONTRIBUTING.md to Linux Kernel Coding Style
- 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.
2026-07-13 15:27:57 +08:00
Mike-Solar f0d41eae52 build: rebrand output artifacts to Oak and fix macOS QML deployment
- 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.
2026-07-13 15:26:15 +08:00
Mike-Solar a4a2903e49 ci: Update CI/CD to use 32x runner for Windows. 2026-07-13 15:10:48 +08:00
Mike-Solar c31415e847 build: drop lib prefix from oakgl/oakvulkan DLLs on Windows
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.
2026-07-13 14:56:40 +08:00
Mike-Solar 8fa32f5df4 cd: switch RPM build back to job container with pre-checkout git install
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.
2026-07-13 14:54:35 +08:00
Mike-Solar c786469a58 cd: fix Arch tarball creation and Fedora container checkout
- 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.
2026-07-13 14:52:19 +08:00
Mike-Solar 2f0beeb7da cd: add DEB, RPM and Arch Linux packages
- 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.
2026-07-13 14:47:46 +08:00
Mike-Solar 847abbfd5c ci: Update CI/CD to use 32x runner for Windows. 2026-07-13 14:39:30 +08:00
Mike-Solar 2e1c40af63 test: use olive-render-worker.exe on Windows in render worker footage test
QFileInfo::exists() does not auto-append .exe on Windows, so the test
failed to locate the worker binary even though it was built.
2026-07-13 14:34:05 +08:00
Mike-Solar 2a11988b77 dpcs: Update build instructions 2026-07-13 14:32:17 +08:00
Mike-Solar 43773c430a docs: update build guides for official macOS support and sync Fedora deps
- 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.
2026-07-13 14:29:14 +08:00
Mike-Solar e797914c62 Edit README 2026-07-13 14:25:23 +08:00
Mike-Solar 8f3c06555f test: skip worker/backend tests when GPU backend unavailable and fix cross-platform path test
- 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.
2026-07-13 14:25:02 +08:00
Mike-Solar a5c98e1297 Edit README 2026-07-13 14:23:05 +08:00
Mike-Solar 288e9050e1 fix: guard SIGPIPE ignore with #ifndef _WIN32
SIGPIPE is not available on Windows (MSYS2 UCRT64), causing a build
failure. The signal handling is only needed on POSIX systems.
2026-07-13 14:03:26 +08:00
Mike-Solar ea78558a31 render: reference-count graph snapshots to prevent premature deletion
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.
2026-07-13 13:43:20 +08:00
Mike-Solar 8580542ac7 render: write graph snapshots to AppLocalData instead of system temp
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.
2026-07-13 13:29:55 +08:00
Mike-Solar 5a5ad3bc14 render: add diagnostics for worker graph load failures
- 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.
2026-07-13 13:25:25 +08:00
Mike-Solar 3abb4ff75b docs: update README and remove obsolete documentation/files
- 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/.
2026-07-13 13:14:37 +08:00
Mike-Solar 182bfd0aad build: bundle render worker/backends and fix concurrent param test
- 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.
2026-07-13 13:12:28 +08:00
Mike-Solar 4f23051b2b Fix waveform sync using stale selection and add Ctrl+Shift+W shortcut 2026-07-13 11:09:32 +08:00
Mike-Solar f3389fe6ba Allow waveform sync with partially cached audio and add regression tests 2026-07-13 10:54:12 +08:00
Mike-Solar 0410ff2fe7 Skip GPU-dependent tests when Vulkan/OpenGL backend is unavailable in CI 2026-07-13 10:44:27 +08:00
Mike-Solar e881bb049f Remove some logs 2026-07-13 10:29:02 +08:00
Mike-Solar 942ffd1635 test: extend coverage for file/qt/xml utilities and core audio/math
Add/extend tests for:
- FileFunctions: config/temp paths, autorecovery, directory overwrite/copy
- QtUtils: word wrap, formatted date/time, qHash for rational/TimeRange
- XMLUtils: cancel atom integration
- Core rational: construction, arithmetic, comparisons, NaN handling
- Core SampleBuffer: allocation, channel ops, volume/silence/reverse/speed

Coverage improvements:
- filefunctions.cpp: 74% -> 90%
- qtutils.cpp: 41% -> 73%
- xmlutils.cpp: 88% -> 100%
- rational.cpp: 74% -> 92%
- samplebuffer.cpp: 55% -> 96%
2026-07-13 10:19:30 +08:00
Mike-Solar c57608b8f8 test: expand gtest coverage for common/core utilities
Add unit tests for:
- CommandLineParser, Debug handler, Decibel, Digit, JobTime, FFmpegUtils
- Core StringUtils, Timecode, TimeRange, Bezier, Color
- Extend Frame tests to cover pixel access, interlace, conversion

Coverage improvements (app + ext/core):
- commandlineparser.cpp: 0% -> 93%
- debug.cpp: 0% -> 85%
- decibel.h: 25% -> 88%
- digit.h: 0% -> 100%
- ffmpegutils.cpp: 29% -> 90%
- frame.cpp: 53% -> 97%
- bezier.cpp: 16% -> 98%
- color.cpp: 13% -> 71%
- Overall line coverage: 17% -> 19%
2026-07-13 10:19:30 +08:00
Mike-Solar 748c53ac5c Expand Google Test coverage across core subsystems
Add and extend unit tests for:
- Task manager (failed tasks, progress signals, multiple tasks)
- Undo stack (multiple undo/redo, push-after-undo, clear, multi-command)
- Config defaults and graphics backend string conversion
- Node keyframe serialization and default state
- Node value string round-trips and table operations
- Node globals and project settings/serialization
- Timeline coordinate, workarea, and marker commands
- Color LUT processor and OCIO transform handling
- Common utilities (range, file functions, Qt helpers, XML utils)
- Render enums (loop mode, alpha association)
- Shader resource availability
- Module smoke tests for Tool and HumanStrings

All tests pass including RenderWorkerFootage GPU worker tests after
rebuilding the stale olive-render-worker binary.
2026-07-13 10:19:30 +08:00
Mike-Solar b7ff687220 Expand gtest coverage across recently changed modules
Adds ~60 new unit tests covering the modules touched in recent bug-fix
passes and UI refactors:

- RenderTicket: result/finish-count semantics, empty watcher defaults,
  ticket-reuse rejection
- Preferences tabs: behavior options migrated into General/Audio tabs,
  translation context helper, audio-tab initialization
- NodeView: context management, 'Show in Parameter Editor' action wiring
- PreviewAutoCacher: construction, pause toggles, single-frame render
  cancellation, force-cache range
- SeekableWidget/TimeBasedWidget: scroll, marker editing, context reset
- OCIOLutNode: empty/missing/unsupported LUT paths, string direction
  values, file-switching regression coverage
- ProxyManager: working filename, disabled state, empty proxy fields
- Frame/VideoParams/AudioParams/Timecode/ExportFormat/ExportCodec:
  additional edge-case and enumeration coverage

Also fixes the PreferencesAudioTab test crash by initializing
AudioManager in the offscreen test environment.

All tests pass: ctest --output-on-failure
2026-07-13 10:19:30 +08:00
Mike-Solar b47887e127 Defer parameter editor panel raise/focus to queued invocation
KDDockWidgets may need an event-loop iteration to finish opening or
tabifying a dock widget before raise() can switch to it. Defer raise(),
activateWindow(), and setFocus() via QMetaObject::invokeMethod with
Qt::QueuedConnection after show().
2026-07-13 10:19:30 +08:00
Mike-Solar 21ae1125f5 Activate parameter editor panel when jumping from node view
Call setAsCurrentTab() and activateWindow() on the ParamPanel dock widget
in ShowSelectedNodeInParamEditor so the parameter editor becomes the active
tab/window when invoked from the node view context menu or Shift+P shortcut.
2026-07-13 10:19:30 +08:00
Mike-Solar dba76ea9f2 Switch to the correct dock/tab when jumping to parameter editor
In NodeParamView::SetSelectedNodes, after locating the target node item,
expand and raise its containing NodeParamViewContext dock widget so the
active tab/folded section switches to the one that actually holds the node.
2026-07-13 10:19:30 +08:00
Mike-Solar a7fdab607d Fix node context-menu jump to parameter editor and drag-release crash
- Ensure right-clicking a node that isn't currently selected makes it the
  sole selection before showing the context menu, so 'Show in Parameter Editor'
  operates on the clicked node.
- Emit all selected node/context pairs when jumping to the parameter editor.
- Guard dragging_items_ iteration in mouseReleaseEvent against deleted items
  by snapshotting keys as QPointer and skipping null entries.
2026-07-13 10:19:30 +08:00
Mike-Solar 5c8f80480d UI: flatten preferences behavior tabs and add node parameter editor shortcut
- Move Behavior-General options (hover focus, slider ladder, scroll zooms)
  into the General preferences tab.
- Move Behavior-Audio option (audio scrubbing) into the Audio preferences tab.
- Promote remaining Behavior categories (Timeline, Playback, Project, Nodes,
  Rendering) to top-level sidebar entries without the 'Behavior - ' prefix.
- Update Chinese translations for the new sidebar titles and fill unfinished
  Behavior tab strings.

NodeView:
- Remove double-click jump-to-parameter-editor behavior; keep expand/collapse.
- Add right-click context menu item 'Show in Parameter Editor'.
- Add Shift+P shortcut bound to the same action.

Render:
- Fix crash in PreviewAutoCacher::ClearSingleFrameRenders when proxy playback
  causes a render ticket to finish synchronously before the watcher pointer is
  returned. Defer the watcher Finished signal via queued connection so the
  caller can safely register the watcher before it is deleted.
2026-07-13 10:19:30 +08:00
Mike-Solar f57ffa482f Flatten Behavior preferences into sidebar categories and scroll to node on double-click
Behavior preferences used a nested QTreeWidget with groups like General,
Audio, Timeline, etc. Replace it with one tab per group in the left sidebar
so users can jump directly to a category without expanding a secondary menu.

Also wire double-clicking a node in the node graph to emit a selection
signal with its context, causing the parameter panel to scroll to that
node's parameters (in addition to showing/raising the panel as before).
2026-07-13 10:19:30 +08:00
Mike-Solar 1f446f55b4 Cache color processors in render worker and canonicalize OCIO roles
ColorProcessor creation was done for every exported frame and the input
reference space was passed as a role string (e.g. "scene_linear"). If
getProcessor() rejected the role name the worker would crash, the pool
would retry the frame, and export throughput would drop to near zero with
minimal CPU/GPU usage.

- Canonicalize role names to colorspace names in ColorProcessor so
  "scene_linear" resolves to the config's actual colorspace.
- Wrap processor creation in a try/catch and initialize cpu_processor_ to
  nullptr on failure instead of dereferencing a null processor.
- Cache created ColorProcessor objects in the worker keyed by transform
  so OCIO processor/shader setup is only paid once per export.
2026-07-13 10:19:30 +08:00
Mike-Solar b3c124fe7a Default viewer display transform to config display/view instead of input colorspace
When no explicit color transform was set, ManagedDisplayWidget fell back to
the project's default input colorspace. For scene-referred reference spaces
like ACEScg / Linear this produced a raw, greenish image on the viewer
instead of a monitor-ready picture. Default to the OCIO config's default
display and view so the viewer looks correct out of the box, while still
honoring any Color Space / Display / View choice the user makes from the
context menu.
2026-07-13 10:19:30 +08:00
Mike-Solar 69cbe37f4f Update project file reference with bilingual Chinese/English docs
Expand docs/project-file-reference.md to cover the full .ove format
implemented in the current codebase, including:

- Root document and project container structure
- Project data: uuid, plugins, nodes, settings
- Detailed Node serialization (inputs, keyframes, connections, hints,
  context, caches)
- Node-specific custom data: Footage proxy/sourcestarttime, ViewerOutput
  workarea/markers, Track height, NodeGroup passthroughs
- VideoParams and AudioParams field lists
- MainWindowLayoutInfo layout block
- Partial documents (markers, keyframes, nodes/timeline)
- Versioning and OpenFX notes

All sections are provided in English with Chinese translations.
2026-07-13 10:19:30 +08:00
Mike-Solar d66779d281 Apply export color transform in render worker
The export color transform was passed to RenderTask as a ColorProcessor,
but the worker IPC render_frame message never carried it. The worker
always set the ticket's coloroutput to null, so frames were returned in
the project's reference space and encoded without the chosen output
transform (e.g. Rec.709 / sRGB), causing the exported video to look
wrongly tinted.

Serialize the ColorTransform through the render_frame control message
and reconstruct the ColorProcessor on the worker side before rendering.
Also expose ColorTransform as a Qt metatype so it can be stored in a
ticket QVariant.
2026-07-13 10:19:30 +08:00
Mike-Solar 43599dba9e Add batch proxy menu to Project Explorer and persist Use Proxy state
The Project Explorer previously only had an old Pre-Cache submenu that
required choosing a sequence. Replace it with the same Proxy menu that
the timeline uses:

- Generate Proxy: batch-generates proxies for all selected footage items
  that have an enabled video stream, using ProxyManager and the current
  proxy settings.
- Use Proxy: checkable toggle that enables/disables proxy playback for the
  selected footage.
- Reveal Proxy / Delete Proxy: consistent with the timeline context menu.

Also make Footage::SaveCustom() write the <proxy> element whenever
proxy_enabled_ is true, even if proxy_path_ is empty, and load it back
in LoadCustom(). This preserves the user's Use Proxy preference across
project saves so it is restored on the next open.

Translation files updated with lupdate; zh_CN and zh_TW translations for
the new Project Explorer strings (and the matching timeline strings) are
filled in.
2026-07-13 10:19:30 +08:00
Mike-Solar ea37dc5da6 Prevent main-process crash when render worker dies during IPC write
When a render-worker process crashed or exited while the worker-pool
thread was writing a control message (e.g. right after an audio-sync and
drag operation triggered a new render), the main process received SIGPIPE
and terminated at WriteControlMessage().

- Ignore SIGPIPE in main() so QProcess can report the broken pipe through
  its normal error path instead of killing the application.
- Harden WriteControlMessage() / TryWriteControlMessage() to check the
  process state and the number of bytes written before waiting.
2026-07-13 10:19:30 +08:00
Mike-Solar 01f8be7267 Fix crash when closing export dialog while export is running
TaskDialog::closeEvent() was calling deleteLater() immediately when the
user closed the window. The ExportTask was a child QObject of the dialog,
so it got destroyed while its Run() method was still executing in the
worker thread, leading to a use-after-free on encoder_->Close().

Defer deletion until the QFutureWatcher reports that the task has actually
finished. A new task_finished_ flag tracks this so closeEvent only deletes
when it is safe, and TaskFinished() closes the dialog (which now deletes
instead of calling close() again while the task is still alive).
2026-07-13 10:19:30 +08:00
Mike-Solar 8d3bde5a50 Implement software scope rendering for backend-neutral display paths
The Waveform, Vectorscope and Histogram panels were black when running on
the Vulkan / backend-neutral render path because ScopeBase::OnPaint()
returned early with a TODO. The viewer emits GPU textures that live in the
viewer's own renderer; on Vulkan each ManagedDisplayWidget has a separate
device, so those textures cannot be sampled directly.

Fix by:

- Adding a backend-neutral branch in ScopeBase::OnPaint() that downloads the
  reference texture (cross-renderer if needed), color-manages it into a U8
  offscreen texture, downloads it to a QImage, and draws via QPainter.
- Adding a pure virtual DrawScopeSoftware() to ScopeBase and implementing it
  for WaveformScope, VectorscopeScope and HistogramScope.
- Fixing the managed_tex_up_to_date_ flag that was never set to true in the
  OpenGL path, which caused the color-managed scope texture to be recreated
  on every paint.

All gtest suites pass.
2026-07-13 10:19:30 +08:00
Mike-Solar f0119a910c Enable video auto-cache by default
- Default ViewerOutput::autocache_input_video_ to true so new sequences
  and viewer outputs use the frame cache without requiring the user to
  manually enable Auto-Cache.
- Update the DefaultSequenceAutoCache2 config default to true for
  consistency.
2026-07-13 10:19:30 +08:00
Mike-Solar 468b6f1d1b Fix playback backup timer interval and waveform-only prequeue
- The playback backup timer was using timebase_dbl() in seconds as its
  QTimer interval, which truncated to 0 ms for normal frame rates. This
  caused the timer to fire continuously and starve the event loop when
  the video display was hidden, e.g. in Show Waveform Only mode, freezing
  the UI and stopping the playhead. Multiply by 1000 to use milliseconds.
- If PlayInternal has neither video nor audio to prequeue, call
  FinishPlayPreprocess immediately so the backup timer starts and the
  playhead advances even in waveform-only mode with no audio.
2026-07-13 10:19:30 +08:00
Mike-Solar 094f4a8e44 Fix audio waveform view scene rect and timebase refresh
- AudioWaveformView now uses the video frame rate as its timebase
  (falling back to the default sequence frame rate), preventing the
  enormous scene rect that froze the UI when it previously used the
  audio sample rate.
- Call UpdateSceneRect() after changing the waveform timebase so the
  QGraphicsScene bounds are recomputed immediately.
- Refresh the waveform view's viewer/timebase whenever the waveform
  visibility mode changes, ensuring the correct timebase is applied
  even if the node was connected earlier with a different setting.
2026-07-13 10:19:30 +08:00
Mike-Solar 282a06b777 Default to full-resolution preview and fix audio-waveform freeze
- Use a divider of 1 (full resolution) for new sequences and for
  footage-derived viewer parameters instead of the auto-downscaling
  heuristic, so the viewer defaults to full-res preview.
- In AudioWaveformView, base the view timebase on the video frame rate
  when video is present; fall back to the audio sample rate only for
  audio-only sources. Using the audio sample rate as the view timebase
  created an enormous scene rect (time * sample_rate * scale), which
  froze the waveform view and stalled playback updates when showing
  the audio waveform.
2026-07-13 10:19:30 +08:00
Mike-Solar 51ea3ca24e Use 10-bit packed preview format and add OCIO LUT regression tests
Renderer / preview:
- Switch the preview/display readback format from F16 to packed
  10-bit RGBA (PixelFormat::U10) to halve GPU->CPU/IPC bandwidth
  while keeping 10-bit panel precision.
- Add U10 support to VideoParams, FFmpeg/OIIO/OCIO utility mappings,
  OpenGL (GL_RGB10_A2), Vulkan (VK_FORMAT_A2B10G10R10_UNORM_PACK32),
  and plugin bit-depth lookups.
- Update preview autocacher comment to reflect the new behavior.

OCIO LUT tests:
- Add four E2E-style ColorLutNode gtests that drive a SolidGenerator
  -> OCIOLutNode graph through NodeTraverser and compare resulting
  pixels on the CPU. They cover forward/inverse transforms and verify
  that switching LUT direction and LUT file updates both the processor
  and the output pixels.

Cleanup:
- Remove a leftover SolidGenerator::Value debug fprintf.
- Capture render worker stderr in RenderWorkerFootageTest for better
  diagnostics.
2026-07-13 10:19:30 +08:00
Mike-Solar a7f7890db0 Use F16 for preview frames to halve GPU-to-CPU readback bandwidth 2026-07-13 10:19:30 +08:00
Mike-Solar e8443f8f8f Fix crash when toggling Use Proxy during playback and proxy generation output format 2026-07-13 10:19:30 +08:00
Mike-Solar 0969e2adc5 Add diagnostics for proxy toggle synchronization
Add temporary qDebug logging to Footage, ProjectCopier,
RenderWorkerPool, and TimelineWidget to trace why Use Proxy cannot be
 toggled and why proxy footage may still be used after disabling.
2026-07-13 10:19:30 +08:00
Mike-Solar 9bb63ca576 Update translation files with new proxy strings
Run lupdate to pull the latest source strings (including new proxy
settings/menu text) into all .ts files. Translations for the new strings
are marked unfinished and can be filled in by translators.
2026-07-13 10:19:30 +08:00
Mike-Solar cf32fcfee1 Add configurable proxy settings and a menu entry
- Extend ProxyManager::ProxyParams with crf and preset, and update
  ProxyTask/ProxyParamsEqual to use them.
- Add global config keys ProxyWidth, ProxyHeight, ProxyCRF, ProxyPreset.
- Add a Proxy Settings group to Preferences -> Disk with width/height/CRF/preset
  controls.
- Read proxy settings from config when generating proxies from the timeline.
- Add Tools -> Proxy Settings... menu entry that opens Preferences on the Disk tab.
- Add ConfigDialogBase::SetCurrentTab() so PreferencesDialog can start on a
  specific tab.
2026-07-13 10:19:30 +08:00
Mike-Solar 818b76b64e Sync Footage proxy state to render worker copy project
Proxy state (enabled/path/state/etc.) is stored as Footage member data,
not as a Node input, so ProjectCopier did not synchronize it to the
internal copy project used by worker processes. This caused toggling
Use Proxy in the UI to have no effect on rendered output.

Add a Footage::ProxySettingsChanged signal and have ProjectCopier sync
the proxy state to the copied Footage node, marking the copy project
modified so RenderWorkerPool writes a fresh graph snapshot.
2026-07-13 10:19:30 +08:00
Mike-Solar 279fb2f442 Fix proxy generation by explicitly setting ffmpeg output format
ffmpeg could not infer the container format because ProxyManager writes
proxies to a temporary '.mp4.working' file. Explicitly pass '-f mp4' (or
the configured proxy extension) so ffmpeg knows the output format
regardless of the temporary extension.
2026-07-13 10:19:30 +08:00
Mike-Solar 94250a7690 Fix proxy decoding path and add proxy generation diagnostics
- RenderWorkerPool::DecodeInputFrame now uses the proxy filename/decoder/stream
  from FootageJob when a proxy is attached, so generated proxies are actually
  used during render.
- Add qDebug/qWarning logging to TimelineWidget::GenerateProxiesForSelectedClips
  and ProxyTask::Run to diagnose why Generate Proxy appears to do nothing.
2026-07-13 10:19:30 +08:00
Mike-Solar e52570c89f Add diagnostics and robust direction reading for OCIO LUT node
- Introduce ReadDirectionInput() helper that accepts both integer and
  string ('Forward'/'Inverse') representations of the direction combo.
- Log every processor creation with the raw direction value and the OCIO
  transform direction being used, along with whether it is main or worker.
- This helps determine why Forward/Inverse switching reportedly has no
  effect in the viewer/worker.

Tests still pass.
2026-07-13 10:19:30 +08:00
Mike-Solar f131abc5f7 Fix OCIOLutNode direction changes being ignored
EnsureProcessor() was returning early whenever a processor existed and
processor_dirty_ was false, without checking whether the file or direction
input had changed. As a result, switching Forward/Inverse in the main GUI
re-used the old processor and produced no visible difference.

Now EnsureProcessor() also compares the current inputs against the values
used to create last_processor_, so direction/file changes always trigger a
regeneration before the color transform job is emitted.
2026-07-13 10:19:30 +08:00
Mike-Solar ca22028a52 Defer OCIO LUT processor generation in render worker and fix stale graph snapshots
- Override OCIOLutNode::Value() to ensure the processor is created/updated
  before the color transform job is emitted.
- In the worker process, mark the processor dirty on input/config changes
  instead of creating it synchronously during LoadGraph, which blocked the
  main process waiting for the graph load acknowledgement.
- Keep eager processor generation in the main GUI process so the viewer
  cache is invalidated immediately when the LUT file or direction changes.
- Mark the ProjectCopier's internal render-proxy project as modified when
  its update queue is processed, and reset the flag after RenderWorkerPool
  writes a new graph snapshot. This fixes the worker loading a stale graph
  snapshot after switching cube files or direction, which caused the old
  LUT effect to persist.

All existing tests pass.
2026-07-13 10:19:30 +08:00
Mike-Solar a753d2f787 Always recreate LUT processor when input changes (debug)
Temporarily disable the last_path_/last_direction_ reuse check to rule
out stale processor reuse as the cause of LUT changes not appearing in
the viewer.
2026-07-13 10:19:30 +08:00
Mike-Solar 632ef5b495 Invalidate cache before cancelling tasks on LUT change
Reversing the order ensures that any render tasks that finish after the
cancel request will find the cache already invalidated, preventing stale
LUT-processed frames from being written back and shown in the viewer.
2026-07-13 10:19:30 +08:00
Mike-Solar fb50b5202e Do not block while cancelling video cache tasks on LUT change 2026-07-13 10:19:30 +08:00
Mike-Solar a4aaf10357 Use QApplication presence to detect main GUI process for LUT refresh
Checking applicationName was not reliable. The worker process uses
QGuiApplication while the main process uses QApplication, so use
qobject_cast<QApplication*> to determine whether it is safe to access
RenderManager/PreviewAutoCacher. This prevents the worker from crashing
when loading or switching LUT files.
2026-07-13 10:19:30 +08:00
Mike-Solar d83569c08e Fix worker crash when loading LUT node
The LUT processor generation was calling RenderManager::GetCacher()
during project load in the render worker, but the worker has no
RenderManager/PreviewAutoCacher. This caused a SEGV and made the main
process hang waiting for the worker. Only cancel/invalidate caches in
the main GUI process.
2026-07-13 10:19:30 +08:00
Mike-Solar 9185b6fd60 Auto-refresh viewer after LUT change without freezing
After synchronously generating the new LUT processor, cancel any
running background video cache jobs before invalidating the cache.
This prevents the preview autocacher from re-rendering the entire
timeline, which was causing the UI to freeze, while still updating
the current visible frame with the new LUT.
2026-07-13 10:19:30 +08:00
Mike-Solar d88341f9db Revert OCIO LUT processor generation to synchronous
Background generation caused the UI to freeze indefinitely when
switching LUT files, likely due to a deadlock between the worker
process, the preview autocacher, and the asynchronous set_processor
path. Synchronous generation is fast enough for typical 33^3 .cube
files and keeps the cache invalidation logic simple and safe.

The passthrough fix in OCIOBaseNode::Value() is retained so the
viewer shows the input frame while a processor is being created.
2026-07-13 10:19:30 +08:00
Mike-Solar 8636ee0dc6 Remove automatic full-cache invalidation on LUT processor change
InvalidateAll() on the OCIO LUT node caused the preview autocacher to
re-render the entire timeline, freezing the UI when switching LUT files.
Instead, let the new processor be used naturally on the next render
request (scrubbing/playback).
2026-07-13 10:19:30 +08:00
Mike-Solar 63a4008ce4 Avoid full cache invalidation in render worker after LUT processor is ready
InvalidateAll() on the OCIO LUT node triggered the worker's
PreviewAutoCacher to re-cache the entire timeline, causing the main
process to freeze while waiting for frames. Only invalidate in the
main GUI process so the viewer refreshes; the worker will naturally
use the new processor on its next render request.
2026-07-13 10:19:30 +08:00
Mike-Solar 800859b0ef Fix OCIO LUT async processor black screen and freeze
- Pass through input texture when the LUT processor is not ready yet,
  preventing black frames while the processor is being generated.
- Serialize processor generation with a single in-flight task to avoid
  concurrent OCIO lock contention that could freeze the UI.
- Invalidate cached frames after the async processor is set so the viewer
  refreshes automatically without requiring the playhead to be moved.
- Add OAK_DISABLE_HWACCEL environment variable to force software decoding.
- Add FFmpegDecoderHW regression test for H.264 4:2:2 10-bit decoding.
2026-07-13 10:19:30 +08:00
Mike-Solar 68c0477715 Add hardware decoding support 2026-07-13 10:19:30 +08:00
Mike-Solar f6211f97a5 Fix render worker reuse and stabilize out-of-process video rendering 2026-07-13 10:19:30 +08:00
Mike-Solar 30e7153154 fix Windows and macOS symbol error 2026-07-13 10:19:30 +08:00
Mike-Solar 83424e3c79 fix Linux outdated package name 2026-07-13 10:19:30 +08:00
Mike-Solar e77de49406 add Comments 2026-07-13 10:19:30 +08:00
Mike-Solar 3c9592da45 完成Vulkan渲染后端 2026-07-13 10:19:30 +08:00
Mike-Solar cd5efa8ea4 Update dynamic backend plan to reflect Vulkan real-render progress
- Document that Vulkan now passes an end-to-end upload/blit/download test
  on real hardware.
- List recently landed fixes: shared vertex/fragment UBO, descriptor set
  layout, render pass dependencies, image layout transitions, framebuffer
  and sampler caching, grayscale swizzle, texture-enable uniforms.
- Add remaining gaps: iterative/pin-pong multi-pass Blit, 1/3-channel
  upload/download alignment, and full viewer/proxy/export smoke tests.
2026-07-13 10:19:30 +08:00
Mike-Solar e908786d43 Improve Vulkan Blit: caching, interpolation, swizzle, enable flags
- Cache a VkFramebuffer per texture to avoid creating/destroying one for
  every Blit() call.
- Create and cache both linear and nearest samplers; honor
  Texture::Interpolation in descriptor writes.
- Add single-channel image-view component swizzle (R -> RGB, A = 1) to
  match OpenGL grayscale behavior.
- Set texture-enable uniforms (NAME_enabled) when shaders declare them.
- Guard Blit() with null destination and emit a clear warning instead of
  recording an invalid render pass.

ctest passes 4/4 in both dynamic-backend ON and OFF builds.
2026-07-13 10:19:30 +08:00