VCM/JB: FrameForDecoding->IncompleteFrameForDecoding
- Update complete frame for decoding
- Remove FrameForDecodingNack
This CL should only be committed after issue http://webrtc-codereview.appspot.com/1313007/
Review URL: https://webrtc-codereview.appspot.com/1316007
git-svn-id: http://webrtc.googlecode.com/svn/trunk@3901 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.cc b/webrtc/modules/video_coding/main/source/jitter_buffer.cc
index abcda82..0c11512 100644
--- a/webrtc/modules/video_coding/main/source/jitter_buffer.cc
+++ b/webrtc/modules/video_coding/main/source/jitter_buffer.cc
@@ -438,7 +438,6 @@
if (!running_) {
return NULL;
}
-
CleanUpOldOrEmptyFrames();
if (last_decoded_state_.in_initial_state()) {
@@ -482,15 +481,16 @@
frame_event_->Reset();
}
- if (it == frame_list_.end()) {
+ if (!decode_with_errors_ && it == frame_list_.end()) {
// Even after signaling we're still missing a complete continuous frame.
- // Look for a complete key frame.
+ // Look for a complete key frame if we're not decoding with errors.
it = find_if(frame_list_.begin(), frame_list_.end(),
CompleteKeyFrameCriteria());
- if (it == frame_list_.end()) {
+ }
+
+ if (it == frame_list_.end()) {
crit_sect_->Leave();
return NULL;
- }
}
VCMFrameBuffer* oldest_frame = *it;
@@ -530,15 +530,15 @@
return oldest_frame;
}
-VCMEncodedFrame* VCMJitterBuffer::GetFrameForDecoding() {
- TRACE_EVENT0("webrtc", "JB::GetFrameForDecoding");
+VCMEncodedFrame* VCMJitterBuffer::MaybeGetIncompleteFrameForDecoding() {
+ TRACE_EVENT0("webrtc", "JB::MaybeGetIncompleteFrameForDecoding");
CriticalSectionScoped cs(crit_sect_);
if (!running_) {
return NULL;
}
-
- if (WaitForRetransmissions()) {
- return GetFrameForDecodingNACK();
+ if (!decode_with_errors_) {
+ // No point to continue, as we are not decoding with errors.
+ return NULL;
}
CleanUpOldOrEmptyFrames();
@@ -548,15 +548,13 @@
}
VCMFrameBuffer* oldest_frame = frame_list_.front();
- if (frame_list_.size() <= 1 &&
- oldest_frame->GetState() != kStateComplete) {
+ // If we have only one frame in the buffer, release it only if it is complete.
+ if (frame_list_.size() <= 1 && oldest_frame->GetState() != kStateComplete) {
return NULL;
}
// Incomplete frame pulled out from jitter buffer,
// update the jitter estimate with what we currently know.
- // This frame shouldn't have been retransmitted, but if we recently
- // turned off NACK this might still happen.
const bool retransmitted = (oldest_frame->GetNackCount() > 0);
if (retransmitted) {
jitter_estimate_.FrameNacked();
@@ -998,57 +996,6 @@
return last_decoded_state_.time_stamp();
}
-VCMEncodedFrame* VCMJitterBuffer::GetFrameForDecodingNACK() {
- TRACE_EVENT0("webrtc", "JB::GetFrameForDecodingNACK");
- CleanUpOldOrEmptyFrames();
- // First look for a complete continuous frame.
- // When waiting for nack, wait for a key frame, if a continuous frame cannot
- // be determined (i.e. initial decoding state).
- if (last_decoded_state_.in_initial_state()) {
- waiting_for_key_frame_ = true;
- }
- FrameList::iterator it = FindOldestCompleteContinuousFrame();
- if (it == frame_list_.end()) {
- // If we didn't find one we're good with a complete key frame.
- it = find_if(frame_list_.begin(), frame_list_.end(),
- CompleteKeyFrameCriteria());
- if (it == frame_list_.end()) {
- return NULL;
- }
- }
- VCMFrameBuffer* oldest_frame = *it;
- // Update jitter estimate
- const bool retransmitted = (oldest_frame->GetNackCount() > 0);
- if (retransmitted) {
- jitter_estimate_.FrameNacked();
- } else if (oldest_frame->Length() > 0) {
- // Ignore retransmitted and empty frames.
- UpdateJitterEstimate(*oldest_frame, false);
- }
- it = frame_list_.erase(it);
- if (frame_list_.empty()) {
- TRACE_EVENT_INSTANT1("webrtc", "JB::FrameListEmptied",
- "type", "GetFrameForDecodingNACK");
- }
-
- // Look for previous frame loss.
- VerifyAndSetPreviousFrameLost(oldest_frame);
-
- // The state must be changed to decoding before cleaning up zero sized
- // frames to avoid empty frames being cleaned up and then given to the
- // decoder.
- oldest_frame->SetState(kStateDecoding);
-
- if (oldest_frame->FrameType() == kVideoFrameKey) {
- waiting_for_key_frame_ = false;
- }
-
- // We have a frame - update decoded state with frame info.
- last_decoded_state_.SetState(oldest_frame);
- DropPacketsFromNackList(last_decoded_state_.sequence_num());
- return oldest_frame;
-}
-
// Set the frame state to free and remove it from the sorted
// frame list. Must be called from inside the critical section crit_sect_.
void VCMJitterBuffer::ReleaseFrameIfNotDecoding(VCMFrameBuffer* frame) {
diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.h b/webrtc/modules/video_coding/main/source/jitter_buffer.h
index 669089f..fd637bb 100644
--- a/webrtc/modules/video_coding/main/source/jitter_buffer.h
+++ b/webrtc/modules/video_coding/main/source/jitter_buffer.h
@@ -104,13 +104,16 @@
// or more packets.
bool CompleteSequenceWithNextFrame();
- // TODO(mikhal/stefan): Merge all GetFrameForDecoding into one.
- // Wait |max_wait_time_ms| for a complete frame to arrive. After timeout NULL
- // is returned.
+ // Returns a complete frame ready for decoding. Allows max_wait_time_ms to
+ // wait for such a frame, if one is unavailable.
+ // Always starts with a key frame.
VCMEncodedFrame* GetCompleteFrameForDecoding(uint32_t max_wait_time_ms);
- // Get a frame for decoding (even an incomplete) without delay.
- VCMEncodedFrame* GetFrameForDecoding();
+ // Get next frame for decoding without delay. If decoding with errors is not
+ // enabled, will return NULL. Actual returned frame will be the next one in
+ // the list, either complete or not.
+ // TODO(mikhal): Consider only allowing decodable/complete.
+ VCMEncodedFrame* MaybeGetIncompleteFrameForDecoding();
// Releases a frame returned from the jitter buffer, should be called when
// done with decoding.
@@ -189,11 +192,6 @@
// Drops all packets in the NACK list up until |last_decoded_sequence_number|.
void DropPacketsFromNackList(uint16_t last_decoded_sequence_number);
- // In NACK-only mode this function doesn't return or release non-complete
- // frames unless we have a complete key frame. In hybrid mode, we may release
- // "decodable", incomplete frames.
- VCMEncodedFrame* GetFrameForDecodingNACK();
-
void ReleaseFrameIfNotDecoding(VCMFrameBuffer* frame);
// Gets an empty frame, creating a new frame if necessary (i.e. increases
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 22fb425..bf81973 100644
--- a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc
+++ b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc
@@ -241,8 +241,9 @@
return ret;
}
- bool DecodeFrame() {
- VCMEncodedFrame* frame = jitter_buffer_->GetFrameForDecoding();
+ bool DecodeIncompleteFrame() {
+ VCMEncodedFrame* frame =
+ jitter_buffer_->MaybeGetIncompleteFrameForDecoding();
bool ret = (frame != NULL);
jitter_buffer_->ReleaseFrame(frame);
return ret;
@@ -416,7 +417,7 @@
EXPECT_GE(InsertFrame(kVideoFrameDelta), kNoError);
// Waiting for a key frame.
EXPECT_FALSE(DecodeCompleteFrame());
- EXPECT_FALSE(DecodeFrame());
+ EXPECT_FALSE(DecodeIncompleteFrame());
// The next complete continuous frame isn't a key frame, but we're waiting
// for one.
@@ -466,7 +467,7 @@
// The next complete continuous frame isn't a key frame, but we're waiting
// for one.
EXPECT_FALSE(DecodeCompleteFrame());
- EXPECT_FALSE(DecodeFrame());
+ EXPECT_FALSE(DecodeIncompleteFrame());
EXPECT_GE(InsertFrame(kVideoFrameKey), kNoError);
// Skipping ahead to the key frame.
EXPECT_TRUE(DecodeCompleteFrame());
@@ -521,9 +522,10 @@
TEST_F(TestJitterBufferNack, NormalOperation) {
EXPECT_EQ(kNack, jitter_buffer_->nack_mode());
+ jitter_buffer_->DecodeWithErrors(true);
EXPECT_GE(InsertFrame(kVideoFrameKey), kNoError);
- EXPECT_TRUE(DecodeFrame());
+ EXPECT_TRUE(DecodeIncompleteFrame());
// ----------------------------------------------------------------
// | 1 | 2 | .. | 8 | 9 | x | 11 | 12 | .. | 19 | x | 21 | .. | 100 |
@@ -544,7 +546,7 @@
EXPECT_EQ(kIncomplete, InsertPacketAndPop(0));
EXPECT_EQ(0, stream_generator->PacketsRemaining());
EXPECT_FALSE(DecodeCompleteFrame());
- EXPECT_FALSE(DecodeFrame());
+ EXPECT_FALSE(DecodeIncompleteFrame());
uint16_t nack_list_size = 0;
bool request_key_frame = false;
uint16_t* list = jitter_buffer_->GetNackList(&nack_list_size,
diff --git a/webrtc/modules/video_coding/main/source/receiver.cc b/webrtc/modules/video_coding/main/source/receiver.cc
index 2595fb2..fffe609 100644
--- a/webrtc/modules/video_coding/main/source/receiver.cc
+++ b/webrtc/modules/video_coding/main/source/receiver.cc
@@ -254,11 +254,8 @@
!jitter_buffer_.CompleteSequenceWithNextFrame()) {
// Jitter buffer state might get corrupt with this frame.
dual_receiver->CopyJitterBufferStateFromReceiver(*this);
- frame = jitter_buffer_.GetFrameForDecoding();
- assert(frame);
- } else {
- frame = jitter_buffer_.GetFrameForDecoding();
}
+ frame = jitter_buffer_.MaybeGetIncompleteFrameForDecoding();
}
if (frame == NULL) {
// Wait for a complete frame.
@@ -282,7 +279,7 @@
dual_receiver->CopyJitterBufferStateFromReceiver(*this);
}
- frame = jitter_buffer_.GetFrameForDecoding();
+ frame = jitter_buffer_.MaybeGetIncompleteFrameForDecoding();
}
return frame;
}
@@ -320,7 +317,7 @@
dual_receiver->CopyJitterBufferStateFromReceiver(*this);
}
- frame = jitter_buffer_.GetFrameForDecoding();
+ frame = jitter_buffer_.MaybeGetIncompleteFrameForDecoding();
}
return frame;
}
@@ -412,6 +409,16 @@
return state_;
}
+void VCMReceiver::SetDecodeWithErrors(bool enable){
+ CriticalSectionScoped cs(crit_sect_);
+ jitter_buffer_.DecodeWithErrors(enable);
+}
+
+bool VCMReceiver::DecodeWithErrors() const {
+ CriticalSectionScoped cs(crit_sect_);
+ return jitter_buffer_.decode_with_errors();
+}
+
int VCMReceiver::SetMinReceiverDelay(int desired_delay_ms) {
CriticalSectionScoped cs(crit_sect_);
if (desired_delay_ms < 0 || desired_delay_ms > kMaxReceiverDelayMs) {
diff --git a/webrtc/modules/video_coding/main/source/receiver.h b/webrtc/modules/video_coding/main/source/receiver.h
index 88f2fb2..f0cf865 100644
--- a/webrtc/modules/video_coding/main/source/receiver.h
+++ b/webrtc/modules/video_coding/main/source/receiver.h
@@ -76,6 +76,10 @@
// Receiver video delay.
int SetMinReceiverDelay(int desired_delay_ms);
+ // Decoding with errors.
+ void SetDecodeWithErrors(bool enable);
+ bool DecodeWithErrors() const;
+
private:
VCMEncodedFrame* FrameForDecoding(uint16_t max_wait_time_ms,
int64_t nextrender_time_ms,
diff --git a/webrtc/modules/video_coding/main/source/video_coding_impl.cc b/webrtc/modules/video_coding/main/source/video_coding_impl.cc
index 64d00c1..74730ec 100644
--- a/webrtc/modules/video_coding/main/source/video_coding_impl.cc
+++ b/webrtc/modules/video_coding/main/source/video_coding_impl.cc
@@ -535,10 +535,17 @@
}
// Enable or disable a video protection method.
+// Note: This API should be deprecated, as it does not offer a distinction
+// between the protection method and decoding with or without errors. If such a
+// behavior is desired, use the following API: SetReceiverRobustnessMode.
int32_t
VideoCodingModuleImpl::SetVideoProtection(VCMVideoProtection videoProtection,
bool enable)
{
+ // By default, do not decode with errors.
+ _receiver.SetDecodeWithErrors(false);
+ // The dual decoder should always be error free.
+ _dualReceiver.SetDecodeWithErrors(false);
switch (videoProtection)
{
@@ -583,6 +590,7 @@
// Enable NACK and always wait for retransmissions and
// compensate with extra delay.
_dualReceiver.SetNackMode(kNack, -1, -1);
+ _receiver.SetDecodeWithErrors(true);
}
else
{
@@ -597,6 +605,7 @@
if (enable)
{
_keyRequestMode = kKeyOnLoss;
+ _receiver.SetDecodeWithErrors(true);
}
else if (_keyRequestMode == kKeyOnLoss)
{
@@ -640,6 +649,8 @@
_receiver.SetNackMode(kNack,
media_optimization::kLowRttNackMs,
-1);
+ _receiver.SetDecodeWithErrors(false);
+ _receiver.SetDecodeWithErrors(false);
}
else
{
@@ -1021,6 +1032,10 @@
}
int64_t dummyRenderTime;
int32_t decodeCount = 0;
+ // The dual decoder's state is copied from the main decoder, which may
+ // decode with errors. Make sure that the dual decoder does not introduce
+ // error.
+ _dualReceiver.SetDecodeWithErrors(false);
VCMEncodedFrame* dualFrame = _dualReceiver.FrameForDecoding(
maxWaitTimeMs,
dummyRenderTime);
@@ -1417,6 +1432,9 @@
_dualReceiver.SetNackMode(kNoNack, -1, -1);
break;
}
+ _receiver.SetDecodeWithErrors(errorMode == kAllowDecodeErrors);
+ // The dual decoder should never decode with errors.
+ _dualReceiver.SetDecodeWithErrors(false);
return VCM_OK;
}
diff --git a/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc b/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc
index d7e0723..b06e296 100644
--- a/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc
+++ b/webrtc/modules/video_coding/main/source/video_coding_robustness_unittest.cc
@@ -329,73 +329,4 @@
InsertPacket(9000, 11, false, true, kVideoFrameDelta);
EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 9000 complete.
}
-
-TEST_F(VCMRobustnessTest, TestModeNoneWithoutErrors) {
- Sequence s1;
- EXPECT_CALL(decoder_, InitDecode(_, _)).Times(1);
- EXPECT_CALL(decoder_, Release()).Times(1);
- EXPECT_CALL(request_callback_, ResendPackets(_, 1))
- .With(Args<0, 1>(ElementsAre(4)))
- .Times(0);
-
- EXPECT_CALL(decoder_, Copy())
- .Times(0);
- EXPECT_CALL(decoderCopy_, Copy())
- .Times(0);
-
- // Decode operations
- EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 0),
- Field(&EncodedImage::_completeFrame,
- true)),
- false, _, _, _))
- .Times(1)
- .InSequence(s1);
- EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 3000),
- Field(&EncodedImage::_completeFrame,
- false)),
- false, _, _, _))
- .Times(1)
- .InSequence(s1);
- EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 6000),
- Field(&EncodedImage::_completeFrame,
- true)),
- false, _, _, _))
- .Times(1)
- .InSequence(s1);
- EXPECT_CALL(frame_type_callback_, RequestKeyFrame())
- .Times(1);
-
- ASSERT_EQ(VCM_OK, vcm_->SetReceiverRobustnessMode(
- VideoCodingModule::kNone,
- VideoCodingModule::kNoDecodeErrors));
-
- InsertPacket(0, 0, true, false, kVideoFrameKey);
- InsertPacket(0, 1, false, false, kVideoFrameKey);
- InsertPacket(0, 2, false, true, kVideoFrameKey);
- EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 0.
- EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list.
-
- clock_->AdvanceTimeMilliseconds(33);
- InsertPacket(3000, 3, true, false, kVideoFrameDelta);
- // Packet 4 missing
- InsertPacket(3000, 5, false, true, kVideoFrameDelta);
- EXPECT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0));
- EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list.
-
- clock_->AdvanceTimeMilliseconds(33);
- InsertPacket(6000, 6, true, false, kVideoFrameDelta);
- InsertPacket(6000, 7, false, false, kVideoFrameDelta);
- InsertPacket(6000, 8, false, true, kVideoFrameDelta);
- EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 3000 incomplete.
- // Schedule key frame request.
- EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list.
-
- clock_->AdvanceTimeMilliseconds(10);
- EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 6000 complete.
- EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list.
-
- // Wait for the key request timer to set.
- clock_->AdvanceTimeMilliseconds(500);
- EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect key frame request.
-}
} // namespace webrtc
diff --git a/webrtc/modules/video_coding/main/test/jitter_buffer_test.cc b/webrtc/modules/video_coding/main/test/jitter_buffer_test.cc
index 975468c..d78b7e3 100644
--- a/webrtc/modules/video_coding/main/test/jitter_buffer_test.cc
+++ b/webrtc/modules/video_coding/main/test/jitter_buffer_test.cc
@@ -136,11 +136,14 @@
TEST(0 == jb.GetFrame(packet));
TEST(-1 == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
TEST(0 == jb.GetCompleteFrameForDecoding(10));
- TEST(0 == jb.GetFrameForDecoding());
+ TEST(0 == jb.MaybeGetIncompleteFrameForDecoding());
// Start
jb.Start();
+ // Allow decoding with errors.
+ jb.DecodeWithErrors(true);
+
// Get frame to use for this timestamp
VCMEncodedFrame* frameIn = jb.GetFrame(packet);
TEST(frameIn != 0);
@@ -808,7 +811,7 @@
TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
// Get the frame
- frameOut = jb.GetFrameForDecoding();
+ frameOut = jb.MaybeGetIncompleteFrameForDecoding();
// One of the packets has been discarded by the jitter buffer.
// Last frame can't be extracted yet.
@@ -1665,7 +1668,7 @@
packet.seqNum = seqNum;
packet.timestamp = timeStamp;
packet.frameType = kFrameEmpty;
- VCMEncodedFrame* testFrame = jb.GetFrameForDecoding();
+ VCMEncodedFrame* testFrame = jb.MaybeGetIncompleteFrameForDecoding();
// timestamp should bever be the last TS inserted
if (testFrame != NULL)
{
@@ -1744,7 +1747,7 @@
// Get packet notification
TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType,
&renderTimeMs));
- frameOut = jb.GetFrameForDecoding();
+ frameOut = jb.MaybeGetIncompleteFrameForDecoding();
// We can decode everything from a NALU until a packet has been lost.
// Thus we can decode the first packet of the first NALU and the second NALU
@@ -1810,7 +1813,7 @@
// This packet should not be decoded because it is an incomplete NAL if it
// is the last
- frameOut = jb.GetFrameForDecoding();
+ frameOut = jb.MaybeGetIncompleteFrameForDecoding();
// Only last NALU is complete
TEST(CheckOutFrame(frameOut, insertedLength, false) == 0);
jb.ReleaseFrame(frameOut);
@@ -1834,7 +1837,7 @@
// Will be sent to the decoder, as a packet belonging to a subsequent frame
// has arrived.
- frameOut = jb.GetFrameForDecoding();
+ frameOut = jb.MaybeGetIncompleteFrameForDecoding();
// Test that a frame can include an empty packet.
@@ -1880,7 +1883,7 @@
TEST(frameIn = jb.GetFrame(packet));
TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
- frameOut = jb.GetFrameForDecoding();
+ frameOut = jb.MaybeGetIncompleteFrameForDecoding();
TEST(frameOut == NULL);
packet.seqNum += 2;
@@ -1889,7 +1892,7 @@
TEST(frameIn = jb.GetFrame(packet));
TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
- frameOut = jb.GetFrameForDecoding();
+ frameOut = jb.MaybeGetIncompleteFrameForDecoding();
TEST(frameOut != NULL);
TEST(CheckOutFrame(frameOut, packet.sizeBytes, false) == 0);