Fix is_first_packet_in_frame when receiving multiple slices per H264 frame

Bug: webrtc:346608838
Change-Id: I70ad3a952f37dde878f77d35c959c6973d283b9c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/354460
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42497}
This commit is contained in:
Sergio Garcia Murillo 2024-06-13 11:09:52 +02:00 committed by WebRTC LUCI CQ
parent a0b22af9e1
commit e19ce9b3db
4 changed files with 51 additions and 25 deletions

View File

@ -53,21 +53,26 @@ bool PpsParser::ParsePpsIds(const uint8_t* data,
return reader.Ok(); return reader.Ok();
} }
absl::optional<uint32_t> PpsParser::ParsePpsIdFromSlice(const uint8_t* data, absl::optional<PpsParser::SliceHeader> PpsParser::ParseSliceHeader(
size_t length) { const uint8_t* data,
size_t length) {
std::vector<uint8_t> unpacked_buffer = H264::ParseRbsp(data, length); std::vector<uint8_t> unpacked_buffer = H264::ParseRbsp(data, length);
BitstreamReader slice_reader(unpacked_buffer); BitstreamReader slice_reader(unpacked_buffer);
PpsParser::SliceHeader slice_header;
// first_mb_in_slice: ue(v) // first_mb_in_slice: ue(v)
slice_reader.ReadExponentialGolomb(); slice_header.first_mb_in_slice = slice_reader.ReadExponentialGolomb();
// slice_type: ue(v) // slice_type: ue(v)
slice_reader.ReadExponentialGolomb(); slice_reader.ReadExponentialGolomb();
// pic_parameter_set_id: ue(v) // pic_parameter_set_id: ue(v)
uint32_t slice_pps_id = slice_reader.ReadExponentialGolomb(); slice_header.pic_parameter_set_id = slice_reader.ReadExponentialGolomb();
// The rest of the slice header requires information from the SPS to parse.
if (!slice_reader.Ok()) { if (!slice_reader.Ok()) {
return absl::nullopt; return absl::nullopt;
} }
return slice_pps_id; return slice_header;
} }
absl::optional<PpsParser::PpsState> PpsParser::ParseInternal( absl::optional<PpsParser::PpsState> PpsParser::ParseInternal(

View File

@ -37,6 +37,13 @@ class PpsParser {
uint32_t sps_id = 0; uint32_t sps_id = 0;
}; };
struct SliceHeader {
SliceHeader() = default;
uint32_t first_mb_in_slice = 0;
uint32_t pic_parameter_set_id = 0;
};
// Unpack RBSP and parse PPS state from the supplied buffer. // Unpack RBSP and parse PPS state from the supplied buffer.
static absl::optional<PpsState> ParsePps(const uint8_t* data, size_t length); static absl::optional<PpsState> ParsePps(const uint8_t* data, size_t length);
@ -45,7 +52,7 @@ class PpsParser {
uint32_t* pps_id, uint32_t* pps_id,
uint32_t* sps_id); uint32_t* sps_id);
static absl::optional<uint32_t> ParsePpsIdFromSlice(const uint8_t* data, static absl::optional<SliceHeader> ParseSliceHeader(const uint8_t* data,
size_t length); size_t length);
protected: protected:

View File

