Call: prepare receive stats for thread switch.

This change collects the receive stats that Call
maintains into a new thread-compatible
internal class which can easily be switched to
the network thread.

Bug: webrtc:11993
Change-Id: I9fa9a7f057149789aa327e5ba8a8cb3379762272
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/220760
Reviewed-by: Tommi <tommi@webrtc.org>
Commit-Queue: Markus Handell <handellm@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34158}
diff --git a/call/call.cc b/call/call.cc
index f543deb..cdc57be 100644
--- a/call/call.cc
+++ b/call/call.cc
@@ -321,6 +321,37 @@
   void SetClientBitratePreferences(const BitrateSettings& preferences) override;
 
  private:
+  // Thread-compatible class that collects received packet stats and exposes
+  // them as UMA histograms on destruction.
+  class ReceiveStats {
+   public:
+    explicit ReceiveStats(Clock* clock);
+    ~ReceiveStats();
+
+    void AddReceivedRtcpBytes(int bytes);
+    void AddReceivedAudioBytes(int bytes, webrtc::Timestamp arrival_time);
+    void AddReceivedVideoBytes(int bytes, webrtc::Timestamp arrival_time);
+
+   private:
+    SequenceChecker sequence_checker_;
+    RateCounter received_bytes_per_second_counter_
+        RTC_GUARDED_BY(sequence_checker_);
+    RateCounter received_audio_bytes_per_second_counter_
+        RTC_GUARDED_BY(sequence_checker_);
+    RateCounter received_video_bytes_per_second_counter_
+        RTC_GUARDED_BY(sequence_checker_);
+    RateCounter received_rtcp_bytes_per_second_counter_
+        RTC_GUARDED_BY(sequence_checker_);
+    absl::optional<Timestamp> first_received_rtp_audio_timestamp_
+        RTC_GUARDED_BY(sequence_checker_);
+    absl::optional<Timestamp> last_received_rtp_audio_timestamp_
+        RTC_GUARDED_BY(sequence_checker_);
+    absl::optional<Timestamp> first_received_rtp_video_timestamp_
+        RTC_GUARDED_BY(sequence_checker_);
+    absl::optional<Timestamp> last_received_rtp_video_timestamp_
+        RTC_GUARDED_BY(sequence_checker_);
+  };
+
   DeliveryStatus DeliverRtcp(MediaType media_type,
                              const uint8_t* packet,
                              size_t length)
@@ -336,7 +367,6 @@
                                  MediaType media_type)
       RTC_SHARED_LOCKS_REQUIRED(worker_thread_);
 
-  void UpdateReceiveHistograms();
   void UpdateAggregateNetworkState();
 
   // Ensure that necessary process threads are started, and any required
@@ -433,17 +463,9 @@
 
   webrtc::RtcEventLog* event_log_;
 
-  // The following members are only accessed (exclusively) from one thread and
-  // from the destructor, and therefore doesn't need any explicit
-  // synchronization.
-  RateCounter received_bytes_per_second_counter_;
-  RateCounter received_audio_bytes_per_second_counter_;
-  RateCounter received_video_bytes_per_second_counter_;
-  RateCounter received_rtcp_bytes_per_second_counter_;
-  absl::optional<int64_t> first_received_rtp_audio_ms_;
-  absl::optional<int64_t> last_received_rtp_audio_ms_;
-  absl::optional<int64_t> first_received_rtp_video_ms_;
-  absl::optional<int64_t> last_received_rtp_video_ms_;
+  // TODO(bugs.webrtc.org/11993) ready to move receive stats access to the
+  // network thread.
+  ReceiveStats receive_stats_ RTC_GUARDED_BY(worker_thread_);
 
   uint32_t last_bandwidth_bps_ RTC_GUARDED_BY(worker_thread_);
   // TODO(holmer): Remove this lock once BitrateController no longer calls
