Add RTCAudioPlayoutStats to GetStats().

This is done by allowing implementations of AudioDeviceModule to
implement the GetStats() method. The default implementation returns
nullopt, in which case RTCAudioPlayoutStats will not be visible in the
stats.

Bug: webrtc:14653
Change-Id: I8e4aa6f1b8fcfa47a30f633d28a4013191752e20
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/290563
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Fredrik Hernqvist <fhernqvist@google.com>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Reviewed-by: Olga Sharonova <olka@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39115}
This commit is contained in:
Fredrik Hernqvist 2023-01-13 16:42:36 +01:00 committed by WebRTC LUCI CQ
parent f7e40717ab
commit efbe753617
20 changed files with 175 additions and 0 deletions

View File

@ -699,6 +699,22 @@ class RTC_EXPORT RTCTransportStats final : public RTCStats {
RTCStatsMember<std::string> ice_state;
};
// https://w3c.github.io/webrtc-stats/#playoutstats-dict*
class RTC_EXPORT RTCAudioPlayoutStats final : public RTCStats {
public:
WEBRTC_RTCSTATS_DECL();
RTCAudioPlayoutStats(const std::string& id, Timestamp timestamp);
RTCAudioPlayoutStats(const RTCAudioPlayoutStats& other);
~RTCAudioPlayoutStats() override;
RTCStatsMember<double> synthesized_samples_duration;
RTCStatsMember<uint64_t> synthesized_samples_events;
RTCStatsMember<double> total_samples_duration;
RTCStatsMember<double> total_playout_delay;
RTCStatsMember<uint64_t> total_samples_count;
};
} // namespace webrtc
#endif // API_STATS_RTCSTATS_OBJECTS_H_

View File

