DecodeFrameHistory can now deal with negative picture IDs.

Bug: webrtc:10263
Change-Id: I5c4f9511074b86dc73c9677a14dd10ee18c6ea4f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/128422
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Johannes Kron <kron@webrtc.org>
Commit-Queue: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27176}
diff --git a/modules/video_coding/utility/decoded_frames_history.cc b/modules/video_coding/utility/decoded_frames_history.cc
index b8709ad..d15cf26 100644
--- a/modules/video_coding/utility/decoded_frames_history.cc
+++ b/modules/video_coding/utility/decoded_frames_history.cc
@@ -33,41 +33,41 @@
   if (static_cast<int>(layers_.size()) < frameid.spatial_layer + 1) {
     size_t old_size = layers_.size();
     layers_.resize(frameid.spatial_layer + 1);
-    for (size_t i = old_size; i < layers_.size(); ++i) {
+
+    for (size_t i = old_size; i < layers_.size(); ++i)
       layers_[i].buffer.resize(window_size_);
-      layers_[i].last_stored_index = 0;
-    }
-    layers_[frameid.spatial_layer].last_stored_index = frameid.picture_id;
-    layers_[frameid.spatial_layer].buffer[frameid.picture_id % window_size_] =
-        true;
+
+    layers_[frameid.spatial_layer].last_picture_id = frameid.picture_id;
+    layers_[frameid.spatial_layer]
+        .buffer[PictureIdToIndex(frameid.picture_id)] = true;
     return;
   }
 
+  int new_index = PictureIdToIndex(frameid.picture_id);
   LayerHistory& history = layers_[frameid.spatial_layer];
 
-  RTC_DCHECK_LT(history.last_stored_index, frameid.picture_id);
+  RTC_DCHECK(history.last_picture_id < frameid.picture_id);
 
-  int64_t id_jump = frameid.picture_id - history.last_stored_index;
-  int last_index = history.last_stored_index % window_size_;
-  int new_index = frameid.picture_id % window_size_;
+  // Clears expired values from the cyclic buffer.
+  if (history.last_picture_id) {
+    int64_t id_jump = frameid.picture_id - *history.last_picture_id;
+    int last_index = PictureIdToIndex(*history.last_picture_id);
 
-  // Need to clear indexes between last_index + 1 and new_index as they fall
-  // out of the history window and now represent new unseen picture ids.
-  if (id_jump >= window_size_) {
-    // Wraps around whole buffer - clear it all.
-    std::fill(history.buffer.begin(), history.buffer.end(), false);
-  } else if (new_index > last_index) {
-    std::fill(history.buffer.begin() + last_index + 1,
-              history.buffer.begin() + new_index, false);
-  } else {
-    std::fill(history.buffer.begin() + last_index + 1, history.buffer.end(),
-              false);
-    std::fill(history.buffer.begin(), history.buffer.begin() + new_index,
-              false);
+    if (id_jump >= window_size_) {
+      std::fill(history.buffer.begin(), history.buffer.end(), false);
+    } else if (new_index > last_index) {
+      std::fill(history.buffer.begin() + last_index + 1,
+                history.buffer.begin() + new_index, false);
+    } else {
+      std::fill(history.buffer.begin() + last_index + 1, history.buffer.end(),
+                false);
+      std::fill(history.buffer.begin(), history.buffer.begin() + new_index,
+                false);
+    }
   }
 
   history.buffer[new_index] = true;
-  history.last_stored_index = frameid.picture_id;
+  history.last_picture_id = frameid.picture_id;
 }
 
 bool DecodedFramesHistory::WasDecoded(const VideoLayerFrameId& frameid) {
@@ -77,24 +77,24 @@
 
   LayerHistory& history = layers_[frameid.spatial_layer];
 
+  if (!history.last_picture_id)
+    return false;
+
   // Reference to the picture_id out of the stored history should happen.
-  if (frameid.picture_id <= history.last_stored_index - window_size_) {
+  if (frameid.picture_id <= *history.last_picture_id - window_size_) {
     RTC_LOG(LS_WARNING) << "Referencing a frame out of the history window. "
                            "Assuming it was undecoded to avoid artifacts.";
     return false;
   }
 
-  if (frameid.picture_id > history.last_stored_index)
+  if (frameid.picture_id > history.last_picture_id)
     return false;
 
-  return history.buffer[frameid.picture_id % window_size_];
+  return history.buffer[PictureIdToIndex(frameid.picture_id)];
 }
 
 void DecodedFramesHistory::Clear() {
-  for (LayerHistory& layer : layers_) {
-    std::fill(layer.buffer.begin(), layer.buffer.end(), false);
-    layer.last_stored_index = 0;
-  }
+  layers_.clear();
   last_decoded_frame_timestamp_.reset();
   last_decoded_frame_.reset();
 }
@@ -108,5 +108,10 @@
   return last_decoded_frame_timestamp_;
 }
 
+int DecodedFramesHistory::PictureIdToIndex(int64_t frame_id) const {
+  int m = frame_id % window_size_;
+  return m >= 0 ? m : m + window_size_;
+}
+
 }  // namespace video_coding
 }  // namespace webrtc
diff --git a/modules/video_coding/utility/decoded_frames_history.h b/modules/video_coding/utility/decoded_frames_history.h
index 3790946..07c21e6 100644
--- a/modules/video_coding/utility/decoded_frames_history.h
+++ b/modules/video_coding/utility/decoded_frames_history.h
@@ -42,13 +42,12 @@
     LayerHistory();
     ~LayerHistory();
     // Cyclic bitset buffer. Stores last known |window_size| bits.
-    // last_stored_index is the last actually stored bit. Previous
-    // |window_size-1| bits are also in the memory. Value for i-th bit is at
-    // buffer[i % window_size].
     std::vector<bool> buffer;
-    int64_t last_stored_index;
+    absl::optional<int64_t> last_picture_id;
   };
 
+  int PictureIdToIndex(int64_t frame_id) const;
+
   const int window_size_;
   std::vector<LayerHistory> layers_;
   absl::optional<VideoLayerFrameId> last_decoded_frame_;
diff --git a/modules/video_coding/utility/decoded_frames_history_unittest.cc b/modules/video_coding/utility/decoded_frames_history_unittest.cc
index 12ed282..b3bcf48 100644
--- a/modules/video_coding/utility/decoded_frames_history_unittest.cc
+++ b/modules/video_coding/utility/decoded_frames_history_unittest.cc
@@ -121,6 +121,25 @@
   EXPECT_EQ(history.GetLastDecodedFrameTimestamp(), 12366u);
 }
 
+TEST(DecodedFramesHistory, NegativePictureIds) {
+  DecodedFramesHistory history(kHistorySize);
+  history.InsertDecoded({-1234, 0}, 12345);
+  history.InsertDecoded({-1233, 0}, 12366);
+  EXPECT_EQ(history.GetLastDecodedFrameId()->picture_id, -1233);
+
+  history.InsertDecoded({-1, 0}, 12377);
+  history.InsertDecoded({0, 0}, 12388);
+  EXPECT_EQ(history.GetLastDecodedFrameId()->picture_id, 0);
+
+  history.InsertDecoded({1, 0}, 12399);
+  EXPECT_EQ(history.GetLastDecodedFrameId()->picture_id, 1);
+
+  EXPECT_EQ(history.WasDecoded({-1234, 0}), true);
+  EXPECT_EQ(history.WasDecoded({-1, 0}), true);
+  EXPECT_EQ(history.WasDecoded({0, 0}), true);
+  EXPECT_EQ(history.WasDecoded({1, 0}), true);
+}
+
 }  // namespace
 }  // namespace video_coding
 }  // namespace webrtc