Add parallel stats interface with optional stats to APM.

This new parallel GetStatistics function uses Optionals to indicate if stats are valid or not, and no longer relies on default values. It also takes an argument to indicate if receive streams are present, and if not several stats will not be set.

Bug: b/67926135
Change-Id: I175de1c65c414bea6ec9ca8b0b16f07cb2308d9f
Reviewed-on: https://webrtc-review.googlesource.com/17942
Commit-Queue: Ivo Creusen <ivoc@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20789}
This commit is contained in:
Ivo Creusen 2017-11-20 13:07:16 +01:00 committed by Commit Bot
parent 0ce0988503
commit ae02609645
9 changed files with 383 additions and 43 deletions

View File

@ -15,4 +15,26 @@ namespace webrtc {
const char MediaStreamTrackInterface::kVideoKind[] = "video";
const char MediaStreamTrackInterface::kAudioKind[] = "audio";
// TODO(ivoc): Remove this when the function becomes pure virtual.
AudioProcessorInterface::AudioProcessorStatistics
AudioProcessorInterface::GetStats(bool /*has_remote_tracks*/) {
AudioProcessorStats stats;
GetStats(&stats);
AudioProcessorStatistics new_stats;
new_stats.aec_divergent_filter_fraction =
rtc::Optional<double>(stats.aec_divergent_filter_fraction);
new_stats.aec_quality_min = rtc::Optional<double>(stats.aec_quality_min);
new_stats.echo_delay_median_ms =
rtc::Optional<int32_t>(stats.echo_delay_median_ms);
new_stats.echo_delay_std_ms = rtc::Optional<int32_t>(stats.echo_delay_std_ms);
new_stats.echo_return_loss = rtc::Optional<double>(stats.echo_return_loss);
new_stats.echo_return_loss_enhancement =
rtc::Optional<double>(stats.echo_return_loss_enhancement);
new_stats.residual_echo_likelihood =
rtc::Optional<double>(stats.residual_echo_likelihood);
new_stats.residual_echo_likelihood_recent_max =
rtc::Optional<double>(stats.residual_echo_likelihood_recent_max);
return new_stats;
}
} // namespace webrtc

View File

