Wire up RTCInboundRtpStreamStats.lastPacketReceivedTimestamp.

This collects this metric for both audio and video streams.
https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp

This is a follow-up to https://webrtc-review.googlesource.com/c/src/+/130479
which calculated this metric. This CL is purely plumbing from
"StreamDataCounters::last_packet_received_timestamp_ms" to
RTCInboundRtpStreamStats.


Bug: webrtc:10449
Change-Id: I757ad19b5b8e84553da5edd4a75efa3e1fe30b56
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/131397
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27628}
diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h
index 8a1fe56..81ec084 100644
--- a/api/stats/rtcstats_objects.h
+++ b/api/stats/rtcstats_objects.h
@@ -397,6 +397,7 @@
   RTCStatsMember<uint32_t> packets_received;
   RTCStatsMember<uint64_t> bytes_received;
   RTCStatsMember<int32_t> packets_lost;  // Signed per RFC 3550
+  RTCStatsMember<double> last_packet_received_timestamp;
   // TODO(hbos): Collect and populate this value for both "audio" and "video",
   // currently not collected for "video". https://bugs.webrtc.org/7065
   RTCStatsMember<double> jitter;
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index e13e880..b4948ee 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -190,6 +190,8 @@
   stats.packets_lost = call_stats.cumulativeLost;
   stats.fraction_lost = Q8ToFloat(call_stats.fractionLost);
   stats.capture_start_ntp_time_ms = call_stats.capture_start_ntp_time_ms_;
+  stats.last_packet_received_timestamp_ms =
+      call_stats.last_packet_received_timestamp_ms;
   stats.codec_name = receive_codec->second.name;
   stats.codec_payload_type = receive_codec->first;
   stats.ext_seqnum = call_stats.extendedMax;
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index 4f00d9f..f65d125 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -757,17 +757,23 @@
   stats.rttMs = GetRTT();
 
   // --- Data counters
-
-  size_t bytesReceived(0);
-  uint32_t packetsReceived(0);
-
   if (statistician) {
-    statistician->GetDataCounters(&bytesReceived, &packetsReceived);
+    StreamDataCounters data_counters;
+    statistician->GetReceiveStreamDataCounters(&data_counters);
+    // TODO(http://crbug.com/webrtc/10525): Bytes received should only include
+    // payload bytes, not header and padding bytes.
+    stats.bytesReceived = data_counters.transmitted.payload_bytes +
+                          data_counters.transmitted.header_bytes +
+                          data_counters.transmitted.padding_bytes;
+    stats.packetsReceived = data_counters.transmitted.packets;
+    stats.last_packet_received_timestamp_ms =
+        data_counters.last_packet_received_timestamp_ms;
+  } else {
+    stats.bytesReceived = 0;
+    stats.packetsReceived = 0;
+    stats.last_packet_received_timestamp_ms = absl::nullopt;
   }
 
-  stats.bytesReceived = bytesReceived;
-  stats.packetsReceived = packetsReceived;
-
   // --- Timestamps
   {
     rtc::CritScope lock(&ts_stats_lock_);
diff --git a/audio/channel_receive.h b/audio/channel_receive.h
index a67b0db..1f78874 100644
--- a/audio/channel_receive.h
+++ b/audio/channel_receive.h
@@ -59,6 +59,10 @@
   // The capture ntp time (in local timebase) of the first played out audio
   // frame.
   int64_t capture_start_ntp_time_ms_;
+  // The timestamp at which the last packet was received, i.e. the time of the
+  // local clock when it was received - not the RTP timestamp of that packet.
+  // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp
+  absl::optional<int64_t> last_packet_received_timestamp_ms;
 };
 
 namespace voe {
diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h
index 3f1a5ad..257042b 100644
--- a/call/audio_receive_stream.h
+++ b/call/audio_receive_stream.h
@@ -73,6 +73,10 @@
     int32_t decoding_plc_cng = 0;
     int32_t decoding_muted_output = 0;
     int64_t capture_start_ntp_time_ms = 0;
+    // The timestamp at which the last packet was received, i.e. the time of the
+    // local clock when it was received - not the RTP timestamp of that packet.
+    // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp
+    absl::optional<int64_t> last_packet_received_timestamp_ms;
     uint64_t jitter_buffer_flushes = 0;
     double relative_packet_arrival_delay_seconds = 0.0;
   };
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index b64803b..c21f89e 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -435,6 +435,10 @@
   int packets_rcvd = 0;
   int packets_lost = 0;
   float fraction_lost = 0.0f;
