Optimize NACK list creation.

- No longer looping through all frame buffers.
- Keeping track of the current nack list index when building the list.
- Don't look for changes in the NACK list if the size has increased.

Review URL: https://webrtc-codereview.appspot.com/1076005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3420 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org 2013-01-28 08:48:13 +00:00
parent b2d7497faf
commit bf535b9b6b
6 changed files with 98 additions and 78 deletions

View File

@ -206,17 +206,16 @@ VCMFrameBuffer::LatestPacketTimeMs() const
// Build hard NACK list:Zero out all entries in list up to and including the // Build hard NACK list:Zero out all entries in list up to and including the
// (first) entry equal to _lowSeqNum. // (first) entry equal to _lowSeqNum.
int VCMFrameBuffer::BuildHardNackList(int* list, int num) { int VCMFrameBuffer::BuildHardNackList(int* list, int num,
if (_sessionInfo.BuildHardNackList(list, num) != 0) { int nack_seq_nums_index) {
return -1; return _sessionInfo.BuildHardNackList(list, num, nack_seq_nums_index);
}
return 0;
} }
// Build selective NACK list: Create a soft (selective) list of entries to zero // Build selective NACK list: Create a soft (selective) list of entries to zero
// out up to and including the (first) entry equal to _lowSeqNum. // out up to and including the (first) entry equal to _lowSeqNum.
int VCMFrameBuffer::BuildSoftNackList(int* list, int num, int rttMs) { int VCMFrameBuffer::BuildSoftNackList(int* list, int num,
return _sessionInfo.BuildSoftNackList(list, num, rttMs); int nack_seq_nums_index, int rttMs) {
return _sessionInfo.BuildSoftNackList(list, num, nack_seq_nums_index, rttMs);
} }
void void

View File

@ -67,10 +67,11 @@ public:
// NACK - Building the NACK lists. // NACK - Building the NACK lists.
// Build hard NACK list: Zero out all entries in list up to and including // Build hard NACK list: Zero out all entries in list up to and including
// _lowSeqNum. // _lowSeqNum.
int BuildHardNackList(int* list, int num); int BuildHardNackList(int* list, int num, int nack_seq_nums_index);
// Build soft NACK list: Zero out only a subset of the packets, discard // Build soft NACK list: Zero out only a subset of the packets, discard
// empty packets. // empty packets.
int BuildSoftNackList(int* list, int num, int rttMs); int BuildSoftNackList(int* list, int num, int nack_seq_nums_index,
int rttMs);
void IncrementNackCount(); void IncrementNackCount();
WebRtc_Word16 GetNackCount() const; WebRtc_Word16 GetNackCount() const;

View File

