Commit Graph
15 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 4b83e088eb Merge main into the playback branch (mixer recordStart moves to the end of preroll; GopCache keeps main's random-access key walk-back)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:30:04 -04:00
Brandon ThomasandClaude Opus 5.5 5823e22e89 M4.6: cancel frame jobs playback no longer needs (stale in-flight jobs, prefetch after Stop)
- filmcraft_media::cancel: a per-job cancellation flag passed through a thread-local, so the
  MediaSource API is unchanged. Frame workers run each job inside it.
- FrameServer::retain_queue (called by schedule_playback every refresh) now also cancels running
  jobs for frames the playhead has passed. Before, a worker blocked on a source's decoder lock
  went on to decode its frame after playback had moved 50–100 frames on, found it evicted and
  restarted the decoder at the keyframe (decoder trace: "seek i=32 key=0 next=142").
- GopCache gives up (CodecError/MediaError::Cancelled) before seeking or decoding for a cancelled
  job and between samples; decoder state stays consistent, so the next request carries on.
- The CPU compositor checks the flag between tracks and effects; cancelled results are never
  cached.
- Stop drops queued prefetch jobs and cancels running ones (FrameServer::stop_prefetch). They
  used to run to completion after Stop, e.g. 14 frames of live effects rendering competing with
  a Render In to Out started right after.

bench, same-session A/B (GPU path, 8 s, load avg 35–41):
  after-preview Full live play (1080p + Lumetri/Sharpen/Levels/Tint, CPU-bound):
    38/154 → 98/94 shown/dropped, 297 → 267 ms CPU/frame, 84 jobs cancelled
  after-preview plays from previews, h264-1080, stack3, h264-2160: unchanged within noise

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:54:44 -04:00
Brandon ThomasandClaude Opus 5.5 94b71a9346 M4.6: GOP cache: no re-decode from the keyframe during playback; ProRes/MJPEG frames decode in parallel
Root causes (seen with the bench's decoder counters):
- A frame-threaded H.264 decoder holds 16–32 pictures in flight. GopCache only continued the
  running decoder when it was at most 16 samples past the wanted sample, so a request for a frame
  that had been fed but not output yet restarted decoding at the keyframe: 3–13 extra seeks per
  8 s play at 4K, each re-decoding up to a GOP. It now tracks the highest pts output since the
  restart and continues while the wanted frame is still inside the decoder.
- The byte budget (384 MB) held 31 4K frames, less than the decoder's lead plus the prefetch
  window, so frames were evicted before they were shown and re-decoded. The cache now always has
  room for 64 frames.
- A request just past the next sync sample restarted there, skipping frames playback still
  wanted, which then forced a restart at the earlier keyframe. A running decoder now carries on
  through up to 48 samples into the next GOP.
- Intra-only streams (ProRes render previews, MJPEG) decoded one frame at a time under the cache
  lock and re-fed up to 64 samples per miss. VideoDecoder::intra_only() lets GopCache decode them
  outside the lock with a pool of decoders, one sample per frame.

Output is unchanged (same decoder, same frames); unit tests cover the in-flight, continue-through
and parallel intra cases.

bench (GPU path, 8 s, same-session A/B, load avg 70–120 on 14 cores):
  h264-2160 Full: 0–12 → 71–171 shown of 192; decoder seeks 7–10 → 1; 229–355 → 175 ms CPU/frame
  h264-2160 Half: 0–15 → 94–159 shown; seeks 6–13 → 1; 247–524 → 173–185 ms CPU/frame
  after-preview Full, 1st play after render: 86/106 → 192/0 shown/dropped, 272 → 26 ms CPU/frame,
    1716 → 182 samples decoded; 2nd play: 135/57 → 192/0
  h264-1080 and stack3 unchanged (1 and 3 seeks before and after)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:46:29 -04:00
Brandon ThomasandClaude Opus 5.5 0aaa4f41de Merge main into M9.8 (VP9): Opus + VP9 together in codecs/mkv; vp9 at opt-level 3 in dev builds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:05:53 -04:00
Brandon ThomasandClaude Opus 5.5 5170376dcb M4.6: headless playback benchmark (cargo xtask bench-playback) and a correct dropped-frame count
- crates/ui-egui/examples/bench_playback.rs drives the Program monitor's own FrameServer,
  prefetch (now FrameServer::schedule_playback, shared with the monitor) and PlaybackMeter at a
  simulated 60 Hz display, optionally compositing on a headless wgpu device. Scenarios:
  h264-1080, stack3 (3 × 1080p, scaled/rotated/opacity), h264-2160, demo, after-preview
  (live play → Render Effects In to Out → 2 plays), seek-storm. Reports latency percentiles,
  on-time ratio, worker/decode wall and thread-CPU time, UI-thread present time, process CPU per
  frame, decoder seeks/samples (codecs::gop_stats) and wasted jobs; --json for machine output.
  Fixtures are generated with ffmpeg into target/fixtures/playback.
- FrameServer records per-job timings (thread CPU via rustix clocks) while profiling is on.
- Dropped/shown were counted per UI refresh: "shown" counted every refresh that had the frame
  (322 "shown" in an 8 s play of 192 frames) and "dropped" counted a frame that arrived a few ms
  after its first refresh. PlaybackMeter counts timeline frames: shown when the exact picture was
  on screen while due, dropped otherwise, including frames passed over without a refresh.

Baseline (GPU path, 8 s, load avg 104–168 on 14 cores; CPU columns are load-robust):
  h264-1080 Full 191/1 shown/dropped, 46 ms CPU/frame; Half 188/4
  stack3 Full 189/3, 102 ms CPU/frame; Half 157/35 (3 extra decoder seeks)
  h264-2160 Full 8/184, 313 ms CPU/frame, 10 seeks; demo Full 114/78, Half 191/1
  after-preview: live 0/192; 1st play after render 124/67, 2nd 119/73 (UI-thread present
  p50 21 ms / p95 45 ms); seek-storm: 0 of 40 seeks shown within 150 ms

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:44:23 -04:00
Brandon ThomasandClaude Opus 5.5 9db963e0e6 M9.8: VP9 wired into codecs — WebM/MKV V_VP9 and MP4 vp09 (+vpcC) import, 8/10/12-bit 4:2:0/4:2:2/4:4:4 (4:4:0 expanded, RGB to RGBA) output, key-frame-checked GOP random access, Decoder::reset / is_keyframe, oracle seek tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:42:11 -04:00
Brandon ThomasandClaude Opus 5.5 608a83f33e M9.13: Opus in WebM/MKV and MP4 import
crates/codecs decodes Opus with filmcraft-opus (48 kHz output):
- Matroska/WebM A_OPUS: CodecPrivate = OpusHead. CodecDelay removes the
  pre-skip exactly (header pre-skip when CodecDelay is absent); packet starts
  are accumulated from TOC durations because block timestamps are only
  tick-accurate. Random access decodes max(SeekPreRoll, 320 ms) first.
- MP4/MOV Opus + dOps: edit list removes pre-skip (dOps pre-skip without
  one); same pre-roll on random access.
- Family-1 surround reordered from Vorbis to WAV/SMPTE channel order;
  corrupt packets are concealed instead of failing.
Tests (ffmpeg/libopus fixtures, skipped when absent): WebM stereo, MKV mono
VoIP (SILK/hybrid), MKV 5.1 multistream, MP4 stereo; sample-aligned against
libopus (52-89 dB sequential, 45-89 dB after random access).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:37:19 -04:00
Brandon ThomasandClaude Opus 5.5 c6cfa8ddb3 M9.10: Matroska/WebM import (H.264/HEVC/ProRes/MJPEG video; AAC/FLAC/MP3/Vorbis/PCM audio); GOP random access shared by MP4 and MKV sources (codecs::gop)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:30:12 -04:00
Brandon ThomasandClaude Opus 5.5 345b4162cf M9.3: HEVC (Main/Main 10) wired into codecs — MP4/MOV hvc1/hev1 import and playback
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:11:25 -04:00
Brandon ThomasandClaude Opus 5.5 a7151d0323 M6.2: own AAC-LC decoder replaces the symphonia bootstrap for AAC; AAC encoder for MP4 export
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:39:00 -04:00
Brandon ThomasandClaude Opus 5.5 efc617cb97 M6.2: own AAC-LC decoder replaces the symphonia bootstrap for AAC; AAC encoder for MP4 export
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:38:47 -04:00
Brandon Thomas 90d615b675 codecs: drop unused field 2026-09-30 07:06:09 -04:00
Brandon ThomasandClaude Opus 5.5 f4d3efe32a M9.5: ProRes decode in the codec hub and ProRes 422 HQ export; decoder colour metadata takes precedence unless the container has an explicit colr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:05:42 -04:00
Brandon ThomasandClaude Opus 5.5 0c1b413595 M1/M2: wire filmcraft-h264 into the codec hub (MP4/MOV H.264 with B-frames, GOP-aware seeking)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:01:36 -04:00
Brandon ThomasandClaude Opus 5.5 8194dac6a8 M1.4–M1.8: filmcraft-codecs hub — MP4/MOV sources with GOP-aware seeking and frame cache, MJPEG, PCM, AAC/MP3/FLAC/ALAC/Vorbis (symphonia bootstrap), standalone audio files; engine registers openers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:42:53 -04:00