@@ -625,6 +647,94 @@
 
 namespace internal {
 
+Call::ReceiveStats::ReceiveStats(Clock* clock)
+    : received_bytes_per_second_counter_(clock, nullptr, false),
+      received_audio_bytes_per_second_counter_(clock, nullptr, false),
+      received_video_bytes_per_second_counter_(clock, nullptr, false),
+      received_rtcp_bytes_per_second_counter_(clock, nullptr, false) {
+  sequence_checker_.Detach();
+}
+
+void Call::ReceiveStats::AddReceivedRtcpBytes(int bytes) {
+  RTC_DCHECK_RUN_ON(&sequence_checker_);
+  if (received_bytes_per_second_counter_.HasSample()) {
+    // First RTP packet has been received.
+    received_bytes_per_second_counter_.Add(static_cast<int>(bytes));
+    received_rtcp_bytes_per_second_counter_.Add(static_cast<int>(bytes));
+  }
+}
+
+void Call::ReceiveStats::AddReceivedAudioBytes(int bytes,
+                                               webrtc::Timestamp arrival_time) {
+  RTC_DCHECK_RUN_ON(&sequence_checker_);
+  received_bytes_per_second_counter_.Add(bytes);
+  received_audio_bytes_per_second_counter_.Add(bytes);
+  if (!first_received_rtp_audio_timestamp_)
+    first_received_rtp_audio_timestamp_ = arrival_time;
+  last_received_rtp_audio_timestamp_ = arrival_time;
+}
+
+void Call::ReceiveStats::AddReceivedVideoBytes(int bytes,
+                                               webrtc::Timestamp arrival_time) {
+  RTC_DCHECK_RUN_ON(&sequence_checker_);
+  received_bytes_per_second_counter_.Add(bytes);
+  received_video_bytes_per_second_counter_.Add(bytes);
+  if (!first_received_rtp_video_timestamp_)
+    first_received_rtp_video_timestamp_ = arrival_time;
+  last_received_rtp_video_timestamp_ = arrival_time;
+}
+
+Call::ReceiveStats::~ReceiveStats() {
+  RTC_DCHECK_RUN_ON(&sequence_checker_);
+  if (first_received_rtp_audio_timestamp_) {
+    RTC_HISTOGRAM_COUNTS_100000(
+        "WebRTC.Call.TimeReceivingAudioRtpPacketsInSeconds",
+        (*last_received_rtp_audio_timestamp_ -
+         *first_received_rtp_audio_timestamp_)
+            .seconds());
+  }
+  if (first_received_rtp_video_timestamp_) {
+    RTC_HISTOGRAM_COUNTS_100000(
+        "WebRTC.Call.TimeReceivingVideoRtpPacketsInSeconds",
+        (*last_received_rtp_video_timestamp_ -
+         *first_received_rtp_video_timestamp_)
+            .seconds());
+  }
+  const int kMinRequiredPeriodicSamples = 5;
+  AggregatedStats video_bytes_per_sec =
+      received_video_bytes_per_second_counter_.GetStats();
+  if (video_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
+    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.VideoBitrateReceivedInKbps",
+                                video_bytes_per_sec.average * 8 / 1000);
+    RTC_LOG(LS_INFO) << "WebRTC.Call.VideoBitrateReceivedInBps, "
+                     << video_bytes_per_sec.ToStringWithMultiplier(8);
+  }
+  AggregatedStats audio_bytes_per_sec =
+      received_audio_bytes_per_second_counter_.GetStats();
+  if (audio_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
+    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.AudioBitrateReceivedInKbps",
+                                audio_bytes_per_sec.average * 8 / 1000);
+    RTC_LOG(LS_INFO) << "WebRTC.Call.AudioBitrateReceivedInBps, "
+                     << audio_bytes_per_sec.ToStringWithMultiplier(8);
+  }
+  AggregatedStats rtcp_bytes_per_sec =
+      received_rtcp_bytes_per_second_counter_.GetStats();
+  if (rtcp_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
+    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.RtcpBitrateReceivedInBps",
+                                rtcp_bytes_per_sec.average * 8);
+    RTC_LOG(LS_INFO) << "WebRTC.Call.RtcpBitrateReceivedInBps, "
+                     << rtcp_bytes_per_sec.ToStringWithMultiplier(8);
+  }
+  AggregatedStats recv_bytes_per_sec =
+      received_bytes_per_second_counter_.GetStats();
+  if (recv_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
+    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.BitrateReceivedInKbps",
+                                recv_bytes_per_sec.average * 8 / 1000);
+    RTC_LOG(LS_INFO) << "WebRTC.Call.BitrateReceivedInBps, "
+                     << recv_bytes_per_sec.ToStringWithMultiplier(8);
+  }
+}
+
 Call::Call(Clock* clock,
            const Call::Config& config,
            std::unique_ptr<RtpTransportControllerSendInterface> transport_send,
@@ -646,10 +756,7 @@
       video_network_state_(kNetworkDown),
       aggregate_network_up_(false),
       event_log_(config.event_log),
-      received_bytes_per_second_counter_(clock_, nullptr, true),
-      received_audio_bytes_per_second_counter_(clock_, nullptr, true),
-      received_video_bytes_per_second_counter_(clock_, nullptr, true),
-      received_rtcp_bytes_per_second_counter_(clock_, nullptr, true),
+      receive_stats_(clock_),
       last_bandwidth_bps_(0),
       min_allocated_send_bitrate_bps_(0),
       configured_max_padding_bitrate_bps_(0),
@@ -710,8 +817,6 @@
                          pacer_bitrate_kbps_counter_);
   }
 
-  UpdateReceiveHistograms();
-
   RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.LifetimeInSeconds",
                               (now.ms() - start_ms_) / 1000);
 }
@@ -737,52 +842,6 @@
   GetTransportControllerSend()->SetClientBitratePreferences(preferences);
 }
 
