From 828de8036db654be22a4da588aa7a39f41d2bd70 Mon Sep 17 00:00:00 2001 From: Fredrik Hernqvist Date: Thu, 19 Jan 2023 15:04:54 +0100 Subject: [PATCH] Populate RTCInboundRtpStreamStats::playoutId when appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: webrtc:14653 Change-Id: I0c59604b218d0839a126c02914626b8ed2bee76c Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/291040 Commit-Queue: Fredrik Hernqvist Reviewed-by: Henrik Boström Cr-Commit-Position: refs/heads/main@{#39149} --- api/stats/rtcstats_objects.h | 1 + pc/rtc_stats_collector.cc | 15 +++++++++- pc/rtc_stats_collector.h | 1 + pc/rtc_stats_collector_unittest.cc | 45 +++++++++++++++++++++++++++++ pc/rtc_stats_integrationtest.cc | 23 +++++++++++++++ pc/rtc_stats_traversal.cc | 4 ++- pc/test/fake_audio_capture_module.h | 4 +++ stats/rtcstats_objects.cc | 2 ++ 8 files changed, 93 insertions(+), 2 deletions(-) diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 96114dfa72..484d2d038e 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -447,6 +447,7 @@ class RTC_EXPORT RTCInboundRTPStreamStats final // TODO(https://crbug.com/webrtc/14174): Implement trackIdentifier and kind. + RTCStatsMember playout_id; RTCStatsMember track_identifier; RTCStatsMember mid; RTCStatsMember remote_id; diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 508661ec8a..f0b0bafcc2 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -27,6 +27,7 @@ #include "api/candidate.h" #include "api/dtls_transport_interface.h" #include "api/media_stream_interface.h" +#include "api/media_types.h" #include "api/rtp_parameters.h" #include "api/sequence_checker.h" #include "api/stats/rtc_stats.h" @@ -70,6 +71,8 @@ namespace { const char kDirectionInbound = 'I'; const char kDirectionOutbound = 'O'; +const char* kAudioPlayoutSingletonId = "AP"; + // TODO(https://crbug.com/webrtc/10656): Consider making IDs less predictable. std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) { return "CF" + fingerprint; @@ -518,7 +521,7 @@ std::unique_ptr CreateAudioPlayoutStats( const AudioDeviceModule::Stats& audio_device_stats, webrtc::Timestamp timestamp) { auto stats = std::make_unique( - /*id=*/"AP", timestamp); + /*id=*/kAudioPlayoutSingletonId, timestamp); stats->synthesized_samples_duration = audio_device_stats.synthesized_samples_duration_s; stats->synthesized_samples_events = @@ -2017,6 +2020,12 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n( .value()); inbound_audio->track_identifier = audio_track->id(); } + if (audio_device_stats_ && stats.media_type == cricket::MEDIA_TYPE_AUDIO && + stats.current_direction && + (*stats.current_direction == RtpTransceiverDirection::kSendRecv || + *stats.current_direction == RtpTransceiverDirection::kRecvOnly)) { + inbound_audio->playout_id = kAudioPlayoutSingletonId; + } auto* inbound_audio_ptr = report->TryAddStats(std::move(inbound_audio)); if (!inbound_audio_ptr) { RTC_LOG(LS_ERROR) @@ -2477,6 +2486,10 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { call_stats_ = pc_->GetCallStats(); audio_device_stats_ = pc_->GetAudioDeviceStats(); }); + + for (auto& stats : transceiver_stats_infos_) { + stats.current_direction = stats.transceiver->current_direction(); + } } void RTCStatsCollector::OnSctpDataChannelCreated(SctpDataChannel* channel) { diff --git a/pc/rtc_stats_collector.h b/pc/rtc_stats_collector.h index bdfe781a5a..89f6e983eb 100644 --- a/pc/rtc_stats_collector.h +++ b/pc/rtc_stats_collector.h @@ -171,6 +171,7 @@ class RTCStatsCollector : public rtc::RefCountInterface, absl::optional mid; absl::optional transport_name; TrackMediaInfoMap track_media_info_map; + absl::optional current_direction; }; void DeliverCachedReport( diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index cd56ee0d04..70eabfc701 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -28,6 +28,7 @@ #include "api/media_stream_interface.h" #include "api/media_stream_track.h" #include "api/rtp_parameters.h" +#include "api/rtp_transceiver_direction.h" #include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats_report.h" #include "api/stats/rtcstats_objects.h" @@ -2494,6 +2495,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { stats_->SetupRemoteTrackAndReceiver( cricket::MEDIA_TYPE_AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1); + // Needed for playoutId to be populated. + pc_->SetAudioDeviceStats(AudioDeviceModule::Stats()); + pc_->GetTransceiversInternal()[0]->internal()->set_current_direction( + RtpTransceiverDirection::kSendRecv); + rtc::scoped_refptr report = stats_->GetStatsReport(); auto stats_of_track_type = @@ -2538,6 +2544,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { expected_audio.relative_packet_arrival_delay = 16; expected_audio.interruption_count = 7788; expected_audio.total_interruption_duration = 778.899; + expected_audio.playout_id = "AP"; ASSERT_TRUE(report->Get(expected_audio.id())); EXPECT_EQ( @@ -2562,6 +2569,44 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { EXPECT_TRUE(report->Get(*expected_audio.codec_id)); } +TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio_PlayoutId) { + cricket::VoiceMediaInfo voice_media_info; + + voice_media_info.receivers.push_back(cricket::VoiceReceiverInfo()); + voice_media_info.receivers[0].local_stats.push_back( + cricket::SsrcReceiverInfo()); + voice_media_info.receivers[0].local_stats[0].ssrc = 1; + + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + stats_->SetupRemoteTrackAndReceiver( + cricket::MEDIA_TYPE_AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1); + // Needed for playoutId to be populated. + pc_->SetAudioDeviceStats(AudioDeviceModule::Stats()); + + { + // We do not expect a playout id when only sending. + pc_->GetTransceiversInternal()[0]->internal()->set_current_direction( + RtpTransceiverDirection::kSendOnly); + rtc::scoped_refptr report = stats_->GetStatsReport(); + ASSERT_TRUE(report->Get("ITTransportName1A1")); + auto stats = + report->Get("ITTransportName1A1")->cast_to(); + ASSERT_FALSE(stats.playout_id.is_defined()); + } + { + // We do expect a playout id when receiving. + pc_->GetTransceiversInternal()[0]->internal()->set_current_direction( + RtpTransceiverDirection::kRecvOnly); + rtc::scoped_refptr report = + stats_->GetFreshStatsReport(); + ASSERT_TRUE(report->Get("ITTransportName1A1")); + auto stats = + report->Get("ITTransportName1A1")->cast_to(); + ASSERT_TRUE(stats.playout_id.is_defined()); + EXPECT_EQ(*stats.playout_id, "AP"); + } +} + TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { cricket::VideoMediaInfo video_media_info; diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index b5abad9d63..2a65726807 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -416,6 +416,9 @@ class RTCStatsReportVerifier { } else if (stats.type() == RTCTransportStats::kType) { verify_successful &= VerifyRTCTransportStats(stats.cast_to()); + } else if (stats.type() == RTCAudioPlayoutStats::kType) { + verify_successful &= + VerifyRTCAudioPlayoutSTats(stats.cast_to()); } else { EXPECT_TRUE(false) << "Unrecognized stats type: " << stats.type(); verify_successful = false; @@ -908,6 +911,12 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(inbound_stream.min_playout_delay); verifier.TestMemberIsUndefined(inbound_stream.goog_timing_frame_info); } + if (inbound_stream.kind.is_defined() && *inbound_stream.kind == "audio") { + verifier.TestMemberIsDefined(inbound_stream.playout_id); + } else { + verifier.TestMemberIsUndefined(inbound_stream.playout_id); + } + return verifier.ExpectAllMembersSuccessfullyTested(); } @@ -1119,6 +1128,20 @@ class RTCStatsReportVerifier { return verifier.ExpectAllMembersSuccessfullyTested(); } + bool VerifyRTCAudioPlayoutSTats(const RTCAudioPlayoutStats& audio_playout) { + RTCStatsVerifier verifier(report_.get(), &audio_playout); + verifier.TestMemberIsNonNegative( + audio_playout.synthesized_samples_events); + verifier.TestMemberIsNonNegative( + audio_playout.synthesized_samples_duration); + verifier.TestMemberIsNonNegative( + audio_playout.total_samples_count); + verifier.TestMemberIsNonNegative( + audio_playout.total_samples_duration); + verifier.TestMemberIsNonNegative(audio_playout.total_playout_delay); + return verifier.ExpectAllMembersSuccessfullyTested(); + } + private: rtc::scoped_refptr report_; }; diff --git a/pc/rtc_stats_traversal.cc b/pc/rtc_stats_traversal.cc index b3f6155e3f..98401126ed 100644 --- a/pc/rtc_stats_traversal.cc +++ b/pc/rtc_stats_traversal.cc @@ -108,6 +108,7 @@ std::vector GetStatsReferencedIds(const RTCStats& stats) { AddIdIfDefined(inbound_rtp.track_id, &neighbor_ids); AddIdIfDefined(inbound_rtp.transport_id, &neighbor_ids); AddIdIfDefined(inbound_rtp.codec_id, &neighbor_ids); + AddIdIfDefined(inbound_rtp.playout_id, &neighbor_ids); } else if (type == RTCOutboundRTPStreamStats::kType) { const auto& outbound_rtp = static_cast(stats); @@ -135,12 +136,13 @@ std::vector GetStatsReferencedIds(const RTCStats& stats) { type == RTCVideoSourceStats::kType) { // RTC[Audio/Video]SourceStats does not have any neighbor references. } else if (type == RTCTransportStats::kType) { - // RTCTransportStats does not have any neighbor references. const auto& transport = static_cast(stats); AddIdIfDefined(transport.rtcp_transport_stats_id, &neighbor_ids); AddIdIfDefined(transport.selected_candidate_pair_id, &neighbor_ids); AddIdIfDefined(transport.local_certificate_id, &neighbor_ids); AddIdIfDefined(transport.remote_certificate_id, &neighbor_ids); + } else if (type == RTCAudioPlayoutStats::kType) { + // RTCAudioPlayoutStats does not have any neighbor references. } else { RTC_DCHECK_NOTREACHED() << "Unrecognized type: " << type; } diff --git a/pc/test/fake_audio_capture_module.h b/pc/test/fake_audio_capture_module.h index 8869c54686..84ddacb26f 100644 --- a/pc/test/fake_audio_capture_module.h +++ b/pc/test/fake_audio_capture_module.h @@ -136,6 +136,10 @@ class FakeAudioCaptureModule : public webrtc::AudioDeviceModule { int32_t EnableBuiltInNS(bool enable) override { return -1; } int32_t GetPlayoutUnderrunCount() const override { return -1; } + + absl::optional GetStats() const override { + return webrtc::AudioDeviceModule::Stats(); + } #if defined(WEBRTC_IOS) int GetPlayoutAudioParameters( webrtc::AudioParameters* params) const override { diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 4b216bd0dd..f392a08a38 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -528,6 +528,7 @@ WEBRTC_RTCSTATS_IMPL( &audio_level, &total_audio_energy, &total_samples_duration, + &playout_id, &frames_received, &frame_width, &frame_height, @@ -565,6 +566,7 @@ WEBRTC_RTCSTATS_IMPL( RTCInboundRTPStreamStats::RTCInboundRTPStreamStats(std::string id, Timestamp timestamp) : RTCReceivedRtpStreamStats(std::move(id), timestamp), + playout_id("playoutId"), track_identifier("trackIdentifier"), mid("mid"), remote_id("remoteId"),