@ -225,6 +225,9 @@ class AudioSourceInterface : public MediaSourceInterface {
// statistics.
class AudioProcessorInterface : public rtc::RefCountInterface {
public:
// Deprecated, use AudioProcessorStatistics instead.
// TODO(ivoc): Remove this when all implementations have switched to the new
// GetStats function. See b/67926135.
struct AudioProcessorStats {
AudioProcessorStats()
: typing_noise_detected(false),
@ -248,10 +251,31 @@ class AudioProcessorInterface : public rtc::RefCountInterface {
float residual_echo_likelihood_recent_max;
float aec_divergent_filter_fraction;
};
// This struct maintains the optionality of the stats, and will replace the
// regular stats struct when all users have been updated.
struct AudioProcessorStatistics {
bool typing_noise_detected = false;
rtc::Optional<double> echo_return_loss;
rtc::Optional<double> echo_return_loss_enhancement;
rtc::Optional<int32_t> echo_delay_median_ms;
rtc::Optional<int32_t> echo_delay_std_ms;
rtc::Optional<double> aec_quality_min;
rtc::Optional<double> residual_echo_likelihood;
rtc::Optional<double> residual_echo_likelihood_recent_max;
rtc::Optional<double> aec_divergent_filter_fraction;
};
// Get audio processor statistics.
virtual void GetStats(AudioProcessorStats* stats) = 0;
// Get audio processor statistics. The |has_remote_tracks| argument should be
// set if there are active remote tracks (this would usually be true during
// a call). If there are no remote tracks some of the stats will not be set by
// the AudioProcessor, because they only make sense if there is at least one
// remote track.
// TODO(ivoc): Make pure virtual when all implementions are updated.
virtual AudioProcessorStatistics GetStats(bool has_remote_tracks);
protected:
virtual ~AudioProcessorInterface() {}
};

View File

@ -1557,18 +1557,31 @@ AudioProcessing::AudioProcessingStatistics::AudioProcessingStatistics(
AudioProcessing::AudioProcessingStatistics::~AudioProcessingStatistics() =
default;
AudioProcessing::AudioProcessingStats::AudioProcessingStats() = default;
AudioProcessing::AudioProcessingStats::AudioProcessingStats(
const AudioProcessingStats& other) = default;
AudioProcessing::AudioProcessingStats::~AudioProcessingStats() = default;
// TODO(ivoc): Remove this when GetStatistics() becomes pure virtual.
AudioProcessing::AudioProcessingStatistics AudioProcessing::GetStatistics()
const {
return AudioProcessingStatistics();
}
// TODO(ivoc): Remove this when GetStatistics() becomes pure virtual.
AudioProcessing::AudioProcessingStats AudioProcessing::GetStatistics(
bool has_remote_tracks) const {
return AudioProcessingStats();
}
AudioProcessing::AudioProcessingStatistics AudioProcessingImpl::GetStatistics()
const {
AudioProcessingStatistics stats;
EchoCancellation::Metrics metrics;
int success = public_submodules_->echo_cancellation->GetMetrics(&metrics);
if (success == Error::kNoError) {
if (public_submodules_->echo_cancellation->GetMetrics(&metrics) ==
Error::kNoError) {
stats.a_nlp.Set(metrics.a_nlp);
stats.divergent_filter_fraction = metrics.divergent_filter_fraction;
stats.echo_return_loss.Set(metrics.echo_return_loss);
@ -1590,6 +1603,50 @@ AudioProcessing::AudioProcessingStatistics AudioProcessingImpl::GetStatistics()
return stats;
}
AudioProcessing::AudioProcessingStats AudioProcessingImpl::GetStatistics(
bool has_remote_tracks) const {
AudioProcessingStats stats;
if (has_remote_tracks) {
EchoCancellation::Metrics metrics;
if (public_submodules_->echo_cancellation->GetMetrics(&metrics) ==
Error::kNoError) {
if (metrics.divergent_filter_fraction != -1.0f) {
stats.divergent_filter_fraction =
rtc::Optional<double>(metrics.divergent_filter_fraction);
}
if (metrics.echo_return_loss.instant != -100) {
stats.echo_return_loss =
rtc::Optional<double>(metrics.echo_return_loss.instant);
}
if (metrics.echo_return_loss_enhancement.instant != -100) {
stats.echo_return_loss_enhancement =
rtc::Optional<double>(metrics.echo_return_loss_enhancement.instant);
}
}
if (config_.residual_echo_detector.enabled) {
rtc::CritScope cs_capture(&crit_capture_);
stats.residual_echo_likelihood = rtc::Optional<double>(
private_submodules_->residual_echo_detector->echo_likelihood());
stats.residual_echo_likelihood_recent_max =
rtc::Optional<double>(private_submodules_->residual_echo_detector
->echo_likelihood_recent_max());
}
int delay_median, delay_std;
float fraction_poor_delays;
if (public_submodules_->echo_cancellation->GetDelayMetrics(
&delay_median, &delay_std, &fraction_poor_delays) ==
Error::kNoError) {
if (delay_median >= 0) {
stats.delay_median_ms = rtc::Optional<int32_t>(delay_median);
}
if (delay_std >= 0) {
stats.delay_standard_deviation_ms = rtc::Optional<int32_t>(delay_std);
}
}
}
return stats;
}
EchoCancellation* AudioProcessingImpl::echo_cancellation() const {
return public_submodules_->echo_cancellation.get();
}

View File

@ -106,6 +106,7 @@ class AudioProcessingImpl : public AudioProcessing {
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
AudioProcessingStatistics GetStatistics() const override;
AudioProcessingStats GetStatistics(bool has_remote_tracks) const override;
// Methods returning pointers to APM submodules.
// No locks are aquired in those, as those locks

View File

@ -2952,4 +2952,166 @@ TEST(ApmConfiguration, EchoControlInjection) {
apm->ProcessReverseStream(&audio);
apm->ProcessStream(&audio);
}
std::unique_ptr<AudioProcessing> CreateApm(bool use_AEC2) {
Config old_config;
if (use_AEC2) {
old_config.Set<ExtendedFilter>(new ExtendedFilter(true));
old_config.Set<DelayAgnostic>(new DelayAgnostic(true));
}
std::unique_ptr<AudioProcessing> apm(AudioProcessing::Create(old_config));
if (!apm) {
return apm;
}
ProcessingConfig processing_config = {
{{32000, 1}, {32000, 1}, {32000, 1}, {32000, 1}}};
if (apm->Initialize(processing_config) != 0) {
return nullptr;
}
// Disable all components except for an AEC and the residual echo detector.
AudioProcessing::Config config;
config.residual_echo_detector.enabled = true;
config.echo_canceller3.enabled = false;
config.high_pass_filter.enabled = false;
config.gain_controller2.enabled = false;
config.level_controller.enabled = false;
apm->ApplyConfig(config);
EXPECT_EQ(apm->gain_control()->Enable(false), 0);
EXPECT_EQ(apm->level_estimator()->Enable(false), 0);
EXPECT_EQ(apm->noise_suppression()->Enable(false), 0);
EXPECT_EQ(apm->voice_detection()->Enable(false), 0);
if (use_AEC2) {
EXPECT_EQ(apm->echo_control_mobile()->Enable(false), 0);
EXPECT_EQ(apm->echo_cancellation()->enable_metrics(true), 0);
EXPECT_EQ(apm->echo_cancellation()->enable_delay_logging(true), 0);
EXPECT_EQ(apm->echo_cancellation()->Enable(true), 0);
} else {
EXPECT_EQ(apm->echo_cancellation()->Enable(false), 0);
EXPECT_EQ(apm->echo_control_mobile()->Enable(true), 0);
}
return apm;
}
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_MAC)
#define MAYBE_ApmStatistics DISABLED_ApmStatistics
#else
#define MAYBE_ApmStatistics ApmStatistics
#endif
TEST(MAYBE_ApmStatistics, AEC2EnabledTest) {
// Set up APM with AEC2 and process some audio.
std::unique_ptr<AudioProcessing> apm = CreateApm(true);
ASSERT_TRUE(apm);
// Set up an audioframe.
AudioFrame frame;
frame.num_channels_ = 1;
SetFrameSampleRate(&frame, AudioProcessing::NativeRate::kSampleRate48kHz);
// Fill the audio frame with a sawtooth pattern.
int16_t* ptr = frame.mutable_data();
for (size_t i = 0; i < frame.kMaxDataSizeSamples; i++) {
ptr[i] = 10000 * ((i % 3) - 1);
}
// Do some processing.
for (int i = 0; i < 200; i++) {
EXPECT_EQ(apm->ProcessReverseStream(&frame), 0);
EXPECT_EQ(apm->set_stream_delay_ms(0), 0);
EXPECT_EQ(apm->ProcessStream(&frame), 0);
}
// Test statistics interface.
AudioProcessing::AudioProcessingStats stats = apm->GetStatistics(true);
// We expect all statistics to be set and have a sensible value.
ASSERT_TRUE(stats.residual_echo_likelihood);
EXPECT_GE(*stats.residual_echo_likelihood, 0.0);
EXPECT_LE(*stats.residual_echo_likelihood, 1.0);
ASSERT_TRUE(stats.residual_echo_likelihood_recent_max);
EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0);
EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0);
ASSERT_TRUE(stats.echo_return_loss);
EXPECT_NE(*stats.echo_return_loss, -100.0);
ASSERT_TRUE(stats.echo_return_loss_enhancement);
EXPECT_NE(*stats.echo_return_loss_enhancement, -100.0);
ASSERT_TRUE(stats.divergent_filter_fraction);
EXPECT_NE(*stats.divergent_filter_fraction, -1.0);
ASSERT_TRUE(stats.delay_standard_deviation_ms);
EXPECT_GE(*stats.delay_standard_deviation_ms, 0);
// We don't check stats.delay_median_ms since it takes too long to settle to a
// value. At least 20 seconds of data need to be processed before it will get
// a value, which would make this test take too much time.
// If there are no receive streams, we expect the stats not to be set. The
// 'false' argument signals to APM that no receive streams are currently
// active. In that situation the statistics would get stuck at their last
// calculated value (AEC and echo detection need at least one stream in each
// direction), so to avoid that, they should not be set by APM.
stats = apm->GetStatistics(false);
EXPECT_FALSE(stats.residual_echo_likelihood);
EXPECT_FALSE(stats.residual_echo_likelihood_recent_max);
EXPECT_FALSE(stats.echo_return_loss);
EXPECT_FALSE(stats.echo_return_loss_enhancement);
EXPECT_FALSE(stats.divergent_filter_fraction);
EXPECT_FALSE(stats.delay_median_ms);
EXPECT_FALSE(stats.delay_standard_deviation_ms);
}
TEST(MAYBE_ApmStatistics, AECMEnabledTest) {
// Set up APM with AECM and process some audio.
std::unique_ptr<AudioProcessing> apm = CreateApm(false);
ASSERT_TRUE(apm);
// Set up an audioframe.
AudioFrame frame;
frame.num_channels_ = 1;
SetFrameSampleRate(&frame, AudioProcessing::NativeRate::kSampleRate48kHz);
// Fill the audio frame with a sawtooth pattern.
int16_t* ptr = frame.mutable_data();
for (size_t i = 0; i < frame.kMaxDataSizeSamples; i++) {
ptr[i] = 10000 * ((i % 3) - 1);
}
// Do some processing.
for (int i = 0; i < 200; i++) {
EXPECT_EQ(apm->ProcessReverseStream(&frame), 0);
EXPECT_EQ(apm->set_stream_delay_ms(0), 0);
EXPECT_EQ(apm->ProcessStream(&frame), 0);
}
// Test statistics interface.
AudioProcessing::AudioProcessingStats stats = apm->GetStatistics(true);
// We expect only the residual echo detector statistics to be set and have a
// sensible value.
EXPECT_TRUE(stats.residual_echo_likelihood);
if (stats.residual_echo_likelihood) {
EXPECT_GE(*stats.residual_echo_likelihood, 0.0);
EXPECT_LE(*stats.residual_echo_likelihood, 1.0);
}
EXPECT_TRUE(stats.residual_echo_likelihood_recent_max);
if (stats.residual_echo_likelihood_recent_max) {
EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0);
EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0);
}
EXPECT_FALSE(stats.echo_return_loss);
EXPECT_FALSE(stats.echo_return_loss_enhancement);
EXPECT_FALSE(stats.divergent_filter_fraction);
EXPECT_FALSE(stats.delay_median_ms);
EXPECT_FALSE(stats.delay_standard_deviation_ms);
// If there are no receive streams, we expect the stats not to be set.
stats = apm->GetStatistics(false);
EXPECT_FALSE(stats.residual_echo_likelihood);
EXPECT_FALSE(stats.residual_echo_likelihood_recent_max);
EXPECT_FALSE(stats.echo_return_loss);
EXPECT_FALSE(stats.echo_return_loss_enhancement);
EXPECT_FALSE(stats.divergent_filter_fraction);
EXPECT_FALSE(stats.delay_median_ms);
EXPECT_FALSE(stats.delay_standard_deviation_ms);
}
} // namespace webrtc

