PR #25005 opened by seitzbg URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25005 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25005.patch
# Summary of the bug When frames carrying `AV_FRAME_DATA_S12M_TIMECODE` reach `hevc_nvenc` (for example frames decoded from HEVC with a time_code SEI, payload type 136), the default `-s12m_tc 1` inserts a time_code SEI built by `ff_alloc_timecode_sei()` in libavcodec/utils.c. That helper reports a fixed 16-byte payload (`*sei_size = sizeof(uint32_t) * 4`) in a zeroed buffer and omits the payload termination required by H.265 Annex D.2.1. Because it writes no extension data, it should append a one bit and zero alignment bits whenever its content is not byte aligned. For one timestamp, it writes 43 syntax bits, zero-pads the buffer, and reports 16 bytes without the terminating one bit. Representative source and encoded payloads (not the same frame; the helper rebuilds the SEI from the frame's S12M side data, which also resets `counting_type`): ``` source: payload_type 136, payload_size 6: 70 c0 b6 8a 84 10 hevc_nvenc: payload_type 136, payload_size 16: 70 40 46 aa 84 00 00 00 00 00 00 00 00 00 00 00 ``` Filters that parse the SEI payload reject it: ``` 70 time_offset_length[0] 00000 = 0 Failed to read unit 1 (type 39): Invalid data found when processing input. Error applying bitstream filters to a packet: Invalid data found when processing input ``` Packets carrying this SEI are rejected by filters that parse its payload, including `trace_headers`, `hevc_metadata`, and `dovi_rpu`. In my samples every encoded video packet carries it, so a filter like `dovi_rpu=strip=1` after the encoder produces no video packets at all. Plex Media Server (1.43.5, in the sessions I observed) appends `-bsf:0 dovi_rpu=strip=1` when transcoding Dolby Vision sources with `hevc_nvenc`, so those transcodes produce no video when the source carries timecode. In current master and 9.0.2, `hevc_nvenc` is the only encoder using this helper (H.264 uses the SDK timecode path and AV1 its own helper). In 6.1, nvenc sent every codec through it. Seen with 9.0.2 and a 6.1-based downstream build. The helper is unchanged in current master. # Steps to reproduce Requires an NVIDIA GPU. 1. Take an input whose decoded frames carry S12M timecode side data, e.g. HEVC with a time_code SEI (many UHD Blu-ray remuxes have one). 2. `ffmpeg -i input.mkv -map 0:v -t 10 -c:v hevc_nvenc out.mkv` 3. `ffmpeg -i out.mkv -map 0:v -c copy -bsf:v trace_headers -f null -` fails on every packet that carries the SEI. `-bsf:v dovi_rpu=strip=1` drops each of them. 4. Repeat step 2 with `-s12m_tc 0`. The output parses. In one 10-second comparison, all 483 compared non-timecode NAL units were byte-identical to the `-s12m_tc 1` output. # Fix When the content is not byte aligned, write `payload_bit_equal_to_one` and zero-pad to a byte boundary. Then set `*sei_size = put_bytes_output(&pb)` so the reported size matches what was written. For 1, 2 and 3 timestamps the payload becomes 6, 11 and 16 bytes, so the existing 16-byte buffer still fits. Also return no SEI for `num_clock_ts == 0`, which the syntax does not allow (CBS accepts 1 to 3), as `ff_alloc_timecode_metadata_av1()` already does. Testing: - The patched libavcodec/utils.c compiles without warnings (`./configure --disable-everything --disable-programs --disable-doc --disable-x86asm --disable-autodetect && make libavcodec/utils.o`, gcc 15.2). - I manually repaired the existing one-timestamp payloads of an affected `hevc_nvenc` stream by truncating them to six bytes and inserting the terminating one bit, modeling the patch's framing change. `trace_headers` then parses them (`bit_equal_to_one = 1`, no error), and `dovi_rpu=strip=1` passes 240 of 240 packets, against 0 before. This does not exercise the patched function itself, multiple timestamps, or the NVENC integration. - I have not run a full NVENC-enabled build with the patch. Related background: trac #8622 (2020), a distinct earlier case in which NVENC-produced SEIs with trailing bytes broke CBS filters and CBS gained reserved_payload_extension_data parsing. >From ad5360dadf1c11f50fb70f0bbc93aa766dad21ef Mon Sep 17 00:00:00 2001 From: Bryan Seitz <[email protected]> Date: Sat, 10 Oct 2026 23:47:16 -0400 Subject: [PATCH] avcodec/utils: terminate the timecode SEI payload and report its real size ff_alloc_timecode_sei() always reported a 16-byte payload and never wrote the payload_bit_equal_to_one / payload_bit_equal_to_zero termination that H.265 Annex D.2.1 requires. With one clock timestamp the helper writes 43 syntax bits, zero-pads the buffer and reports 16 bytes with no terminating one bit. Filters that parse the SEI payload (trace_headers, hevc_metadata, dovi_rpu) reject every packet carrying it with "Failed to read unit". The helper writes no extension data, so append a one bit and zero alignment bits whenever the content is not byte aligned, and return the number of bytes actually written. Also emit nothing for num_clock_ts == 0, which the syntax does not allow, as ff_alloc_timecode_metadata_av1() already does. Signed-off-by: Bryan Seitz <[email protected]> --- libavcodec/utils.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 306696ecc5..ae2e62bd9c 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -1027,6 +1027,10 @@ int ff_alloc_timecode_sei(const AVFrame *frame, AVRational rate, size_t prefix_l } tc = (uint32_t*)sd->data; m = tc[0] & 3; + if (!m) { // num_clock_ts must be 1..3 + *data = NULL; + return 0; + } *sei_size = sizeof(uint32_t) * 4; *data = av_mallocz(*sei_size + prefix_len); @@ -1043,7 +1047,12 @@ int ff_alloc_timecode_sei(const AVFrame *frame, AVRational rate, size_t prefix_l put_timecode_fields(&pb, rate, tc[j]); put_bits(&pb, 5, 0); // time_offset_length } + if (put_bits_count(&pb) & 7) { + put_bits(&pb, 1, 1); // payload_bit_equal_to_one + align_put_bits(&pb); // payload_bit_equal_to_zero + } flush_put_bits(&pb); + *sei_size = put_bytes_output(&pb); return 0; } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
