diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.cc b/webrtc/modules/video_coding/main/source/jitter_buffer.cc index cd914be8ff..686b182b3d 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.cc @@ -573,6 +573,9 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, last_decoded_state_.UpdateOldPacket(&packet); DropPacketsFromNackList(last_decoded_state_.sequence_num()); + // Also see if this old packet made more incomplete frames continuous. + FindAndInsertContinuousFramesWithState(last_decoded_state_); + if (num_consecutive_old_packets_ > kMaxConsecutiveOldPackets) { LOG(LS_WARNING) << num_consecutive_old_packets_ @@ -749,6 +752,16 @@ void VCMJitterBuffer::FindAndInsertContinuousFrames( VCMDecodingState decoding_state; decoding_state.CopyFrom(last_decoded_state_); decoding_state.SetState(&new_frame); + FindAndInsertContinuousFramesWithState(decoding_state); +} + +void VCMJitterBuffer::FindAndInsertContinuousFramesWithState( + const VCMDecodingState& original_decoded_state) { + // Copy original_decoded_state so we can move the state forward with each + // decodable frame we find. + VCMDecodingState decoding_state; + decoding_state.CopyFrom(original_decoded_state); + // When temporal layers are available, we search for a complete or decodable // frame until we hit one of the following: // 1. Continuous base or sync layer. @@ -756,7 +769,8 @@ void VCMJitterBuffer::FindAndInsertContinuousFrames( for (FrameList::iterator it = incomplete_frames_.begin(); it != incomplete_frames_.end();) { VCMFrameBuffer* frame = it->second; - if (IsNewerTimestamp(new_frame.TimeStamp(), frame->TimeStamp())) { + if (IsNewerTimestamp(original_decoded_state.time_stamp(), + frame->TimeStamp())) { ++it; continue; } diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.h b/webrtc/modules/video_coding/main/source/jitter_buffer.h index d2cbc27ec6..5ccc92e814 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.h +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.h @@ -214,6 +214,12 @@ class VCMJitterBuffer { // all decodable frames into account. bool IsContinuous(const VCMFrameBuffer& frame) const EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + // Looks for frames in |incomplete_frames_| which are continuous in the + // provided |decoded_state|. Starts the search from the timestamp of + // |decoded_state|. + void FindAndInsertContinuousFramesWithState( + const VCMDecodingState& decoded_state) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); // Looks for frames in |incomplete_frames_| which are continuous in // |last_decoded_state_| taking all decodable frames into account. Starts // the search from |new_frame|. diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc index d70973ef0d..36abf6d1c9 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc @@ -552,6 +552,63 @@ TEST_F(TestBasicJitterBuffer, FrameReordering2Frames2PacketsEach) { jitter_buffer_->ReleaseFrame(frame_out); } +TEST_F(TestBasicJitterBuffer, TestReorderingWithPadding) { + packet_->frameType = kVideoFrameKey; + packet_->isFirstPacket = true; + packet_->markerBit = true; + + // Send in an initial good packet/frame (Frame A) to start things off. + bool retransmitted = false; + EXPECT_EQ(kCompleteSession, + jitter_buffer_->InsertPacket(*packet_, &retransmitted)); + VCMEncodedFrame* frame_out = DecodeCompleteFrame(); + EXPECT_TRUE(frame_out != NULL); + jitter_buffer_->ReleaseFrame(frame_out); + + // Now send in a complete delta frame (Frame C), but with a sequence number + // gap. No pic index either, so no temporal scalability cheating :) + packet_->frameType = kVideoFrameDelta; + // Leave a gap of 2 sequence numbers and two frames. + packet_->seqNum = seq_num_ + 3; + packet_->timestamp = timestamp_ + (66 * 90); + // Still isFirst = marker = true. + // Session should be complete (frame is complete), but there's nothing to + // decode yet. + EXPECT_EQ(kCompleteSession, + jitter_buffer_->InsertPacket(*packet_, &retransmitted)); + frame_out = DecodeCompleteFrame(); + EXPECT_TRUE(frame_out == NULL); + + // Now send in a complete delta frame (Frame B) that is continuous from A, but + // doesn't fill the full gap to C. The rest of the gap is going to be padding. + packet_->seqNum = seq_num_ + 1; + packet_->timestamp = timestamp_ + (33 * 90); + // Still isFirst = marker = true. + EXPECT_EQ(kCompleteSession, + jitter_buffer_->InsertPacket(*packet_, &retransmitted)); + frame_out = DecodeCompleteFrame(); + EXPECT_TRUE(frame_out != NULL); + jitter_buffer_->ReleaseFrame(frame_out); + + // But Frame C isn't continuous yet. + frame_out = DecodeCompleteFrame(); + EXPECT_TRUE(frame_out == NULL); + + // Add in the padding. These are empty packets (data length is 0) with no + // marker bit and matching the timestamp of Frame B. + VCMPacket empty_packet(data_, 0, seq_num_ + 2, timestamp_ + (33 * 90), false); + EXPECT_EQ(kOldPacket, + jitter_buffer_->InsertPacket(empty_packet, &retransmitted)); + empty_packet.seqNum += 1; + EXPECT_EQ(kOldPacket, + jitter_buffer_->InsertPacket(empty_packet, &retransmitted)); + + // But now Frame C should be ready! + frame_out = DecodeCompleteFrame(); + EXPECT_TRUE(frame_out != NULL); + jitter_buffer_->ReleaseFrame(frame_out); +} + TEST_F(TestBasicJitterBuffer, DuplicatePackets) { packet_->frameType = kVideoFrameKey; packet_->isFirstPacket = true;