View File

@ -20,6 +20,7 @@
#include <string.h>
#include <vector>
#include "api/optional.h"
#include "modules/audio_processing/beamformer/array_util.h"
#include "modules/audio_processing/include/config.h"
#include "rtc_base/arraysize.h"
@ -563,9 +564,45 @@ class AudioProcessing : public rtc::RefCountInterface {
float residual_echo_likelihood_recent_max = -1.0f;
};
// This version of the stats uses Optionals, it will replace the regular
// AudioProcessingStatistics struct.
struct AudioProcessingStats {
AudioProcessingStats();
AudioProcessingStats(const AudioProcessingStats& other);
~AudioProcessingStats();
// AEC Statistics.
// ERL = 10log_10(P_far / P_echo)
rtc::Optional<double> echo_return_loss;
// ERLE = 10log_10(P_echo / P_out)
rtc::Optional<double> echo_return_loss_enhancement;
// Fraction of time that the AEC linear filter is divergent, in a 1-second
// non-overlapped aggregation window.
rtc::Optional<double> divergent_filter_fraction;
// The delay metrics consists of the delay median and standard deviation. It
// also consists of the fraction of delay estimates that can make the echo
// cancellation perform poorly. The values are aggregated until the first
// call to |GetStatistics()| and afterwards aggregated and updated every
// second. Note that if there are several clients pulling metrics from
// |GetStatistics()| during a session the first call from any of them will
// change to one second aggregation window for all.
rtc::Optional<int32_t> delay_median_ms;
rtc::Optional<int32_t> delay_standard_deviation_ms;
// Residual echo detector likelihood.
rtc::Optional<double> residual_echo_likelihood;
// Maximum residual echo likelihood from the last time period.
rtc::Optional<double> residual_echo_likelihood_recent_max;
};
// TODO(ivoc): Make this pure virtual when all subclasses have been updated.
virtual AudioProcessingStatistics GetStatistics() const;
// This returns the stats as optionals and it will replace the regular
// GetStatistics.
virtual AudioProcessingStats GetStatistics(bool has_remote_tracks) const;
// These provide access to the component interfaces and should never return
// NULL. The pointers will be valid for the lifetime of the APM instance.
// The memory for these objects is entirely managed internally.