@ -926,24 +926,25 @@ uint16_t* VCMJitterBuffer::CreateNackList(uint16_t* nack_list_size,
nack_seq_nums_internal_[i] = seq_number_iterator; nack_seq_nums_internal_[i] = seq_number_iterator;
seq_number_iterator++; seq_number_iterator++;
} }
if (number_of_seq_num > 0) {
// Now we have a list of all sequence numbers that could have been sent. // Now we have a list of all sequence numbers that could have been sent.
// Zero out the ones we have received. // Zero out the ones we have received.
for (i = 0; i < max_number_of_frames_; i++) { int nack_seq_nums_index = 0;
// We don't need to check if frame is decoding since low_seq_num is based for (FrameList::iterator it = frame_list_.begin(); it != frame_list_.end();
// on the last decoded sequence number. ++it) {
VCMFrameBufferStateEnum state = frame_buffers_[i]->GetState();
if (kStateFree != state) {
// Reaching thus far means we are going to update the NACK list // Reaching thus far means we are going to update the NACK list
// When in hybrid mode, we use the soft NACKing feature. // When in hybrid mode, we use the soft NACKing feature.
if (nack_mode_ == kNackHybrid) { if (nack_mode_ == kNackHybrid) {
frame_buffers_[i]->BuildSoftNackList(nack_seq_nums_internal_, nack_seq_nums_index = (*it)->BuildSoftNackList(nack_seq_nums_internal_,
number_of_seq_num, number_of_seq_num,
nack_seq_nums_index,
rtt_ms_); rtt_ms_);
} else { } else {
// Used when the frame is being processed by the decoding thread // Used when the frame is being processed by the decoding thread
// don't need to use that info in this loop. // don't need to use that info in this loop.
frame_buffers_[i]->BuildHardNackList(nack_seq_nums_internal_, nack_seq_nums_index = (*it)->BuildHardNackList(nack_seq_nums_internal_,
number_of_seq_num); number_of_seq_num,
nack_seq_nums_index);
} }
} }
} }
@ -980,7 +981,6 @@ uint16_t* VCMJitterBuffer::CreateNackList(uint16_t* nack_list_size,
// Larger list: NACK list was extended since the last call. // Larger list: NACK list was extended since the last call.
*list_extended = true; *list_extended = true;
} }
for (unsigned int j = 0; j < *nack_list_size; j++) { for (unsigned int j = 0; j < *nack_list_size; j++) {
// Check if the list has been extended since it was last created, i.e, // Check if the list has been extended since it was last created, i.e,
// new items have been added. // new items have been added.

View File

@ -353,23 +353,22 @@ int VCMSessionInfo::MakeDecodable() {
} }
int VCMSessionInfo::BuildHardNackList(int* seq_num_list, int VCMSessionInfo::BuildHardNackList(int* seq_num_list,
int seq_num_list_length) { int seq_num_list_length,
if (NULL == seq_num_list || seq_num_list_length < 1) { int nack_seq_nums_index) {
return -1; assert(seq_num_list && seq_num_list_length > 0);
}
if (packets_.empty() && empty_seq_num_low_ == -1) { if (packets_.empty() && empty_seq_num_low_ == -1) {
return 0; return nack_seq_nums_index;
} }
// Find end point (index of entry equals the sequence number of the first // Find end point (index of entry equals the sequence number of the first
// packet). // packet).
int index = 0;
int low_seq_num = (packets_.empty()) ? empty_seq_num_low_: int low_seq_num = (packets_.empty()) ? empty_seq_num_low_:
packets_.front().seqNum; packets_.front().seqNum;
for (; index < seq_num_list_length; ++index) { for (; nack_seq_nums_index < seq_num_list_length; ++nack_seq_nums_index) {
if (seq_num_list[index] == low_seq_num) { if (seq_num_list[nack_seq_nums_index] == low_seq_num) {
seq_num_list[index] = -1; seq_num_list[nack_seq_nums_index] = -1;
++index; ++nack_seq_nums_index;
break; break;
} }
} }
@ -379,43 +378,43 @@ int VCMSessionInfo::BuildHardNackList(int* seq_num_list,
PacketIterator it = packets_.begin(); PacketIterator it = packets_.begin();
PacketIterator prev_it = it; PacketIterator prev_it = it;
++it; ++it;
while (it != packets_.end() && index < seq_num_list_length) { while (it != packets_.end() && nack_seq_nums_index < seq_num_list_length) {
if (!InSequence(it, prev_it)) { if (!InSequence(it, prev_it)) {
// Found a sequence number gap due to packet loss. // Found a sequence number gap due to packet loss.
index += PacketsMissing(it, prev_it); nack_seq_nums_index += PacketsMissing(it, prev_it);
session_nack_ = true; session_nack_ = true;
} }
seq_num_list[index] = -1; seq_num_list[nack_seq_nums_index] = -1;
++index; ++nack_seq_nums_index;
prev_it = it; prev_it = it;
++it; ++it;
} }
if (!packets_.front().isFirstPacket) if (!packets_.front().isFirstPacket)
session_nack_ = true; session_nack_ = true;
} }
index = ClearOutEmptyPacketSequenceNumbers(seq_num_list, seq_num_list_length, nack_seq_nums_index = ClearOutEmptyPacketSequenceNumbers(seq_num_list,
index); seq_num_list_length,
return 0; nack_seq_nums_index);
return nack_seq_nums_index;
} }
int VCMSessionInfo::BuildSoftNackList(int* seq_num_list, int VCMSessionInfo::BuildSoftNackList(int* seq_num_list,
int seq_num_list_length, int seq_num_list_length,
int nack_seq_nums_index,
int rtt_ms) { int rtt_ms) {
if (NULL == seq_num_list || seq_num_list_length < 1) { assert(seq_num_list && seq_num_list_length > 0);
return -1;
}
if (packets_.empty() && empty_seq_num_low_ == -1) { if (packets_.empty() && empty_seq_num_low_ == -1) {
return 0; return nack_seq_nums_index;
} }
int index = 0;
int low_seq_num = (packets_.empty()) ? empty_seq_num_low_: int low_seq_num = (packets_.empty()) ? empty_seq_num_low_:
packets_.front().seqNum; packets_.front().seqNum;
// Find entrance point (index of entry equals the sequence number of the // Find entrance point (nack_seq_nums_index of entry equals the sequence
// first packet). // number of the first packet).
for (; index < seq_num_list_length; ++index) { for (; nack_seq_nums_index < seq_num_list_length; ++nack_seq_nums_index) {
if (seq_num_list[index] == low_seq_num) { if (seq_num_list[nack_seq_nums_index] == low_seq_num) {
seq_num_list[index] = -1; seq_num_list[nack_seq_nums_index] = -1;
break; break;
} }
} }
@ -423,9 +422,10 @@ int VCMSessionInfo::BuildSoftNackList(int* seq_num_list,
// TODO(mikhal): 1. Update score based on RTT value 2. Add partition data. // TODO(mikhal): 1. Update score based on RTT value 2. Add partition data.
// Use the previous available. // Use the previous available.
bool base_available = false; bool base_available = false;
if ((index > 0) && (seq_num_list[index] == -1)) { if ((nack_seq_nums_index > 0) && (seq_num_list[nack_seq_nums_index] == -1)) {
// Found first packet, for now let's go only one back. // Found first packet, for now let's go only one back.
if ((seq_num_list[index - 1] == -1) || (seq_num_list[index - 1] == -2)) { if ((seq_num_list[nack_seq_nums_index - 1] == -1) ||
(seq_num_list[nack_seq_nums_index - 1] == -2)) {
// This is indeed the first packet, as previous packet was populated. // This is indeed the first packet, as previous packet was populated.
base_available = true; base_available = true;
} }
@ -460,10 +460,10 @@ int VCMSessionInfo::BuildSoftNackList(int* seq_num_list,
if (!packets_.empty()) { if (!packets_.empty()) {
PacketIterator it = packets_.begin(); PacketIterator it = packets_.begin();
PacketIterator prev_it = it; PacketIterator prev_it = it;
++index; ++nack_seq_nums_index;
++it; ++it;
// TODO(holmer): Rewrite this in a way which better makes use of the list. // TODO(holmer): Rewrite this in a way which better makes use of the list.
while (it != packets_.end() && index < seq_num_list_length) { while (it != packets_.end() && nack_seq_nums_index < seq_num_list_length) {
// Only process media packet sequence numbers. // Only process media packet sequence numbers.
if (LatestSequenceNumber((*it).seqNum, media_high_seq_num, NULL) == if (LatestSequenceNumber((*it).seqNum, media_high_seq_num, NULL) ==
(*it).seqNum && (*it).seqNum != media_high_seq_num) (*it).seqNum && (*it).seqNum != media_high_seq_num)
@ -479,23 +479,24 @@ int VCMSessionInfo::BuildSoftNackList(int* seq_num_list,
if (score > nack_score_threshold) { if (score > nack_score_threshold) {
allow_nack = true; allow_nack = true;
} else { } else {
seq_num_list[index] = -1; seq_num_list[nack_seq_nums_index] = -1;
} }
++index; ++nack_seq_nums_index;
} }
} }
seq_num_list[index] = -1; seq_num_list[nack_seq_nums_index] = -1;
++index; ++nack_seq_nums_index;
prev_it = it; prev_it = it;
++it; ++it;
} }
} }
index = ClearOutEmptyPacketSequenceNumbers(seq_num_list, seq_num_list_length, nack_seq_nums_index = ClearOutEmptyPacketSequenceNumbers(seq_num_list,
index); seq_num_list_length,
nack_seq_nums_index);
session_nack_ = allow_nack; session_nack_ = allow_nack;
return 0; return nack_seq_nums_index;
} }
int VCMSessionInfo::ClearOutEmptyPacketSequenceNumbers( int VCMSessionInfo::ClearOutEmptyPacketSequenceNumbers(
@ -527,12 +528,8 @@ int VCMSessionInfo::PacketsMissing(const PacketIterator& packet_it,
const PacketIterator& prev_packet_it) { const PacketIterator& prev_packet_it) {
if (packet_it == prev_packet_it) if (packet_it == prev_packet_it)
return 0; return 0;
if ((*prev_packet_it).seqNum > (*packet_it).seqNum) // Wrap. return static_cast<uint16_t>((*packet_it).seqNum -
return static_cast<WebRtc_UWord16>( (*prev_packet_it).seqNum - 1);
static_cast<WebRtc_UWord32>((*packet_it).seqNum + 0x10000) -
(*prev_packet_it).seqNum) - 1;
else
return (*packet_it).seqNum - (*prev_packet_it).seqNum - 1;
} }
bool bool

View File

@ -29,12 +29,14 @@ class VCMSessionInfo {
// Build hard NACK list: Zero out all entries in list up to and including // Build hard NACK list: Zero out all entries in list up to and including
// _lowSeqNum. // _lowSeqNum.
int BuildHardNackList(int* seq_num_list, int BuildHardNackList(int* seq_num_list,
int seq_num_list_length); int seq_num_list_length,
int nack_seq_nums_index);
// Build soft NACK list: Zero out only a subset of the packets, discard // Build soft NACK list: Zero out only a subset of the packets, discard
// empty packets. // empty packets.
int BuildSoftNackList(int* seq_num_list, int BuildSoftNackList(int* seq_num_list,
int seq_num_list_length, int seq_num_list_length,
int nack_seq_nums_index,
int rtt_ms); int rtt_ms);
void Reset(); void Reset();
int InsertPacket(const VCMPacket& packet, int InsertPacket(const VCMPacket& packet,

View File

@ -817,14 +817,15 @@ TEST_F(TestNackList, NoLosses) {
EXPECT_EQ(10 * kPacketBufferSize, session_.SessionLength()); EXPECT_EQ(10 * kPacketBufferSize, session_.SessionLength());
BuildSeqNumList(low, packet_.seqNum); BuildSeqNumList(low, packet_.seqNum);
EXPECT_EQ(0, session_.BuildHardNackList(seq_num_list_, seq_num_list_length_)); EXPECT_EQ(seq_num_list_length_, session_.BuildHardNackList(
seq_num_list_, seq_num_list_length_, 0));
EXPECT_FALSE(session_.session_nack()); EXPECT_FALSE(session_.session_nack());
SCOPED_TRACE("Calling VerifyAll"); SCOPED_TRACE("Calling VerifyAll");
VerifyAll(-1); VerifyAll(-1);
BuildSeqNumList(low, packet_.seqNum); BuildSeqNumList(low, packet_.seqNum);
EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_, seq_num_list_length_, EXPECT_EQ(seq_num_list_length_, session_.BuildSoftNackList(
60)); seq_num_list_, seq_num_list_length_, 0, 60));
SCOPED_TRACE("Calling VerifyAll"); SCOPED_TRACE("Calling VerifyAll");
VerifyAll(-1); VerifyAll(-1);
} }
@ -853,7 +854,9 @@ TEST_F(TestNackList, FiveLossesSpreadOut) {
EXPECT_EQ(5 * kPacketBufferSize, session_.SessionLength()); EXPECT_EQ(5 * kPacketBufferSize, session_.SessionLength());
BuildSeqNumList(low, packet_.seqNum); BuildSeqNumList(low, packet_.seqNum);
EXPECT_EQ(0, session_.BuildHardNackList(seq_num_list_, seq_num_list_length_)); // Will be at |seq_num_list_length - 1| since the last packet is missing.
EXPECT_EQ(seq_num_list_length_ - 1, session_.BuildHardNackList(
seq_num_list_, seq_num_list_length_, 0));
for (int i = 0; i < seq_num_list_length_; ++i) { for (int i = 0; i < seq_num_list_length_; ++i) {
if (i % 2) if (i % 2)
EXPECT_EQ(static_cast<uint16_t>(low + i), seq_num_list_[i]); EXPECT_EQ(static_cast<uint16_t>(low + i), seq_num_list_[i]);
@ -862,8 +865,9 @@ TEST_F(TestNackList, FiveLossesSpreadOut) {
} }
BuildSeqNumList(low, packet_.seqNum); BuildSeqNumList(low, packet_.seqNum);
EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_, seq_num_list_length_, // Will be at |seq_num_list_length - 1| since the last packet is missing.
60)); EXPECT_EQ(seq_num_list_length_ - 1, session_.BuildSoftNackList(
seq_num_list_, seq_num_list_length_, 0, 60));
EXPECT_EQ(true, session_.session_nack()); EXPECT_EQ(true, session_.session_nack());
for (int i = 0; i < seq_num_list_length_; ++i) { for (int i = 0; i < seq_num_list_length_; ++i) {
if (i % 2) if (i % 2)
@ -885,14 +889,17 @@ TEST_F(TestNackList, FirstAndLastLost) {
EXPECT_EQ(kPacketBufferSize, session_.SessionLength()); EXPECT_EQ(kPacketBufferSize, session_.SessionLength());
BuildSeqNumList(low, packet_.seqNum + 1); BuildSeqNumList(low, packet_.seqNum + 1);
EXPECT_EQ(0, session_.BuildHardNackList(seq_num_list_, seq_num_list_length_)); // Will be at |seq_num_list_length - 1| since the last packet is missing.
EXPECT_EQ(seq_num_list_length_ - 1, session_.BuildHardNackList(
seq_num_list_, seq_num_list_length_, 0));
EXPECT_EQ(0xFFFF, seq_num_list_[0]); EXPECT_EQ(0xFFFF, seq_num_list_[0]);
EXPECT_EQ(-1, seq_num_list_[1]); EXPECT_EQ(-1, seq_num_list_[1]);
EXPECT_EQ(1, seq_num_list_[2]); EXPECT_EQ(1, seq_num_list_[2]);
BuildSeqNumList(low, packet_.seqNum + 1); BuildSeqNumList(low, packet_.seqNum + 1);
EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_,seq_num_list_length_, // Will be at |seq_num_list_length - 1| since the last packet is missing.
60)); EXPECT_EQ(seq_num_list_length_ - 1, session_.BuildSoftNackList(
seq_num_list_, seq_num_list_length_, 0, 60));
EXPECT_EQ(true, session_.session_nack()); EXPECT_EQ(true, session_.session_nack());
EXPECT_EQ(0xFFFF, seq_num_list_[0]); EXPECT_EQ(0xFFFF, seq_num_list_[0]);
EXPECT_EQ(-1, seq_num_list_[1]); EXPECT_EQ(-1, seq_num_list_[1]);
@ -917,10 +924,24 @@ TEST_F(TestNackList, LostAllButEmptyPackets) {
FillPacket(0); FillPacket(0);
ASSERT_EQ(session_.InsertPacket(packet_, frame_buffer_, false, 0), 0); ASSERT_EQ(session_.InsertPacket(packet_, frame_buffer_, false, 0), 0);
// Test soft NACKing.
EXPECT_EQ(0, session_.SessionLength()); EXPECT_EQ(0, session_.SessionLength());
BuildSeqNumList(low, packet_.seqNum + 1); BuildSeqNumList(low, packet_.seqNum + 1);
EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_, seq_num_list_length_, // Will be at |seq_num_list_length - 1| since the last packet is missing.
60)); EXPECT_EQ(seq_num_list_length_ - 1, session_.BuildSoftNackList(
seq_num_list_, seq_num_list_length_, 0, 60));
EXPECT_EQ(true, session_.session_nack());
EXPECT_EQ(0, seq_num_list_[0]);
EXPECT_EQ(-1, seq_num_list_[1]);
EXPECT_EQ(-2, seq_num_list_[2]);
EXPECT_EQ(-2, seq_num_list_[3]);
EXPECT_EQ(4, seq_num_list_[4]);
// Test hard NACKing.
BuildSeqNumList(low, packet_.seqNum + 1);
// Will be at |seq_num_list_length - 1| since the last packet is missing.
EXPECT_EQ(seq_num_list_length_ - 1, session_.BuildHardNackList(
seq_num_list_, seq_num_list_length_, 0));
EXPECT_EQ(true, session_.session_nack()); EXPECT_EQ(true, session_.session_nack());
EXPECT_EQ(0, seq_num_list_[0]); EXPECT_EQ(0, seq_num_list_[0]);
EXPECT_EQ(-1, seq_num_list_[1]); EXPECT_EQ(-1, seq_num_list_[1]);