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
This commit is contained in:
mikhal@webrtc.org
2013-04-25 20:27:04 +00:00
parent b84f13f185
commit dc3cd217b2
8 changed files with 74 additions and 164 deletions

View File

@ -438,7 +438,6 @@ VCMEncodedFrame* VCMJitterBuffer::GetCompleteFrameForDecoding(
if (!running_) {
return NULL;
}
CleanUpOldOrEmptyFrames();
if (last_decoded_state_.in_initial_state()) {
@ -482,16 +481,17 @@ VCMEncodedFrame* VCMJitterBuffer::GetCompleteFrameForDecoding(
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()) {
crit_sect_->Leave();
return NULL;
}
}
VCMFrameBuffer* oldest_frame = *it;
@ -530,15 +530,15 @@ VCMEncodedFrame* VCMJitterBuffer::GetCompleteFrameForDecoding(
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 @@ VCMEncodedFrame* VCMJitterBuffer::GetFrameForDecoding() {
}
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 @@ int64_t VCMJitterBuffer::LastDecodedTimestamp() const {
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) {

View File

@ -104,13 +104,16 @@ class VCMJitterBuffer {
// 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 @@ class VCMJitterBuffer {
// 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

View File

@ -241,8 +241,9 @@ class TestRunningJitterBuffer : public ::testing::Test {
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 @@ TEST_F(TestJitterBufferNack, NackTooOldPackets) {
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 @@ TEST_F(TestJitterBufferNack, NackListFull) {
// 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, UseNackToRecoverFirstKeyFrame) {
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 @@ TEST_F(TestJitterBufferNack, NormalOperation) {
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,

View File

@ -254,11 +254,8 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(
!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 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(
dual_receiver->CopyJitterBufferStateFromReceiver(*this);
}
frame = jitter_buffer_.GetFrameForDecoding();
frame = jitter_buffer_.MaybeGetIncompleteFrameForDecoding();
}
return frame;
}
@ -320,7 +317,7 @@ VCMEncodedFrame* VCMReceiver::FrameForRendering(uint16_t max_wait_time_ms,
dual_receiver->CopyJitterBufferStateFromReceiver(*this);
}
frame = jitter_buffer_.GetFrameForDecoding();
frame = jitter_buffer_.MaybeGetIncompleteFrameForDecoding();
}
return frame;
}
@ -412,6 +409,16 @@ VCMReceiverState VCMReceiver::State() const {
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) {

View File

@ -76,6 +76,10 @@ class VCMReceiver {
// 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,

View File

@ -535,10 +535,17 @@ VideoCodingModuleImpl::RegisterProtectionCallback(
}
// 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 @@ VideoCodingModuleImpl::SetVideoProtection(VCMVideoProtection videoProtection,
// 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 @@ VideoCodingModuleImpl::SetVideoProtection(VCMVideoProtection videoProtection,
if (enable)
{
_keyRequestMode = kKeyOnLoss;
_receiver.SetDecodeWithErrors(true);
}
else if (_keyRequestMode == kKeyOnLoss)
{
@ -640,6 +649,8 @@ VideoCodingModuleImpl::SetVideoProtection(VCMVideoProtection videoProtection,
_receiver.SetNackMode(kNack,
media_optimization::kLowRttNackMs,
-1);
_receiver.SetDecodeWithErrors(false);
_receiver.SetDecodeWithErrors(false);
}
else
{
@ -1021,6 +1032,10 @@ VideoCodingModuleImpl::DecodeDualFrame(uint16_t maxWaitTimeMs)
}
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 @@ int VideoCodingModuleImpl::SetReceiverRobustnessMode(
_dualReceiver.SetNackMode(kNoNack, -1, -1);
break;
}
_receiver.SetDecodeWithErrors(errorMode == kAllowDecodeErrors);
// The dual decoder should never decode with errors.
_dualReceiver.SetDecodeWithErrors(false);
return VCM_OK;
}

View File

@ -329,73 +329,4 @@ TEST_F(VCMRobustnessTest, TestModeNoneWithErrors) {
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

View File

@ -136,11 +136,14 @@ int JitterBufferTest(CmdArgs& args)
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 @@ int JitterBufferTest(CmdArgs& args)
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 @@ int JitterBufferTest(CmdArgs& args)
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 @@ int JitterBufferTest(CmdArgs& args)
// 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 @@ int JitterBufferTest(CmdArgs& args)
// 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 @@ int JitterBufferTest(CmdArgs& args)
// 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 @@ int JitterBufferTest(CmdArgs& args)
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 @@ int JitterBufferTest(CmdArgs& args)
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);