Populate RTCInboundRtpStreamStats::playoutId when appropriate

Bug: webrtc:14653
Change-Id: I0c59604b218d0839a126c02914626b8ed2bee76c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/291040
Commit-Queue: Fredrik Hernqvist <fhernqvist@google.com>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39149}
This commit is contained in:
Fredrik Hernqvist 2023-01-19 15:04:54 +01:00 committed by WebRTC LUCI CQ
parent 4abca6699d
commit 828de8036d
8 changed files with 93 additions and 2 deletions

View File

@ -447,6 +447,7 @@ class RTC_EXPORT RTCInboundRTPStreamStats final
// TODO(https://crbug.com/webrtc/14174): Implement trackIdentifier and kind.
RTCStatsMember<std::string> playout_id;
RTCStatsMember<std::string> track_identifier;
RTCStatsMember<std::string> mid;
RTCStatsMember<std::string> remote_id;

View File

@ -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<RTCAudioPlayoutStats> CreateAudioPlayoutStats(
const AudioDeviceModule::Stats& audio_device_stats,
webrtc::Timestamp timestamp) {
auto stats = std::make_unique<RTCAudioPlayoutStats>(
/*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) {

View File

@ -171,6 +171,7 @@ class RTCStatsCollector : public rtc::RefCountInterface,
absl::optional<std::string> mid;
absl::optional<std::string> transport_name;
TrackMediaInfoMap track_media_info_map;
absl::optional<RtpTransceiverDirection> current_direction;
};
void DeliverCachedReport(

View File

@ -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<const RTCStatsReport> 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<const RTCStatsReport> report = stats_->GetStatsReport();
ASSERT_TRUE(report->Get("ITTransportName1A1"));
auto stats =
report->Get("ITTransportName1A1")->cast_to<RTCInboundRTPStreamStats>();
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<const RTCStatsReport> report =
stats_->GetFreshStatsReport();
ASSERT_TRUE(report->Get("ITTransportName1A1"));
auto stats =
report->Get("ITTransportName1A1")->cast_to<RTCInboundRTPStreamStats>();
ASSERT_TRUE(stats.playout_id.is_defined());
EXPECT_EQ(*stats.playout_id, "AP");
}
}
TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) {
cricket::VideoMediaInfo video_media_info;

View File

@ -416,6 +416,9 @@ class RTCStatsReportVerifier {
} else if (stats.type() == RTCTransportStats::kType) {
verify_successful &=
VerifyRTCTransportStats(stats.cast_to<RTCTransportStats>());
} else if (stats.type() == RTCAudioPlayoutStats::kType) {
verify_successful &=
VerifyRTCAudioPlayoutSTats(stats.cast_to<RTCAudioPlayoutStats>());
} 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<uint64_t>(
audio_playout.synthesized_samples_events);
verifier.TestMemberIsNonNegative<double>(
audio_playout.synthesized_samples_duration);
verifier.TestMemberIsNonNegative<uint64_t>(
audio_playout.total_samples_count);
verifier.TestMemberIsNonNegative<double>(
audio_playout.total_samples_duration);
verifier.TestMemberIsNonNegative<double>(audio_playout.total_playout_delay);
return verifier.ExpectAllMembersSuccessfullyTested();
}
private:
rtc::scoped_refptr<const RTCStatsReport> report_;
};

View File

@ -108,6 +108,7 @@ std::vector<const std::string*> 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<const RTCOutboundRTPStreamStats&>(stats);
@ -135,12 +136,13 @@ std::vector<const std::string*> 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<const RTCTransportStats&>(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;
}

View File

@ -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<webrtc::AudioDeviceModule::Stats> GetStats() const override {
return webrtc::AudioDeviceModule::Stats();
}
#if defined(WEBRTC_IOS)
int GetPlayoutAudioParameters(
webrtc::AudioParameters* params) const override {

View File

@ -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"),