Add CLAUDE.md with context not covered by README or code comments
Documents things a future session would otherwise have to rediscover: no test suite/fixtures exist (verification requires real sample clips and ffprobe checks), the libvmaf stderr spam is a dev-machine quirk not a bug, detection thresholds were empirically tuned on one real dataset, the -r 30 normalization is load-bearing for bitrate targeting and shouldn't be removed casually, and a cross-platform GUI was considered and deliberately deferred.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# CLAUDE.md
|
||||
|
||||
Guidance for working in this repo that isn't already covered by README.md
|
||||
or the scripts' own docstrings/comments.
|
||||
|
||||
## There is no test suite, and no fixture files in the repo
|
||||
|
||||
`.gitignore` excludes `*.mov`/`*.MOV`, so there are no sample clips checked
|
||||
in. The overlap-detection logic depends on real iPhone HEVC/PCM quirks
|
||||
(variable frame rate, the exact multi-track Live Photo container structure,
|
||||
genuinely-correlated real-world audio at clip boundaries) that aren't
|
||||
practical to fake convincingly with synthetic ffmpeg test inputs. To verify
|
||||
a change to `concat_live_clips.py`, ask the user for a real set of
|
||||
sequential Live Photo `.MOV` files (or reuse ones from earlier in the
|
||||
conversation if still on disk) and check results with `ffprobe`:
|
||||
|
||||
- `ffprobe -show_entries format=duration` — output duration should equal
|
||||
the sum of (each clip's duration − its detected overlap).
|
||||
- `ffprobe -select_streams v -show_entries frame=pts_time` — verify strictly
|
||||
increasing timestamps (no reordering artifacts at the joins).
|
||||
- `ffprobe -show_entries format_tags` — confirm GPS/device/Live-Photo tags
|
||||
made it through from the source clip.
|
||||
- Cross-check detected overlap/gap values against the clips'
|
||||
`com.apple.quicktime.creationdate` tags by hand for a sanity read before
|
||||
trusting a change to the correlation math.
|
||||
|
||||
## The `libvmaf` stderr spam is an environment quirk, not a bug
|
||||
|
||||
Every `libx265` encode on this dev machine prints repeated
|
||||
`libvmaf ERROR could not read model from path: ...` / `problem loading
|
||||
model file` lines. This reproduces with a trivial synthetic
|
||||
`-c:v libx265` encode with zero flags, and `-x265-params log-level=none`
|
||||
does not suppress it — it comes from `libvmaf` itself, not from anything
|
||||
this script requests, and doesn't affect output correctness (exit code 0,
|
||||
correct output every time this was checked). Don't spend time chasing it as
|
||||
if it were caused by this codebase; the user has explicitly said they're
|
||||
fine with it showing.
|
||||
|
||||
## Detection thresholds are empirically tuned on one real dataset, not derived analytically
|
||||
|
||||
`--confidence-threshold` (0.9), `--min-signal-rms` (25),
|
||||
`--bitrate-multiplier` (1.2), and `--bitrate-floor` (10M) were chosen by
|
||||
testing against two real clip sets from one iPhone (see conversation
|
||||
history / commit messages for the actual numbers observed: correlation
|
||||
scores landed either ~1.000 or well below ~0.6, RMS values were in the
|
||||
hundreds for real audio content). They aren't validated across other
|
||||
devices, iOS versions, or recording environments. If a user reports a false
|
||||
abort or a false pass, prefer surfacing the actual score/RMS numbers
|
||||
(already printed) over silently loosening a default.
|
||||
|
||||
## Rate-control depends on the `-r 30` normalization — don't remove it
|
||||
|
||||
`concat_live_clips.py` forces constant 30fps output (see the comment above
|
||||
`video_args += ["-r", "30", ...]`). This was added after the bitrate-target
|
||||
mode was found to silently undershoot by >10x (target ~13Mbps, actual
|
||||
~1Mbps, x265 hitting max QP) because the source clips' irregular VFR
|
||||
timestamps confused x265's ABR rate control once concatenated. If touching
|
||||
the encoding args, keep this normalization or re-verify bitrate targeting
|
||||
actually hits its target (check `ffprobe -show_entries
|
||||
stream=bit_rate` on the output, not just that the encode succeeds).
|
||||
|
||||
## A cross-platform GUI was considered and explicitly deferred
|
||||
|
||||
The user asked about a GUI, wanted it to look good on Mac/Windows/Linux and
|
||||
possibly iOS, then decided against it once the iOS scope implication became
|
||||
clear (iOS would require either a full native rewrite or a client-server
|
||||
model, not just a new frontend on the same script). Don't reintroduce that
|
||||
scope unprompted; if asked again, the open question is specifically
|
||||
desktop-only vs. desktop+mobile, since that decision determines the whole
|
||||
architecture (e.g. Tkinter/PySide6 vs. a local web app).
|
||||
Reference in New Issue
Block a user