Clear the FrameBuffer in case of a backward jump in the picture id.

Even though this is against the spec we allow a stream to continue if
a backwards jump in the picture id occurs on a keyframe.

BUG=webrtc:7001, webrtc:5514

Review-Url: https://codereview.webrtc.org/2640793003
Cr-Original-Commit-Position: refs/heads/master@{#16146}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: fcc600651dfe50e2d6e27fc77e5acbf04f5a63cd
diff --git a/modules/video_coding/frame_buffer2.cc b/modules/video_coding/frame_buffer2.cc
index 279c613..58c41bf 100644
--- a/modules/video_coding/frame_buffer2.cc
+++ b/modules/video_coding/frame_buffer2.cc
@@ -135,6 +135,7 @@
 
     PropagateDecodability(next_frame_it->second);
     AdvanceLastDecodedFrame(next_frame_it);
+    last_decoded_frame_timestamp_ = frame->timestamp;
     *frame_out = std::move(frame);
     return kFrameFound;
   } else {
@@ -189,14 +190,27 @@
 
   if (last_decoded_frame_it_ != frames_.end() &&
       key < last_decoded_frame_it_->first) {
-    LOG(LS_WARNING) << "Frame with (picture_id:spatial_id) (" << key.picture_id
-                    << ":" << static_cast<int>(key.spatial_layer)
-                    << ") inserted after frame ("
-                    << last_decoded_frame_it_->first.picture_id << ":"
-                    << static_cast<int>(
-                           last_decoded_frame_it_->first.spatial_layer)
-                    << ") was handed off for decoding, dropping frame.";
-    return last_continuous_picture_id;
+    if (AheadOf(frame->timestamp, last_decoded_frame_timestamp_) &&
+        frame->num_references == 0) {
+      // If this frame has a newer timestamp but an earlier picture id then we
+      // assume there has been a jump in the picture id due to some encoder
+      // reconfiguration or some other reason. Even though this is not according
+      // to spec we can still continue to decode from this frame if it is a
+      // keyframe.
+      LOG(LS_WARNING) << "A jump in picture id was detected, clearing buffer.";
+      ClearFramesAndHistory();
+      last_continuous_picture_id = -1;
+    } else {
+      LOG(LS_WARNING) << "Frame with (picture_id:spatial_id) ("
+                      << key.picture_id << ":"
+                      << static_cast<int>(key.spatial_layer)
+                      << ") inserted after frame ("
+                      << last_decoded_frame_it_->first.picture_id << ":"
+                      << static_cast<int>(
+                             last_decoded_frame_it_->first.spatial_layer)
+                      << ") was handed off for decoding, dropping frame.";
+      return last_continuous_picture_id;
+    }
   }
 
   auto info = frames_.insert(std::make_pair(key, FrameInfo())).first;
@@ -390,5 +404,13 @@
   }
 }
 
+void FrameBuffer::ClearFramesAndHistory() {
+  frames_.clear();
+  last_decoded_frame_it_ = frames_.end();
+  last_continuous_frame_it_ = frames_.end();
+  num_frames_history_ = 0;
+  num_frames_buffered_ = 0;
+}
+
 }  // namespace video_coding
 }  // namespace webrtc
diff --git a/modules/video_coding/frame_buffer2.h b/modules/video_coding/frame_buffer2.h
index b41ef2f..f667fd5 100644
--- a/modules/video_coding/frame_buffer2.h
+++ b/modules/video_coding/frame_buffer2.h
@@ -143,6 +143,8 @@
 
   void UpdateHistograms() const;
 
+  void ClearFramesAndHistory() EXCLUSIVE_LOCKS_REQUIRED(crit_);
+
   FrameMap frames_ GUARDED_BY(crit_);
 
   rtc::CriticalSection crit_;
@@ -151,6 +153,7 @@
   VCMJitterEstimator* const jitter_estimator_ GUARDED_BY(crit_);
   VCMTiming* const timing_ GUARDED_BY(crit_);
   VCMInterFrameDelay inter_frame_delay_ GUARDED_BY(crit_);
+  uint32_t last_decoded_frame_timestamp_ GUARDED_BY(crit_);
   FrameMap::iterator last_decoded_frame_it_ GUARDED_BY(crit_);
   FrameMap::iterator last_continuous_frame_it_ GUARDED_BY(crit_);
   int num_frames_history_ GUARDED_BY(crit_);
diff --git a/modules/video_coding/frame_buffer2_unittest.cc b/modules/video_coding/frame_buffer2_unittest.cc
index 6079bb9..96be01f 100644
--- a/modules/video_coding/frame_buffer2_unittest.cc
+++ b/modules/video_coding/frame_buffer2_unittest.cc
@@ -419,5 +419,22 @@
   EXPECT_EQ(pid + 3, InsertFrame(pid + 3, 1, ts, true, pid + 2));
 }
 
+TEST_F(TestFrameBuffer2, PictureIdJumpBack) {
+  uint16_t pid = Rand();
+  uint32_t ts = Rand();
+
+  EXPECT_EQ(pid, InsertFrame(pid, 0, ts, false));
+  EXPECT_EQ(pid + 1, InsertFrame(pid + 1, 0, ts + 1, false, pid));
+  ExtractFrame();
+  CheckFrame(0, pid, 0);
+
+  // Jump back in pid but increase ts.
+  EXPECT_EQ(pid - 1, InsertFrame(pid - 1, 0, ts + 2, false));
+  ExtractFrame();
+  ExtractFrame();
+  CheckFrame(1, pid - 1, 0);
+  CheckNoFrame(2);
+}
+
 }  // namespace video_coding
 }  // namespace webrtc