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
diff --git a/webrtc/modules/video_coding/main/source/frame_buffer.cc b/webrtc/modules/video_coding/main/source/frame_buffer.cc
index fe30bea..fe10240 100644
--- a/webrtc/modules/video_coding/main/source/frame_buffer.cc
+++ b/webrtc/modules/video_coding/main/source/frame_buffer.cc
@@ -206,17 +206,16 @@
 
 // Build hard NACK list:Zero out all entries in list up to and including the
 // (first) entry equal to _lowSeqNum.
-int VCMFrameBuffer::BuildHardNackList(int* list, int num) {
-  if (_sessionInfo.BuildHardNackList(list, num) != 0) {
-   return -1;
-  }
-  return 0;
+int VCMFrameBuffer::BuildHardNackList(int* list, int num,
+                                      int nack_seq_nums_index) {
+  return _sessionInfo.BuildHardNackList(list, num, nack_seq_nums_index);
 }
 
 // Build selective NACK list: Create a soft (selective) list of entries to zero
 // out up to and including the (first) entry equal to _lowSeqNum.
-int VCMFrameBuffer::BuildSoftNackList(int* list, int num, int rttMs) {
-  return _sessionInfo.BuildSoftNackList(list, num, rttMs);
+int VCMFrameBuffer::BuildSoftNackList(int* list, int num,
+                                      int nack_seq_nums_index, int rttMs) {
+  return _sessionInfo.BuildSoftNackList(list, num, nack_seq_nums_index, rttMs);
 }
 
 void
diff --git a/webrtc/modules/video_coding/main/source/frame_buffer.h b/webrtc/modules/video_coding/main/source/frame_buffer.h
index eeacfad..ef4d154 100644
--- a/webrtc/modules/video_coding/main/source/frame_buffer.h
+++ b/webrtc/modules/video_coding/main/source/frame_buffer.h
@@ -67,10 +67,11 @@
     // NACK - Building the NACK lists.
     // Build hard NACK list: Zero out all entries in list up to and including
     // _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
     // 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();
     WebRtc_Word16 GetNackCount() const;
 
diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.cc b/webrtc/modules/video_coding/main/source/jitter_buffer.cc
index 8121497..b77a95a 100644
--- a/webrtc/modules/video_coding/main/source/jitter_buffer.cc
+++ b/webrtc/modules/video_coding/main/source/jitter_buffer.cc
@@ -926,24 +926,25 @@
     nack_seq_nums_internal_[i] = seq_number_iterator;
     seq_number_iterator++;
   }
-  // Now we have a list of all sequence numbers that could have been sent.
-  // Zero out the ones we have received.
-  for (i = 0; i < max_number_of_frames_; i++) {
-    // We don't need to check if frame is decoding since low_seq_num is based
-    // on the last decoded sequence number.
-    VCMFrameBufferStateEnum state = frame_buffers_[i]->GetState();
-    if (kStateFree != state) {
+  if (number_of_seq_num > 0) {
+    // Now we have a list of all sequence numbers that could have been sent.
+    // Zero out the ones we have received.
+    int nack_seq_nums_index = 0;
+    for (FrameList::iterator it = frame_list_.begin(); it != frame_list_.end();
+        ++it) {
       // Reaching thus far means we are going to update the NACK list
       // When in hybrid mode, we use the soft NACKing feature.
       if (nack_mode_ == kNackHybrid) {
-        frame_buffers_[i]->BuildSoftNackList(nack_seq_nums_internal_,
-                                             number_of_seq_num,
-                                             rtt_ms_);
+        nack_seq_nums_index = (*it)->BuildSoftNackList(nack_seq_nums_internal_,
+                                                       number_of_seq_num,
+                                                       nack_seq_nums_index,
+                                                       rtt_ms_);
       } else {
         // Used when the frame is being processed by the decoding thread
         // don't need to use that info in this loop.
-        frame_buffers_[i]->BuildHardNackList(nack_seq_nums_internal_,
-                                             number_of_seq_num);
+        nack_seq_nums_index = (*it)->BuildHardNackList(nack_seq_nums_internal_,
+                                                       number_of_seq_num,
+                                                       nack_seq_nums_index);
       }
     }
   }
@@ -980,7 +981,6 @@
     // Larger list: NACK list was extended since the last call.
     *list_extended = true;
   }
-
   for (unsigned int j = 0; j < *nack_list_size; j++) {
     // Check if the list has been extended since it was last created, i.e,
     // new items have been added.
diff --git a/webrtc/modules/video_coding/main/source/session_info.cc b/webrtc/modules/video_coding/main/source/session_info.cc
index e40ffea..6c5448b 100644
--- a/webrtc/modules/video_coding/main/source/session_info.cc
+++ b/webrtc/modules/video_coding/main/source/session_info.cc
@@ -353,23 +353,22 @@
 }
 
 int VCMSessionInfo::BuildHardNackList(int* seq_num_list,
-                                      int seq_num_list_length) {
-  if (NULL == seq_num_list || seq_num_list_length < 1) {
-    return -1;
-  }
+                                      int seq_num_list_length,
+                                      int nack_seq_nums_index) {
+  assert(seq_num_list && seq_num_list_length > 0);
+
   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
   // packet).
-  int index = 0;
   int low_seq_num = (packets_.empty()) ? empty_seq_num_low_:
       packets_.front().seqNum;
-  for (; index < seq_num_list_length; ++index) {
-    if (seq_num_list[index] == low_seq_num) {
-      seq_num_list[index] = -1;
-      ++index;
+  for (; nack_seq_nums_index < seq_num_list_length; ++nack_seq_nums_index) {
+    if (seq_num_list[nack_seq_nums_index] == low_seq_num) {
+      seq_num_list[nack_seq_nums_index] = -1;
+      ++nack_seq_nums_index;
       break;
     }
   }
@@ -379,43 +378,43 @@
     PacketIterator it = packets_.begin();
     PacketIterator prev_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)) {
         // 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;
       }
-      seq_num_list[index] = -1;
-      ++index;
+      seq_num_list[nack_seq_nums_index] = -1;
+      ++nack_seq_nums_index;
       prev_it = it;
       ++it;
     }
     if (!packets_.front().isFirstPacket)
         session_nack_ = true;
   }
-  index = ClearOutEmptyPacketSequenceNumbers(seq_num_list, seq_num_list_length,
-                                             index);
-  return 0;
+  nack_seq_nums_index = ClearOutEmptyPacketSequenceNumbers(seq_num_list,
+                                                           seq_num_list_length,
+                                                           nack_seq_nums_index);
+  return nack_seq_nums_index;
 }
 
 int VCMSessionInfo::BuildSoftNackList(int* seq_num_list,
                                       int seq_num_list_length,
+                                      int nack_seq_nums_index,
                                       int rtt_ms) {
-  if (NULL == seq_num_list || seq_num_list_length < 1) {
-    return -1;
-  }
+  assert(seq_num_list && seq_num_list_length > 0);
+
   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_:
       packets_.front().seqNum;
-  // Find entrance point (index of entry equals the sequence number of the
-  // first packet).
-  for (; index < seq_num_list_length; ++index) {
-    if (seq_num_list[index] == low_seq_num) {
-      seq_num_list[index] = -1;
+  // Find entrance point (nack_seq_nums_index of entry equals the sequence
+  // number of the first packet).
+  for (; nack_seq_nums_index < seq_num_list_length; ++nack_seq_nums_index) {
+    if (seq_num_list[nack_seq_nums_index] == low_seq_num) {
+      seq_num_list[nack_seq_nums_index] = -1;
       break;
     }
   }
@@ -423,9 +422,10 @@
   // TODO(mikhal): 1. Update score based on RTT value 2. Add partition data.
   // Use the previous available.
   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.
-    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.
       base_available = true;
     }
@@ -460,10 +460,10 @@
   if (!packets_.empty()) {
     PacketIterator it = packets_.begin();
     PacketIterator prev_it = it;
-    ++index;
+    ++nack_seq_nums_index;
     ++it;
     // 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.
       if (LatestSequenceNumber((*it).seqNum, media_high_seq_num, NULL) ==
         (*it).seqNum && (*it).seqNum != media_high_seq_num)
@@ -479,23 +479,24 @@
           if (score > nack_score_threshold) {
             allow_nack = true;
           } else {
-            seq_num_list[index] = -1;
+            seq_num_list[nack_seq_nums_index] = -1;
           }
-          ++index;
+          ++nack_seq_nums_index;
         }
       }
-      seq_num_list[index] = -1;
-      ++index;
+      seq_num_list[nack_seq_nums_index] = -1;
+      ++nack_seq_nums_index;
       prev_it = it;
       ++it;
     }
   }
 