View File

@ -99,42 +99,50 @@ void ExtractCommonReceiveProperties(const cricket::MediaReceiverInfo& info,
report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
}
void SetAudioProcessingStats(StatsReport* report,
bool typing_noise_detected,
int echo_return_loss,
int echo_return_loss_enhancement,
int echo_delay_median_ms,
float aec_quality_min,
int echo_delay_std_ms,
float residual_echo_likelihood,
float residual_echo_likelihood_recent_max) {
void SetAudioProcessingStats(
StatsReport* report,
bool typing_noise_detected,
rtc::Optional<double> echo_return_loss,
rtc::Optional<double> echo_return_loss_enhancement,
rtc::Optional<int32_t> echo_delay_median_ms,
rtc::Optional<double> aec_quality_min,
rtc::Optional<int32_t> echo_delay_std_ms,
rtc::Optional<double> residual_echo_likelihood,
rtc::Optional<double> residual_echo_likelihood_recent_max) {
report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
typing_noise_detected);
if (aec_quality_min >= 0.0f) {
// TODO(ivoc): Remove the checks for default values once the whole stat
// chain uses optionals.
if (aec_quality_min && *aec_quality_min >= 0.0) {
report->AddFloat(StatsReport::kStatsValueNameEchoCancellationQualityMin,
aec_quality_min);
*aec_quality_min);
}
const IntForAdd ints[] = {
{ StatsReport::kStatsValueNameEchoDelayMedian, echo_delay_median_ms },
{ StatsReport::kStatsValueNameEchoDelayStdDev, echo_delay_std_ms },
};
for (const auto& i : ints) {
if (i.value >= 0) {
report->AddInt(i.name, i.value);
}
if (echo_delay_median_ms && *echo_delay_median_ms >= 0) {
report->AddInt(StatsReport::kStatsValueNameEchoDelayMedian,
*echo_delay_median_ms);
}
if (echo_delay_std_ms && *echo_delay_std_ms >= 0) {
report->AddInt(StatsReport::kStatsValueNameEchoDelayStdDev,
*echo_delay_std_ms);
}
// These can take on valid negative values.
report->AddInt(StatsReport::kStatsValueNameEchoReturnLoss, echo_return_loss);
report->AddInt(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
echo_return_loss_enhancement);
if (residual_echo_likelihood >= 0.0f) {
report->AddFloat(StatsReport::kStatsValueNameResidualEchoLikelihood,
residual_echo_likelihood);
if (echo_return_loss) {
report->AddInt(StatsReport::kStatsValueNameEchoReturnLoss,
static_cast<int32_t>(*echo_return_loss));
}
if (residual_echo_likelihood_recent_max >= 0.0f) {
if (echo_return_loss_enhancement) {
report->AddInt(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
static_cast<int32_t>(*echo_return_loss_enhancement));
}
if (residual_echo_likelihood && *residual_echo_likelihood >= 0.0) {
report->AddFloat(StatsReport::kStatsValueNameResidualEchoLikelihood,
*residual_echo_likelihood);
}
if (residual_echo_likelihood_recent_max &&
*residual_echo_likelihood_recent_max >= 0.0) {
report->AddFloat(
StatsReport::kStatsValueNameResidualEchoLikelihoodRecentMax,
residual_echo_likelihood_recent_max);
*residual_echo_likelihood_recent_max);
}
}
@ -196,11 +204,16 @@ void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
ExtractCommonSendProperties(info, report);
// TODO(ivoc): Update VoiceSenderInfo to pass Optionals all the way from APM.
SetAudioProcessingStats(
report, info.typing_noise_detected, info.echo_return_loss,
info.echo_return_loss_enhancement, info.echo_delay_median_ms,
info.aec_quality_min, info.echo_delay_std_ms,
info.residual_echo_likelihood, info.residual_echo_likelihood_recent_max);
report, info.typing_noise_detected,
rtc::Optional<double>(info.echo_return_loss),
rtc::Optional<double>(info.echo_return_loss_enhancement),
rtc::Optional<int32_t>(info.echo_delay_median_ms),
rtc::Optional<double>(info.aec_quality_min),
rtc::Optional<int32_t>(info.echo_delay_std_ms),
rtc::Optional<double>(info.residual_echo_likelihood),
rtc::Optional<double>(info.residual_echo_likelihood_recent_max));
const FloatForAdd floats[] = {
{ StatsReport::kStatsValueNameTotalAudioEnergy, info.total_input_energy },
@ -877,7 +890,7 @@ void StatsCollector::ExtractVoiceInfo() {
ExtractStatsFromList(voice_info.senders, transport_id, this,
StatsReport::kSend);
UpdateStatsFromExistingLocalAudioTracks();
UpdateStatsFromExistingLocalAudioTracks(voice_info.receivers.size() > 0);
}
void StatsCollector::ExtractVideoInfo(
@ -973,7 +986,8 @@ StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type,
return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction));
}
void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
void StatsCollector::UpdateStatsFromExistingLocalAudioTracks(
bool has_remote_tracks) {
RTC_DCHECK(pc_->signaling_thread()->IsCurrent());
// Loop through the existing local audio tracks.
for (const auto& it : local_audio_tracks_) {
@ -996,12 +1010,13 @@ void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
continue;
report->set_timestamp(stats_gathering_started_);
UpdateReportFromAudioTrack(track, report);
UpdateReportFromAudioTrack(track, report, has_remote_tracks);
}
}
void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
StatsReport* report) {
StatsReport* report,
bool has_remote_tracks) {
RTC_DCHECK(pc_->signaling_thread()->IsCurrent());
RTC_DCHECK(track != NULL);
@ -1015,8 +1030,8 @@ void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
auto audio_processor(track->GetAudioProcessor());
if (audio_processor.get()) {
AudioProcessorInterface::AudioProcessorStats stats;
audio_processor->GetStats(&stats);
AudioProcessorInterface::AudioProcessorStatistics stats =
audio_processor->GetStats(has_remote_tracks);
SetAudioProcessingStats(
report, stats.typing_noise_detected, stats.echo_return_loss,
@ -1025,8 +1040,10 @@ void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
stats.residual_echo_likelihood,
stats.residual_echo_likelihood_recent_max);
report->AddFloat(StatsReport::kStatsValueNameAecDivergentFilterFraction,
stats.aec_divergent_filter_fraction);
if (stats.aec_divergent_filter_fraction) {
report->AddFloat(StatsReport::kStatsValueNameAecDivergentFilterFraction,
*stats.aec_divergent_filter_fraction);
}
}
}