-void Call::UpdateReceiveHistograms() {
-  if (first_received_rtp_audio_ms_) {
-    RTC_HISTOGRAM_COUNTS_100000(
-        "WebRTC.Call.TimeReceivingAudioRtpPacketsInSeconds",
-        (*last_received_rtp_audio_ms_ - *first_received_rtp_audio_ms_) / 1000);
-  }
-  if (first_received_rtp_video_ms_) {
-    RTC_HISTOGRAM_COUNTS_100000(
-        "WebRTC.Call.TimeReceivingVideoRtpPacketsInSeconds",
-        (*last_received_rtp_video_ms_ - *first_received_rtp_video_ms_) / 1000);
-  }
-  const int kMinRequiredPeriodicSamples = 5;
-  AggregatedStats video_bytes_per_sec =
-      received_video_bytes_per_second_counter_.GetStats();
-  if (video_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
-    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.VideoBitrateReceivedInKbps",
-                                video_bytes_per_sec.average * 8 / 1000);
-    RTC_LOG(LS_INFO) << "WebRTC.Call.VideoBitrateReceivedInBps, "
-                     << video_bytes_per_sec.ToStringWithMultiplier(8);
-  }
-  AggregatedStats audio_bytes_per_sec =
-      received_audio_bytes_per_second_counter_.GetStats();
-  if (audio_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
-    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.AudioBitrateReceivedInKbps",
-                                audio_bytes_per_sec.average * 8 / 1000);
-    RTC_LOG(LS_INFO) << "WebRTC.Call.AudioBitrateReceivedInBps, "
-                     << audio_bytes_per_sec.ToStringWithMultiplier(8);
-  }
-  AggregatedStats rtcp_bytes_per_sec =
-      received_rtcp_bytes_per_second_counter_.GetStats();
-  if (rtcp_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
-    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.RtcpBitrateReceivedInBps",
-                                rtcp_bytes_per_sec.average * 8);
-    RTC_LOG(LS_INFO) << "WebRTC.Call.RtcpBitrateReceivedInBps, "
-                     << rtcp_bytes_per_sec.ToStringWithMultiplier(8);
-  }
-  AggregatedStats recv_bytes_per_sec =
-      received_bytes_per_second_counter_.GetStats();
-  if (recv_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
-    RTC_HISTOGRAM_COUNTS_100000("WebRTC.Call.BitrateReceivedInKbps",
-                                recv_bytes_per_sec.average * 8 / 1000);
-    RTC_LOG(LS_INFO) << "WebRTC.Call.BitrateReceivedInBps, "
-                     << recv_bytes_per_sec.ToStringWithMultiplier(8);
-  }
-}
-
 PacketReceiver* Call::Receiver() {
   return this;
 }
@@ -1381,11 +1440,7 @@
   // TODO(pbos): Make sure it's a valid packet.
   //             Return DELIVERY_UNKNOWN_SSRC if it can be determined that
   //             there's no receiver of the packet.
-  if (received_bytes_per_second_counter_.HasSample()) {
-    // First RTP packet has been received.
-    received_bytes_per_second_counter_.Add(static_cast<int>(length));
-    received_rtcp_bytes_per_second_counter_.Add(static_cast<int>(length));
-  }
+  receive_stats_.AddReceivedRtcpBytes(static_cast<int>(length));
   bool rtcp_delivered = false;
   if (media_type == MediaType::ANY || media_type == MediaType::VIDEO) {
     for (VideoReceiveStream2* stream : video_receive_streams_) {
@@ -1471,29 +1526,19 @@
   int length = static_cast<int>(parsed_packet.size());
   if (media_type == MediaType::AUDIO) {
     if (audio_receiver_controller_.OnRtpPacket(parsed_packet)) {
-      received_bytes_per_second_counter_.Add(length);
-      received_audio_bytes_per_second_counter_.Add(length);
+      receive_stats_.AddReceivedAudioBytes(length,
+                                           parsed_packet.arrival_time());
       event_log_->Log(
           std::make_unique<RtcEventRtpPacketIncoming>(parsed_packet));
-      const int64_t arrival_time_ms = parsed_packet.arrival_time().ms();
-      if (!first_received_rtp_audio_ms_) {
-        first_received_rtp_audio_ms_.emplace(arrival_time_ms);
-      }
-      last_received_rtp_audio_ms_.emplace(arrival_time_ms);
       return DELIVERY_OK;
     }
   } else if (media_type == MediaType::VIDEO) {
     parsed_packet.set_payload_type_frequency(kVideoPayloadTypeFrequency);
     if (video_receiver_controller_.OnRtpPacket(parsed_packet)) {
-      received_bytes_per_second_counter_.Add(length);
-      received_video_bytes_per_second_counter_.Add(length);
+      receive_stats_.AddReceivedVideoBytes(length,
+                                           parsed_packet.arrival_time());
       event_log_->Log(
           std::make_unique<RtcEventRtpPacketIncoming>(parsed_packet));
-      const int64_t arrival_time_ms = parsed_packet.arrival_time().ms();
-      if (!first_received_rtp_video_ms_) {
-        first_received_rtp_video_ms_.emplace(arrival_time_ms);
-      }
-      last_received_rtp_video_ms_.emplace(arrival_time_ms);
       return DELIVERY_OK;
     }
   }