Files
LlamaCasty/HANDOFF.md
T
gramps 76f51e6f4e fix: FramePump paces one frame per deadline slot — duplicate-on-lag, never skip (slice 15)
ty-1723/1726 device takes showed accelerated playback + audio tail cut-off:
a render overrun (~35ms vs the 16.6ms slot) SKIPPED the missed slots (slice 10's
freshness choice), so a 60fps-authoring pump wrote one frame per 35ms into a
60fps container — 1723: 697 frames/11.62s vs 11.84s audio; 1726: 163/2.72s vs
2.93s, video ending 0.21-0.24s early.

OBS never leaves a wall-time hole: the video thread emits one frame per tick and
a lagging producer DUPLICATES the newest frame ("lagged frames due to rendering
lag/stalls" — obs-output.c; "If the video frame queue is full, it will duplicate
the last frame" — docs.obsproject.com/backend-design). The pump's submit is now a
bounded catch-up over the missed slots (while now >= nextTick), fresh on the first,
repeated after — duration == wall, judder not fast-forward. Safe because Channel.
TryWrite never blocks (the take-9 smear was the blocking pipe-write; each emit is
nanoseconds). Burned frame index moved inside the loop: every emitted slot carries
its own +1 (also fixes the old unconditional pre-gate bump that gapped the judge
sequence on non-submitting fast-render iterations).

Good Dog test: Pump_Overrun_Renders_EmitsEverySlot_NotSkipped (60fps, 35ms render
cost, asserts >=0.65 of the wall slots emitted). 294/294 green, 0 warnings.

No push — web/A/V work is commit-local until greenlight.
2026-09-14 17:40:59 -07:00

5.5 KiB
Raw Blame History

HANDOFF — 2026-09-14 (composition capture DEVICE-VERIFIED ✓; slice-15 pacing fix committed locally — A/V re-measure next)

Branch / Commit State

main HEAD = slice-15 commit (FramePump duplicate-on-lag pacing — committed LOCALLY, NOT pushed; web/A/V work stays commit-local until greenlight). Before it: c01206f (composition capture redesign, also local/unpushed), before that b22d08e (signed audio-sync, pushed). Working tree clean.

⚠️ Branding (2026-09-14, creator-corrected): product = llamacasty, internals = ytLive

The product is llamacasty; repo path, csproj AssemblyName/RootNamespace, DB/log paths (%APPDATA%\ytLlive\...), and most code names are the legacy ytLive/ytLlive. User-facing language says "llamacasty"; code/assembly/repo names stay ytLive. See ai.md → Brand.

✅ DEVICE-VERIFIED — composition capture looks good; then the A/V defect surfaced

The creator ran real takes off the c01206f build and reports the video looks very good — the Windows.Graphics.Capture web path is confirmed on device (open item closed). BUT the same takes showed the A/V defect we have seen before: both desktop and webcam playback are accelerated, the audio "gets speedier", then cuts off at the end.

🔬 Sliced and measured (evidence)

With the A/V recipe (MyMistakes.md → "Measuring audio-video A/V sync" + /tmp/opencode/avsync.py):

  • ty-20260914-1723-0000-2.mp4: 697 video frames @60fps = 11.62s vs audio 11.84s.
  • ty-20260914-1726-0000-2.mp4 (clap take): 163 frames = 2.72s vs 2.93s audio. Video ends 0.21–0.24s before audio → the cut-off. Clap: audio env peak 1.655s vs video motion 0.75–1.03s; cross-correlation lag +0.667s (audio late).
  • startup.log: FramePump stall: iteration 33-46ms (> 2× the 17ms interval): worst render 33ms … dropped 0.

Root cause (slice 15): slice 10's freshness choice skipped the overrun's missed slots — one fresh frame per ~35ms stall authored into a 60fps container = accelerated playback. OBS's answer is duplicate-on-lag (libobs/video-io.c, docs.obsproject.com/backend-design): fill each missed slot by repeating the newest frame — duration == wall, judder never fast-forward.

🔬 Committed locally — slice 15: one frame per deadline slot (duplicate-on-lag)

What: Services/Encoder/FramePump.cs submit is now a bounded catch-up — while (now >= nextTick) { submit latest composite; nextTick += intervalTicks; } clamped to a deadlineNow captured once per iteration. First missed slot gets the fresh composite, the rest get repeats of it (OBS duplication) — recording duration == wall time under any render load, no acceleration, no audio tail cut. The burned _outputIndex moved inside the submit loop (every emitted slot gets its own +1; also fixes the old unconditional bump that gapped the judge sequence on non-submitting fast-render iterations).

Good Dog test: ytLive.Tests/FramePumpTests.cs → Pump_Overrun_Renders_EmitsEverySlot_NotSkipped (60fps, 35ms render cost, asserts ≥0.65 of the wall slots emitted — the old skip-pump wrote ~1/35ms). 294/294 green, app build 0 warnings. Files: Services/Encoder/FramePump.cs, ytLive.Tests/FramePumpTests.cs. Docs in same commit: ai.md Slice 15, MyMistakes (supersedes the slice-10 "no burst re-write" clause + latest numbers), this HANDOFF.

⚠️ Open items (before PUSHABLE)

  • Device re-verify: a take on the slice-15 build must play at REAL time (no acceleration, no cut-off audio tail). Confirm via ffprobe: video stream duration ≈ audio ≈ container.
  • Re-measure the true A/V offset with a clap take now that video pacing is honest (/tmp/opencode/avsync.py). The +0.6s reading on 1726 was confounded by the 1.1x acceleration. If a real residual remains after the pacing fix, the backlog is the AUDIO pipeline (mixer ring / advance), not video.
  • No push yet — commit-locally-until-greenlight for web/A/V work.

Open threads (carried)

  • Audio-silence verification — fixed (724af14); creator heard real audio.
  • Webcam MJPG missing / ~10–14Hz, layer SortOrder, truncation-with-dynamic-scenes — queued.
  • Sync control user-doc tutorial — REQUIRED before 1.0 (creator directive; TASK 22).
  • Signed A/V sync: verify the negative (advance) direction on device.

Landmines

  • testhost shares startup.log — filter by time.
  • cmd.exe /c "taskkill /F /IM ytLive.exe" (WSL double-slashes mangle) before rebuilds — a live app process locks ytLive.exe and the apphost copy fails (MSB3021, seen today).
  • Build/tests: Windows dotnet host (/mnt/c/Program Files/dotnet/dotnet.exe). 0 warnings — only ./scripts/verify.sh "<files>"'s clean build counts. Building ytLive.csproj alone does NOT rebuild ytLive.Tests.dll — run the Tests csproj before vstest.
  • ffmpeg/ffprobe: /mnt/c/Program Files/Krita (x64)/bin/ with Windows paths.
  • MyMistakes.md has the A/V sync measurement recipe, the deadline-pacing lessons (item 1 + slice 15), and the CoreMessaging DQ recipe — grep before re-deriving.
  • sqlite3 at /home/gramps/android-sdk/platform-tools/sqlite3 for /mnt/c/Users/gramp/AppData/Roaming/ytLlive/ytLlive.db.

Next step

Have the creator record a clap take on the slice-15 build → ffprobe durations (video == audio == wall) + /tmp/opencode/avsync.py for the honest offset. If durations match, the acceleration defect is closed; then decide push with the user, and attack any true audio-latency residual as its own work unit.