@ -90,6 +90,7 @@ rtc_library("rtc_media_base") {
"../call:video_stream_api",
"../common_video",
"../modules/async_audio_processing",
"../modules/audio_device",
"../modules/audio_processing:audio_processing_statistics",
"../modules/rtp_rtcp:rtp_rtcp_format",
"../rtc_base",
@ -749,6 +750,7 @@ if (rtc_include_tests) {
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
]
sources = [
"base/fake_frame_source.cc",

View File

@ -15,6 +15,7 @@
#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
#include "absl/types/optional.h"
#include "rtc_base/checks.h"
namespace cricket {
@ -490,6 +491,10 @@ bool FakeVoiceEngine::StartAecDump(webrtc::FileWrapper file,
int64_t max_size_bytes) {
return false;
}
absl::optional<webrtc::AudioDeviceModule::Stats>
FakeVoiceEngine::GetAudioDeviceStats() {
return absl::nullopt;
}
void FakeVoiceEngine::StopAecDump() {}
std::vector<webrtc::RtpHeaderExtensionCapability>

View File

@ -537,6 +537,8 @@ class FakeVoiceEngine : public VoiceEngineInterface {
int GetInputLevel();
bool StartAecDump(webrtc::FileWrapper file, int64_t max_size_bytes) override;
void StopAecDump() override;
absl::optional<webrtc::AudioDeviceModule::Stats> GetAudioDeviceStats()
override;
std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
const override;
void SetRtpHeaderExtensions(

View File

@ -116,6 +116,9 @@ class VoiceEngineInterface : public RtpHeaderExtensionQueryInterface {
// Stops recording AEC dump.
virtual void StopAecDump() = 0;
virtual absl::optional<webrtc::AudioDeviceModule::Stats>
GetAudioDeviceStats() = 0;
};
class VideoEngineInterface : public RtpHeaderExtensionQueryInterface {

View File

@ -626,6 +626,11 @@ void WebRtcVoiceEngine::StopAecDump() {
}
}
absl::optional<webrtc::AudioDeviceModule::Stats>
WebRtcVoiceEngine::GetAudioDeviceStats() {
return adm()->GetStats();
}
webrtc::AudioDeviceModule* WebRtcVoiceEngine::adm() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK(adm_);

View File

@ -88,6 +88,9 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface {
// Stops AEC dump.
void StopAecDump() override;
absl::optional<webrtc::AudioDeviceModule::Stats> GetAudioDeviceStats()
override;
private:
// Every option that is "set" will be applied. Every option not "set" will be
// ignored. This allows us to selectively turn on and off different options

View File

@ -54,6 +54,7 @@ rtc_source_set("audio_device_api") {
"../../rtc_base:refcount",
"../../rtc_base:stringutils",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
rtc_library("audio_device_buffer") {

View File

@ -11,6 +11,7 @@
#ifndef MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_H_
#define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_H_
#include "absl/types/optional.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/task_queue_factory.h"
#include "modules/audio_device/include/audio_device_defines.h"
@ -41,6 +42,16 @@ class AudioDeviceModule : public rtc::RefCountInterface {
kDefaultDevice = -2
};
struct Stats {
// The fields below correspond to similarly-named fields in the WebRTC stats
// spec. https://w3c.github.io/webrtc-stats/#playoutstats-dict*
double synthesized_samples_duration_s = 0;
uint64_t synthesized_samples_events = 0;
double total_samples_duration_s = 0;
double total_playout_delay_s = 0;
uint64_t total_samples_count = 0;
};
public:
// Creates a default ADM for usage in production code.
static rtc::scoped_refptr<AudioDeviceModule> Create(
@ -150,6 +161,10 @@ class AudioDeviceModule : public rtc::RefCountInterface {
// TODO(alexnarest): Make it abstract after upstream projects support it.
virtual int32_t GetPlayoutUnderrunCount() const { return -1; }
// Used to generate RTC stats. If not implemented, RTCAudioPlayoutStats will
// not be present in the stats.
virtual absl::optional<Stats> GetStats() const { return absl::nullopt; }
// Only supported on iOS.
#if defined(WEBRTC_IOS)
virtual int GetPlayoutAudioParameters(AudioParameters* params) const = 0;

View File

@ -1015,7 +1015,10 @@ rtc_source_set("peer_connection_internal") {
":sctp_data_channel",
"../api:libjingle_peerconnection_api",
"../call:call_interfaces",
"../modules/audio_device",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
rtc_source_set("rtc_stats_collector") {
@ -1060,6 +1063,7 @@ rtc_source_set("rtc_stats_collector") {
"../media:media_channel",
"../media:media_channel_impl",
"../media:rtc_media_base",
"../modules/audio_device",
"../modules/audio_processing:audio_processing_statistics",
"../modules/rtp_rtcp:rtp_rtcp_format",
"../p2p:rtc_p2p",

View File

@ -22,6 +22,7 @@
#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/jsep_ice_candidate.h"
#include "api/rtp_parameters.h"
#include "api/rtp_transceiver_direction.h"
@ -2500,6 +2501,13 @@ Call::Stats PeerConnection::GetCallStats() {
}
}
absl::optional<AudioDeviceModule::Stats> PeerConnection::GetAudioDeviceStats() {
if (context_->media_engine()) {
return context_->media_engine()->voice().GetAudioDeviceStats();
}
return absl::nullopt;
}
bool PeerConnection::SetupDataChannelTransport_n(const std::string& mid) {
DataChannelTransportInterface* transport =
transport_controller_->GetDataChannelTransport(mid);

View File

@ -298,6 +298,8 @@ class PeerConnection : public PeerConnectionInternal,
const std::set<std::string>& transport_names) override;
Call::Stats GetCallStats() override;
absl::optional<AudioDeviceModule::Stats> GetAudioDeviceStats() override;
bool GetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override;

View File

@ -17,8 +17,10 @@
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/peer_connection_interface.h"
#include "call/call.h"
#include "modules/audio_device/include/audio_device.h"
#include "pc/jsep_transport_controller.h"
#include "pc/peer_connection_message_handler.h"
#include "pc/rtp_transceiver.h"
@ -162,6 +164,8 @@ class PeerConnectionInternal : public PeerConnectionInterface,
virtual Call::Stats GetCallStats() = 0;
virtual absl::optional<AudioDeviceModule::Stats> GetAudioDeviceStats() = 0;
virtual bool GetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) = 0;

View File

@ -37,6 +37,7 @@
#include "common_video/include/quality_limitation_reason.h"
#include "media/base/media_channel.h"
#include "media/base/media_channel_impl.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing_statistics.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@ -513,6 +514,21 @@ std::unique_ptr<RTCInboundRTPStreamStats> CreateInboundAudioStreamStats(
return inbound_audio;
}
std::unique_ptr<RTCAudioPlayoutStats> CreateAudioPlayoutStats(
const AudioDeviceModule::Stats& audio_device_stats,
webrtc::Timestamp timestamp) {
auto stats = std::make_unique<RTCAudioPlayoutStats>(
/*id=*/"AP", timestamp);
stats->synthesized_samples_duration =
audio_device_stats.synthesized_samples_duration_s;
stats->synthesized_samples_events =
audio_device_stats.synthesized_samples_events;
stats->total_samples_count = audio_device_stats.total_samples_count;
stats->total_samples_duration = audio_device_stats.total_samples_duration_s;
stats->total_playout_delay = audio_device_stats.total_playout_delay_s;
return stats;
}
std::unique_ptr<RTCRemoteOutboundRtpStreamStats>
CreateRemoteOutboundAudioStreamStats(
const cricket::VoiceReceiverInfo& voice_receiver_info,
@ -1486,6 +1502,7 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThreadImpl(
ProduceMediaStreamTrackStats_s(timestamp, partial_report);
ProduceMediaSourceStats_s(timestamp, partial_report);
ProducePeerConnectionStats_s(timestamp, partial_report);
ProduceAudioPlayoutStats_s(timestamp, partial_report);
}
void RTCStatsCollector::ProducePartialResultsOnNetworkThread(
@ -1935,6 +1952,17 @@ void RTCStatsCollector::ProducePeerConnectionStats_s(
report->AddStats(std::move(stats));
}
void RTCStatsCollector::ProduceAudioPlayoutStats_s(
Timestamp timestamp,
RTCStatsReport* report) const {
RTC_DCHECK_RUN_ON(signaling_thread_);
rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
if (audio_device_stats_) {
report->AddStats(CreateAudioPlayoutStats(*audio_device_stats_, timestamp));
}
}
void RTCStatsCollector::ProduceRTPStreamStats_n(
Timestamp timestamp,
const std::vector<RtpTransceiverStatsInfo>& transceiver_stats_infos,
@ -2447,6 +2475,7 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() {
}
call_stats_ = pc_->GetCallStats();
audio_device_stats_ = pc_->GetAudioDeviceStats();
});
}

View File

@ -29,6 +29,7 @@
#include "api/stats/rtcstats_objects.h"
#include "call/call.h"
#include "media/base/media_channel.h"
#include "modules/audio_device/include/audio_device.h"
#include "pc/data_channel_utils.h"
#include "pc/peer_connection_internal.h"
#include "pc/rtp_receiver.h"
@ -204,6 +205,9 @@ class RTCStatsCollector : public rtc::RefCountInterface,
// Produces `RTCPeerConnectionStats`.
void ProducePeerConnectionStats_s(Timestamp timestamp,
RTCStatsReport* report) const;
// Produces `RTCAudioPlayoutStats`.
void ProduceAudioPlayoutStats_s(Timestamp timestamp,
RTCStatsReport* report) const;
// Produces `RTCInboundRTPStreamStats`, `RTCOutboundRTPStreamStats`,
// `RTCRemoteInboundRtpStreamStats`, `RTCRemoteOutboundRtpStreamStats` and any
// referenced `RTCCodecStats`. This has to be invoked after transport stats
@ -298,6 +302,8 @@ class RTCStatsCollector : public rtc::RefCountInterface,
Call::Stats call_stats_;
absl::optional<AudioDeviceModule::Stats> audio_device_stats_;
// A timestamp, in microseconds, that is based on a timer that is
// monotonically increasing. That is, even if the system clock is modified the
// difference between the timer and this timestamp is how fresh the cached

View File

@ -42,6 +42,7 @@
#include "api/video_codecs/scalability_mode.h"
#include "common_video/include/quality_limitation_reason.h"
#include "media/base/media_channel.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing_statistics.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@ -2705,6 +2706,31 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) {
EXPECT_TRUE(report->Get(*expected_video.codec_id));
}
TEST_F(RTCStatsCollectorTest, CollectRTCAudioPlayoutStats) {
AudioDeviceModule::Stats audio_device_stats;
audio_device_stats.synthesized_samples_duration_s = 1;
audio_device_stats.synthesized_samples_events = 2;
audio_device_stats.total_samples_count = 3;
audio_device_stats.total_samples_duration_s = 4;
audio_device_stats.total_playout_delay_s = 5;
pc_->SetAudioDeviceStats(audio_device_stats);
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
auto stats_of_track_type = report->GetStatsOfType<RTCAudioPlayoutStats>();
ASSERT_EQ(1U, stats_of_track_type.size());
RTCAudioPlayoutStats expected_stats("AP", report->timestamp());
expected_stats.synthesized_samples_duration = 1;
expected_stats.synthesized_samples_events = 2;
expected_stats.total_samples_count = 3;
expected_stats.total_samples_duration = 4;
expected_stats.total_playout_delay = 5;
ASSERT_TRUE(report->Get(expected_stats.id()));
EXPECT_EQ(report->Get(expected_stats.id())->cast_to<RTCAudioPlayoutStats>(),
expected_stats);
}
TEST_F(RTCStatsCollectorTest, CollectGoogTimingFrameInfo) {
cricket::VideoMediaInfo video_media_info;

View File

@ -17,6 +17,7 @@
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/sctp_transport_interface.h"
#include "pc/peer_connection_internal.h"
@ -273,6 +274,10 @@ class FakePeerConnectionBase : public PeerConnectionInternal {
Call::Stats GetCallStats() override { return Call::Stats(); }
absl::optional<AudioDeviceModule::Stats> GetAudioDeviceStats() override {
return absl::nullopt;
}
bool GetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override {

View File

@ -328,6 +328,11 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
void SetCallStats(const Call::Stats& call_stats) { call_stats_ = call_stats; }
void SetAudioDeviceStats(
absl::optional<AudioDeviceModule::Stats> audio_device_stats) {
audio_device_stats_ = audio_device_stats;
}
void SetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate> certificate) {
@ -410,6 +415,10 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
Call::Stats GetCallStats() override { return call_stats_; }
absl::optional<AudioDeviceModule::Stats> GetAudioDeviceStats() override {
return audio_device_stats_;
}
bool GetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override {
@ -490,6 +499,8 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
Call::Stats call_stats_;
absl::optional<AudioDeviceModule::Stats> audio_device_stats_;
std::map<std::string, rtc::scoped_refptr<rtc::RTCCertificate>>
local_certificates_by_transport_;
std::map<std::string, std::unique_ptr<rtc::SSLCertChain>>

View File

@ -17,6 +17,7 @@
#include <string>
#include <vector>
#include "modules/audio_device/include/audio_device.h"
#include "pc/peer_connection_internal.h"
#include "test/gmock.h"
@ -302,6 +303,10 @@ class MockPeerConnectionInternal : public PeerConnectionInternal {
(const std::set<std::string>&),
(override));
MOCK_METHOD(Call::Stats, GetCallStats, (), (override));
MOCK_METHOD(absl::optional<AudioDeviceModule::Stats>,
GetAudioDeviceStats,
(),
(override));
MOCK_METHOD(bool,
GetLocalCertificate,
(const std::string&, rtc::scoped_refptr<rtc::RTCCertificate>*),

View File

@ -895,4 +895,27 @@ RTCTransportStats::RTCTransportStats(const RTCTransportStats& other) = default;
RTCTransportStats::~RTCTransportStats() {}
RTCAudioPlayoutStats::RTCAudioPlayoutStats(const std::string& id,
Timestamp timestamp)
: RTCStats(std::move(id), timestamp),
synthesized_samples_duration("synthesizedSamplesDuration"),
synthesized_samples_events("synthesizedSamplesEvents"),
total_samples_duration("totalSamplesDuration"),
total_playout_delay("totalPlayoutDelay"),
total_samples_count("totalSamplesCount") {}
RTCAudioPlayoutStats::RTCAudioPlayoutStats(const RTCAudioPlayoutStats& other) =
default;
RTCAudioPlayoutStats::~RTCAudioPlayoutStats() {}
// clang-format off
WEBRTC_RTCSTATS_IMPL(RTCAudioPlayoutStats, RTCStats, "audio-playout",
&synthesized_samples_duration,
&synthesized_samples_events,
&total_samples_duration,
&total_playout_delay,
&total_samples_count)
// clang-format on
} // namespace webrtc