diff options
author | agalusza@google.com <agalusza@google.com@4adac7df-926f-26a2-2b94-8c16560cd09d> | 2013-08-08 01:12:33 +0000 |
---|---|---|
committer | agalusza@google.com <agalusza@google.com@4adac7df-926f-26a2-2b94-8c16560cd09d> | 2013-08-08 01:12:33 +0000 |
commit | d177c10e2de490cfa001a00ca4d0ae654292afc7 (patch) | |
tree | 6a849807b67da3decff8edfc942f44950b03674e | |
parent | 676ff1ed893d6ae59a7ce29a4428e0d7c9f855d9 (diff) | |
download | webrtc-d177c10e2de490cfa001a00ca4d0ae654292afc7.tar.gz |
Added logic for kSelectiveErrors to VCMJitterBuffer and corresponding unit tests.
R=marpan@google.com, mikhal@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/1943004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4503 4adac7df-926f-26a2-2b94-8c16560cd09d
8 files changed, 374 insertions, 54 deletions
diff --git a/webrtc/modules/video_coding/main/source/frame_buffer.cc b/webrtc/modules/video_coding/main/source/frame_buffer.cc index 32481e2a1d..d2d75fc971 100644 --- a/webrtc/modules/video_coding/main/source/frame_buffer.cc +++ b/webrtc/modules/video_coding/main/source/frame_buffer.cc @@ -207,6 +207,14 @@ VCMFrameBuffer::Reset() { VCMEncodedFrame::Reset(); } +void +VCMFrameBuffer::ClearStateIfIncomplete() { + if (_state == kStateDecodable) { + _state = kStateIncomplete; + _sessionInfo.ClearStateIfIncomplete(); + } +} + // Set state of frame void VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state) { diff --git a/webrtc/modules/video_coding/main/source/frame_buffer.h b/webrtc/modules/video_coding/main/source/frame_buffer.h index 8bd9690164..0fe270bfa7 100644 --- a/webrtc/modules/video_coding/main/source/frame_buffer.h +++ b/webrtc/modules/video_coding/main/source/frame_buffer.h @@ -83,6 +83,12 @@ class VCMFrameBuffer : public VCMEncodedFrame { // them. int NotDecodablePackets() const; + // If _state is kStateDecodable, changes it to kStateIncomplete. + // Used by the dual decoder. After the mode is changed to kNoErrors from + // kWithErrors or kSelective errors, any states that have been marked + // decodable and are not complete are marked as non-decodable. + void ClearStateIfIncomplete(); + private: void SetState(VCMFrameBufferStateEnum state); // Set state of frame diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.cc b/webrtc/modules/video_coding/main/source/jitter_buffer.cc index 17218f87bd..aaabab140f 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.cc @@ -427,9 +427,11 @@ bool VCMJitterBuffer::CompleteSequenceWithNextFrame() { CriticalSectionScoped cs(crit_sect_); // Finding oldest frame ready for decoder, check sequence number and size CleanUpOldOrEmptyFrames(); - if (!decodable_frames_.empty()) - return true; - if (incomplete_frames_.size() <= 1) { + if (!decodable_frames_.empty()) { + if (decodable_frames_.Front()->GetState() == kStateComplete) { + return true; + } + } else if (incomplete_frames_.size() <= 1) { // Frame not ready to be decoded. return true; } @@ -447,7 +449,8 @@ bool VCMJitterBuffer::NextCompleteTimestamp( } CleanUpOldOrEmptyFrames(); - if (decodable_frames_.empty()) { + if (decodable_frames_.empty() || + decodable_frames_.Front()->GetState() != kStateComplete) { const int64_t end_wait_time_ms = clock_->TimeInMilliseconds() + max_wait_time_ms; int64_t wait_time_ms = max_wait_time_ms; @@ -464,7 +467,8 @@ bool VCMJitterBuffer::NextCompleteTimestamp( } // Finding oldest frame ready for decoder. CleanUpOldOrEmptyFrames(); - if (decodable_frames_.empty()) { + if (decodable_frames_.empty() || + decodable_frames_.Front()->GetState() != kStateComplete) { wait_time_ms = end_wait_time_ms - clock_->TimeInMilliseconds(); } else { break; @@ -478,7 +482,8 @@ bool VCMJitterBuffer::NextCompleteTimestamp( // We already have a frame, reset the event. frame_event_->Reset(); } - if (decodable_frames_.empty()) { + if (decodable_frames_.empty() || + decodable_frames_.Front()->GetState() != kStateComplete) { crit_sect_->Leave(); return false; } @@ -499,14 +504,16 @@ bool VCMJitterBuffer::NextMaybeIncompleteTimestamp(uint32_t* timestamp) { CleanUpOldOrEmptyFrames(); - VCMFrameBuffer* oldest_frame = NextFrame(); - if (!oldest_frame) { + if (decodable_frames_.empty()) { return false; } - if (decodable_frames_.empty() && incomplete_frames_.size() <= 1 + VCMFrameBuffer* oldest_frame = decodable_frames_.Front(); + + // If we have exactly one frame in the buffer, release it only if it is + // complete. We know decodable_frames_ is not empty due to the prevoius + // check. + if (decodable_frames_.size() == 1 && incomplete_frames_.empty() && oldest_frame->GetState() != kStateComplete) { - // If we have only one frame in the buffer, release it only if it is - // complete. return false; } // Always start with a complete key frame. @@ -749,19 +756,18 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, " size:%d type %d", this, frame, frame->Length(), frame->FrameType()); } - // Don't let the first packet be overridden by a complete session. - ret = kCompleteSession; - // Only update return value for a JB flush indicator. *retransmitted = (frame->GetNackCount() > 0); CountFrame(*frame); frame->SetCountedFrame(true); *retransmitted = (frame->GetNackCount() > 0); if (IsContinuous(*frame) && previous_state != kStateComplete) { - if (!first) { - incomplete_frames_.PopFrame(packet.timestamp); + if (previous_state != kStateDecodable) { + if (!first) { + incomplete_frames_.PopFrame(packet.timestamp); + } + decodable_frames_.InsertFrame(frame); + FindAndInsertContinuousFrames(*frame); } - decodable_frames_.InsertFrame(frame); - FindAndInsertContinuousFrames(*frame); // Signal that we have a decodable frame. frame_event_->Set(); } else if (first) { @@ -772,6 +778,23 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, break; } case kDecodableSession: + assert(previous_state != kStateComplete); + *retransmitted = (frame->GetNackCount() > 0); + frame->SetCountedFrame(true); + if ((IsContinuous(*frame) || decode_error_mode_ == kWithErrors) + && previous_state != kStateDecodable) { + incomplete_frames_.PopFrame(packet.timestamp); + decodable_frames_.InsertFrame(frame); + FindAndInsertContinuousFrames(*frame); + // Signal that we have a decodable frame. + frame_event_->Set(); + } else if (first) { + ret = kFirstPacket; + incomplete_frames_.InsertFrame(frame); + } + // Signal that we have a received packet. + packet_event_->Set(); + break; case kIncomplete: { // No point in storing empty continuous frames. if (frame->GetState() == kStateEmpty && @@ -780,6 +803,9 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, frame->Reset(); frame = NULL; ret = kNoError; + } else if (previous_state == kStateDecodable) { + decodable_frames_.PopFrame(packet.timestamp); + incomplete_frames_.InsertFrame(frame); } else if (first) { ret = kFirstPacket; incomplete_frames_.InsertFrame(frame); @@ -957,9 +983,10 @@ uint16_t* VCMJitterBuffer::GetNackList(uint16_t* nack_list_size, return NULL; } if (last_decoded_state_.in_initial_state()) { - const bool first_frame_is_key = NextFrame() && - NextFrame()->FrameType() == kVideoFrameKey && - NextFrame()->HaveFirstPacket(); + VCMFrameBuffer* next_frame = NextFrame(); + const bool first_frame_is_key = next_frame && + next_frame->FrameType() == kVideoFrameKey && + next_frame->HaveFirstPacket(); if (!first_frame_is_key) { bool have_non_empty_frame = decodable_frames_.end() != find_if( decodable_frames_.begin(), decodable_frames_.end(), @@ -1015,6 +1042,39 @@ uint16_t* VCMJitterBuffer::GetNackList(uint16_t* nack_list_size, return &nack_seq_nums_[0]; } +void VCMJitterBuffer::DecodeErrorMode(VCMDecodeErrorMode error_mode) { + // If we are not moving from kWithErrors or KSelectiveErrors to kNoErrors, + // set decode_error_mode_ and apply new error mode only to new packets. + // Also no need for further processing if we have no old, previously + // decodable (and potentially incomplete) frames. + if (decode_error_mode_ == kNoErrors || error_mode != kNoErrors || + decodable_frames_.empty()) { + decode_error_mode_ = error_mode; + return; + } else { + // We just went from kWithErrors or kSelectiveErrors to kNoErrors. Make + // sure no incomplete frames are marked decodable. + // Begin by skipping over all complete frames. + FrameList::const_iterator it = decodable_frames_.begin(); + VCMFrameBuffer* frame; + for (; it != decodable_frames_.end(); ++it) { + if (it->second->GetState() != kStateComplete) { + break; + } + } + // Continue from first incomplete and previously decodable frame and move + // subsequent frames to incomplete_frames_. + while (it != decodable_frames_.end()) { + frame = it->second; + ++it; + frame = decodable_frames_.PopFrame(frame->TimeStamp()); + frame->ClearStateIfIncomplete(); + incomplete_frames_.InsertFrame(frame); + } + decode_error_mode_ = error_mode; + } +} + VCMFrameBuffer* VCMJitterBuffer::NextFrame() const { if (!decodable_frames_.empty()) return decodable_frames_.Front(); diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.h b/webrtc/modules/video_coding/main/source/jitter_buffer.h index ab429a7fe8..e340cb36a1 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.h +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.h @@ -175,10 +175,11 @@ class VCMJitterBuffer { // Returns a list of the sequence numbers currently missing. uint16_t* GetNackList(uint16_t* nack_list_size, bool* request_key_frame); - // Enable/disable decoding with errors. - // TODO(agalusza): Add logic for handling kSelectiveErrors. - void DecodeErrorMode(VCMDecodeErrorMode error_mode) - {decode_error_mode_ = error_mode;} + // Set decode error mode. Setting kNoErrors will have immediate effect. + // Setting kWithErrors and kSelectiveErrors will take full effect once the + // existing incomplete frames leave the JB or have a packet added (as that + // would cause their state to be reevlauated). + void DecodeErrorMode(VCMDecodeErrorMode error_mode); int64_t LastDecodedTimestamp() const; VCMDecodeErrorMode decode_error_mode() const {return decode_error_mode_;} @@ -245,6 +246,8 @@ class VCMJitterBuffer { bool RecycleFramesUntilKeyFrame(); // Updates the frame statistics. + // Counts only complete frames, so decodable incomplete frames will not be + // counted. void CountFrame(const VCMFrameBuffer& frame); // Update rolling average of packets per 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 1020aaac2a..678d7f30eb 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc @@ -69,6 +69,7 @@ class TestBasicJitterBuffer : public ::testing::Test { VCMEncodedFrame* frame = jitter_buffer_->ExtractAndSetDecode(timestamp); return frame; } + void CheckOutFrame(VCMEncodedFrame* frame_out, unsigned int size, bool startCode) { @@ -242,6 +243,13 @@ TEST_F(TestBasicJitterBuffer, StopRunning) { EXPECT_TRUE(NULL == DecodeCompleteFrame()); EXPECT_TRUE(NULL == DecodeIncompleteFrame()); jitter_buffer_->Start(); + // Allow selective errors. + jitter_buffer_->DecodeErrorMode(kSelectiveErrors); + + // No packets inserted. + EXPECT_TRUE(NULL == DecodeCompleteFrame()); + EXPECT_TRUE(NULL == DecodeIncompleteFrame()); + // Allow decoding with errors. jitter_buffer_->DecodeErrorMode(kWithErrors); @@ -559,10 +567,166 @@ TEST_F(TestBasicJitterBuffer, H264InsertStartCode) { &retransmitted)); frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, size_ * 2 + 4 * 2, true); + EXPECT_EQ(kVideoFrameKey, frame_out->FrameType()); +} +TEST_F(TestBasicJitterBuffer, PacketLossWithSelectiveErrorrs) { + jitter_buffer_->DecodeErrorMode(kSelectiveErrors); + // Always start with a key frame. Use 10 packets to test Decodable State + // boundaries. + packet_->frameType = kVideoFrameKey; + packet_->isFirstPacket = true; + packet_->markerBit = false; + packet_->seqNum = seq_num_; + packet_->timestamp = timestamp_; + + bool retransmitted = false; + EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + uint32_t timestamp = 0; + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + packet_->isFirstPacket = false; + for (int i = 1; i < 9; ++i) { + packet_->seqNum++; + EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + } + + // last packet + packet_->markerBit = true; + packet_->seqNum++; + + EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + VCMEncodedFrame* frame_out = DecodeCompleteFrame(); + CheckOutFrame(frame_out, 10 * size_, false); EXPECT_EQ(kVideoFrameKey, frame_out->FrameType()); + + // An incomplete frame can only be decoded once a subsequent frame has begun + // to arrive. Insert packet in distant frame for this purpose. + packet_->frameType = kVideoFrameDelta; + packet_->isFirstPacket = true; + packet_->markerBit = false; + packet_->seqNum += 100; + packet_->timestamp += 33*90*8; + EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + // Insert second frame + packet_->seqNum -= 99; + packet_->timestamp -= 33*90*7; + + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + packet_->isFirstPacket = false; + for (int i = 1; i < 8; ++i) { + packet_->seqNum++; + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + } + + packet_->seqNum++; + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + frame_out = DecodeIncompleteFrame(); + ASSERT_FALSE(NULL == frame_out); + CheckOutFrame(frame_out, 9 * size_, false); + EXPECT_EQ(kVideoFrameDelta, frame_out->FrameType()); + + packet_->markerBit = true; + packet_->seqNum++; + EXPECT_EQ(kOldPacket, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); +} + +TEST_F(TestBasicJitterBuffer, PacketLossStateChangedFromErrorsToNone) { + jitter_buffer_->DecodeErrorMode(kWithErrors); + + // First frame is always a key frame. + packet_->frameType = kVideoFrameKey; + packet_->isFirstPacket = true; + packet_->markerBit = true; + packet_->seqNum = seq_num_; + packet_->timestamp = timestamp_; + + bool retransmitted = false; + EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + uint32_t timestamp = 0; + EXPECT_TRUE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + + VCMEncodedFrame* frame_out = DecodeCompleteFrame(); + CheckOutFrame(frame_out, size_, false); + EXPECT_EQ(kVideoFrameKey, frame_out->FrameType()); + + packet_->frameType = kVideoFrameDelta; + packet_->isFirstPacket = false; + packet_->markerBit = false; + packet_->seqNum += 2; + packet_->timestamp += 33*90; + + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + // At least two frames must be present before an incomplete can be decoded. + EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + packet_->seqNum += 3; + packet_->timestamp += 33*90*2; + + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + jitter_buffer_->DecodeErrorMode(kNoErrors); + + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + // Complete the next frame. + packet_->seqNum -= 2; + packet_->markerBit = true; + packet_->timestamp -= 33*90*2; + + EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); + + packet_->seqNum -= 2; + packet_->markerBit = false; + packet_->isFirstPacket = true; + + EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); + EXPECT_TRUE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + frame_out = DecodeCompleteFrame(); + ASSERT_FALSE(frame_out == NULL); + + CheckOutFrame(frame_out, 3 * size_, false); + EXPECT_EQ(kVideoFrameDelta, frame_out->FrameType()); + EXPECT_EQ(packet_->timestamp, frame_out->TimeStamp()); + + EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, ×tamp)); + EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(×tamp)); } TEST_F(TestBasicJitterBuffer, PacketLoss) { @@ -584,8 +748,8 @@ TEST_F(TestBasicJitterBuffer, PacketLoss) { packet_->completeNALU = kNaluStart; bool retransmitted = false; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); for (int i = 0; i < 11; ++i) { webrtc::FrameType frametype = kVideoFrameDelta; seq_num_++; @@ -597,8 +761,8 @@ TEST_F(TestBasicJitterBuffer, PacketLoss) { packet_->timestamp = timestamp_; packet_->completeNALU = kNaluStart; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); VCMEncodedFrame* frame_out = DecodeCompleteFrame(); @@ -1134,8 +1298,8 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->markerBit = false; bool retransmitted = false; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); seq_num_ += 2; // Skip one packet packet_->seqNum = seq_num_; @@ -1145,7 +1309,7 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->markerBit = false; EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + &retransmitted)); seq_num_++; packet_->seqNum = seq_num_; @@ -1155,14 +1319,14 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->markerBit = false; EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + &retransmitted)); seq_num_++; packet_->seqNum = seq_num_; packet_->completeNALU = kNaluComplete; packet_->markerBit = true; // Last packet EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + &retransmitted)); // The JB will only output (incomplete) frames if a packet belonging to a // subsequent frame was already inserted. Insert one packet of a subsequent // frame. place high timestamp so the JB would always have a next frame @@ -1175,8 +1339,8 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->completeNALU = kNaluStart; packet_->markerBit = false; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); VCMEncodedFrame* frame_out = DecodeIncompleteFrame(); @@ -1199,8 +1363,8 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->markerBit = false; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); insertedLength += packet_->sizeBytes; // This packet should be decoded. seq_num_--; @@ -1211,7 +1375,7 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->completeNALU = kNaluStart; packet_->markerBit = false; EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + &retransmitted)); insertedLength += packet_->sizeBytes; // This packet should be decoded. seq_num_ += 3; // One packet drop @@ -1222,10 +1386,10 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->completeNALU = kNaluComplete; packet_->markerBit = false; EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + &retransmitted)); insertedLength += packet_->sizeBytes; // This packet should be decoded. - seq_num_ += 1; + seq_num_++; packet_->seqNum = seq_num_; packet_->timestamp = timestamp_; packet_->frameType = kVideoFrameKey; @@ -1233,7 +1397,7 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->completeNALU = kNaluStart; packet_->markerBit = false; EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + &retransmitted)); // This packet should be decoded since it's the beginning of a NAL. insertedLength += packet_->sizeBytes; @@ -1245,7 +1409,7 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->completeNALU = kNaluEnd; packet_->markerBit = true; EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + &retransmitted)); // This packet should not be decoded because it is an incomplete NAL if it // is the last. frame_out = DecodeIncompleteFrame(); @@ -1255,7 +1419,7 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { // Test to insert empty packet. - seq_num_ += 1; + seq_num_++; timestamp_ += 33 * 90; VCMPacket emptypacket(data_, 0, seq_num_, timestamp_, true); emptypacket.seqNum = seq_num_; @@ -1276,7 +1440,7 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { // Test that a frame can include an empty packet. - seq_num_ += 1; + seq_num_++; timestamp_ += 33 * 90; packet_->seqNum = seq_num_; @@ -1286,10 +1450,10 @@ TEST_F(TestBasicJitterBuffer, H264IncompleteNalu) { packet_->completeNALU = kNaluComplete; packet_->markerBit = false; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); - seq_num_ += 1; + seq_num_++; emptypacket.seqNum = seq_num_; emptypacket.timestamp = timestamp_; emptypacket.frameType = kVideoFrameKey; @@ -1327,8 +1491,8 @@ TEST_F(TestBasicJitterBuffer, NextFrameWhenIncomplete) { packet_->markerBit = false; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); frame_out = DecodeIncompleteFrame(); EXPECT_TRUE(frame_out == NULL); @@ -1338,8 +1502,8 @@ TEST_F(TestBasicJitterBuffer, NextFrameWhenIncomplete) { packet_->isFirstPacket = true; - EXPECT_EQ(kFirstPacket, jitter_buffer_->InsertPacket(*packet_, - &retransmitted)); + EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_, + &retransmitted)); frame_out = DecodeIncompleteFrame(); @@ -1694,7 +1858,7 @@ TEST_F(TestJitterBufferNack, NormalOperation) { stream_generator_->GenerateFrame(kVideoFrameKey, 100, 0, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); + EXPECT_EQ(kDecodableSession, InsertPacketAndPop(0)); // Verify that the frame is incomplete. EXPECT_FALSE(DecodeCompleteFrame()); while (stream_generator_->PacketsRemaining() > 1) { diff --git a/webrtc/modules/video_coding/main/source/session_info.cc b/webrtc/modules/video_coding/main/source/session_info.cc index d524b99ace..797ad2103a 100644 --- a/webrtc/modules/video_coding/main/source/session_info.cc +++ b/webrtc/modules/video_coding/main/source/session_info.cc @@ -375,6 +375,12 @@ int VCMSessionInfo::MakeDecodable() { return return_length; } +void VCMSessionInfo::ClearStateIfIncomplete() { + // We don't need to check for completeness first because the two are + // orthogonal. If complete_ is true, decodable_ is irrelevant. + decodable_ = false; +} + bool VCMSessionInfo::HaveFirstPacket() const { return !packets_.empty() && packets_.front().isFirstPacket; diff --git a/webrtc/modules/video_coding/main/source/session_info.h b/webrtc/modules/video_coding/main/source/session_info.h index 708a48affa..e063665354 100644 --- a/webrtc/modules/video_coding/main/source/session_info.h +++ b/webrtc/modules/video_coding/main/source/session_info.h @@ -65,6 +65,13 @@ class VCMSessionInfo { // memory to remove any empty space. // Returns the number of bytes deleted from the session. int MakeDecodable(); + + // Sets decodable_ to false. + // Used by the dual decoder. After the mode is changed to kNoErrors from + // kWithErrors or kSelective errors, any states that have been marked + // decodable and are not complete are marked as non-decodable. + void ClearStateIfIncomplete(); + int SessionLength() const; int NumPackets() const; bool HaveFirstPacket() const; diff --git a/webrtc/modules/video_coding/main/source/session_info_unittest.cc b/webrtc/modules/video_coding/main/source/session_info_unittest.cc index 5d4bf48843..91d53b61c7 100644 --- a/webrtc/modules/video_coding/main/source/session_info_unittest.cc +++ b/webrtc/modules/video_coding/main/source/session_info_unittest.cc @@ -227,6 +227,71 @@ TEST_F(TestSessionInfo, NormalOperation) { } } +TEST_F(TestSessionInfo, ErrorsEqualDecodableState) { + packet_.seqNum = 0xFFFF; + packet_.isFirstPacket = false; + packet_.markerBit = false; + FillPacket(3); + ASSERT_EQ(session_.InsertPacket(packet_, + frame_buffer_, + kWithErrors, + frame_data), + packet_buffer_size()); + EXPECT_EQ(0, session_.packets_not_decodable()); + EXPECT_TRUE(session_.decodable()); +} + +TEST_F(TestSessionInfo, SelectiveDecodableState) { + packet_.seqNum = 0xFFFF; + packet_.isFirstPacket = false; + packet_.markerBit = false; + FillPacket(1); + frame_data.rolling_average_packets_per_frame = 11; + frame_data.rtt_ms = 150; + ASSERT_EQ(session_.InsertPacket(packet_, + frame_buffer_, + kSelectiveErrors, + frame_data), + packet_buffer_size()); + EXPECT_EQ(0, session_.packets_not_decodable()); + EXPECT_FALSE(session_.decodable()); + + packet_.seqNum -= 1; + FillPacket(0); + packet_.isFirstPacket = true; + ASSERT_EQ(session_.InsertPacket(packet_, + frame_buffer_, + kSelectiveErrors, + frame_data), + packet_buffer_size()); + EXPECT_EQ(0, session_.packets_not_decodable()); + EXPECT_TRUE(session_.decodable()); + + packet_.isFirstPacket = false; + packet_.seqNum += 1; + for (int i = 2; i < 8; ++i) { + packet_.seqNum += 1; + FillPacket(i); + ASSERT_EQ(session_.InsertPacket(packet_, + frame_buffer_, + kSelectiveErrors, + frame_data), + packet_buffer_size()); + EXPECT_EQ(0, session_.packets_not_decodable()); + EXPECT_TRUE(session_.decodable()); + } + + packet_.seqNum += 1; + FillPacket(8); + ASSERT_EQ(session_.InsertPacket(packet_, + frame_buffer_, + kSelectiveErrors, + frame_data), + packet_buffer_size()); + EXPECT_EQ(0, session_.packets_not_decodable()); + EXPECT_TRUE(session_.decodable()); +} + TEST_F(TestVP8Partitions, TwoPartitionsOneLoss) { // Partition 0 | Partition 1 // [ 0 ] [ 2 ] | [ 3 ] @@ -943,4 +1008,5 @@ TEST_F(TestNalUnits, ReorderWrapLosses) { EXPECT_EQ(0, session_.SessionLength()); EXPECT_EQ(2, session_.packets_not_decodable()); } + } // namespace webrtc |