Skip to content

fix(playback): use hev1 when HEVC parameter sets are in-band - #411

Closed
A-wes wants to merge 1 commit into
lostb1t:mainfrom
A-wes:fix/hevc-empty-hvcc-box
Closed

fix(playback): use hev1 when HEVC parameter sets are in-band#411
A-wes wants to merge 1 commit into
lostb1t:mainfrom
A-wes:fix/hevc-empty-hvcc-box

Conversation

@A-wes

@A-wes A-wes commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Hello! So while my 10-bit streams weren't working I tried to use 8-bit alternatives, only to discover that they seem to crash clients too, but this time due to other issue. Unfortunately I do not have deep codecs knowledge, unlike claude, so submitting it as is. Both code in this PR and explanation below are written by Claude. I did verify this fix worked for WEB-DL playback on my NVIDIA Shield

When stream-copying HEVC into fMP4/HLS, -tag:v hvc1 asserts that the sample entry carries a complete HEVCDecoderConfigurationRecord with VPS, SPS and PPS out-of-band. Some sources — typically WEB-DL repackages from third-party muxers — report extradata that is a bare header-only record with numOfArrays = 0, leaving parameter sets in-band in the bitstream. Forcing hvc1 on those writes an empty 8-byte hvcC box, which ExoPlayer-based clients cannot initialise a decoder from. hev1 degrades gracefully in the same situation.

ffprobe reports extradata_size in its default -show_streams output, and per ISO/IEC 14496-15 §8.3.3.1.2 the fixed header of a HEVCDecoderConfigurationRecord is exactly 23 bytes, so extradata_size > 23 is a sound, parsing-free signal for "has real out-of-band parameter sets" — no need to decode the extradata itself.

probe.rs captures extradata_size per HEVC video stream and derives hevc_params_out_of_band, exposed as an additive remux-namespace field on MediaStream (MediaStreamRemuxExt, following the existing extension pattern). It is threaded through TranscodeSession / TranscodeParams / ProgressiveTranscodeParams to both -tag:v sites via a new hevc_copy_tag() helper.

The helper defaults to today's hvc1 whenever the signal is unknown or positive, so a source that works now cannot regress.

Note: -bsf:v extract_extradata is a commonly cited fix for this symptom and was deliberately not used — it requires Annex-B NAL framing and fails with "No start code is found" against Matroska's length-prefixed HEVC packets, which would break every HEVC copy from an MKV source.

When stream-copying HEVC into fMP4/HLS, `-tag:v hvc1` asserts that the
sample entry carries a complete `HEVCDecoderConfigurationRecord` with VPS,
SPS and PPS out-of-band. Some sources — typically WEB-DL repackages from
third-party muxers — report extradata that is a bare header-only record with
`numOfArrays = 0`, leaving parameter sets in-band in the bitstream. Forcing
`hvc1` on those writes an empty 8-byte `hvcC` box, which ExoPlayer-based
clients cannot initialise a decoder from. `hev1` degrades gracefully in the
same situation.

`ffprobe` reports `extradata_size` in its default `-show_streams` output, and
per ISO/IEC 14496-15 §8.3.3.1.2 the fixed header of a
`HEVCDecoderConfigurationRecord` is exactly 23 bytes, so `extradata_size > 23`
is a sound, parsing-free signal for "has real out-of-band parameter sets" —
no need to decode the extradata itself.

`probe.rs` captures `extradata_size` per HEVC video stream and derives
`hevc_params_out_of_band`, exposed as an additive `remux`-namespace field on
`MediaStream` (`MediaStreamRemuxExt`, following the existing extension
pattern). It is threaded through `TranscodeSession` / `TranscodeParams` /
`ProgressiveTranscodeParams` to both `-tag:v` sites via a new
`hevc_copy_tag()` helper.

The helper defaults to today's `hvc1` whenever the signal is unknown or
positive, so a source that works now cannot regress.

The HLS master playlist's CODECS attribute follows the same decision, since it
has to name the fourcc the segments actually carry — a manifest advertising
`hvc1.*` over `hev1` sample entries is rejected by clients that validate CODECS
through MSE. `hevc_hls_codec_string()` now takes the tag from its caller: the
copy path passes `hevc_copy_tag()`, while the encode path passes `hvc1`
unconditionally, because no `-tag:v` is forced there and ffmpeg writes its own
`hvc1` regardless of what the source extradata looked like.

Note: `-bsf:v extract_extradata` is a commonly cited fix for this symptom and
was deliberately not used — it requires Annex-B NAL framing and fails with
"No start code is found" against Matroska's length-prefixed HEVC packets,
which would break every HEVC copy from an MKV source.
@lostb1t

lostb1t commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Superseded by #413, which takes the signal from the client's DeviceProfile instead of the source's extradata. I kept your CODECS fix.

Would appreciate you testing #413 on your Shield if you get a chance

@lostb1t lostb1t closed this Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants