49 Commits
Author SHA1 Message Date
Jonathan Thomas 54ada2010d Fix Codacy findings in Timeline tests 2026-09-26 21:23:40 -05:00
Jonathan Thomas a2e1297aa4 Fix preview alignment, export timestamps, and capture color range
Align reduced preview dimensions after aspect fitting while preserving native output sizes. Pad export scaler rows and copy planes by stride, including compatibility handling for legacy GIF palettes.

Preserve delayed audio encoder timestamps, skip unavailable packets, and use actual resampler output counts. Share JPEG-range normalization with live capture and add sizing, export, audio, GIF, and color regression tests.
2026-09-25 19:43:06 -05:00
Jonathan Thomas 03b66ebe92 Fix transition routing and stacking for clips with tied start times
Preserve clip order for equal layer and position values, and route video and audio transitions according to compositing order. Cover fade directions, rounded start frames, audio gains, and JSON order preservation.
2026-09-25 12:10:59 -05:00
Jonathan Thomas b590640fba Speed up JSON keyframe updates and add focused transform benchmarks
Interactive transform edits repeatedly replace growing X/Y keyframe curves.
Applying each update copied the same JSON data through several layers and
rebuilt keyframe storage from scratch, making edits increasingly expensive
as an animation accumulated points.

Move JSON values that are already owned by the loader through the existing
by-value interfaces instead of copying them again. Clip and effect loading
now share a base-field helper that reads common fields without copying all
of the derived animation data. Parsed SetJson input and nested color/point
values use the same ownership approach.

Keep allocated keyframe capacity across replacements and append sorted
points directly. Unordered input and duplicate frames still use AddPoint,
preserving ordering and replacement behavior. Existing public signatures,
virtual method layout, JSON format, and timeline locking remain unchanged.

Add two optional benchmark cases for a 720p/24fps image clip with 0 or 1,000
existing keyframes per axis. Each performs 100 successive X/Y transform
updates through Timeline::ApplyJsonDiff and reports the median of three
trials. Input preparation stays outside the measurement, and final point
counts and values are checked so a skipped update cannot look faster.
These cases belong to the separate benchmark executable, not normal CTest
runs. Report Ops/s because an operation can now be a JSON edit or a frame.

Document the reproduction commands and local before/after measurements.
The measured cost per update fell from 0.82 to 0.33 ms for the initially
empty curves and from 17.99 to 6.73 ms for curves starting at 1,000 points.
These isolate native JSON application; they do not measure GUI rendering
or guarantee the same performance on other machines.

Add regression coverage for caller-owned JSON, sorted and unordered input,
duplicate and fractional frames, interpolation handles, large curve
replacement, multiple actions in one diff, unchanged properties, and cache
invalidation.

Validation: rebuilt the library, benchmark, and affected test executables;
all 197 KeyFrame, Timeline, Clip, Point, Color, ColorGrade, Mask, and
EffectMask tests passed in 6.2 seconds. Both remaining transform benchmark
cases also completed successfully. The slower 5,000-point benchmark case
is intentionally omitted.
2026-09-20 00:15:23 -05:00
Jonathan Thomas 1dfc3e3d2c Read clip bounds before deleting timeline-owned clips
RemoveClip frees JSON-owned clips. Cache invalidation in apply_json_to_clips previously read their position and duration after deletion. Capture bounds first.