+  // The timestamp at which the last packet was received, i.e. the time of the
+  // local clock when it was received - not the RTP timestamp of that packet.
+  // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp
+  absl::optional<int64_t> last_packet_received_timestamp_ms;
   std::string codec_name;
   absl::optional<int> codec_payload_type;
   std::vector<SsrcReceiverInfo> local_stats;
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 762f033..57ff372 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -2697,6 +2697,8 @@
   info.frames_decoded = stats.frames_decoded;
   info.frames_rendered = stats.frames_rendered;
   info.qp_sum = stats.qp_sum;
+  info.last_packet_received_timestamp_ms =
+      stats.rtp_stats.last_packet_received_timestamp_ms;
   info.first_frame_received_to_decoded_ms =
       stats.first_frame_received_to_decoded_ms;
   info.interframe_delay_max_ms = stats.interframe_delay_max_ms;
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index b35090d..0765427 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -2267,6 +2267,8 @@
     rinfo.decoding_plc_cng = stats.decoding_plc_cng;
     rinfo.decoding_muted_output = stats.decoding_muted_output;
     rinfo.capture_start_ntp_time_ms = stats.capture_start_ntp_time_ms;
+    rinfo.last_packet_received_timestamp_ms =
+        stats.last_packet_received_timestamp_ms;
     rinfo.jitter_buffer_flushes = stats.jitter_buffer_flushes;
     rinfo.relative_packet_arrival_delay_seconds =
         stats.relative_packet_arrival_delay_seconds;
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 7427356..3bf8566 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -244,6 +244,12 @@
           rtc::kNumMillisecsPerSec;
   // |fir_count|, |pli_count| and |sli_count| are only valid for video and are
   // purposefully left undefined for audio.
