Reporting audio device underrun counter
Bug: webrtc:10884
Change-Id: I35636fcbc1e2a19a89242379cdff6ec5c12fd21a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/149200
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Commit-Queue: Alex Narest <alexnarest@google.com>
Cr-Commit-Position: refs/heads/master@{#28874}
diff --git a/api/stats_types.cc b/api/stats_types.cc
index 441522e..7dcbd13 100644
--- a/api/stats_types.cc
+++ b/api/stats_types.cc
@@ -653,6 +653,8 @@
return "googTypingNoiseState";
case kStatsValueNameWritable:
return "googWritable";
+ case kStatsValueNameAudioDeviceUnderrunCounter:
+ return "googAudioDeviceUnderrunCounter";
}
return nullptr;
diff --git a/api/stats_types.h b/api/stats_types.h
index 5b8ad4f..71bf164 100644
--- a/api/stats_types.h
+++ b/api/stats_types.h
@@ -238,6 +238,7 @@
kStatsValueNameTransportType,
kStatsValueNameTypingNoiseState,
kStatsValueNameWritable,
+ kStatsValueNameAudioDeviceUnderrunCounter,
};
class IdBase : public rtc::RefCountInterface {
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index 2909126..b0b0b88 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -687,6 +687,7 @@
std::vector<VoiceReceiverInfo> receivers;
RtpCodecParametersMap send_codecs;
RtpCodecParametersMap receive_codecs;
+ int32_t device_underrun_count = 0;
};
struct VideoMediaInfo {
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 7e62bc6..07be793 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -2267,6 +2267,7 @@
info->receive_codecs.insert(
std::make_pair(codec_params.payload_type, std::move(codec_params)));
}
+ info->device_underrun_count = engine_->adm()->GetPlayoutUnderrunCount();
return true;
}
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index 91fcfeb..9556e5f 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -2243,6 +2243,7 @@
// Check stats for the added streams.
{
+ EXPECT_CALL(adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
cricket::VoiceMediaInfo info;
EXPECT_EQ(true, channel_->GetStats(&info));
@@ -2262,6 +2263,7 @@
{
cricket::VoiceMediaInfo info;
EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
+ EXPECT_CALL(adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true, channel_->GetStats(&info));
EXPECT_EQ(static_cast<size_t>(arraysize(kSsrcs4)), info.senders.size());
EXPECT_EQ(0u, info.receivers.size());
@@ -2273,6 +2275,7 @@
cricket::VoiceMediaInfo info;
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
SetAudioReceiveStreamStats();
+ EXPECT_CALL(adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true, channel_->GetStats(&info));
EXPECT_EQ(static_cast<size_t>(arraysize(kSsrcs4)), info.senders.size());
EXPECT_EQ(1u, info.receivers.size());
@@ -2431,6 +2434,7 @@
// Check stats for the added streams.
{
+ EXPECT_CALL(adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
cricket::VoiceMediaInfo info;
EXPECT_EQ(true, channel_->GetStats(&info));
@@ -2446,6 +2450,7 @@
{
cricket::VoiceMediaInfo info;
SetSend(true);
+ EXPECT_CALL(adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true, channel_->GetStats(&info));
VerifyVoiceSenderInfo(info.senders[0], true);
VerifyVoiceSendRecvCodecs(info);
@@ -2455,6 +2460,7 @@
{
cricket::VoiceMediaInfo info;
EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
+ EXPECT_CALL(adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true, channel_->GetStats(&info));
EXPECT_EQ(1u, info.senders.size());
EXPECT_EQ(0u, info.receivers.size());
@@ -2466,6 +2472,7 @@
cricket::VoiceMediaInfo info;
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
SetAudioReceiveStreamStats();
+ EXPECT_CALL(adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true, channel_->GetStats(&info));
EXPECT_EQ(1u, info.senders.size());
EXPECT_EQ(1u, info.receivers.size());
diff --git a/modules/audio_device/audio_device_data_observer.cc b/modules/audio_device/audio_device_data_observer.cc
index 877d8d9..e81670e 100644
--- a/modules/audio_device/audio_device_data_observer.cc
+++ b/modules/audio_device/audio_device_data_observer.cc
@@ -261,6 +261,9 @@
int32_t EnableBuiltInNS(bool enable) override {
return impl_->EnableBuiltInNS(enable);
}
+ int32_t GetPlayoutUnderrunCount() const override {
+ return impl_->GetPlayoutUnderrunCount();
+ }
// Only supported on iOS.
#if defined(WEBRTC_IOS)
int GetPlayoutAudioParameters(AudioParameters* params) const override {
diff --git a/modules/audio_device/audio_device_generic.cc b/modules/audio_device/audio_device_generic.cc
index 13d359c..7b8cfd1 100644
--- a/modules/audio_device/audio_device_generic.cc
+++ b/modules/audio_device/audio_device_generic.cc
@@ -44,6 +44,11 @@
return -1;
}
+int32_t AudioDeviceGeneric::GetPlayoutUnderrunCount() const {
+ RTC_LOG_F(LS_ERROR) << "Not supported on this platform";
+ return -1;
+}
+
#if defined(WEBRTC_IOS)
int AudioDeviceGeneric::GetPlayoutAudioParameters(
AudioParameters* params) const {
diff --git a/modules/audio_device/audio_device_generic.h b/modules/audio_device/audio_device_generic.h
index 7d3c83e..41e24eb 100644
--- a/modules/audio_device/audio_device_generic.h
+++ b/modules/audio_device/audio_device_generic.h
@@ -125,6 +125,9 @@
virtual int32_t EnableBuiltInAGC(bool enable);
virtual int32_t EnableBuiltInNS(bool enable);
+ // Play underrun count.
+ virtual int32_t GetPlayoutUnderrunCount() const;
+
// iOS only.
// TODO(henrika): add Android support.
#if defined(WEBRTC_IOS)
diff --git a/modules/audio_device/audio_device_impl.cc b/modules/audio_device/audio_device_impl.cc
index aaba49a..95f9f03 100644
--- a/modules/audio_device/audio_device_impl.cc
+++ b/modules/audio_device/audio_device_impl.cc
@@ -910,6 +910,14 @@
return ok;
}
+int32_t AudioDeviceModuleImpl::GetPlayoutUnderrunCount() const {
+ RTC_LOG(INFO) << __FUNCTION__;
+ CHECKinitialized_();
+ int32_t underrunCount = audio_device_->GetPlayoutUnderrunCount();
+ RTC_LOG(INFO) << "output: " << underrunCount;
+ return underrunCount;
+}
+
#if defined(WEBRTC_IOS)
int AudioDeviceModuleImpl::GetPlayoutAudioParameters(
AudioParameters* params) const {
diff --git a/modules/audio_device/audio_device_impl.h b/modules/audio_device/audio_device_impl.h
index 5a76595..45f73dc 100644
--- a/modules/audio_device/audio_device_impl.h
+++ b/modules/audio_device/audio_device_impl.h
@@ -137,6 +137,9 @@
bool BuiltInNSIsAvailable() const override;
int32_t EnableBuiltInNS(bool enable) override;
+ // Play underrun count.
+ int32_t GetPlayoutUnderrunCount() const override;
+
#if defined(WEBRTC_IOS)
int GetPlayoutAudioParameters(AudioParameters* params) const override;
int GetRecordAudioParameters(AudioParameters* params) const override;
diff --git a/modules/audio_device/include/audio_device.h b/modules/audio_device/include/audio_device.h
index 04d53a8..42ba203 100644
--- a/modules/audio_device/include/audio_device.h
+++ b/modules/audio_device/include/audio_device.h
@@ -146,6 +146,10 @@
virtual int32_t EnableBuiltInAGC(bool enable) = 0;
virtual int32_t EnableBuiltInNS(bool enable) = 0;
+ // Play underrun count. Only supported on Android.
+ // TODO(alexnarest): Make it abstract after upstream projects support it.
+ virtual int32_t GetPlayoutUnderrunCount() const { return -1; }
+
// Only supported on iOS.
#if defined(WEBRTC_IOS)
virtual int GetPlayoutAudioParameters(AudioParameters* params) const = 0;
diff --git a/modules/audio_device/include/audio_device_default.h b/modules/audio_device/include/audio_device_default.h
index 8b052fb..3779d6f 100644
--- a/modules/audio_device/include/audio_device_default.h
+++ b/modules/audio_device/include/audio_device_default.h
@@ -114,6 +114,8 @@
bool BuiltInNSIsAvailable() const override { return false; }
int32_t EnableBuiltInNS(bool enable) override { return -1; }
+ int32_t GetPlayoutUnderrunCount() const override { return -1; }
+
#if defined(WEBRTC_IOS)
int GetPlayoutAudioParameters(AudioParameters* params) const override {
return -1;
diff --git a/modules/audio_device/include/mock_audio_device.h b/modules/audio_device/include/mock_audio_device.h
index 011886f..8f9d9b6 100644
--- a/modules/audio_device/include/mock_audio_device.h
+++ b/modules/audio_device/include/mock_audio_device.h
@@ -91,6 +91,7 @@
MOCK_METHOD1(EnableBuiltInAEC, int32_t(bool enable));
MOCK_METHOD1(EnableBuiltInAGC, int32_t(bool enable));
MOCK_METHOD1(EnableBuiltInNS, int32_t(bool enable));
+ MOCK_CONST_METHOD0(GetPlayoutUnderrunCount, int32_t());
#if defined(WEBRTC_IOS)
MOCK_CONST_METHOD1(GetPlayoutAudioParameters, int(AudioParameters* params));
MOCK_CONST_METHOD1(GetRecordAudioParameters, int(AudioParameters* params));
diff --git a/pc/stats_collector.cc b/pc/stats_collector.cc
index 37b4b4b..260f601 100644
--- a/pc/stats_collector.cc
+++ b/pc/stats_collector.cc
@@ -636,6 +636,14 @@
return report;
}
+StatsReport* StatsCollector::PrepareADMReport() {
+ RTC_DCHECK(pc_->signaling_thread()->IsCurrent());
+ StatsReport::Id id(StatsReport::NewTypedId(
+ StatsReport::kStatsReportTypeSession, pc_->session_id()));
+ StatsReport* report = reports_.FindOrAddNew(id);
+ return report;
+}
+
bool StatsCollector::IsValidTrack(const std::string& track_id) {
return reports_.Find(StatsReport::NewTypedId(
StatsReport::kStatsReportTypeTrack, track_id)) != nullptr;
@@ -956,6 +964,12 @@
void ExtractStats(StatsCollector* collector) const override {
ExtractSenderReceiverStats(collector, voice_media_info.receivers,
voice_media_info.senders);
+ if (voice_media_info.device_underrun_count == -2 ||
+ voice_media_info.device_underrun_count > 0) {
+ StatsReport* report = collector->PrepareADMReport();
+ report->AddInt(StatsReport::kStatsValueNameAudioDeviceUnderrunCounter,
+ voice_media_info.device_underrun_count);
+ }
}
bool HasRemoteAudio() const override {
diff --git a/pc/stats_collector.h b/pc/stats_collector.h
index 569f1a6..fa9d587 100644
--- a/pc/stats_collector.h
+++ b/pc/stats_collector.h
@@ -84,6 +84,8 @@
const StatsReport::Id& transport_id,
StatsReport::Direction direction);
+ StatsReport* PrepareADMReport();
+
// A track is invalid if there is no report data for it.
bool IsValidTrack(const std::string& track_id);
diff --git a/pc/test/fake_audio_capture_module.h b/pc/test/fake_audio_capture_module.h
index 433fda0..0af3810 100644
--- a/pc/test/fake_audio_capture_module.h
+++ b/pc/test/fake_audio_capture_module.h
@@ -128,6 +128,8 @@
int32_t EnableBuiltInAGC(bool enable) override { return -1; }
bool BuiltInNSIsAvailable() const override { return false; }
int32_t EnableBuiltInNS(bool enable) override { return -1; }
+
+ int32_t GetPlayoutUnderrunCount() const override { return -1; }
#if defined(WEBRTC_IOS)
int GetPlayoutAudioParameters(
webrtc::AudioParameters* params) const override {
diff --git a/sdk/android/src/java/org/webrtc/audio/WebRtcAudioTrack.java b/sdk/android/src/java/org/webrtc/audio/WebRtcAudioTrack.java
index 16a6fcf..a00aec0 100644
--- a/sdk/android/src/java/org/webrtc/audio/WebRtcAudioTrack.java
+++ b/sdk/android/src/java/org/webrtc/audio/WebRtcAudioTrack.java
@@ -342,6 +342,19 @@
return audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
}
+ @CalledByNative
+ private int GetPlayoutUnderrunCount() {
+ if (Build.VERSION.SDK_INT >= 24) {
+ if (audioTrack != null) {
+ return audioTrack.getUnderrunCount();
+ } else {
+ return -1;
+ }
+ } else {
+ return -2;
+ }
+ }
+
private void logMainParameters() {
Logging.d(TAG,
"AudioTrack: "
diff --git a/sdk/android/src/jni/audio_device/audio_device_module.cc b/sdk/android/src/jni/audio_device/audio_device_module.cc
index 0fcff9b..0008e7e 100644
--- a/sdk/android/src/jni/audio_device/audio_device_module.cc
+++ b/sdk/android/src/jni/audio_device/audio_device_module.cc
@@ -584,6 +584,12 @@
return result;
}
+ int32_t GetPlayoutUnderrunCount() const override {
+ if (!initialized_)
+ return -1;
+ return output_->GetPlayoutUnderrunCount();
+ }
+
int32_t AttachAudioBuffer() {
RTC_LOG(INFO) << __FUNCTION__;
output_->AttachAudioBuffer(audio_device_buffer_.get());
diff --git a/sdk/android/src/jni/audio_device/audio_device_module.h b/sdk/android/src/jni/audio_device/audio_device_module.h
index 34979fe..1918336 100644
--- a/sdk/android/src/jni/audio_device/audio_device_module.h
+++ b/sdk/android/src/jni/audio_device/audio_device_module.h
@@ -65,6 +65,7 @@
virtual absl::optional<uint32_t> MaxSpeakerVolume() const = 0;
virtual absl::optional<uint32_t> MinSpeakerVolume() const = 0;
virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) = 0;
+ virtual int GetPlayoutUnderrunCount() = 0;
};
// Extract an android.media.AudioManager from an android.content.Context.
diff --git a/sdk/android/src/jni/audio_device/audio_track_jni.cc b/sdk/android/src/jni/audio_device/audio_track_jni.cc
index 856e18a..12e9fbf 100644
--- a/sdk/android/src/jni/audio_device/audio_track_jni.cc
+++ b/sdk/android/src/jni/audio_device/audio_track_jni.cc
@@ -169,6 +169,10 @@
return volume;
}
+int AudioTrackJni::GetPlayoutUnderrunCount() {
+ return Java_WebRtcAudioTrack_GetPlayoutUnderrunCount(env_, j_audio_track_);
+}
+
// TODO(henrika): possibly add stereo support.
void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
RTC_LOG(INFO) << "AttachAudioBuffer";
diff --git a/sdk/android/src/jni/audio_device/audio_track_jni.h b/sdk/android/src/jni/audio_device/audio_track_jni.h
index 2a76845..c7d0600 100644
--- a/sdk/android/src/jni/audio_device/audio_track_jni.h
+++ b/sdk/android/src/jni/audio_device/audio_track_jni.h
@@ -65,6 +65,7 @@
absl::optional<uint32_t> SpeakerVolume() const override;
absl::optional<uint32_t> MaxSpeakerVolume() const override;
absl::optional<uint32_t> MinSpeakerVolume() const override;
+ int GetPlayoutUnderrunCount() override;
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override;
diff --git a/sdk/android/src/jni/audio_device/opensles_player.h b/sdk/android/src/jni/audio_device/opensles_player.h
index 4b8a0aa..a2a49f9 100644
--- a/sdk/android/src/jni/audio_device/opensles_player.h
+++ b/sdk/android/src/jni/audio_device/opensles_player.h
@@ -82,6 +82,8 @@
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override;
+ int GetPlayoutUnderrunCount() override { return -1; }
+
private:
// These callback methods are called when data is required for playout.
// They are both called from an internal "OpenSL ES thread" which is not
diff --git a/sdk/objc/native/src/audio/audio_device_module_ios.h b/sdk/objc/native/src/audio/audio_device_module_ios.h
index 6e28c32..625eec2 100644
--- a/sdk/objc/native/src/audio/audio_device_module_ios.h
+++ b/sdk/objc/native/src/audio/audio_device_module_ios.h
@@ -125,6 +125,8 @@
bool BuiltInNSIsAvailable() const override;
int32_t EnableBuiltInNS(bool enable) override;
+ int32_t GetPlayoutUnderrunCount() const override;
+
#if defined(WEBRTC_IOS)
int GetPlayoutAudioParameters(AudioParameters* params) const override;
int GetRecordAudioParameters(AudioParameters* params) const override;
diff --git a/sdk/objc/native/src/audio/audio_device_module_ios.mm b/sdk/objc/native/src/audio/audio_device_module_ios.mm
index e82a4e7..74d2965 100644
--- a/sdk/objc/native/src/audio/audio_device_module_ios.mm
+++ b/sdk/objc/native/src/audio/audio_device_module_ios.mm
@@ -642,6 +642,14 @@
return ok;
}
+ int32_t AudioDeviceModuleIOS::GetPlayoutUnderrunCount() const {
+ RTC_LOG(INFO) << __FUNCTION__;
+ CHECKinitialized_();
+ int32_t ok = audio_device_->GetPlayoutUnderrunCount();
+ RTC_LOG(INFO) << "output: " << ok;
+ return ok;
+ }
+
#if defined(WEBRTC_IOS)
int AudioDeviceModuleIOS::GetPlayoutAudioParameters(
AudioParameters* params) const {