diff --git a/modules/video_coding/h26x_packet_buffer.cc b/modules/video_coding/h26x_packet_buffer.cc index 3770edbf67..e6e74413c0 100644 --- a/modules/video_coding/h26x_packet_buffer.cc +++ b/modules/video_coding/h26x_packet_buffer.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -72,6 +73,16 @@ bool HasSps(const H26xPacketBuffer::Packet& packet) { }); } +int64_t* GetContinuousSequence(rtc::ArrayView last_continuous, + int64_t unwrapped_seq_num) { + for (int64_t& last : last_continuous) { + if (unwrapped_seq_num - 1 == last) { + return &last; + } + } + return nullptr; +} + #ifdef RTC_ENABLE_H265 bool HasVps(const H26xPacketBuffer::Packet& packet) { std::vector nalu_indices = H265::FindNaluIndices( @@ -88,7 +99,9 @@ bool HasVps(const H26xPacketBuffer::Packet& packet) { } // namespace H26xPacketBuffer::H26xPacketBuffer(bool h264_idr_only_keyframes_allowed) - : h264_idr_only_keyframes_allowed_(h264_idr_only_keyframes_allowed) {} + : h264_idr_only_keyframes_allowed_(h264_idr_only_keyframes_allowed) { + last_continuous_in_sequence_.fill(std::numeric_limits::min()); +} H26xPacketBuffer::InsertResult H26xPacketBuffer::InsertPacket( std::unique_ptr packet) { @@ -138,18 +151,25 @@ H26xPacketBuffer::InsertResult H26xPacketBuffer::FindFrames( // Check if the packet is continuous or the beginning of a new coded video // sequence. - if (unwrapped_seq_num - 1 != last_continuous_unwrapped_seq_num_) { - if (unwrapped_seq_num <= last_continuous_unwrapped_seq_num_ || - !BeginningOfStream(*packet)) { + int64_t* last_continuous_unwrapped_seq_num = + GetContinuousSequence(last_continuous_in_sequence_, unwrapped_seq_num); + if (last_continuous_unwrapped_seq_num == nullptr) { + if (!BeginningOfStream(*packet)) { return result; } - last_continuous_unwrapped_seq_num_ = unwrapped_seq_num; + last_continuous_in_sequence_[last_continuous_in_sequence_index_] = + unwrapped_seq_num; + last_continuous_unwrapped_seq_num = + &last_continuous_in_sequence_[last_continuous_in_sequence_index_]; + last_continuous_in_sequence_index_ = + (last_continuous_in_sequence_index_ + 1) % + last_continuous_in_sequence_.size(); } for (int64_t seq_num = unwrapped_seq_num; seq_num < unwrapped_seq_num + kBufferSize;) { - RTC_DCHECK_GE(seq_num, *last_continuous_unwrapped_seq_num_); + RTC_DCHECK_GE(seq_num, *last_continuous_unwrapped_seq_num); // Packets that were never assembled into a completed frame will stay in // the 'buffer_'. Check that the `packet` sequence number match the expected @@ -158,7 +178,7 @@ H26xPacketBuffer::InsertResult H26xPacketBuffer::FindFrames( return result; } - last_continuous_unwrapped_seq_num_ = seq_num; + *last_continuous_unwrapped_seq_num = seq_num; // Last packet of the frame, try to assemble the frame. if (packet->marker_bit) { uint32_t rtp_timestamp = packet->timestamp; diff --git a/modules/video_coding/h26x_packet_buffer.h b/modules/video_coding/h26x_packet_buffer.h index ef6d0db373..3576e6e459 100644 --- a/modules/video_coding/h26x_packet_buffer.h +++ b/modules/video_coding/h26x_packet_buffer.h @@ -72,6 +72,7 @@ class H26xPacketBuffer { }; static constexpr int kBufferSize = 2048; + static constexpr int kNumTrackedSequences = 5; std::unique_ptr& GetPacket(int64_t unwrapped_seq_num); bool BeginningOfStream(const Packet& packet) const; @@ -91,7 +92,8 @@ class H26xPacketBuffer { // Indicates whether IDR frames without SPS and PPS are allowed. const bool h264_idr_only_keyframes_allowed_; std::array, kBufferSize> buffer_; - absl::optional last_continuous_unwrapped_seq_num_; + std::array last_continuous_in_sequence_; + int64_t last_continuous_in_sequence_index_ = 0; SeqNumUnwrapper seq_num_unwrapper_; // Map from pps_pic_parameter_set_id to the PPS payload associated with this diff --git a/modules/video_coding/h26x_packet_buffer_unittest.cc b/modules/video_coding/h26x_packet_buffer_unittest.cc index 50f35f3643..3ceccd9cd1 100644 --- a/modules/video_coding/h26x_packet_buffer_unittest.cc +++ b/modules/video_coding/h26x_packet_buffer_unittest.cc @@ -1043,6 +1043,72 @@ TEST(H26xPacketBufferTest, FullPacketBufferDoesNotBlockKeyframe) { SizeIs(1)); } +TEST(H26xPacketBufferTest, AssembleFrameAfterReordering) { + H26xPacketBuffer packet_buffer(/*h264_allow_idr_only_keyframes=*/false); + + EXPECT_THAT(packet_buffer + .InsertPacket(H264Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(2) + .Time(2) + .Marker() + .Build()) + .packets, + SizeIs(1)); + + EXPECT_THAT(packet_buffer + .InsertPacket(H264Packet(kH264SingleNalu) + .Slice() + .SeqNum(1) + .Time(1) + .Marker() + .Build()) + .packets, + IsEmpty()); + + EXPECT_THAT(packet_buffer + .InsertPacket(H264Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(0) + .Time(0) + .Marker() + .Build()) + .packets, + SizeIs(2)); +} + +TEST(H26xPacketBufferTest, AssembleFrameAfterLoss) { + H26xPacketBuffer packet_buffer(/*h264_allow_idr_only_keyframes=*/false); + + EXPECT_THAT(packet_buffer + .InsertPacket(H264Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(0) + .Time(0) + .Marker() + .Build()) + .packets, + SizeIs(1)); + + EXPECT_THAT(packet_buffer + .InsertPacket(H264Packet(kH264StapA) + .Sps() + .Pps() + .Idr() + .SeqNum(2) + .Time(2) + .Marker() + .Build()) + .packets, + SizeIs(1)); +} + #ifdef RTC_ENABLE_H265 TEST(H26xPacketBufferTest, H265VpsSpsPpsIdrIsKeyframe) { H26xPacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false);