View File

@ -120,9 +120,10 @@ class StatsCollector {
StatsReport::Direction direction);
// Helper method to get stats from the local audio tracks.
void UpdateStatsFromExistingLocalAudioTracks();
void UpdateStatsFromExistingLocalAudioTracks(bool has_remote_tracks);
void UpdateReportFromAudioTrack(AudioTrackInterface* track,
StatsReport* report);
StatsReport* report,
bool has_remote_tracks);
// Helper method to get the id for the track identified by ssrc.
// |direction| tells if the track is for sending or receiving.

View File

@ -87,6 +87,18 @@ class FakeAudioProcessor : public webrtc::AudioProcessorInterface {
stats->aec_quality_min = 5.1f;
stats->echo_delay_std_ms = 6;
}
AudioProcessorInterface::AudioProcessorStatistics GetStats(
bool /*has_recv_streams*/) override {
AudioProcessorStatistics stats;
stats.typing_noise_detected = true;
stats.echo_return_loss = rtc::Optional<double>(2.0);
stats.echo_return_loss_enhancement = rtc::Optional<double>(3.0);
stats.echo_delay_median_ms = rtc::Optional<int32_t>(4);
stats.aec_quality_min = rtc::Optional<double>(5.1);
stats.echo_delay_std_ms = rtc::Optional<int32_t>(6);
return stats;
}
};
class FakeAudioTrack
@ -128,6 +140,13 @@ class FakeAudioProcessorWithInitValue : public webrtc::AudioProcessorInterface {
stats->aec_quality_min = -1.0f;
stats->echo_delay_std_ms = -1;
}
AudioProcessorInterface::AudioProcessorStatistics GetStats(
bool /*has_recv_streams*/) override {
AudioProcessorStatistics stats;
stats.typing_noise_detected = false;
return stats;
}
};
class FakeAudioTrackWithInitValue