From 8f16289e10297cd8ad7566d48a4e6906a5229f68 Mon Sep 17 00:00:00 2001 From: Philipp Hancke Date: Tue, 9 Apr 2024 16:50:20 -0700 Subject: [PATCH] stats: implement remote-outbound-rtp for video following the audio changes. Note that RTT-related fields require DLRR and are not implemented yet. BUG=webrtc:12529 Change-Id: I3f9449fbe876a1b282a32f2bcebe1cf3e10989bf Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/346580 Reviewed-by: Florent Castelli Reviewed-by: Harald Alvestrand Commit-Queue: Philipp Hancke Cr-Commit-Position: refs/heads/main@{#42069} --- call/video_receive_stream.h | 12 +++++- media/base/media_channel.h | 23 ++++++----- media/engine/webrtc_video_engine.cc | 10 +++++ pc/rtc_stats_collector.cc | 61 +++++++++++++++++++---------- pc/rtc_stats_integrationtest.cc | 8 +++- video/rtp_video_stream_receiver2.cc | 6 +++ video/rtp_video_stream_receiver2.h | 3 ++ video/video_receive_stream2.cc | 12 ++++++ 8 files changed, 102 insertions(+), 33 deletions(-) diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h index 12f6bf60c8..20e9245584 100644 --- a/call/video_receive_stream.h +++ b/call/video_receive_stream.h @@ -115,9 +115,11 @@ class VideoReceiveStreamInterface : public MediaReceiveStreamInterface { TimeDelta total_decode_time = TimeDelta::Zero(); // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalprocessingdelay TimeDelta total_processing_delay = TimeDelta::Zero(); - // TODO(bugs.webrtc.org/13986): standardize + + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalassemblytime TimeDelta total_assembly_time = TimeDelta::Zero(); uint32_t frames_assembled_from_multiple_packets = 0; + // Total inter frame delay in seconds. // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalinterframedelay double total_inter_frame_delay = 0; @@ -154,6 +156,14 @@ class VideoReceiveStreamInterface : public MediaReceiveStreamInterface { // Timing frame info: all important timestamps for a full lifetime of a // single 'timing frame'. absl::optional timing_frame_info; + + // Remote outbound stats derived by the received RTCP sender reports. + // https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict* + absl::optional last_sender_report_timestamp_ms; + absl::optional last_sender_report_remote_timestamp_ms; + uint32_t sender_reports_packets_sent = 0; + uint64_t sender_reports_bytes_sent = 0; + uint64_t sender_reports_reports_count = 0; }; struct Config { diff --git a/media/base/media_channel.h b/media/base/media_channel.h index f941823323..124f68c6ff 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -476,6 +476,19 @@ struct MediaReceiverInfo { absl::optional fec_packets_discarded; // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-fecbytesreceived absl::optional fec_bytes_received; + + // Remote outbound stats derived by the received RTCP sender reports. + // https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict* + absl::optional last_sender_report_timestamp_ms; + absl::optional last_sender_report_remote_timestamp_ms; + uint64_t sender_reports_packets_sent = 0; + uint64_t sender_reports_bytes_sent = 0; + uint64_t sender_reports_reports_count = 0; + // These require a DLRR block, see + // https://w3c.github.io/webrtc-stats/#dom-rtcremoteoutboundrtpstreamstats-roundtriptime + absl::optional round_trip_time; + webrtc::TimeDelta total_round_trip_time = webrtc::TimeDelta::Zero(); + int round_trip_time_measurements = 0; }; struct VoiceSenderInfo : public MediaSenderInfo { @@ -551,16 +564,6 @@ struct VoiceReceiverInfo : public MediaReceiverInfo { // longer than 150 ms). int32_t interruption_count = 0; int32_t total_interruption_duration_ms = 0; - // Remote outbound stats derived by the received RTCP sender reports. - // https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict* - absl::optional last_sender_report_timestamp_ms; - absl::optional last_sender_report_remote_timestamp_ms; - uint64_t sender_reports_packets_sent = 0; - uint64_t sender_reports_bytes_sent = 0; - uint64_t sender_reports_reports_count = 0; - absl::optional round_trip_time; - webrtc::TimeDelta total_round_trip_time = webrtc::TimeDelta::Zero(); - int round_trip_time_measurements = 0; }; struct VideoSenderInfo : public MediaSenderInfo { diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 24709ddc6f..d4dccdf18f 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -3737,6 +3737,16 @@ WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo( } } + // remote-outbound-rtp stats. + info.last_sender_report_timestamp_ms = stats.last_sender_report_timestamp_ms; + info.last_sender_report_remote_timestamp_ms = + stats.last_sender_report_remote_timestamp_ms; + info.sender_reports_packets_sent = stats.sender_reports_packets_sent; + info.sender_reports_bytes_sent = stats.sender_reports_bytes_sent; + info.sender_reports_reports_count = stats.sender_reports_reports_count; + // TODO(bugs.webrtc.org/12529): RTT-related fields are missing and can only be + // present if DLRR is enabled. + if (log_stats) RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis()); diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index a49e8ef0b2..1f94b0e7cc 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -514,53 +514,54 @@ std::unique_ptr CreateAudioPlayoutStats( } std::unique_ptr -CreateRemoteOutboundAudioStreamStats( - const cricket::VoiceReceiverInfo& voice_receiver_info, +CreateRemoteOutboundMediaStreamStats( + const cricket::MediaReceiverInfo& media_receiver_info, const std::string& mid, + const std::string& kind, const RTCInboundRtpStreamStats& inbound_audio_stats, const std::string& transport_id) { - if (!voice_receiver_info.last_sender_report_timestamp_ms.has_value()) { + if (!media_receiver_info.last_sender_report_timestamp_ms.has_value()) { // Cannot create `RTCRemoteOutboundRtpStreamStats` when the RTCP SR arrival // timestamp is not available - i.e., until the first sender report is // received. return nullptr; } - RTC_DCHECK_GT(voice_receiver_info.sender_reports_reports_count, 0); + RTC_DCHECK_GT(media_receiver_info.sender_reports_reports_count, 0); // Create. auto stats = std::make_unique( /*id=*/RTCRemoteOutboundRTPStreamStatsIDFromSSRC( - cricket::MEDIA_TYPE_AUDIO, voice_receiver_info.ssrc()), - Timestamp::Millis(*voice_receiver_info.last_sender_report_timestamp_ms)); + cricket::MEDIA_TYPE_AUDIO, media_receiver_info.ssrc()), + Timestamp::Millis(*media_receiver_info.last_sender_report_timestamp_ms)); // Populate. // - RTCRtpStreamStats. - stats->ssrc = voice_receiver_info.ssrc(); - stats->kind = "audio"; + stats->ssrc = media_receiver_info.ssrc(); + stats->kind = kind; stats->transport_id = transport_id; if (inbound_audio_stats.codec_id.has_value()) { stats->codec_id = *inbound_audio_stats.codec_id; } // - RTCSentRtpStreamStats. - stats->packets_sent = voice_receiver_info.sender_reports_packets_sent; - stats->bytes_sent = voice_receiver_info.sender_reports_bytes_sent; + stats->packets_sent = media_receiver_info.sender_reports_packets_sent; + stats->bytes_sent = media_receiver_info.sender_reports_bytes_sent; // - RTCRemoteOutboundRtpStreamStats. stats->local_id = inbound_audio_stats.id(); // last_sender_report_remote_timestamp_ms is set together with // last_sender_report_timestamp_ms. RTC_DCHECK( - voice_receiver_info.last_sender_report_remote_timestamp_ms.has_value()); + media_receiver_info.last_sender_report_remote_timestamp_ms.has_value()); stats->remote_timestamp = static_cast( - *voice_receiver_info.last_sender_report_remote_timestamp_ms); - stats->reports_sent = voice_receiver_info.sender_reports_reports_count; - if (voice_receiver_info.round_trip_time.has_value()) { + *media_receiver_info.last_sender_report_remote_timestamp_ms); + stats->reports_sent = media_receiver_info.sender_reports_reports_count; + if (media_receiver_info.round_trip_time.has_value()) { stats->round_trip_time = - voice_receiver_info.round_trip_time->seconds(); + media_receiver_info.round_trip_time->seconds(); } stats->round_trip_time_measurements = - voice_receiver_info.round_trip_time_measurements; + media_receiver_info.round_trip_time_measurements; stats->total_round_trip_time = - voice_receiver_info.total_round_trip_time.seconds(); + media_receiver_info.total_round_trip_time.seconds(); return stats; } @@ -1709,8 +1710,8 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n( continue; } // Remote-outbound. - auto remote_outbound_audio = CreateRemoteOutboundAudioStreamStats( - voice_receiver_info, mid, *inbound_audio_ptr, transport_id); + auto remote_outbound_audio = CreateRemoteOutboundMediaStreamStats( + voice_receiver_info, mid, "audio", *inbound_audio_ptr, transport_id); // Add stats. if (remote_outbound_audio) { // When the remote outbound stats are available, the remote ID for the @@ -1782,7 +1783,7 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n( std::string mid = *stats.mid; std::string transport_id = RTCTransportStatsIDFromTransportChannel( *stats.transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - // Inbound + // Inbound and remote-outbound. for (const cricket::VideoReceiverInfo& video_receiver_info : stats.track_media_info_map.video_media_info()->receivers) { if (!video_receiver_info.connected()) @@ -1795,9 +1796,27 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n( if (video_track) { inbound_video->track_identifier = video_track->id(); } - if (!report->TryAddStats(std::move(inbound_video))) { + auto* inbound_video_ptr = report->TryAddStats(std::move(inbound_video)); + if (!inbound_video_ptr) { RTC_LOG(LS_ERROR) << "Unable to add video 'inbound-rtp' to report, ID is not unique."; + continue; + } + // Remote-outbound. + auto remote_outbound_video = CreateRemoteOutboundMediaStreamStats( + video_receiver_info, mid, "video", *inbound_video_ptr, transport_id); + // Add stats. + if (remote_outbound_video) { + // When the remote outbound stats are available, the remote ID for the + // local inbound stats is set. + auto* remote_outbound_video_ptr = + report->TryAddStats(std::move(remote_outbound_video)); + if (remote_outbound_video_ptr) { + inbound_video_ptr->remote_id = remote_outbound_video_ptr->id(); + } else { + RTC_LOG(LS_ERROR) << "Unable to add video 'remote-outbound-rtp' to " + << "report, ID is not unique."; + } } } // Outbound diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index de8e6e3f67..1ff060c061 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -939,10 +939,16 @@ class RTCStatsReportVerifier { VerifyRTCRtpStreamStats(remote_outbound_stream, verifier); VerifyRTCSentRtpStreamStats(remote_outbound_stream, verifier); verifier.TestAttributeIsIDReference(remote_outbound_stream.local_id, - RTCOutboundRtpStreamStats::kType); + RTCInboundRtpStreamStats::kType); verifier.TestAttributeIsNonNegative( remote_outbound_stream.remote_timestamp); verifier.TestAttributeIsDefined(remote_outbound_stream.reports_sent); + // RTT-related attributes need DLRR. + verifier.MarkAttributeTested(remote_outbound_stream.round_trip_time, true); + verifier.MarkAttributeTested( + remote_outbound_stream.round_trip_time_measurements, true); + verifier.MarkAttributeTested(remote_outbound_stream.total_round_trip_time, + true); return verifier.ExpectAllAttributesSuccessfullyTested(); } diff --git a/video/rtp_video_stream_receiver2.cc b/video/rtp_video_stream_receiver2.cc index 8fef92c294..ae9cb190a0 100644 --- a/video/rtp_video_stream_receiver2.cc +++ b/video/rtp_video_stream_receiver2.cc @@ -1048,6 +1048,12 @@ absl::optional RtpVideoStreamReceiver2::LastReceivedKeyframePacketMs() return absl::nullopt; } +absl::optional +RtpVideoStreamReceiver2::GetSenderReportStats() const { + RTC_DCHECK_RUN_ON(&packet_sequence_checker_); + return rtp_rtcp_->GetSenderReportStats(); +} + void RtpVideoStreamReceiver2::ManageFrame( std::unique_ptr frame) { RTC_DCHECK_RUN_ON(&packet_sequence_checker_); diff --git a/video/rtp_video_stream_receiver2.h b/video/rtp_video_stream_receiver2.h index 16212832bf..8ea3ffd310 100644 --- a/video/rtp_video_stream_receiver2.h +++ b/video/rtp_video_stream_receiver2.h @@ -209,6 +209,9 @@ class RtpVideoStreamReceiver2 : public LossNotificationSender, absl::optional LastReceivedFrameRtpTimestamp() const; absl::optional LastReceivedKeyframePacketMs() const; + absl::optional GetSenderReportStats() + const; + private: // Implements RtpVideoFrameReceiver. void ManageFrame(std::unique_ptr frame) override; diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc index 7d307c6d2c..6f63f760c0 100644 --- a/video/video_receive_stream2.cc +++ b/video/video_receive_stream2.cc @@ -568,6 +568,18 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const { stats.rtx_rtp_stats = rtx_statistician->GetStats(); } } + + absl::optional rtcp_sr_stats = + rtp_video_stream_receiver_.GetSenderReportStats(); + if (rtcp_sr_stats) { + stats.last_sender_report_timestamp_ms = + rtcp_sr_stats->last_arrival_timestamp.ToMs(); + stats.last_sender_report_remote_timestamp_ms = + rtcp_sr_stats->last_remote_timestamp.ToMs(); + stats.sender_reports_packets_sent = rtcp_sr_stats->packets_sent; + stats.sender_reports_bytes_sent = rtcp_sr_stats->bytes_sent; + stats.sender_reports_reports_count = rtcp_sr_stats->reports_count; + } return stats; }