+  if (voice_receiver_info.last_packet_received_timestamp_ms) {
+    inbound_audio->last_packet_received_timestamp =
+        static_cast<double>(
+            *voice_receiver_info.last_packet_received_timestamp_ms) /
+        rtc::kNumMillisecsPerSec;
+  }
 }
 
 void SetInboundRTPStreamStatsFromVideoReceiverInfo(
@@ -267,6 +273,12 @@
   inbound_video->frames_decoded = video_receiver_info.frames_decoded;
   if (video_receiver_info.qp_sum)
     inbound_video->qp_sum = *video_receiver_info.qp_sum;
+  if (video_receiver_info.last_packet_received_timestamp_ms) {
+    inbound_video->last_packet_received_timestamp =
+        static_cast<double>(
+            *video_receiver_info.last_packet_received_timestamp_ms) /
+        rtc::kNumMillisecsPerSec;
+  }
   // TODO(https://crbug.com/webrtc/10529): When info's |content_info| is
   // optional, support the "unspecified" value.
   if (video_receiver_info.content_type == VideoContentType::SCREENSHARE)
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index 7e843a6..82cd241 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -1625,6 +1625,8 @@
   voice_media_info.receivers[0].codec_payload_type = 42;
   voice_media_info.receivers[0].jitter_ms = 4500;
   voice_media_info.receivers[0].fraction_lost = 5.5f;
+  voice_media_info.receivers[0].last_packet_received_timestamp_ms =
+      absl::nullopt;
 
   RtpCodecParameters codec_parameters;
   codec_parameters.payload_type = 42;
@@ -1656,12 +1658,25 @@
   expected_audio.packets_received = 2;
   expected_audio.bytes_received = 3;
   expected_audio.packets_lost = -1;
+  // |expected_audio.last_packet_received_timestamp| should be undefined.
   expected_audio.jitter = 4.5;
   expected_audio.fraction_lost = 5.5;
   ASSERT_TRUE(report->Get(expected_audio.id()));
   EXPECT_EQ(
       report->Get(expected_audio.id())->cast_to<RTCInboundRTPStreamStats>(),
       expected_audio);
+
+  // Set previously undefined values and "GetStats" again.
+  voice_media_info.receivers[0].last_packet_received_timestamp_ms = 3000;
+  expected_audio.last_packet_received_timestamp = 3.0;
+  voice_media_channel->SetStats(voice_media_info);
+
+  report = stats_->GetFreshStatsReport();
+
+  ASSERT_TRUE(report->Get(expected_audio.id()));
+  EXPECT_EQ(
+      report->Get(expected_audio.id())->cast_to<RTCInboundRTPStreamStats>(),
+      expected_audio);
   EXPECT_TRUE(report->Get(*expected_audio.track_id));
   EXPECT_TRUE(report->Get(*expected_audio.transport_id));
   EXPECT_TRUE(report->Get(*expected_audio.codec_id));
@@ -1684,6 +1699,8 @@
   video_media_info.receivers[0].nacks_sent = 7;
   video_media_info.receivers[0].frames_decoded = 8;
   video_media_info.receivers[0].qp_sum = absl::nullopt;
+  video_media_info.receivers[0].last_packet_received_timestamp_ms =
+      absl::nullopt;
   video_media_info.receivers[0].content_type = VideoContentType::UNSPECIFIED;
 
   RtpCodecParameters codec_parameters;
@@ -1719,6 +1736,7 @@
   expected_video.fraction_lost = 4.5;
   expected_video.frames_decoded = 8;
   // |expected_video.qp_sum| should be undefined.
+  // |expected_video.last_packet_received_timestamp| should be undefined.
   // |expected_video.content_type| should be undefined.
 
   ASSERT_TRUE(report->Get(expected_video.id()));
@@ -1728,7 +1746,9 @@
 
   // Set previously undefined values and "GetStats" again.
   video_media_info.receivers[0].qp_sum = 9;
+  video_media_info.receivers[0].last_packet_received_timestamp_ms = 1000;
   expected_video.qp_sum = 9;
+  expected_video.last_packet_received_timestamp = 1.0;
   video_media_info.receivers[0].content_type = VideoContentType::SCREENSHARE;
   expected_video.content_type = "screenshare";
   video_media_channel->SetStats(video_media_info);
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc
index 6935769..9b1b6d0 100644
--- a/pc/rtc_stats_integrationtest.cc
+++ b/pc/rtc_stats_integrationtest.cc
@@ -719,6 +719,7 @@
     // packets_lost is defined as signed, but this should never happen in
     // this test. See RFC 3550.
     verifier.TestMemberIsNonNegative<int32_t>(inbound_stream.packets_lost);
+    verifier.TestMemberIsDefined(inbound_stream.last_packet_received_timestamp);
     if (inbound_stream.media_type.is_defined() &&
         *inbound_stream.media_type == "video") {
       verifier.TestMemberIsUndefined(inbound_stream.jitter);
diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc
index 7c023d1..4bf3e68 100644
--- a/stats/rtcstats_objects.cc
+++ b/stats/rtcstats_objects.cc
@@ -578,6 +578,7 @@
     &packets_received,
     &bytes_received,
     &packets_lost,
+    &last_packet_received_timestamp,
     &jitter,
     &fraction_lost,
     &round_trip_time,
@@ -605,6 +606,7 @@
       packets_received("packetsReceived"),
       bytes_received("bytesReceived"),
       packets_lost("packetsLost"),
+      last_packet_received_timestamp("lastPacketReceivedTimestamp"),
       jitter("jitter"),
       fraction_lost("fractionLost"),
       round_trip_time("roundTripTime"),
@@ -627,6 +629,7 @@
       packets_received(other.packets_received),
       bytes_received(other.bytes_received),
       packets_lost(other.packets_lost),
+      last_packet_received_timestamp(other.last_packet_received_timestamp),
       jitter(other.jitter),
       fraction_lost(other.fraction_lost),
       round_trip_time(other.round_trip_time),