aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoragalusza@google.com <agalusza@google.com@4adac7df-926f-26a2-2b94-8c16560cd09d>2013-08-08 01:12:33 +0000
committeragalusza@google.com <agalusza@google.com@4adac7df-926f-26a2-2b94-8c16560cd09d>2013-08-08 01:12:33 +0000
commitd177c10e2de490cfa001a00ca4d0ae654292afc7 (patch)
tree6a849807b67da3decff8edfc942f44950b03674e
parent676ff1ed893d6ae59a7ce29a4428e0d7c9f855d9 (diff)
downloadwebrtc-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
-rw-r--r--webrtc/modules/video_coding/main/source/frame_buffer.cc8
-rw-r--r--webrtc/modules/video_coding/main/source/frame_buffer.h6
-rw-r--r--webrtc/modules/video_coding/main/source/jitter_buffer.cc102
-rw-r--r--webrtc/modules/video_coding/main/source/jitter_buffer.h11
-rw-r--r--webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc222
-rw-r--r--webrtc/modules/video_coding/main/source/session_info.cc6
-rw-r--r--webrtc/modules/video_coding/main/source/session_info.h7
-rw-r--r--webrtc/modules/video_coding/main/source/session_info_unittest.cc66
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, &timestamp));
+ EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ 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, &timestamp));
+ EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+ }
+
+ // 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, &timestamp));
+ EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ // 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, &timestamp));
+ EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ 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, &timestamp));
+ EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+ }
+
+ packet_->seqNum++;
+ EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_,
+ &retransmitted));
+ EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, &timestamp));
+ EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ 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, &timestamp));
+
+ 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, &timestamp));
+ // At least two frames must be present before an incomplete can be decoded.
+ EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ packet_->seqNum += 3;
+ packet_->timestamp += 33*90*2;
+
+ EXPECT_EQ(kDecodableSession, jitter_buffer_->InsertPacket(*packet_,
+ &retransmitted));
+
+ EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, &timestamp));
+ EXPECT_TRUE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ jitter_buffer_->DecodeErrorMode(kNoErrors);
+
+ EXPECT_FALSE(jitter_buffer_->NextCompleteTimestamp(0, &timestamp));
+ EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ // 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, &timestamp));
+ EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
+
+ packet_->seqNum -= 2;
+ packet_->markerBit = false;
+ packet_->isFirstPacket = true;
+
+ EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_,
+ &retransmitted));
+ EXPECT_TRUE(jitter_buffer_->NextCompleteTimestamp(0, &timestamp));
+ 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, &timestamp));
+ EXPECT_FALSE(jitter_buffer_->NextMaybeIncompleteTimestamp(&timestamp));
}
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