From 93a3fb987aaa5b7fea1fb8076c5e791a2396bee2 Mon Sep 17 00:00:00 2001 From: Robert Edmonds Date: Mon, 18 Dec 2023 16:30:43 -0500 Subject: [PATCH] H265NalUnitFragment: Mask `nuhTempIdPlus1` correctly Previously, `H265NalUnitFragment::fragmentsFrom()` was masking the value of nuhTempIdPlus1 against the value 0xE (0b1110) which was corrupting the generated NALU fragments. This commit updates the `fragmentsFrom()` method to mask against the value 0x7 (0b111) instead. However, it looks like the masking in this method is redundant with the equivalent masking that is done by `H265NalUnitHeader`'s setters. According to [RFC 7798](https://datatracker.ietf.org/doc/html/rfc7798#section-1.1.4): 1.1.4. NAL Unit Header HEVC maintains the NAL unit concept of H.264 with modifications. HEVC uses a two-byte NAL unit header, as shown in Figure 1. The payload of a NAL unit refers to the NAL unit excluding the NAL unit header. +---------------+---------------+ |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |F| Type | LayerId | TID | +-------------+-----------------+ Figure 1: The Structure of the HEVC NAL Unit Header [...] TID: 3 bits nuh_temporal_id_plus1. This field specifies the temporal identifier of the NAL unit plus 1. The value of TemporalId is equal to TID minus 1. A TID value of 0 is illegal to ensure that there is at least one bit in the NAL unit header equal to 1, so to enable independent considerations of start code emulations in the NAL unit header and in the NAL unit payload data. The minimum value of the "TID" field is 1, because it will have 1 subtracted from it to recover the original HEVC "TemporalId" value. Without this commit, the RTP fragments being generated by libdatachannel's `H265RtpPacketizer` were being rejected by this consistency check in libavformat: https://github.com/FFmpeg/FFmpeg/blob/release/6.1/libavformat/rtpdec_hevc.c#L219-L223 With this commit, I can successfully relay HEVC video from ffmpeg through a libdatachannel program (based on the media-sender and media-receiver examples) and play it with ffplay. --- src/h265nalunit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/h265nalunit.cpp b/src/h265nalunit.cpp index b196afc72..e81c48d29 100644 --- a/src/h265nalunit.cpp +++ b/src/h265nalunit.cpp @@ -38,7 +38,7 @@ H265NalUnitFragment::fragmentsFrom(shared_ptr nalu, uint16_t maximu maximumFragmentSize -= (H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE); auto f = nalu->forbiddenBit(); uint8_t nuhLayerId = nalu->nuhLayerId() & 0x3F; // 6 bits - uint8_t nuhTempIdPlus1 = nalu->nuhTempIdPlus1() & 0xE; // 3 bits + uint8_t nuhTempIdPlus1 = nalu->nuhTempIdPlus1() & 0x7; // 3 bits uint8_t naluType = nalu->unitType() & 0x3F; // 6 bits auto payload = nalu->payload(); vector> result{};