-  index = ClearOutEmptyPacketSequenceNumbers(seq_num_list, seq_num_list_length,
-                                             index);
+  nack_seq_nums_index = ClearOutEmptyPacketSequenceNumbers(seq_num_list,
+                                                           seq_num_list_length,
+                                                           nack_seq_nums_index);
 
   session_nack_ = allow_nack;
-  return 0;
+  return nack_seq_nums_index;
 }
 
 int VCMSessionInfo::ClearOutEmptyPacketSequenceNumbers(
@@ -527,12 +528,8 @@
                                    const PacketIterator& prev_packet_it) {
   if (packet_it == prev_packet_it)
     return 0;
-  if ((*prev_packet_it).seqNum > (*packet_it).seqNum)  // Wrap.
-    return static_cast<WebRtc_UWord16>(
-        static_cast<WebRtc_UWord32>((*packet_it).seqNum + 0x10000) -
-        (*prev_packet_it).seqNum) - 1;
-  else
-    return (*packet_it).seqNum - (*prev_packet_it).seqNum - 1;
+  return static_cast<uint16_t>((*packet_it).seqNum -
+                               (*prev_packet_it).seqNum - 1);
 }
 
 bool
diff --git a/webrtc/modules/video_coding/main/source/session_info.h b/webrtc/modules/video_coding/main/source/session_info.h
index bf7e543..0135ee9 100644
--- a/webrtc/modules/video_coding/main/source/session_info.h
+++ b/webrtc/modules/video_coding/main/source/session_info.h
@@ -29,12 +29,14 @@
   // Build hard NACK list: Zero out all entries in list up to and including
   // _lowSeqNum.
   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
   // empty packets.
   int BuildSoftNackList(int* seq_num_list,
                         int seq_num_list_length,
+                        int nack_seq_nums_index,
                         int rtt_ms);
   void Reset();
   int InsertPacket(const VCMPacket& packet,
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 e017735..2a78813 100644
--- a/webrtc/modules/video_coding/main/source/session_info_unittest.cc
+++ b/webrtc/modules/video_coding/main/source/session_info_unittest.cc
@@ -817,14 +817,15 @@
 
   EXPECT_EQ(10 * kPacketBufferSize, session_.SessionLength());
   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());
   SCOPED_TRACE("Calling VerifyAll");
   VerifyAll(-1);
 
   BuildSeqNumList(low, packet_.seqNum);