Add a JSON insert/delete regression covering cache invalidation, unaffected frames, and repeated deletes. Valgrind reproduces four invalid reads against the installed pre-fix library and reports zero with this change. Native suite: 518 passed, two VAAPI prerequisite skips. This addresses a concrete defect in the BV93 timeline path, not every crash grouped under BV93.
2026-09-12 22:02:59 -05:00
Jonathan Thomas 732f18294e Recover stale timeline clip reader state 2026-08-18 01:00:44 -05:00
Jonathan Thomas d01ffe1ea8 Merge pull request #1077 from OpenShot/black-frames
Avoid Black Frames (Adding a new clip - and it does not appear)
2026-05-20 23:15:32 -05:00
Jonathan Thomas 195632ac86 Fix persistent black frames after clip open failure
- Only mark timeline clips as open after Clip::Open() succeeds
- Allow later GetFrame() calls to retry opening clips after transient failures
- Prevent cache rebuilds from repeatedly generating blank frames for closed clips
- Add regression coverage for a failed first open followed by a successful retry
2026-05-12 22:31:12 -05:00
Jonathan Thomas ae3d769f0a Fix stale timeline cache after adding clips
- Invalidate affected final_cache frames in AddClip()
- Remove duplicate JSON insert invalidation
- Add regression test for stale black frames
2026-05-12 18:38:45 -05:00
Jonathan Thomas 4f289bc442 Fix saturation stride and parent effect updates
- Respect QImage::bytesPerLine() in saturation and mask blending
- Preserve child effect IDs when inheriting parent effect changes
- Propagate parent updates correctly through nested child effects
- Add regression tests for padded image stride and parent effect inheritance
2026-05-12 14:22:06 -05:00
Jonathan Thomas 6ccb526cb9 Fixing GetMaxFrame() to fix tiny float overshoot, causing occasional black frames at the end of an export. For example, it might have concluded that we had 505.00001 frames, and this would then be converted later to 506 with ceil(). 2026-05-08 13:52:28 -05:00
Jonathan Thomas fec6e901ed Preserve audio-only reader dimensions on pasted clips
-Set the parent timeline before hydrating clips inserted through JSON diffs, matching full project JSON load behavior. This lets copied/pasted clips expose timeline context while their nested readers are rebuilt.
-Avoid clobbering valid stored dimensions for audio-only FFmpeg readers with the 720x480 fallback. The fallback is now only used when width or height are missing, while parent timeline preview dimensions can still override when available.
-Add regression coverage for audio-only FFmpegReader JSON hydration and timeline JSON-diff insertion preserving 1280x720 dimensions.
2026-04-30 23:45:14 -05:00
Jonathan Thomas 19eee97f42 Fixing unit test related to verifying clip cache, which obviously is disabled now. 2026-03-24 17:55:48 -05:00
Jonathan Thomas bad0ee5f5f Slower, but safer memory access to Clip image - to prevent mutations from corrupting cached frame pixels 2026-03-24 15:37:50 -05:00
Jonathan Thomas 3502813f2a - Added Fade Audio to Mask transitions.
- Timeline now fades audio based on transition placement.
- Added tests and enabled it for auto overlap transitions in openshot-qt.
2026-03-12 23:01:18 -05:00
Jonathan Thomas 579d38e20b - Unified mask trim on canonical start / end.
- Kept legacy mask_start / mask_end JSON loading.
- Fixed global mask start being applied twice.
- Defaulted mask timing to Source FPS.
- Made mask readers honor split-file reader start / end.
- Added tests for all of the above.
2026-03-09 14:03:54 -05:00
Jonathan Thomas 74abca2b4f - Playback stays fast because frames are not copied in the normal path.
- Right after edits (ApplyJsonDiff), rendering temporarily switches to safe
    composition to prevent frame mutation/bleed.
- After that short window drains, it automatically returns to fast playback mode.
2026-03-06 22:02:30 -06:00
Jonathan Thomas b7b417562c Fixing a Clip cache regression which caused effects to not clear their parent's clip cache after updating, causing stale preview images 2026-03-05 22:06:55 -06:00
Jonathan Thomas 543c58de63 - Timeline::GetFrame no longer clamps past-end requests to MaxFrame; it returns requested past-end black frames but does not cache them, so scrubbing past end no longer pollutes final_cache.
- VideoCacheThread now preserves raw playhead intent while clamping only for cache/readiness internals, and playback click behavior was corrected so non-preroll playback seeks don’t clear cache unexpectedly.
2026-03-04 23:49:47 -06:00
Jonathan Thomas 4db8047bbb Clamping video cache thread to timeline max frame, so we don't churn/invalidate the cache at the very end of the timeline for no reason. Also, clamping Timeline::GetFrame to the max frame on the timeline, so we don't waste resources generating black frames past the end frame. 2026-03-04 13:17:47 -06:00
Jonathan Thomas 4387f8b394 - Timeline composition no longer mutates cached clip frames (fixes trails/ghosting).
- Clip updates via ApplyJsonDiff now also clear clip cache (not just timeline/reader ranges).
- Added regressions for cache invalidation + no cached-frame mutation; kept paused preroll/cache-bar behavior intact.
2026-03-01 14:19:27 -06:00
Jonathan Thomas b39cb385e8 Merge branch 'develop' into comfy-ui 2026-02-27 12:19:46 -06:00
Jonathan Thomas 00f82c239f More improvements to EffectBase support of masks (allowing all effects to use animated masks better).
- Mask: Start/End (seconds) picks the exact part of a mask video to use.
  - Time Mode: Timeline follows project timing; Source FPS follows mask file timing.
  - Loop mode then defines playback behavior: Play Once, Repeat, or Ping-Pong.
2026-02-24 23:41:32 -06:00
Jonathan Thomas 7efc48006a Modify all throw InvalidFile(...) callsites: each now has a distinct message string, and adding unit tests to verify this.
So a crash line will now be more like:
  what(): FFmpegReader could not open media file. for file C:\...\TitleFileName%04d.png
  instead of only File could not be opened.
2026-02-23 16:55:49 -06:00
Jonathan Thomas c43092eaa5 Merge branch 'develop' into caching-protections
# Conflicts:
#	src/Qt/VideoCacheThread.cpp
2026-02-13 11:29:32 -06:00