---
key: ffmpeg.hls-cold-start-burst
title: 2026-07-20 — HLS cold start is fixed with `-readrate_initial_burst`, not by raising the work-ahead limit (#350)
status: active
since: '2026-07-20'
supersedes: none
superseded-by: none
rule: HLS cold-start latency is fixed with a bounded `-readrate_initial_burst` (gated on FFmpeg ≥6.1 capability detection), not by raising `work_ahead_limit`, which would remove the concurrency guarantee it exists for.
signals: 'HLS cold start, readrate, work_ahead_limit, HlsSessionWorker, FFmpegKnownOption capability gate · paths: `HlsSessionWorker`, `SetRealtimeInput`, `FFmpegPlaybackSettingsCalculator`, `FFmpegKnownOption`/`HasOption` · issues: #350'
mechanics: '`SetRealtimeInput` readrate-burst option; `FFmpegKnownOption.HasOption` version-capability gate'
---

> **Correction (2026-07-21, #529):** the claim below that the burst is "bounded" is true only in
> *seconds of input* — it is **not** a bound on memory or hardware surfaces. `-readrate` was also
> incidentally bounding how fast decoded frames enter the filter graph, and removing that on a QSV
> pipeline whose profile stores `qsvExtraHardwareFrames: 0` exhausts the upload pool: the graph fails
> with `-12 (Cannot allocate memory)`, `h264_qsv` never opens, and zero segments are written. The
> burst is **not** the root cause (a work-ahead start takes no `-readrate` at all and was already
> failing the same way in production), but it removed the throttle on every realtime session and so
> made the failure near-deterministic. See `ffmpeg.qsv-extra-hw-frames-floor`; the decision recorded
> here still stands, with that floor in place.

> **Correction (2026-07-21, #536):** the bullet below stating that "every concurrent tune-in falls
> back to the throttled path" described the *intent*, not the behaviour. The slot check and its
> increment straddled an `await`, so N simultaneous tune-ins all read `0 < limit` and all started
> unthrottled; the "one winner, two throttled" observation held only because those tunes were
> effectively staggered. The measurement and the conclusions drawn from it stand — slot availability
> *was* the variable behind the bimodality — but the guarantee itself was not enforced until
> `ffmpeg.work-ahead-slot-atomic`.

- **`-readrate` throttles from the first read, so it sets a floor on time-to-first-segment.** The
  realtime playback path pins input reading to 1.05× wall clock so a channel behaves like live TV.
  Since `OutputFormatHls.SegmentSeconds` is 4 and the segmenter serves the playlist only once the
  first segment exists, the playlist cannot appear sooner than ~4/1.05 ≈ 3.8 s. Measured on real
  media: time-to-first-playlist 5369/5344 ms with `-readrate 1.05`, 648/649 ms with an initial burst.
- **The cold-start bimodality was never about the media.** `HlsSessionWorker` grants an unthrottled
  `SeekAndWorkAhead` start only while `_workAheadCount < ffmpeg.segmenter.work_ahead_limit` (prod: 1);
  every concurrent tune-in falls back to the throttled path. Three concurrent tunes on prod: the one
  that won the slot reached `firstGop` in 866 ms, the other two in 3845 ms and 6357 ms. This is why
  the earlier rounds found no correlation with subtitle burn-in, GOP length, or source file — the
  variable was slot availability, and the same channel could differ 7.2× between tunes.
- **Two earlier hypotheses are falsified, not deferred.** Accurate-seek decode-discard (the issue's
  ranked #1 driver) costs 30–100 ms on real prod media, and capping `-probesize`/`-analyzeduration`
  buys 20–50 ms. Neither can account for seconds. Recorded here so they are not re-proposed.
- **Burst rather than a bigger work-ahead budget.** Raising `work_ahead_limit` would fix latency by
  deleting the guarantee that limit exists for — it caps how many unthrottled transcodes N viewers can
  start at once. The burst is bounded (`SegmentSeconds * 2` = 8 s of input, enough for the first
  segments at the default `InitialSegmentCount` of 1), after which live pacing resumes. An operator
  who raises `InitialSegmentCount` above 2 gets less of the benefit; that is a deliberate trade.
- **The burst is per ffmpeg process — i.e. per playout item — not per session.** `SetRealtimeInput`
  runs on every pipeline build and `HlsSessionWorker` spawns a process per item, so each item boundary
  bursts too; this is *not* only the session's cold start. Two consequences, both accepted: item
  transitions get the same head start (a benefit), and on a channel whose items are shorter than the
  burst every item transcodes unthrottled, so the instantaneous-concurrency guarantee that
  `work_ahead_limit` provides is weaker than before — weaker, not absent, because `HlsSessionWorker`'s
  `transcodedBuffer <= 1min` gate still stops the loop at a 60 s buffer, leaving average CPU
  unchanged. Making the burst strictly cold-start-only would mean plumbing a "first process of this
  session" flag through `FFmpegState`; that complexity was not judged worth a bounded peak.
- **Still images are excluded.** Their video input is paced by the realtime *filter* and takes no
  readrate at all, so a burst would only run the audio input ahead of the video for songs and offline
  filler, with no cold-start gain to show for it.
- **Non-HLS realtime outputs (`TransportStream`, HLS-Direct) burst too**, since
  `FFmpegPlaybackSettingsCalculator` makes them unconditionally realtime. That is untested by the
  benchmark, which was segmenter-only; it is kept because the same first-read throttle delays those
  clients identically, and the outer `WrapSegmenter`/`Concat` processes still pace at `readrate 1.0`
  with no burst.
- **Gated on runtime capability, not on a parsed version.** `-readrate_initial_burst` needs FFmpeg
  ≥ 6.1, and `FFmpegKnownOption`/`HasOption` already existed for exactly this (its `AllOptions` list
  had simply been empty). Detection parses `ffmpeg -h long`, so an older binary silently keeps
  today's behavior instead of failing to start — the same fail-safe posture as the other capability
  gates, and cheaper to reason about than the version-string parsing in `NvidiaHardwareCapabilities`.
