Fix dropped frames not counted issue

There's been reports of dropped frames that are not counted and
correctly reported by getStats().

If a HW decoder is used and the system is provoked by stressing
the system, I've been able to reproduce this problem. It turns out
that we've missed frames that are dropped because there is no
callback to the Decoded() function.

This CL restructures the code so that dropped frames are counted
even in cases where there's no corresponding callback for some frames.

Bug: webrtc:11229
Change-Id: I0216edba3733399c188649908d459ee86a9093d0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/214783
Commit-Queue: Johannes Kron <kron@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33671}
diff --git a/modules/video_coding/generic_decoder.cc b/modules/video_coding/generic_decoder.cc
index 85c68da..621fd73 100644
--- a/modules/video_coding/generic_decoder.cc
+++ b/modules/video_coding/generic_decoder.cc
@@ -93,16 +93,27 @@
   // callbacks from one call to Decode().
   absl::optional<VCMFrameInformation> frameInfo;
   int timestamp_map_size = 0;
+  int dropped_frames = 0;
   {
     MutexLock lock(&lock_);
+    int initial_timestamp_map_size = _timestampMap.Size();
     frameInfo = _timestampMap.Pop(decodedImage.timestamp());
     timestamp_map_size = _timestampMap.Size();
+    // _timestampMap.Pop() erases all frame upto the specified timestamp and
+    // return the frame info for this timestamp if it exists. Thus, the
+    // difference in the _timestampMap size before and after Pop() will show
+    // internally dropped frames.
+    dropped_frames =
+        initial_timestamp_map_size - timestamp_map_size - (frameInfo ? 1 : 0);
+  }
+
+  if (dropped_frames > 0) {
+    _receiveCallback->OnDroppedFrames(dropped_frames);
   }
 
   if (!frameInfo) {
     RTC_LOG(LS_WARNING) << "Too many frames backed up in the decoder, dropping "
                            "this one.";
-    _receiveCallback->OnDroppedFrames(1);
     return;
   }
 
@@ -197,17 +208,29 @@
 
 void VCMDecodedFrameCallback::Map(uint32_t timestamp,
                                   const VCMFrameInformation& frameInfo) {
-  MutexLock lock(&lock_);
-  _timestampMap.Add(timestamp, frameInfo);
+  int dropped_frames = 0;
+  {
+    MutexLock lock(&lock_);
+    int initial_size = _timestampMap.Size();
+    _timestampMap.Add(timestamp, frameInfo);
+    // If no frame is dropped, the new size should be |initial_size| + 1
+    dropped_frames = (initial_size + 1) - _timestampMap.Size();
+  }
+  if (dropped_frames > 0) {
+    _receiveCallback->OnDroppedFrames(dropped_frames);
+  }
 }
 
-int32_t VCMDecodedFrameCallback::Pop(uint32_t timestamp) {
-  MutexLock lock(&lock_);
-  if (_timestampMap.Pop(timestamp) == absl::nullopt) {
-    return VCM_GENERAL_ERROR;
+void VCMDecodedFrameCallback::ClearTimestampMap() {
+  int dropped_frames = 0;
+  {
+    MutexLock lock(&lock_);
+    dropped_frames = _timestampMap.Size();
+    _timestampMap.Clear();
   }
-  _receiveCallback->OnDroppedFrames(1);
-  return VCM_OK;
+  if (dropped_frames > 0) {
+    _receiveCallback->OnDroppedFrames(dropped_frames);
+  }
 }
 
 VCMGenericDecoder::VCMGenericDecoder(std::unique_ptr<VideoDecoder> decoder)
@@ -281,11 +304,10 @@
   if (ret < WEBRTC_VIDEO_CODEC_OK) {
     RTC_LOG(LS_WARNING) << "Failed to decode frame with timestamp "
                         << frame.Timestamp() << ", error code: " << ret;
-    _callback->Pop(frame.Timestamp());
-    return ret;
+    _callback->ClearTimestampMap();
   } else if (ret == WEBRTC_VIDEO_CODEC_NO_OUTPUT) {
-    // No output
-    _callback->Pop(frame.Timestamp());
+    // No output.
+    _callback->ClearTimestampMap();
   }
   return ret;
 }