OBS Replay Buffer MKV Fragments Fail ffmpeg Concat Before Whisper Batch - How to Fix
Problem: Facilitators save OBS Replay Buffer clips into playtest-vod/inbox/. Whisper transcribes each MKV fine, but ffmpeg -f concat (or your merge script) exits non-zero or produces a merged file with dropped segments, wrong timestamps, or silent gaps. The nightly batch never reaches T3_transcript because concat_ok stays false.
Who is affected now: Teams running the June–October 2026 OBS Replay Buffer concat evening workflow and local Whisper playtest VOD triage on Windows 11 laptops. Replay Buffer exports one MKV per highlight; batch nights accumulate dozens of fragments with different OBS segment settings, mixed sample rates, or non-monotonic DTS—concat fails even when every clip plays in VLC. This is merge hygiene, not zero-duration audio (missing tracks) or API 413 chunking (cloud size). Preventive: pin one OBS playtest profile (48 kHz, fixed buffer length) via the uniform fragments preflight (ninety-second gate) and Lesson 258 — replay buffer uniform receipt on BUILD_RECEIPT before the next batch night.
Fastest safe fix: ffprobe every fragment → normalize to 48 kHz mono WAV per clip → concat WAV (or re-encode MKV with +genpts) → verify merged duration ≈ sum of parts → set concat_ok: true in playtest_vod_triage_receipt_v1.json → run Whisper on one merged WAV (or keep per-clip ASR and stitch timestamps in post).
Direct answer
ffmpeg concat is strict about timebases. OBS Replay Buffer fragments often have gap timestamps, 44100 Hz vs 48000 Hz mismatches, or variable frame rate video that breaks the concat demuxer. Per-clip Whisper works because each file is self-contained; batch concat fails because the demuxer expects compatible streams and monotonic PTS. Re-encode audio to a single format before concat, or use the filter_complex concat path with explicit normalization.
Why this issue spikes in June–October 2026
- Replay Buffer adoption — 16-tool concat listicle and playtest ops READMEs push fragment-per-highlight capture instead of one long MKV.
concat_okgates — Lesson 200 and BUILD_RECEIPT rows fail closed when merge fails.- Mixed facilitator laptops — Different OBS segment length, encoder, and audio track settings across hosts.
- Whisper batch economics — Teams want one merged transcript per session; per-clip ASR without timestamp merge hides the concat failure until Thursday row review.
- October async playtest volume — More fragments per Discord thread before Tuesday CSV ingest.
Symptoms and search phrases
ffmpegconcat:Non-monotonous DTS in output streamorTimestamp discontinuity.- Merged file shorter than sum of fragment durations (segments dropped).
- Per-clip
faster-whisperOK; merged WAV silent or truncated. ffprobeshows 44100 Hz on some MKVs and 48000 Hz on others.- Concat works in VLC playlist but fails in ffmpeg demuxer.
- Batch log:
T1_ingesttrue,T2_audiotrue,concat_okfalse. - Error after OBS update: new Replay Buffer profile changed audio sample rate.
Root causes (check in order)
- Non-monotonic DTS across Replay fragments (gap when saving back-to-back).
- Mixed sample rates (44100 vs 48000) or channel layouts (stereo vs mono).
- Different time bases (VFR video + audio start offset per clip).
- Concat demuxer used on MKV with incompatible codecs instead of normalized WAV ladder.
- Missing
+genptswhen re-muxing after timestamp repair. - Fragment list order wrong (concat list not sorted by
build_id+ wall time). - Corrupt partial save — one fragment truncated (ffprobe duration cliff).
Beginner path (first 45 minutes)
Prerequisites: OBS Studio 30+, ffmpeg/ffprobe on PATH, playtest-vod/inbox/ and playtest-vod/work/, facilitator README with OBS profile name.
- Pick two failing MKVs that pass zero-duration audio gate individually.
- Run ffprobe table (Step 2) on both—note sample rate and duration.
- Export 48 kHz mono WAV per clip (Step 3).
- Concat WAV only with concat demuxer (Step 4).
- If pass → add fragments to nightly list; set
concat_okon receipt. - If fail → try
+genptsre-encode path (Step 5) on MKV pair only.
Common mistake: Concatenating MKV directly while audio exists but timestamps are incompatible—normalize first.
Fastest safe fix path
Step 1 — Lock OBS Replay fragment settings (prevention at source)
| Setting | Recommended |
|---|---|
| Replay Buffer length | Fixed (e.g. 120 s)—document in README |
| Settings → Audio → Sample rate | 48 kHz project-wide for playtest profile |
| Same OBS profile every facilitator night | Name in playtest-vod/README.md |
| Save Replay spacing | ≥2 s between saves to reduce DTS overlap |
Official: OBS Replay Buffer, OBS Audio Guide.
Step 2 — ffprobe each fragment before merge
$frag = "playtest-vod/inbox/2026-05-27_build-playtest-07_surface-playtest_replay.mkv"
ffprobe -v error -show_entries stream=codec_type,codec_name,sample_rate,duration `
-of json $frag | Out-File "playtest-vod/logs/probe_$($frag.BaseName).json"
Pass criteria per fragment:
| Field | Pass |
|---|---|
codec_type=audio present |
Yes |
sample_rate |
Same across all fragments in batch (prefer 48000) |
duration |
≥ 1 s; within 5% of wall-clock expectation |
Video duration (if present) |
No N/A on audio when video plays |
Fail: Quarantine fragment; do not add to concat_list.txt.
Deep reference: OBS ffprobe concat_ok preflight.
Step 3 — Normalize each fragment to 48 kHz mono WAV
ffmpeg -y -i $frag -vn -ac 1 -ar 48000 -c:a pcm_s16le `
"playtest-vod/work/$($frag.BaseName)_48k.wav"
Why WAV ladder: Concat demuxer on uniform PCM avoids MKV timestamp baggage. Pair with normalize preflight.
Step 4 — Concat demuxer on WAV list
playtest-vod/work/concat_list.txt (ffmpeg format):
file '2026-05-27_build-playtest-07_surface-playtest_replay_48k.wav'
file '2026-05-27_build-playtest-07b_surface-playtest_replay_48k.wav'
ffmpeg -y -f concat -safe 0 -i "playtest-vod/work/concat_list.txt" -c copy `
"playtest-vod/work/merged_playtest_48k.wav"
ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 `
"playtest-vod/work/merged_playtest_48k.wav"
Pass: Merged duration ≈ sum of WAV durations (±2 s). Then run Whisper once on merged_playtest_48k.wav.
Step 5 — MKV repair with +genpts (when WAV ladder still fails)
When video must stay in container or DTS errors persist on MKV:
ffmpeg -y -fflags +genpts -i $frag -c:v copy -c:a aac -ar 48000 -ac 1 `
"playtest-vod/work/$($frag.BaseName)_repair.mkv"
For multi-fragment MKV concat after repair:
ffmpeg -y -f concat -safe 0 -i "playtest-vod/work/mkv_concat_list.txt" `
-fflags +genpts -c copy "playtest-vod/work/merged_repair.mkv"
Guide depth: MKV timestamp gap re-encode preflight.
Step 6 — Receipt field concat_ok
{
"schema": "playtest_vod_triage_receipt_v1",
"batch_date": "2026-05-27",
"gates": {
"T1_ingest": true,
"T2_audio": true,
"concat_ok": true,
"T3_transcript": true
},
"merge": {
"fragment_count": 12,
"normalized_sample_rate_hz": 48000,
"merged_wav": "playtest-vod/work/merged_playtest_48k.wav",
"merged_duration_sec": 1847.2,
"ffmpeg_concat_exit_code": 0
}
}
Fail closed: If concat_ok is false, do not set T3_transcript true on batch-level receipts—prevents ghost summaries from partial merges.
Working dev path — batch gate and decision tree
| Check | Artifact | Pass |
|---|---|---|
| Per-fragment ffprobe | playtest-vod/logs/probe_*.json |
Uniform sample_rate |
| WAV normalize | work/*_48k.wav |
All exist |
| concat demuxer | work/merged_playtest_48k.wav |
Exit 0; duration sum OK |
| Whisper smoke | transcripts/merged.txt |
Non-empty |
| Receipt | playtest_vod_triage_receipt_v1.json |
concat_ok: true |
When concat still fails after WAV ladder, branch per Lesson 207 — Whisper path when concat fails: per-clip ASR + timestamp stitch (document offsets in CSV) instead of forcing a broken merge.
Optional CI sketch:
import json, subprocess, sys
from pathlib import Path
def duration(path: Path) -> float:
cmd = ["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=nw=1:nk=1", str(path)]
return float(subprocess.check_output(cmd).decode().strip())
work = Path("playtest-vod/work")
wavs = sorted(work.glob("*_48k.wav"))
if len(wavs) < 2:
sys.exit(0)
list_file = work / "concat_list.txt"
list_file.write_text("\n".join(f"file '{w.name}'" for w in wavs), encoding="utf-8")
out = work / "merged_playtest_48k.wav"
subprocess.check_call([
"ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", str(list_file),
"-c", "copy", str(out)
])
expected = sum(duration(w) for w in wavs)
actual = duration(out)
if abs(actual - expected) > 2.0:
print(json.dumps({"concat_ok": False, "expected": expected, "actual": actual}))
sys.exit(1)
print("OK")
Verification checklist
- [ ] Every inbox MKV passes audio stream gate before merge
- [ ] All fragments share 48 kHz (or documented conversion)
- [ ]
ffmpeg -f concaton WAV list exits 0 - [ ] Merged duration ≈ sum of parts
- [ ] Whisper returns non-empty text on merged WAV
- [ ]
playtest_vod_triage_receipt_v1.jsonhasconcat_ok: true - [ ] Facilitator README lists OBS profile + fragment spacing
- [ ] Filename includes
build_id+surfaceper playtest isolation
Prevention
- Standardize OBS Replay profile (48 kHz, fixed buffer length) in community playtest ops.
- Run normalize + ffprobe preflight on night one smoke.
- Never concat MKV with mixed sample rates—always WAV ladder first.
- Add
concat_okto Wednesday smoke when playtest VOD is in scope. - When merge is impossible, use per-clip Whisper + stitch (Lesson 207)—do not fake
concat_ok.
Troubleshooting
| Symptom | Fix |
|---|---|
Non-monotonous DTS |
WAV ladder or +genpts repair (Steps 3–5) |
| Merged shorter than sum | One bad fragment—ffprobe each; remove outlier |
| 44100 vs 48000 mix | Re-export all to 48 kHz WAV |
| Per-clip OK, merge silent | Concat list wrong order or -c copy on incompatible WAV |
| VLC playlist OK, ffmpeg fails | VLC is lenient—do not use as gate |
| After OBS 31 update | Re-select audio devices; re-save smoke Replay |
FAQ
Should we concat MKV or WAV before Whisper?
WAV ladder is the most reliable for audio-only triage. MKV concat is OK after +genpts repair if you must keep video.
Is this the same as zero-duration audio?
No—zero-duration help is missing audio tracks. Here audio exists but merge fails.
Can we skip concat and run Whisper per clip?
Yes—set concat_ok: false honestly and use per-clip transcripts with timestamp offsets; see concat decision tree blog.
Does Silero VAD fix concat?
VAD splits audio for API limits—it does not repair DTS gaps. Fix merge first.
Related links
- OBS Replay Buffer normalize before ffmpeg concat (guide)
- OBS MKV timestamp gap re-encode concat (guide)
- OBS ffprobe concat_ok receipt fields (guide)
- Your First OBS Replay Buffer ffmpeg Concat Evening (blog)
- When ffmpeg Concat Fails — Per-Clip or Cloud API (blog)
- 16 Free OBS ffmpeg Silero Concat Tools (blog)
- OBS Zero-Duration Audio Before Whisper (help)
- Lesson 200 — concat_ok gate before Whisper batch (course)
- Lesson 207 — path when concat fails (course)
- 15 Free Local Whisper ffmpeg Playtest VOD Tools
- 15 Free OpenAI Whisper API Chunking Tools
- Official: ffmpeg concat demuxer, ffprobe
Normalize fragments, prove concat_ok, then batch Whisper—merge failures are timestamp hygiene, not model regression.