@ -214,7 +214,7 @@ TEST_F(PpsParserTest, MaxPps) {
RunTest(); RunTest();
} }
TEST_F(PpsParserTest, PpsIdFromSlice) { TEST_F(PpsParserTest, ParseSliceHeader) {
std::vector<H264::NaluIndex> nalu_indices = std::vector<H264::NaluIndex> nalu_indices =
H264::FindNaluIndices(kH264BitstreamChunk, sizeof(kH264BitstreamChunk)); H264::FindNaluIndices(kH264BitstreamChunk, sizeof(kH264BitstreamChunk));
EXPECT_EQ(nalu_indices.size(), 3ull); EXPECT_EQ(nalu_indices.size(), 3ull);
@ -222,9 +222,14 @@ TEST_F(PpsParserTest, PpsIdFromSlice) {
H264::NaluType nalu_type = H264::NaluType nalu_type =
H264::ParseNaluType(kH264BitstreamChunk[index.payload_start_offset]); H264::ParseNaluType(kH264BitstreamChunk[index.payload_start_offset]);
if (nalu_type == H264::NaluType::kIdr) { if (nalu_type == H264::NaluType::kIdr) {
absl::optional<uint32_t> pps_id = PpsParser::ParsePpsIdFromSlice( // Skip NAL type header and parse slice header.
kH264BitstreamChunk + index.payload_start_offset, index.payload_size); absl::optional<PpsParser::SliceHeader> slice_header =
EXPECT_EQ(pps_id, 0u); PpsParser::ParseSliceHeader(
kH264BitstreamChunk + index.payload_start_offset + 1,
index.payload_size - 1);
ASSERT_TRUE(slice_header.has_value());
EXPECT_EQ(slice_header->first_mb_in_slice, 0u);
EXPECT_EQ(slice_header->pic_parameter_set_id, 0u);
break; break;
} }
} }

View File

@ -196,10 +196,13 @@ absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> ProcessStapAOrSingleNalu(
VideoFrameType::kVideoFrameKey; VideoFrameType::kVideoFrameKey;
[[fallthrough]]; [[fallthrough]];
case H264::NaluType::kSlice: { case H264::NaluType::kSlice: {
absl::optional<uint32_t> pps_id = PpsParser::ParsePpsIdFromSlice( absl::optional<PpsParser::SliceHeader> slice_header =
&payload_data[start_offset], end_offset - start_offset); PpsParser::ParseSliceHeader(&payload_data[start_offset],
if (pps_id) { end_offset - start_offset);
nalu.pps_id = *pps_id; if (slice_header) {
nalu.pps_id = slice_header->pic_parameter_set_id;
parsed_payload->video_header.is_first_packet_in_frame &=
slice_header->first_mb_in_slice == 0;
} else { } else {
RTC_LOG(LS_WARNING) << "Failed to parse PPS id from slice of type: " RTC_LOG(LS_WARNING) << "Failed to parse PPS id from slice of type: "
<< static_cast<int>(nalu.type); << static_cast<int>(nalu.type);
@ -237,21 +240,26 @@ absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> ParseFuaNalu(
uint8_t fnri = rtp_payload.cdata()[0] & (kH264FBit | kH264NriMask); uint8_t fnri = rtp_payload.cdata()[0] & (kH264FBit | kH264NriMask);
uint8_t original_nal_type = rtp_payload.cdata()[1] & kH264TypeMask; uint8_t original_nal_type = rtp_payload.cdata()[1] & kH264TypeMask;
bool first_fragment = (rtp_payload.cdata()[1] & kH264SBit) > 0; bool first_fragment = (rtp_payload.cdata()[1] & kH264SBit) > 0;
bool is_first_packet_in_frame = false;
NaluInfo nalu; NaluInfo nalu;
nalu.type = original_nal_type; nalu.type = original_nal_type;
nalu.sps_id = -1; nalu.sps_id = -1;
nalu.pps_id = -1; nalu.pps_id = -1;
if (first_fragment) { if (first_fragment) {
absl::optional<uint32_t> pps_id = if (original_nal_type == H264::NaluType::kIdr ||
PpsParser::ParsePpsIdFromSlice(rtp_payload.cdata() + 2 * kNalHeaderSize, original_nal_type == H264::NaluType::kSlice) {
rtp_payload.size() - 2 * kNalHeaderSize); absl::optional<PpsParser::SliceHeader> slice_header =
if (pps_id) { PpsParser::ParseSliceHeader(rtp_payload.cdata() + 2 * kNalHeaderSize,
nalu.pps_id = *pps_id; rtp_payload.size() - 2 * kNalHeaderSize);
} else { if (slice_header) {
RTC_LOG(LS_WARNING) nalu.pps_id = slice_header->pic_parameter_set_id;
<< "Failed to parse PPS from first fragment of FU-A NAL " is_first_packet_in_frame = slice_header->first_mb_in_slice == 0;
"unit with original type: " } else {
<< static_cast<int>(nalu.type); RTC_LOG(LS_WARNING)
<< "Failed to parse PPS from first fragment of FU-A NAL "
"unit with original type: "
<< static_cast<int>(nalu.type);
}
} }
uint8_t original_nal_header = fnri | original_nal_type; uint8_t original_nal_header = fnri | original_nal_type;
rtp_payload = rtp_payload =
@ -272,7 +280,8 @@ absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> ParseFuaNalu(
parsed_payload->video_header.height = 0; parsed_payload->video_header.height = 0;
parsed_payload->video_header.codec = kVideoCodecH264; parsed_payload->video_header.codec = kVideoCodecH264;
parsed_payload->video_header.simulcastIdx = 0; parsed_payload->video_header.simulcastIdx = 0;
parsed_payload->video_header.is_first_packet_in_frame = first_fragment; parsed_payload->video_header.is_first_packet_in_frame =
is_first_packet_in_frame;
auto& h264_header = parsed_payload->video_header.video_type_header auto& h264_header = parsed_payload->video_header.video_type_header
.emplace<RTPVideoHeaderH264>(); .emplace<RTPVideoHeaderH264>();
h264_header.packetization_type = kH264FuA; h264_header.packetization_type = kH264FuA;