LARGE-SEGMENT BENCH — 30 s / 60 s delivery segments vs 4 s date: 2026-10-23 box: simulated 2 vCPU / 4 GB (taskset -c 0,1 + ulimit -v 4194304) source asset: sim1080, 1920x1080@30, region 128x32 (C=8 R=2), seed 6b1f2a9c... encoder: /tmp/opencode/x264-stable/x264, patched slice-map + #ZONE, CAVLC, bframes 0, no-deblock, threads 1, crf 28, keyint 900 bench root: /tmp/opencode/sim1080 WHY KEYINT 900 - The contract requires an IDR at every segment boundary. - 30 s at 30 fps = 900 frames -> keyint 900 gives exact 30 s IDR boundaries. - 60 s contains two 30 s GOPs/IDRs (keyint 900), i.e. the multiple-GOP / multiple-IDR case requested. A 60 s single-IDR variant would be keyint 1800. - With the original keyint 120 stream, 30 s is NOT IDR-aligned (900 is not a multiple of 120); the nearest aligned sizes are 28 s (840f) or 32 s (960f). ARTIFACTS - fixed60.264 1800 frames, 22,368,077 B, 2982.41 kb/s, 83 s encode - fixed60-1moof.m4s one moof+mdat, 1800 samples, 2 IDRs, 22,397,822 B - fixed60-2moof.m4s two moof+mdat, 900 samples each, 2 IDRs, 22,390,757 B - fixed30-a.m4s 900 samples, 1 IDR, 11,210,107 B - fixed30-b.m4s 900 samples, 1 IDR, 11,181,331 B - 4s baseline segs/seg0000.m4s, 120 samples, 1,517,221 B - outputs bench-out/spl-*.m4s - grouped-per-GOP test bench-out/group-32s.m4s, group-60s.m4s SPLICE BENCH (C wm-splice, full post-probe, warm cache) segment samples input B output B median min max RSS 4 s 120 1,517,221 1,518,830 10.7 ms 10.1 11.5 10.3 MB 30 s-a 900 11,210,107 11,182,726 68.0 ms 64.3 72.1 65.8 MB 30 s-b 900 11,181,331 11,157,855 65.7 ms 65.5 69.8 - 60 s 1moof 1800 22,397,822 22,346,965 146.8 ms 139.3 158.0 129.7 MB 60 s 2moof 1800 22,390,757 22,339,900 138.7 ms 138.5 144.2 129.8 MB Per minute of content (splice CPU only): 4 s: 15 x 10.7 = 160.5 ms/min 30 s: 2 x 66.9 = 133.7 ms/min 60 s: 1 x 142.8 = 142.8 ms/min => 15x fewer requests at 60 s buys ~11% less CPU than 4 s; 30 s is the minimum, 60 s is not better than 30 s because cost is per-sample. VERIFICATION (all real) - wm-probe check --expect-substituted: PASS for 4 s, 30 s-a, 30 s-b, 60 s-1moof (1800 samples), 60 s-2moof (1800 samples). - stock ffmpeg -err_detect explode+crccheck -xerror: exit 0, 0 warning bytes on every spliced output. - 60 s-1moof key frames at sample 0 and sample 900 (ffprobe). - Pixel proof on 60 s-1moof: frame 0 and frame 1500 (second GOP): non-region diffs = 0, region changed = 3864 / 3844. => mark persists in both GOPs from the second IDR. - 30 s exact: fixed30-a/b each 900 samples, 1 IDR, probe PASS. - Grouped existing 4 s GOPs (different zones per GOP) into 32 s/60 s: 8 and 15 sub-splices + concat; 121 ms / 234 ms; strict decode exit 0. Each sub-splice passed; whole-file probe with one schedule is not applicable because zones differ per internal GOP. SLO CHECK (manifesto: p50 <= 30 ms, p99 <= 150 ms per segment) 4 s: 10.7 ms -> PASS p50 and p99 30 s: 63-72 ms -> FAIL p50 (30 ms), PASS p99 60 s: 139-158 ms -> FAIL p50; one 1moof run hit 158 ms > p99 ceiling => on this measured reference path, only 4 s fits the per-segment SLO. The 60 s variant is borderline/over p99; it is not safely deployable under the current SLO. REQUEST / STARTUP COST (full-segment download assumption) 120 min: 1800 requests (4 s), 240 (30 s), 120 (60 s) one segment size: 1.52 MB, 11.2 MB, 22.4 MB download at 10 Mb/s: 1.21 s, 8.97 s, 17.92 s download at 25 Mb/s: 0.49 s, 3.59 s, 7.17 s => fewer requests, but 30/60 s segments make startup/seek/ABR granularity 30/60 s and can require downloading 11/22 MB before playback. ANTI-CROP COST - One fixed zone per 30/60 s means the mark does not move for 900/1800 frames. A static crop of that zone removes the mark for the whole segment. - 4 s segments rotate the zone 15x per minute; 60 s segments do not. - The grouped-per-GOP test preserves the 4 s zone rotation, but the current splicer/probe need per-GOP schedule support (whole-file one-schedule probe fails), and the current per-GOP path costs 234 ms/60 s (>p99). CACHE / MEMORY - 256 MiB clean cache covers ~11 min at ~3 Mb/s regardless of segment size: 168 x 4 s, 22 x 30 s, 11 x 60 s. - 5-min TTL: 75 x 4 s, 10 x 30 s, 5 x 60 s; same ~114 MB per session window. - Peak per-splice RSS grows 10.3 -> 65.8 -> 129.7 MB. On a 4 GB box this cuts in-flight concurrency ~13x for 60 s segments. CONCLUSION - 30 s/60 s segments are technically valid and verified: multi-GOP and multi-IDR in one fMP4 segment works (one moof or multiple moofs), strict decode clean, mark present in every GOP. - They are not a free win. The splice cost is per-sample, so fewer requests does not mean proportionally less CPU; 30/60 s blow the p50 SLO, 60 s is borderline p99, startup/seek/ABR granularity is 7.5-15x worse, anti-crop rotation is 7.5-15x weaker, and per-request memory is 6-13x higher. - Keep 4 s for the contract delivery. If fewer requests are needed, 6 s is the safe upper end of FR-O7 and should be measured; 30/60 s only makes sense offline or after redefining the SLO and implementing per-GOP schedules in a single splicer pass.