-  EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_, seq_num_list_length_,
-                                          60));
+  EXPECT_EQ(seq_num_list_length_, session_.BuildSoftNackList(
+      seq_num_list_, seq_num_list_length_, 0, 60));
   SCOPED_TRACE("Calling VerifyAll");
   VerifyAll(-1);
 }
@@ -853,7 +854,9 @@
 
   EXPECT_EQ(5 * kPacketBufferSize, session_.SessionLength());
   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) {
     if (i % 2)
       EXPECT_EQ(static_cast<uint16_t>(low + i), seq_num_list_[i]);
@@ -862,8 +865,9 @@
   }
 
   BuildSeqNumList(low, packet_.seqNum);
-  EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_, seq_num_list_length_,
-                                          60));
+  // Will be at |seq_num_list_length - 1| since the last packet is missing.
+  EXPECT_EQ(seq_num_list_length_ - 1, session_.BuildSoftNackList(
+      seq_num_list_, seq_num_list_length_, 0, 60));
   EXPECT_EQ(true, session_.session_nack());
   for (int i = 0; i < seq_num_list_length_; ++i) {
     if (i % 2)
@@ -885,14 +889,17 @@
 
   EXPECT_EQ(kPacketBufferSize, session_.SessionLength());
   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(-1, seq_num_list_[1]);
   EXPECT_EQ(1, seq_num_list_[2]);
 
   BuildSeqNumList(low, packet_.seqNum + 1);
-  EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_,seq_num_list_length_,
-                                          60));
+  // Will be at |seq_num_list_length - 1| since the last packet is missing.
+  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(0xFFFF, seq_num_list_[0]);
   EXPECT_EQ(-1, seq_num_list_[1]);
@@ -917,10 +924,24 @@
   FillPacket(0);
   ASSERT_EQ(session_.InsertPacket(packet_, frame_buffer_, false, 0), 0);
 
+  // Test soft NACKing.
   EXPECT_EQ(0, session_.SessionLength());
   BuildSeqNumList(low, packet_.seqNum + 1);
-  EXPECT_EQ(0, session_.BuildSoftNackList(seq_num_list_, seq_num_list_length_,
-                                          60));
+  // Will be at |seq_num_list_length - 1| since the last packet is missing.
+  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(0, seq_num_list_[0]);
   EXPECT_EQ(-1, seq_num_list_[1]);