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();
}
absl::optional<uint32_t> PpsParser::ParsePpsIdFromSlice(const uint8_t* data,
absl::optional<PpsParser::SliceHeader> PpsParser::ParseSliceHeader(
const uint8_t* data,
size_t length) {
std::vector<uint8_t> unpacked_buffer = H264::ParseRbsp(data, length);
BitstreamReader slice_reader(unpacked_buffer);
PpsParser::SliceHeader slice_header;
// first_mb_in_slice: ue(v)
slice_reader.ReadExponentialGolomb();
slice_header.first_mb_in_slice = slice_reader.ReadExponentialGolomb();
// slice_type: ue(v)
slice_reader.ReadExponentialGolomb();
// 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()) {
return absl::nullopt;
}
return slice_pps_id;
return slice_header;
}
absl::optional<PpsParser::PpsState> PpsParser::ParseInternal(

View File

@ -37,6 +37,13 @@ class PpsParser {
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.
static absl::optional<PpsState> ParsePps(const uint8_t* data, size_t length);
@ -45,7 +52,7 @@ class PpsParser {
uint32_t* pps_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);
protected:

View File

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

View File

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