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 <orphis@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@meta.com>
Cr-Commit-Position: refs/heads/main@{#42069}
This commit is contained in:
Philipp Hancke 2024-04-09 16:50:20 -07:00 committed by WebRTC LUCI CQ
parent 7167c6fec9
commit 8f16289e10
8 changed files with 102 additions and 33 deletions

View file

@ -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<webrtc::TimingFrameInfo> timing_frame_info;
// Remote outbound stats derived by the received RTCP sender reports.
// https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*
absl::optional<int64_t> last_sender_report_timestamp_ms;
absl::optional<int64_t> 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 {

View file

@ -476,6 +476,19 @@ struct MediaReceiverInfo {
absl::optional<uint64_t> fec_packets_discarded;
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-fecbytesreceived
absl::optional<uint64_t> fec_bytes_received;
// Remote outbound stats derived by the received RTCP sender reports.
// https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*
absl::optional<int64_t> last_sender_report_timestamp_ms;
absl::optional<int64_t> 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<webrtc::TimeDelta> 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<int64_t> last_sender_report_timestamp_ms;
absl::optional<int64_t> 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<webrtc::TimeDelta> round_trip_time;
webrtc::TimeDelta total_round_trip_time = webrtc::TimeDelta::Zero();
int round_trip_time_measurements = 0;
};
struct VideoSenderInfo : public MediaSenderInfo {

View file

@ -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());

View file

@ -514,53 +514,54 @@ std::unique_ptr<RTCAudioPlayoutStats> CreateAudioPlayoutStats(
}
std::unique_ptr<RTCRemoteOutboundRtpStreamStats>
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<RTCRemoteOutboundRtpStreamStats>(
/*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<double>(
*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<double>();
media_receiver_info.round_trip_time->seconds<double>();
}
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<double>();
media_receiver_info.total_round_trip_time.seconds<double>();
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

View file

@ -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<double>(
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();
}

View file

@ -1048,6 +1048,12 @@ absl::optional<int64_t> RtpVideoStreamReceiver2::LastReceivedKeyframePacketMs()
return absl::nullopt;
}
absl::optional<RtpRtcpInterface::SenderReportStats>
RtpVideoStreamReceiver2::GetSenderReportStats() const {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
return rtp_rtcp_->GetSenderReportStats();
}
void RtpVideoStreamReceiver2::ManageFrame(
std::unique_ptr<RtpFrameObject> frame) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);

View file

@ -209,6 +209,9 @@ class RtpVideoStreamReceiver2 : public LossNotificationSender,
absl::optional<uint32_t> LastReceivedFrameRtpTimestamp() const;
absl::optional<int64_t> LastReceivedKeyframePacketMs() const;
absl::optional<RtpRtcpInterface::SenderReportStats> GetSenderReportStats()
const;
private:
// Implements RtpVideoFrameReceiver.
void ManageFrame(std::unique_ptr<RtpFrameObject> frame) override;

View file

@ -568,6 +568,18 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
stats.rtx_rtp_stats = rtx_statistician->GetStats();
}
}
absl::optional<RtpRtcpInterface::SenderReportStats> 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;
}