Blackwell NVENC emits non-deterministic hvcC parameter-set bytes across encoder restarts
Personal notes. Measured on one Blackwell-class NVENC GPU feeding HEVC into fragmented MP4 for browser playback. Not tried across every Blackwell SKU, driver, or ffmpeg version, and one part of the causal chain below was never isolated cleanly (marked where). Treat this as a well-supported observation plus a low-risk fix, not gospel. No warranty, your mileage may vary, corrections welcome.
At 10,000 feet
Setting: a video pipeline that encodes live HEVC (H.265) video on an NVIDIA Blackwell-generation GPU’s hardware encoder (NVENC) and serves it to web browsers as fragmented MP4. Browsers play this kind of stream through Media Source Extensions (MSE), which binds playback to a single codec configuration.
Problem: browser playback would intermittently freeze, and the trail led to the codec configuration bytes themselves.
What this note establishes: every time the encoder process restarts, it
emits different HEVC decoder-configuration bytes (the hvcC record) even
though nothing about the encoder settings changed. Measured across thousands of
segments: 5-16 distinct configurations per stream. I could not find this
behavior reported publicly anywhere.
Takeaway: mux browser-bound HEVC with -tag:v hvc1 (at the MP4 muxing
stage), which pins the parameter sets out-of-band as Apple’s HLS spec already
requires. It is low-risk and spec-correct even though the causal link between
the byte drift and the freezes is not proven (details below).
The finding
A Blackwell-generation NVENC encoder emits non-deterministic hvcC / HEVC
parameter-set bytes across encoder restarts. Same GPU, same encoder flags, but
each time the encoder process comes back up the HEVC decoder configuration record
in the MP4 sample description carries different bytes. I could not find this
reported anywhere, so the byte-level evidence is the point of this note.
The drift, by the numbers
Scanning recorded HEVC segments and hashing the hvcC box body (the HEVC decoder
configuration record in the MP4 sample description) yielded multiple distinct
values per stream, even though the encoder flags never changed:
| Stream | Segments scanned | Distinct hvcC values |
|---|---|---|
| A | 4623 | 6 |
| B | 3167 | 5 |
| C | 1728 | 5 |
| D | 333 | 12 |
| E | 2865 | 16 |
A new hvcC value appeared after each encoder restart (process respawn,
watchdog kick, pipeline re-init). Each distinct value rewrites VPS / SPS / HRD
fields inside the config record.
The codec string flip
The codec string itself shifted generationally when moving from the previous (Turing) GPU to Blackwell, and the compatibility flags flipped with it:
- Turing emitted
hev1.1.2.L150.90 - Blackwell emitted
hev1.1.6.L150.90
The general_profile_compatibility_flags byte changed from 0x40 to 0x60
between generations. That particular change is a fixed generational difference,
not the per-restart drift, but it is the same class of surprise: the bytes a
browser decoder keys off of are not what you assumed they were, and they move
both across GPU generations (the 0x40 -> 0x60 flip, hev1.1.2 -> hev1.1.6)
and across restarts of a single Blackwell encoder (the table above).
Why this matters for browsers (standard background)
Short version of decade-old, canonical Apple-HLS knowledge, included only so the
byte drift has context: HEVC parameter sets (VPS/SPS/PPS) can live either in-band
(inside media samples, allowed to change over the stream: the hev1 sample-entry
tag many muxers pick by default) or out-of-band only (in the hvcC sample
description, must not change within the track: the hvc1 tag). Media Source
Extensions binds a SourceBuffer to a single decoder configuration, and Apple’s
HLS authoring spec requires hvc1 for exactly this reason. The well-known fix for
HEVC-in-browser trouble is to mux with -tag:v hvc1 so parameter sets are pinned
out of band. None of that is a discovery; it is why the parameter-set bytes above
are worth caring about at all. One implementation nuance worth stating: -tag:v
hvc1 only takes effect at the MP4 muxing stage (it is an MP4 sample-entry
property and a no-op on TS/elementary streams), so in a pipeline that encodes to
TS then remuxes to MP4 with -c copy, the tag belongs on the remux stage.
Honesty: what is established and what is not
- “No public report” is the only novelty claim, and it is weaker than “first.”
What I have established is the absence of a public report of Blackwell NVENC
drifting
hvcCacross restarts, not that this is genuinely the first occurrence. Someone may have hit it and never written it up. - The drift itself is verified; its effect on playback is not. The
hvcCdrift is real and measurable (the table). Whether it causes browser MSE freezes is correlation with a plausible mechanism, not proven causation. After applyinghvc1the freezes I was chasing stopped, which is equally consistent with (a)hvc1genuinely fixing it, (b)hvc1merely making MSE more tolerant while the drift continues underneath, or (c) coincidence, since right after the change the encoder had not yet accumulated many restarts so recent segments happened to share onehvcC. - The mechanism is even debatable. A properly authored out-of-band
hvc1configuration arguably should not be sensitive to in-band SPS drift at all, so it is not obvious the parameter drift is what breaks MSE in the first place. That tension is unresolved here and is a reason to hold the causal story loosely.
hvc1 is spec-required and low-risk regardless, so it remains the correct first
move even though the causal chain is unproven. Verification after applying it:
codec_tag_string=hvc1 on fresh segments, and an MP4 container check (MP4Box-style,
the same class of parser Chrome uses) passed. If freezes return after the encoder
accumulates restarts over days, the drift itself is still there to address.
If hvc1 alone is not enough (fallback)
The deeper fix (not deployed here, therefore unverified) is to clamp the
parameter-set fields at the bitstream level so the encoder cannot emit varying
VPS/SPS/HRD values in the first place. The candidate is the hevc_metadata
bitstream filter to normalize every VUI/HRD field to fixed values before muxing.
That is the next step if an hvc1-tagged stream still drifts across restarts.
References
- ISO/IEC 14496-15 (HEVC in ISO base media file format):
hvc1vshev1sample entries and where parameter sets may live - Apple HLS Authoring Specification (requires
hvc1for HEVC) - W3C Media Source Extensions (SourceBuffer codec configuration constraints)
- ffmpeg
-tag:vmuxer option;hevc_metadatabitstream filter
Licensed under the site footer’s CC BY 4.0. If this saved you a debugging session, the optional thanks link in the footer is appreciated, no obligation.