From d4404c232d4d4e8c0866e4b58adb1f6e4d4fa8f4 Mon Sep 17 00:00:00 2001 From: Fredrik Solenberg Date: Mon, 2 Oct 2017 15:08:56 +0000 Subject: [PATCH] Revert "Remove AudioDeviceObserver and make ADM not inherit from the Module interface." This reverts commit 34cdd2d402b08aee4e17a6fd38c87e0e5cd7aa30. Reason for revert: Breaks Chromium Original change's description: > Remove AudioDeviceObserver and make ADM not inherit from the Module interface. > > (Re-upload of https://codereview.webrtc.org/3020493002/) > > Bug: webrtc:4690, webrtc:7306 > Change-Id: I67fb9ebca1296aabc08eae8a292a5c69832dc35e > Reviewed-on: https://webrtc-review.googlesource.com/5360 > Commit-Queue: Fredrik Solenberg > Reviewed-by: Henrik Andreassson > Cr-Commit-Position: refs/heads/master@{#20083} TBR=solenberg@webrtc.org,henrika@webrtc.org Change-Id: Iad03cafb7865f5a22394c3d4d1d3ff3e0fccd4ff No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: webrtc:4690, webrtc:7306 Reviewed-on: https://webrtc-review.googlesource.com/5402 Reviewed-by: Fredrik Solenberg Commit-Queue: Fredrik Solenberg Cr-Commit-Position: refs/heads/master@{#20085} --- call/call_unittest.cc | 4 + media/engine/apm_helpers_unittest.cc | 5 + media/engine/webrtcvoiceengine_unittest.cc | 3 + .../android/audio_device_template.h | 24 +++++ modules/audio_device/audio_device_buffer.h | 2 + modules/audio_device/audio_device_config.h | 1 + .../audio_device_data_observer.cc | 9 ++ modules/audio_device/audio_device_generic.h | 9 ++ modules/audio_device/audio_device_impl.cc | 89 ++++++++++++++++- modules/audio_device/audio_device_impl.h | 8 ++ .../audio_device/dummy/audio_device_dummy.cc | 16 +++ .../audio_device/dummy/audio_device_dummy.h | 9 ++ .../audio_device/dummy/file_audio_device.cc | 16 +++ .../audio_device/dummy/file_audio_device.h | 9 ++ modules/audio_device/include/audio_device.h | 14 ++- .../include/audio_device_defines.h | 16 +++ .../audio_device/include/fake_audio_device.h | 3 + .../audio_device/include/mock_audio_device.h | 8 +- modules/audio_device/ios/audio_device_ios.h | 8 ++ .../ios/audio_device_not_implemented_ios.mm | 16 +++ .../linux/audio_device_alsa_linux.cc | 60 +++++++++++- .../linux/audio_device_alsa_linux.h | 14 +++ .../linux/audio_device_pulse_linux.cc | 66 ++++++++++++- .../linux/audio_device_pulse_linux.h | 13 +++ modules/audio_device/mac/audio_device_mac.cc | 49 ++++++++++ modules/audio_device/mac/audio_device_mac.h | 14 +++ .../audio_device/win/audio_device_core_win.cc | 97 ++++++++++++++++++- .../audio_device/win/audio_device_core_win.h | 15 +++ modules/include/module.h | 18 ++++ pc/test/fakeaudiocapturemodule.cc | 32 +++++- pc/test/fakeaudiocapturemodule.h | 13 +++ pc/test/fakeaudiocapturemodule_unittest.cc | 8 ++ test/mock_voice_engine.h | 2 + voice_engine/voe_base_impl.cc | 35 +++++++ voice_engine/voe_base_impl.h | 7 +- 35 files changed, 698 insertions(+), 14 deletions(-) diff --git a/call/call_unittest.cc b/call/call_unittest.cc index d614de666c..b1ebb823a8 100644 --- a/call/call_unittest.cc +++ b/call/call_unittest.cc @@ -432,6 +432,10 @@ TEST(CallBitrateTest, TEST(CallTest, RecreatingAudioStreamWithSameSsrcReusesRtpState) { constexpr uint32_t kSSRC = 12345; testing::NiceMock mock_adm; + // Reply with a 10ms timer every time TimeUntilNextProcess is called to + // avoid entering a tight loop on the process thread. + EXPECT_CALL(mock_adm, TimeUntilNextProcess()) + .WillRepeatedly(testing::Return(10)); rtc::scoped_refptr mock_mixer( new rtc::RefCountedObject); diff --git a/media/engine/apm_helpers_unittest.cc b/media/engine/apm_helpers_unittest.cc index ff9308a47e..315cdc8553 100644 --- a/media/engine/apm_helpers_unittest.cc +++ b/media/engine/apm_helpers_unittest.cc @@ -25,6 +25,11 @@ constexpr AgcConfig kDefaultAgcConfig = { 3, 9, true }; struct TestHelper { TestHelper() { + // Reply with a 10ms timer every time TimeUntilNextProcess is called to + // avoid entering a tight loop on the process thread. + EXPECT_CALL(mock_audio_device_, TimeUntilNextProcess()) + .WillRepeatedly(testing::Return(10)); + // This replicates the conditions from voe_auto_test. Config config; config.Set(new ExperimentalAgc(false)); diff --git a/media/engine/webrtcvoiceengine_unittest.cc b/media/engine/webrtcvoiceengine_unittest.cc index cf155e5869..30396d970d 100644 --- a/media/engine/webrtcvoiceengine_unittest.cc +++ b/media/engine/webrtcvoiceengine_unittest.cc @@ -3341,6 +3341,9 @@ TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) { testing::NiceMock adm; EXPECT_CALL(adm, AddRef()).Times(3).WillRepeatedly(Return(0)); EXPECT_CALL(adm, Release()).Times(3).WillRepeatedly(Return(0)); + // Return 100ms just in case this function gets called. If we don't, + // we could enter a tight loop since the mock would return 0. + EXPECT_CALL(adm, TimeUntilNextProcess()).WillRepeatedly(Return(100)); { rtc::scoped_refptr apm = webrtc::AudioProcessing::Create(); diff --git a/modules/audio_device/android/audio_device_template.h b/modules/audio_device/android/audio_device_template.h index df9efeeb49..8d69cb4aa7 100644 --- a/modules/audio_device/android/audio_device_template.h +++ b/modules/audio_device/android/audio_device_template.h @@ -391,6 +391,30 @@ class AudioDeviceTemplate : public AudioDeviceGeneric { return 0; } + bool PlayoutWarning() const override { + return false; + } + + bool PlayoutError() const override { + return false; + } + + bool RecordingWarning() const override { + return false; + } + + bool RecordingError() const override { + return false; + } + + void ClearPlayoutWarning() override { LOG(INFO) << __FUNCTION__; } + + void ClearPlayoutError() override { LOG(INFO) << __FUNCTION__; } + + void ClearRecordingWarning() override { LOG(INFO) << __FUNCTION__; } + + void ClearRecordingError() override { LOG(INFO) << __FUNCTION__; } + void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override { LOG(INFO) << __FUNCTION__; output_.AttachAudioBuffer(audioBuffer); diff --git a/modules/audio_device/audio_device_buffer.h b/modules/audio_device/audio_device_buffer.h index b59779dfe2..d0eda40957 100644 --- a/modules/audio_device/audio_device_buffer.h +++ b/modules/audio_device/audio_device_buffer.h @@ -28,6 +28,8 @@ const size_t kMaxDeltaTimeInMs = 500; // TODO(henrika): remove when no longer used by external client. const size_t kMaxBufferSizeBytes = 3840; // 10ms in stereo @ 96kHz +class AudioDeviceObserver; + class AudioDeviceBuffer { public: enum LogState { diff --git a/modules/audio_device/audio_device_config.h b/modules/audio_device/audio_device_config.h index 2d1e94fbd4..e2ad1fd547 100644 --- a/modules/audio_device/audio_device_config.h +++ b/modules/audio_device/audio_device_config.h @@ -13,6 +13,7 @@ // Enumerators // +enum { kAdmMaxIdleTimeProcess = 1000 }; enum { GET_MIC_VOLUME_INTERVAL_MS = 1000 }; // Platform specifics diff --git a/modules/audio_device/audio_device_data_observer.cc b/modules/audio_device/audio_device_data_observer.cc index a72fb8dc11..a03c7fad11 100644 --- a/modules/audio_device/audio_device_data_observer.cc +++ b/modules/audio_device/audio_device_data_observer.cc @@ -36,6 +36,12 @@ class ADMWrapper : public AudioDeviceModule, public AudioTransport { // Make sure we have a valid ADM before returning it to user. bool IsValid() { return is_valid_; } + // RefCountedModule methods overrides. + int64_t TimeUntilNextProcess() override { + return impl_->TimeUntilNextProcess(); + } + void Process() override { return impl_->Process(); } + // AudioTransport methods overrides. int32_t RecordedDataIsAvailable(const void* audioSamples, const size_t nSamples, @@ -121,6 +127,9 @@ class ADMWrapper : public AudioDeviceModule, public AudioTransport { return impl_->ActiveAudioLayer(audio_layer); } ErrorCode LastError() const override { return impl_->LastError(); } + int32_t RegisterEventObserver(AudioDeviceObserver* event_callback) override { + return impl_->RegisterEventObserver(event_callback); + } int32_t Init() override { return impl_->Init(); } int32_t Terminate() override { return impl_->Terminate(); } bool Initialized() const override { return impl_->Initialized(); } diff --git a/modules/audio_device/audio_device_generic.h b/modules/audio_device/audio_device_generic.h index 5679bd4093..8f3e9dc004 100644 --- a/modules/audio_device/audio_device_generic.h +++ b/modules/audio_device/audio_device_generic.h @@ -142,6 +142,15 @@ class AudioDeviceGeneric { virtual int GetRecordAudioParameters(AudioParameters* params) const; #endif // WEBRTC_IOS + virtual bool PlayoutWarning() const = 0; + virtual bool PlayoutError() const = 0; + virtual bool RecordingWarning() const = 0; + virtual bool RecordingError() const = 0; + virtual void ClearPlayoutWarning() = 0; + virtual void ClearPlayoutError() = 0; + virtual void ClearRecordingWarning() = 0; + virtual void ClearRecordingError() = 0; + virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) = 0; virtual ~AudioDeviceGeneric() {} diff --git a/modules/audio_device/audio_device_impl.cc b/modules/audio_device/audio_device_impl.cc index 5701f5948a..820d5f6a46 100644 --- a/modules/audio_device/audio_device_impl.cc +++ b/modules/audio_device/audio_device_impl.cc @@ -116,9 +116,11 @@ rtc::scoped_refptr AudioDeviceModule::Create( AudioDeviceModuleImpl::AudioDeviceModuleImpl(const int32_t id, const AudioLayer audioLayer) - : _ptrAudioDevice(NULL), + : _ptrCbAudioDeviceObserver(NULL), + _ptrAudioDevice(NULL), _id(id), _platformAudioLayer(audioLayer), + _lastProcessTime(rtc::TimeMillis()), _platformType(kPlatformNotSupported), _initialized(false), _lastError(kAdmErrNone) { @@ -357,6 +359,78 @@ AudioDeviceModuleImpl::~AudioDeviceModuleImpl() { } } +// ============================================================================ +// Module +// ============================================================================ + +// ---------------------------------------------------------------------------- +// Module::TimeUntilNextProcess +// +// Returns the number of milliseconds until the module want a worker thread +// to call Process(). +// ---------------------------------------------------------------------------- + +int64_t AudioDeviceModuleImpl::TimeUntilNextProcess() { + int64_t now = rtc::TimeMillis(); + int64_t deltaProcess = kAdmMaxIdleTimeProcess - (now - _lastProcessTime); + return deltaProcess; +} + +// ---------------------------------------------------------------------------- +// Module::Process +// +// Check for posted error and warning reports. Generate callbacks if +// new reports exists. +// ---------------------------------------------------------------------------- + +void AudioDeviceModuleImpl::Process() { + _lastProcessTime = rtc::TimeMillis(); + + // kPlayoutWarning + if (_ptrAudioDevice->PlayoutWarning()) { + rtc::CritScope lock(&_critSectEventCb); + if (_ptrCbAudioDeviceObserver) { + LOG(WARNING) << "=> OnWarningIsReported(kPlayoutWarning)"; + _ptrCbAudioDeviceObserver->OnWarningIsReported( + AudioDeviceObserver::kPlayoutWarning); + } + _ptrAudioDevice->ClearPlayoutWarning(); + } + + // kPlayoutError + if (_ptrAudioDevice->PlayoutError()) { + rtc::CritScope lock(&_critSectEventCb); + if (_ptrCbAudioDeviceObserver) { + LOG(LERROR) << "=> OnErrorIsReported(kPlayoutError)"; + _ptrCbAudioDeviceObserver->OnErrorIsReported( + AudioDeviceObserver::kPlayoutError); + } + _ptrAudioDevice->ClearPlayoutError(); + } + + // kRecordingWarning + if (_ptrAudioDevice->RecordingWarning()) { + rtc::CritScope lock(&_critSectEventCb); + if (_ptrCbAudioDeviceObserver) { + LOG(WARNING) << "=> OnWarningIsReported(kRecordingWarning)"; + _ptrCbAudioDeviceObserver->OnWarningIsReported( + AudioDeviceObserver::kRecordingWarning); + } + _ptrAudioDevice->ClearRecordingWarning(); + } + + // kRecordingError + if (_ptrAudioDevice->RecordingError()) { + rtc::CritScope lock(&_critSectEventCb); + if (_ptrCbAudioDeviceObserver) { + LOG(LERROR) << "=> OnErrorIsReported(kRecordingError)"; + _ptrCbAudioDeviceObserver->OnErrorIsReported( + AudioDeviceObserver::kRecordingError); + } + _ptrAudioDevice->ClearRecordingError(); + } +} + // ============================================================================ // Public API // ============================================================================ @@ -1246,6 +1320,19 @@ bool AudioDeviceModuleImpl::Recording() const { return (_ptrAudioDevice->Recording()); } +// ---------------------------------------------------------------------------- +// RegisterEventObserver +// ---------------------------------------------------------------------------- + +int32_t AudioDeviceModuleImpl::RegisterEventObserver( + AudioDeviceObserver* eventCallback) { + LOG(INFO) << __FUNCTION__; + rtc::CritScope lock(&_critSectEventCb); + _ptrCbAudioDeviceObserver = eventCallback; + + return 0; +} + // ---------------------------------------------------------------------------- // RegisterAudioCallback // ---------------------------------------------------------------------------- diff --git a/modules/audio_device/audio_device_impl.h b/modules/audio_device/audio_device_impl.h index 864ab48833..77c4cc51a2 100644 --- a/modules/audio_device/audio_device_impl.h +++ b/modules/audio_device/audio_device_impl.h @@ -44,11 +44,15 @@ class AudioDeviceModuleImpl : public AudioDeviceModule { AudioDeviceModuleImpl(const int32_t id, const AudioLayer audioLayer); ~AudioDeviceModuleImpl() override; + int64_t TimeUntilNextProcess() override; + void Process() override; + // Retrieve the currently utilized audio layer int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override; // Error handling ErrorCode LastError() const override; + int32_t RegisterEventObserver(AudioDeviceObserver* eventCallback) override; // Full-duplex transportation of PCM audio int32_t RegisterAudioCallback(AudioTransport* audioCallback) override; @@ -174,8 +178,11 @@ class AudioDeviceModuleImpl : public AudioDeviceModule { AudioLayer PlatformAudioLayer() const; rtc::CriticalSection _critSect; + rtc::CriticalSection _critSectEventCb; rtc::CriticalSection _critSectAudioCb; + AudioDeviceObserver* _ptrCbAudioDeviceObserver; + AudioDeviceGeneric* _ptrAudioDevice; AudioDeviceBuffer _audioDeviceBuffer; @@ -184,6 +191,7 @@ class AudioDeviceModuleImpl : public AudioDeviceModule { #endif int32_t _id; AudioLayer _platformAudioLayer; + int64_t _lastProcessTime; PlatformType _platformType; bool _initialized; mutable ErrorCode _lastError; diff --git a/modules/audio_device/dummy/audio_device_dummy.cc b/modules/audio_device/dummy/audio_device_dummy.cc index 7ababa1667..cb41e4a9b8 100644 --- a/modules/audio_device/dummy/audio_device_dummy.cc +++ b/modules/audio_device/dummy/audio_device_dummy.cc @@ -158,5 +158,21 @@ int32_t AudioDeviceDummy::PlayoutDelay(uint16_t& delayMS) const { return -1; } int32_t AudioDeviceDummy::RecordingDelay(uint16_t& delayMS) const { return -1; } +bool AudioDeviceDummy::PlayoutWarning() const { return false; } + +bool AudioDeviceDummy::PlayoutError() const { return false; } + +bool AudioDeviceDummy::RecordingWarning() const { return false; } + +bool AudioDeviceDummy::RecordingError() const { return false; } + +void AudioDeviceDummy::ClearPlayoutWarning() {} + +void AudioDeviceDummy::ClearPlayoutError() {} + +void AudioDeviceDummy::ClearRecordingWarning() {} + +void AudioDeviceDummy::ClearRecordingError() {} + void AudioDeviceDummy::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {} } // namespace webrtc diff --git a/modules/audio_device/dummy/audio_device_dummy.h b/modules/audio_device/dummy/audio_device_dummy.h index 5e2cecfa8f..5865720183 100644 --- a/modules/audio_device/dummy/audio_device_dummy.h +++ b/modules/audio_device/dummy/audio_device_dummy.h @@ -111,6 +111,15 @@ class AudioDeviceDummy : public AudioDeviceGeneric { int32_t PlayoutDelay(uint16_t& delayMS) const override; int32_t RecordingDelay(uint16_t& delayMS) const override; + bool PlayoutWarning() const override; + bool PlayoutError() const override; + bool RecordingWarning() const override; + bool RecordingError() const override; + void ClearPlayoutWarning() override; + void ClearPlayoutError() override; + void ClearRecordingWarning() override; + void ClearRecordingError() override; + void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override; }; diff --git a/modules/audio_device/dummy/file_audio_device.cc b/modules/audio_device/dummy/file_audio_device.cc index 7b3e17ec75..2194748564 100644 --- a/modules/audio_device/dummy/file_audio_device.cc +++ b/modules/audio_device/dummy/file_audio_device.cc @@ -398,6 +398,22 @@ int32_t FileAudioDevice::PlayoutDelay(uint16_t& delayMS) const { int32_t FileAudioDevice::RecordingDelay(uint16_t& delayMS) const { return -1; } +bool FileAudioDevice::PlayoutWarning() const { return false; } + +bool FileAudioDevice::PlayoutError() const { return false; } + +bool FileAudioDevice::RecordingWarning() const { return false; } + +bool FileAudioDevice::RecordingError() const { return false; } + +void FileAudioDevice::ClearPlayoutWarning() {} + +void FileAudioDevice::ClearPlayoutError() {} + +void FileAudioDevice::ClearRecordingWarning() {} + +void FileAudioDevice::ClearRecordingError() {} + void FileAudioDevice::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { rtc::CritScope lock(&_critSect); diff --git a/modules/audio_device/dummy/file_audio_device.h b/modules/audio_device/dummy/file_audio_device.h index 11ed8b8d65..61e717ef42 100644 --- a/modules/audio_device/dummy/file_audio_device.h +++ b/modules/audio_device/dummy/file_audio_device.h @@ -132,6 +132,15 @@ class FileAudioDevice : public AudioDeviceGeneric { int32_t PlayoutDelay(uint16_t& delayMS) const override; int32_t RecordingDelay(uint16_t& delayMS) const override; + bool PlayoutWarning() const override; + bool PlayoutError() const override; + bool RecordingWarning() const override; + bool RecordingError() const override; + void ClearPlayoutWarning() override; + void ClearPlayoutError() override; + void ClearRecordingWarning() override; + void ClearRecordingError() override; + void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override; private: diff --git a/modules/audio_device/include/audio_device.h b/modules/audio_device/include/audio_device.h index 55e6421a75..b378f9ffce 100644 --- a/modules/audio_device/include/audio_device.h +++ b/modules/audio_device/include/audio_device.h @@ -12,12 +12,12 @@ #define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_H_ #include "modules/audio_device/include/audio_device_defines.h" +#include "modules/include/module.h" #include "rtc_base/scoped_ref_ptr.h" -#include "rtc_base/refcount.h" namespace webrtc { -class AudioDeviceModule : public rtc::RefCountInterface { +class AudioDeviceModule : public RefCountedModule { public: enum ErrorCode { kAdmErrNone = 0, @@ -52,11 +52,21 @@ class AudioDeviceModule : public rtc::RefCountInterface { const int32_t id, const AudioLayer audio_layer); + // TODO(solenberg): Remove temporary implementation of Module interface. + int64_t TimeUntilNextProcess() override { + // Make sure Process() isn't called very often. + return 1000000; + } + void Process() override {} + // Retrieve the currently utilized audio layer virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const = 0; // Error handling virtual ErrorCode LastError() const = 0; + virtual int32_t RegisterEventObserver(AudioDeviceObserver* eventCallback) { + return 0; + } // Full-duplex transportation of PCM audio virtual int32_t RegisterAudioCallback(AudioTransport* audioCallback) = 0; diff --git a/modules/audio_device/include/audio_device_defines.h b/modules/audio_device/include/audio_device_defines.h index 510b07c049..133819ac33 100644 --- a/modules/audio_device/include/audio_device_defines.h +++ b/modules/audio_device/include/audio_device_defines.h @@ -24,6 +24,22 @@ static const int kAdmMaxGuidSize = 128; static const int kAdmMinPlayoutBufferSizeMs = 10; static const int kAdmMaxPlayoutBufferSizeMs = 250; +// ---------------------------------------------------------------------------- +// AudioDeviceObserver +// ---------------------------------------------------------------------------- + +class AudioDeviceObserver { + public: + enum ErrorCode { kRecordingError = 0, kPlayoutError = 1 }; + enum WarningCode { kRecordingWarning = 0, kPlayoutWarning = 1 }; + + virtual void OnErrorIsReported(const ErrorCode error) = 0; + virtual void OnWarningIsReported(const WarningCode warning) = 0; + + protected: + virtual ~AudioDeviceObserver() {} +}; + // ---------------------------------------------------------------------------- // AudioTransport // ---------------------------------------------------------------------------- diff --git a/modules/audio_device/include/fake_audio_device.h b/modules/audio_device/include/fake_audio_device.h index e6f05e7b81..c6765dfc03 100644 --- a/modules/audio_device/include/fake_audio_device.h +++ b/modules/audio_device/include/fake_audio_device.h @@ -23,6 +23,9 @@ class FakeAudioDeviceModule : public AudioDeviceModule { virtual int32_t Release() const { return 0; } private: + virtual int32_t RegisterEventObserver(AudioDeviceObserver* eventCallback) { + return 0; + } virtual int32_t RegisterAudioCallback(AudioTransport* audioCallback) { return 0; } diff --git a/modules/audio_device/include/mock_audio_device.h b/modules/audio_device/include/mock_audio_device.h index 87dc64cd07..f824eeb72b 100644 --- a/modules/audio_device/include/mock_audio_device.h +++ b/modules/audio_device/include/mock_audio_device.h @@ -21,12 +21,18 @@ namespace test { class MockAudioDeviceModule : public AudioDeviceModule { public: - // RefCounted + // Module. + MOCK_METHOD0(TimeUntilNextProcess, int64_t()); + MOCK_METHOD0(Process, void()); + MOCK_METHOD1(ProcessThreadAttached, void(ProcessThread*)); + // RefCountedModule. MOCK_CONST_METHOD0(AddRef, int32_t()); MOCK_CONST_METHOD0(Release, int32_t()); // AudioDeviceModule. MOCK_CONST_METHOD1(ActiveAudioLayer, int32_t(AudioLayer* audioLayer)); MOCK_CONST_METHOD0(LastError, ErrorCode()); + MOCK_METHOD1(RegisterEventObserver, + int32_t(AudioDeviceObserver* eventCallback)); MOCK_METHOD1(RegisterAudioCallback, int32_t(AudioTransport* audioCallback)); MOCK_METHOD0(Init, int32_t()); MOCK_METHOD0(Terminate, int32_t()); diff --git a/modules/audio_device/ios/audio_device_ios.h b/modules/audio_device/ios/audio_device_ios.h index 8235bf0d1b..caaeaace00 100644 --- a/modules/audio_device/ios/audio_device_ios.h +++ b/modules/audio_device/ios/audio_device_ios.h @@ -139,6 +139,14 @@ class AudioDeviceIOS : public AudioDeviceGeneric, int32_t StereoRecordingIsAvailable(bool& available) override; int32_t SetStereoRecording(bool enable) override; int32_t StereoRecording(bool& enabled) const override; + bool PlayoutWarning() const override; + bool PlayoutError() const override; + bool RecordingWarning() const override; + bool RecordingError() const override; + void ClearPlayoutWarning() override {} + void ClearPlayoutError() override {} + void ClearRecordingWarning() override {} + void ClearRecordingError() override {} // AudioSessionObserver methods. May be called from any thread. void OnInterruptionBegin() override; diff --git a/modules/audio_device/ios/audio_device_not_implemented_ios.mm b/modules/audio_device/ios/audio_device_not_implemented_ios.mm index 6dfc02bfe4..dee3cc7119 100644 --- a/modules/audio_device/ios/audio_device_not_implemented_ios.mm +++ b/modules/audio_device/ios/audio_device_not_implemented_ios.mm @@ -91,6 +91,22 @@ int32_t AudioDeviceIOS::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType) { return -1; } +bool AudioDeviceIOS::PlayoutWarning() const { + return false; +} + +bool AudioDeviceIOS::PlayoutError() const { + return false; +} + +bool AudioDeviceIOS::RecordingWarning() const { + return false; +} + +bool AudioDeviceIOS::RecordingError() const { + return false; +} + int32_t AudioDeviceIOS::InitMicrophone() { return 0; } diff --git a/modules/audio_device/linux/audio_device_alsa_linux.cc b/modules/audio_device/linux/audio_device_alsa_linux.cc index bfddf3d8c1..f271606dfd 100644 --- a/modules/audio_device/linux/audio_device_alsa_linux.cc +++ b/modules/audio_device/linux/audio_device_alsa_linux.cc @@ -89,7 +89,11 @@ AudioDeviceLinuxALSA::AudioDeviceLinuxALSA() : _playIsInitialized(false), _AGC(false), _recordingDelay(0), - _playoutDelay(0) + _playoutDelay(0), + _playWarning(0), + _playError(0), + _recWarning(0), + _recError(0) { memset(_oldKeyState, 0, sizeof(_oldKeyState)); LOG(LS_INFO) << __FUNCTION__ << " created"; @@ -162,6 +166,10 @@ AudioDeviceGeneric::InitStatus AudioDeviceLinuxALSA::Init() { << "failed to open X display, typing detection will not work"; } #endif + _playWarning = 0; + _playError = 0; + _recWarning = 0; + _recError = 0; _initialized = true; @@ -1006,6 +1014,8 @@ int32_t AudioDeviceLinuxALSA::InitPlayout() _handlePlayout, _playoutFramesIn10MS); // Init varaibles used for play + _playWarning = 0; + _playError = 0; if (_handlePlayout != NULL) { @@ -1437,6 +1447,54 @@ bool AudioDeviceLinuxALSA::Playing() const return (_playing); } +bool AudioDeviceLinuxALSA::PlayoutWarning() const +{ + rtc::CritScope lock(&_critSect); + return (_playWarning > 0); +} + +bool AudioDeviceLinuxALSA::PlayoutError() const +{ + rtc::CritScope lock(&_critSect); + return (_playError > 0); +} + +bool AudioDeviceLinuxALSA::RecordingWarning() const +{ + rtc::CritScope lock(&_critSect); + return (_recWarning > 0); +} + +bool AudioDeviceLinuxALSA::RecordingError() const +{ + rtc::CritScope lock(&_critSect); + return (_recError > 0); +} + +void AudioDeviceLinuxALSA::ClearPlayoutWarning() +{ + rtc::CritScope lock(&_critSect); + _playWarning = 0; +} + +void AudioDeviceLinuxALSA::ClearPlayoutError() +{ + rtc::CritScope lock(&_critSect); + _playError = 0; +} + +void AudioDeviceLinuxALSA::ClearRecordingWarning() +{ + rtc::CritScope lock(&_critSect); + _recWarning = 0; +} + +void AudioDeviceLinuxALSA::ClearRecordingError() +{ + rtc::CritScope lock(&_critSect); + _recError = 0; +} + // ============================================================================ // Private Methods // ============================================================================ diff --git a/modules/audio_device/linux/audio_device_alsa_linux.h b/modules/audio_device/linux/audio_device_alsa_linux.h index 05871f0365..96a15ebac1 100644 --- a/modules/audio_device/linux/audio_device_alsa_linux.h +++ b/modules/audio_device/linux/audio_device_alsa_linux.h @@ -125,6 +125,15 @@ public: int32_t PlayoutDelay(uint16_t& delayMS) const override; int32_t RecordingDelay(uint16_t& delayMS) const override; + bool PlayoutWarning() const override; + bool PlayoutError() const override; + bool RecordingWarning() const override; + bool RecordingError() const override; + void ClearPlayoutWarning() override; + void ClearPlayoutError() override; + void ClearRecordingWarning() override; + void ClearRecordingError() override; + void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override; private: @@ -197,6 +206,11 @@ private: snd_pcm_sframes_t _recordingDelay; snd_pcm_sframes_t _playoutDelay; + uint16_t _playWarning; + uint16_t _playError; + uint16_t _recWarning; + uint16_t _recError; + char _oldKeyState[32]; #if defined(USE_X11) Display* _XDisplay; diff --git a/modules/audio_device/linux/audio_device_pulse_linux.cc b/modules/audio_device/linux/audio_device_pulse_linux.cc index 9b16c696a7..ecbe63b97a 100644 --- a/modules/audio_device/linux/audio_device_pulse_linux.cc +++ b/modules/audio_device/linux/audio_device_pulse_linux.cc @@ -54,6 +54,10 @@ AudioDeviceLinuxPulse::AudioDeviceLinuxPulse() _sndCardPlayDelay(0), _sndCardRecDelay(0), _writeErrors(0), + _playWarning(0), + _playError(0), + _recWarning(0), + _recError(0), _deviceIndex(-1), _numPlayDevices(0), _numRecDevices(0), @@ -156,6 +160,11 @@ AudioDeviceGeneric::InitStatus AudioDeviceLinuxPulse::Init() { return InitStatus::OTHER_ERROR; } + _playWarning = 0; + _playError = 0; + _recWarning = 0; + _recError = 0; + // Get X display handle for typing detection _XDisplay = XOpenDisplay(NULL); if (!_XDisplay) { @@ -1283,6 +1292,46 @@ bool AudioDeviceLinuxPulse::Playing() const { return (_playing); } +bool AudioDeviceLinuxPulse::PlayoutWarning() const { + rtc::CritScope lock(&_critSect); + return (_playWarning > 0); +} + +bool AudioDeviceLinuxPulse::PlayoutError() const { + rtc::CritScope lock(&_critSect); + return (_playError > 0); +} + +bool AudioDeviceLinuxPulse::RecordingWarning() const { + rtc::CritScope lock(&_critSect); + return (_recWarning > 0); +} + +bool AudioDeviceLinuxPulse::RecordingError() const { + rtc::CritScope lock(&_critSect); + return (_recError > 0); +} + +void AudioDeviceLinuxPulse::ClearPlayoutWarning() { + rtc::CritScope lock(&_critSect); + _playWarning = 0; +} + +void AudioDeviceLinuxPulse::ClearPlayoutError() { + rtc::CritScope lock(&_critSect); + _playError = 0; +} + +void AudioDeviceLinuxPulse::ClearRecordingWarning() { + rtc::CritScope lock(&_critSect); + _recWarning = 0; +} + +void AudioDeviceLinuxPulse::ClearRecordingError() { + rtc::CritScope lock(&_critSect); + _recError = 0; +} + // ============================================================================ // Private Methods // ============================================================================ @@ -2142,7 +2191,12 @@ bool AudioDeviceLinuxPulse::PlayThreadProcess() { NULL, (int64_t)0, PA_SEEK_RELATIVE) != PA_OK) { _writeErrors++; if (_writeErrors > 10) { - LOG(LS_ERROR) << "Playout error: _writeErrors=" + if (_playError == 1) { + LOG(LS_WARNING) << "pending playout error exists"; + } + // Triggers callback from module process thread. + _playError = 1; + LOG(LS_ERROR) << "kPlayoutError message posted: _writeErrors=" << _writeErrors << ", error=" << LATE(pa_context_errno)(_paContext); _writeErrors = 0; @@ -2186,7 +2240,12 @@ bool AudioDeviceLinuxPulse::PlayThreadProcess() { NULL, (int64_t)0, PA_SEEK_RELATIVE) != PA_OK) { _writeErrors++; if (_writeErrors > 10) { - LOG(LS_ERROR) << "Playout error: _writeErrors=" + if (_playError == 1) { + LOG(LS_WARNING) << "pending playout error exists"; + } + // Triggers callback from module process thread. + _playError = 1; + LOG(LS_ERROR) << "kPlayoutError message posted: _writeErrors=" << _writeErrors << ", error=" << LATE(pa_context_errno)(_paContext); _writeErrors = 0; @@ -2299,7 +2358,8 @@ bool AudioDeviceLinuxPulse::RecThreadProcess() { size_t sampleDataSize; if (LATE(pa_stream_peek)(_recStream, &sampleData, &sampleDataSize) != 0) { - LOG(LS_ERROR) << "RECORD_ERROR, error = " + _recError = 1; // triggers callback from module process thread + LOG(LS_ERROR) << "RECORD_ERROR message posted, error = " << LATE(pa_context_errno)(_paContext); break; } diff --git a/modules/audio_device/linux/audio_device_pulse_linux.h b/modules/audio_device/linux/audio_device_pulse_linux.h index 6fd571773f..2a83b5c0a3 100644 --- a/modules/audio_device/linux/audio_device_pulse_linux.h +++ b/modules/audio_device/linux/audio_device_pulse_linux.h @@ -187,6 +187,15 @@ public: int32_t PlayoutDelay(uint16_t& delayMS) const override; int32_t RecordingDelay(uint16_t& delayMS) const override; + bool PlayoutWarning() const override; + bool PlayoutError() const override; + bool RecordingWarning() const override; + bool RecordingError() const override; + void ClearPlayoutWarning() override; + void ClearPlayoutError() override; + void ClearRecordingWarning() override; + void ClearRecordingError() override; + void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override; private: @@ -297,6 +306,10 @@ private: uint32_t _sndCardRecDelay; int32_t _writeErrors; + uint16_t _playWarning; + uint16_t _playError; + uint16_t _recWarning; + uint16_t _recError; uint16_t _deviceIndex; int16_t _numPlayDevices; diff --git a/modules/audio_device/mac/audio_device_mac.cc b/modules/audio_device/mac/audio_device_mac.cc index fdeaaf13a2..d139ee1a5c 100644 --- a/modules/audio_device/mac/audio_device_mac.cc +++ b/modules/audio_device/mac/audio_device_mac.cc @@ -144,6 +144,10 @@ AudioDeviceMac::AudioDeviceMac() _captureDelayUs(0), _renderDelayUs(0), _renderDelayOffsetSamples(0), + _playWarning(0), + _playError(0), + _recWarning(0), + _recError(0), _paCaptureBuffer(NULL), _paRenderBuffer(NULL), _captureBufSizeSamples(0), @@ -336,6 +340,11 @@ AudioDeviceGeneric::InitStatus AudioDeviceMac::Init() { } } + _playWarning = 0; + _playError = 0; + _recWarning = 0; + _recError = 0; + get_mic_volume_counter_ms_ = 0; _initialized = true; @@ -1564,6 +1573,38 @@ bool AudioDeviceMac::Playing() const { return (_playing); } +bool AudioDeviceMac::PlayoutWarning() const { + return (_playWarning > 0); +} + +bool AudioDeviceMac::PlayoutError() const { + return (_playError > 0); +} + +bool AudioDeviceMac::RecordingWarning() const { + return (_recWarning > 0); +} + +bool AudioDeviceMac::RecordingError() const { + return (_recError > 0); +} + +void AudioDeviceMac::ClearPlayoutWarning() { + _playWarning = 0; +} + +void AudioDeviceMac::ClearPlayoutError() { + _playError = 0; +} + +void AudioDeviceMac::ClearRecordingWarning() { + _recWarning = 0; +} + +void AudioDeviceMac::ClearRecordingError() { + _recError = 0; +} + // ============================================================================ // Private Methods // ============================================================================ @@ -1976,6 +2017,10 @@ int32_t AudioDeviceMac::HandleDeviceChange() { LOG(LS_WARNING) << "Capture device is not alive (probably removed)"; AtomicSet32(&_captureDeviceIsAlive, 0); _mixerManager.CloseMicrophone(); + if (_recError == 1) { + LOG(LS_WARNING) << "pending recording error exists"; + } + _recError = 1; // triggers callback from module process thread } else if (err != noErr) { logCAMsg(rtc::LS_ERROR, "Error in AudioDeviceGetProperty()", (const char*)&err); @@ -1995,6 +2040,10 @@ int32_t AudioDeviceMac::HandleDeviceChange() { LOG(LS_WARNING) << "Render device is not alive (probably removed)"; AtomicSet32(&_renderDeviceIsAlive, 0); _mixerManager.CloseSpeaker(); + if (_playError == 1) { + LOG(LS_WARNING) << "pending playout error exists"; + } + _playError = 1; // triggers callback from module process thread } else if (err != noErr) { logCAMsg(rtc::LS_ERROR, "Error in AudioDeviceGetProperty()", (const char*)&err); diff --git a/modules/audio_device/mac/audio_device_mac.h b/modules/audio_device/mac/audio_device_mac.h index c72b741baf..db8cfea50e 100644 --- a/modules/audio_device/mac/audio_device_mac.h +++ b/modules/audio_device/mac/audio_device_mac.h @@ -153,6 +153,15 @@ class AudioDeviceMac : public AudioDeviceGeneric { virtual int32_t PlayoutDelay(uint16_t& delayMS) const; virtual int32_t RecordingDelay(uint16_t& delayMS) const; + virtual bool PlayoutWarning() const; + virtual bool PlayoutError() const; + virtual bool RecordingWarning() const; + virtual bool RecordingError() const; + virtual void ClearPlayoutWarning(); + virtual void ClearPlayoutError(); + virtual void ClearRecordingWarning(); + virtual void ClearRecordingError(); + virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); private: @@ -324,6 +333,11 @@ class AudioDeviceMac : public AudioDeviceGeneric { int32_t _renderDelayOffsetSamples; + uint16_t _playWarning; + uint16_t _playError; + uint16_t _recWarning; + uint16_t _recError; + PaUtilRingBuffer* _paCaptureBuffer; PaUtilRingBuffer* _paRenderBuffer; diff --git a/modules/audio_device/win/audio_device_core_win.cc b/modules/audio_device/win/audio_device_core_win.cc index 05f8e6436d..aaf31fc91e 100644 --- a/modules/audio_device/win/audio_device_core_win.cc +++ b/modules/audio_device/win/audio_device_core_win.cc @@ -464,6 +464,10 @@ AudioDeviceWindowsCore::AudioDeviceWindowsCore() _speakerIsInitialized(false), _microphoneIsInitialized(false), _AGC(false), + _playWarning(0), + _playError(0), + _recWarning(0), + _recError(0), _playBufDelay(80), _usingInputDeviceIndex(false), _usingOutputDeviceIndex(false), @@ -681,6 +685,11 @@ AudioDeviceGeneric::InitStatus AudioDeviceWindowsCore::Init() { return InitStatus::OK; } + _playWarning = 0; + _playError = 0; + _recWarning = 0; + _recError = 0; + // Enumerate all audio rendering and capturing endpoint devices. // Note that, some of these will not be able to select by the user. // The complete collection is for internal use only. @@ -3052,6 +3061,78 @@ bool AudioDeviceWindowsCore::Playing() const return (_playing); } +// ---------------------------------------------------------------------------- +// PlayoutWarning +// ---------------------------------------------------------------------------- + +bool AudioDeviceWindowsCore::PlayoutWarning() const +{ + return ( _playWarning > 0); +} + +// ---------------------------------------------------------------------------- +// PlayoutError +// ---------------------------------------------------------------------------- + +bool AudioDeviceWindowsCore::PlayoutError() const +{ + return ( _playError > 0); +} + +// ---------------------------------------------------------------------------- +// RecordingWarning +// ---------------------------------------------------------------------------- + +bool AudioDeviceWindowsCore::RecordingWarning() const +{ + return ( _recWarning > 0); +} + +// ---------------------------------------------------------------------------- +// RecordingError +// ---------------------------------------------------------------------------- + +bool AudioDeviceWindowsCore::RecordingError() const +{ + return ( _recError > 0); +} + +// ---------------------------------------------------------------------------- +// ClearPlayoutWarning +// ---------------------------------------------------------------------------- + +void AudioDeviceWindowsCore::ClearPlayoutWarning() +{ + _playWarning = 0; +} + +// ---------------------------------------------------------------------------- +// ClearPlayoutError +// ---------------------------------------------------------------------------- + +void AudioDeviceWindowsCore::ClearPlayoutError() +{ + _playError = 0; +} + +// ---------------------------------------------------------------------------- +// ClearRecordingWarning +// ---------------------------------------------------------------------------- + +void AudioDeviceWindowsCore::ClearRecordingWarning() +{ + _recWarning = 0; +} + +// ---------------------------------------------------------------------------- +// ClearRecordingError +// ---------------------------------------------------------------------------- + +void AudioDeviceWindowsCore::ClearRecordingError() +{ + _recError = 0; +} + // ============================================================================ // Private Methods // ============================================================================ @@ -3441,8 +3522,11 @@ Exit: _TraceCOMError(hr); } } + // Trigger callback from module process thread + _playError = 1; LOG(LS_ERROR) - << "Playout error: rendering thread has ended pre-maturely"; + << "kPlayoutError message posted: rendering thread has ended" + << " pre-maturely"; } else { @@ -3625,8 +3709,10 @@ DWORD AudioDeviceWindowsCore::DoCaptureThreadPollDMO() if (FAILED(hr)) { - LOG(LS_ERROR) - << "Recording error: capturing thread has ended prematurely"; + // Trigger callback from module process thread + _recError = 1; + LOG(LS_ERROR) << "kRecordingError message posted: capturing thread has" + << " ended prematurely"; } else { @@ -3926,8 +4012,11 @@ Exit: } } + // Trigger callback from module process thread + _recError = 1; LOG(LS_ERROR) - << "Recording error: capturing thread has ended pre-maturely"; + << "kRecordingError message posted: capturing thread has ended" + << " pre-maturely"; } else { diff --git a/modules/audio_device/win/audio_device_core_win.h b/modules/audio_device/win/audio_device_core_win.h index 6d94acefd1..d4bf4738c8 100644 --- a/modules/audio_device/win/audio_device_core_win.h +++ b/modules/audio_device/win/audio_device_core_win.h @@ -177,6 +177,16 @@ public: virtual int32_t EnableBuiltInAEC(bool enable); +public: + virtual bool PlayoutWarning() const; + virtual bool PlayoutError() const; + virtual bool RecordingWarning() const; + virtual bool RecordingError() const; + virtual void ClearPlayoutWarning(); + virtual void ClearPlayoutError(); + virtual void ClearRecordingWarning(); + virtual void ClearRecordingError(); + public: virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); @@ -322,6 +332,11 @@ private: bool _AGC; + uint16_t _playWarning; + uint16_t _playError; + uint16_t _recWarning; + uint16_t _recError; + uint16_t _playBufDelay; uint16_t _newMicLevel; diff --git a/modules/include/module.h b/modules/include/module.h index fc2a1b5fc9..becffb9ad3 100644 --- a/modules/include/module.h +++ b/modules/include/module.h @@ -58,6 +58,24 @@ class Module { protected: virtual ~Module() {} }; + +// Reference counted version of the Module interface. +class RefCountedModule : public Module { + public: + // Increase the reference count by one. + // Returns the incremented reference count. + virtual int32_t AddRef() const = 0; + + // Decrease the reference count by one. + // Returns the decreased reference count. + // Returns 0 if the last reference was just released. + // When the reference count reaches 0 the object will self-destruct. + virtual int32_t Release() const = 0; + + protected: + ~RefCountedModule() override = default; +}; + } // namespace webrtc #endif // MODULES_INCLUDE_MODULE_H_ diff --git a/pc/test/fakeaudiocapturemodule.cc b/pc/test/fakeaudiocapturemodule.cc index ea734ed931..ffee283023 100644 --- a/pc/test/fakeaudiocapturemodule.cc +++ b/pc/test/fakeaudiocapturemodule.cc @@ -21,6 +21,10 @@ // Even simpler buffers would likely just contain audio sample values of 0. static const int kHighSampleValue = 10000; +// Same value as src/modules/audio_device/main/source/audio_device_config.h in +// https://code.google.com/p/webrtc/ +static const int kAdmMaxIdleTimeProcess = 1000; + // Constants here are derived by running VoE using a real ADM. // The constants correspond to 10ms of mono audio at 44kHz. static const int kTimePerFrameMs = 10; @@ -36,7 +40,8 @@ enum { }; FakeAudioCaptureModule::FakeAudioCaptureModule() - : audio_callback_(nullptr), + : last_process_time_ms_(0), + audio_callback_(nullptr), recording_(false), playing_(false), play_is_initialized_(false), @@ -67,6 +72,23 @@ int FakeAudioCaptureModule::frames_received() const { return frames_received_; } +int64_t FakeAudioCaptureModule::TimeUntilNextProcess() { + const int64_t current_time = rtc::TimeMillis(); + if (current_time < last_process_time_ms_) { + // TODO: wraparound could be handled more gracefully. + return 0; + } + const int64_t elapsed_time = current_time - last_process_time_ms_; + if (kAdmMaxIdleTimeProcess < elapsed_time) { + return 0; + } + return kAdmMaxIdleTimeProcess - elapsed_time; +} + +void FakeAudioCaptureModule::Process() { + last_process_time_ms_ = rtc::TimeMillis(); +} + int32_t FakeAudioCaptureModule::ActiveAudioLayer( AudioLayer* /*audio_layer*/) const { RTC_NOTREACHED(); @@ -78,6 +100,13 @@ webrtc::AudioDeviceModule::ErrorCode FakeAudioCaptureModule::LastError() const { return webrtc::AudioDeviceModule::kAdmErrNone; } +int32_t FakeAudioCaptureModule::RegisterEventObserver( + webrtc::AudioDeviceObserver* /*event_callback*/) { + // Only used to report warnings and errors. This fake implementation won't + // generate any so discard this callback. + return 0; +} + int32_t FakeAudioCaptureModule::RegisterAudioCallback( webrtc::AudioTransport* audio_callback) { rtc::CritScope cs(&crit_callback_); @@ -476,6 +505,7 @@ bool FakeAudioCaptureModule::Initialize() { // sent to it. Note that the audio processing pipeline will likely distort the // original signal. SetSendBuffer(kHighSampleValue); + last_process_time_ms_ = rtc::TimeMillis(); return true; } diff --git a/pc/test/fakeaudiocapturemodule.h b/pc/test/fakeaudiocapturemodule.h index fd2290b3da..b73b642769 100644 --- a/pc/test/fakeaudiocapturemodule.h +++ b/pc/test/fakeaudiocapturemodule.h @@ -52,9 +52,18 @@ class FakeAudioCaptureModule // pulled frame was generated/pushed from a FakeAudioCaptureModule. int frames_received() const; + // Following functions are inherited from webrtc::AudioDeviceModule. + // Only functions called by PeerConnection are implemented, the rest do + // nothing and return success. If a function is not expected to be called by + // PeerConnection an assertion is triggered if it is in fact called. + int64_t TimeUntilNextProcess() override; + void Process() override; + int32_t ActiveAudioLayer(AudioLayer* audio_layer) const override; ErrorCode LastError() const override; + int32_t RegisterEventObserver( + webrtc::AudioDeviceObserver* event_callback) override; // Note: Calling this method from a callback may result in deadlock. int32_t RegisterAudioCallback( @@ -201,6 +210,10 @@ class FakeAudioCaptureModule // Pushes frames to the registered webrtc::AudioTransport. void SendFrameP(); + // The time in milliseconds when Process() was last called or 0 if no call + // has been made. + int64_t last_process_time_ms_; + // Callback for playout and recording. webrtc::AudioTransport* audio_callback_; diff --git a/pc/test/fakeaudiocapturemodule_unittest.cc b/pc/test/fakeaudiocapturemodule_unittest.cc index 0e7574f4c6..312c4e8f96 100644 --- a/pc/test/fakeaudiocapturemodule_unittest.cc +++ b/pc/test/fakeaudiocapturemodule_unittest.cc @@ -133,6 +133,14 @@ class FakeAdmTest : public testing::Test, size_t rec_buffer_bytes_; }; +TEST_F(FakeAdmTest, TestProcess) { + // Next process call must be some time in the future (or now). + EXPECT_LE(0, fake_audio_capture_module_->TimeUntilNextProcess()); + // Process call updates TimeUntilNextProcess() but there are no guarantees on + // timing so just check that Process can be called successfully. + fake_audio_capture_module_->Process(); +} + TEST_F(FakeAdmTest, PlayoutTest) { EXPECT_EQ(0, fake_audio_capture_module_->RegisterAudioCallback(this)); diff --git a/test/mock_voice_engine.h b/test/mock_voice_engine.h index 67bc993c9b..2567be4052 100644 --- a/test/mock_voice_engine.h +++ b/test/mock_voice_engine.h @@ -63,6 +63,8 @@ class MockVoiceEngine : public VoiceEngineImpl { return proxy; })); + ON_CALL(mock_audio_device_, TimeUntilNextProcess()) + .WillByDefault(testing::Return(1000)); ON_CALL(*this, audio_device_module()) .WillByDefault(testing::Return(&mock_audio_device_)); ON_CALL(*this, audio_transport()) diff --git a/voice_engine/voe_base_impl.cc b/voice_engine/voe_base_impl.cc index b14bf954bb..b4c2b7a983 100644 --- a/voice_engine/voe_base_impl.cc +++ b/voice_engine/voe_base_impl.cc @@ -41,6 +41,22 @@ VoEBaseImpl::~VoEBaseImpl() { TerminateInternal(); } +void VoEBaseImpl::OnErrorIsReported(const ErrorCode error) { + if (error == AudioDeviceObserver::kRecordingError) { + LOG_F(LS_ERROR) << "VE_RUNTIME_REC_ERROR"; + } else if (error == AudioDeviceObserver::kPlayoutError) { + LOG_F(LS_ERROR) << "VE_RUNTIME_PLAY_ERROR"; + } +} + +void VoEBaseImpl::OnWarningIsReported(const WarningCode warning) { + if (warning == AudioDeviceObserver::kRecordingWarning) { + LOG_F(LS_WARNING) << "VE_RUNTIME_REC_WARNING"; + } else if (warning == AudioDeviceObserver::kPlayoutWarning) { + LOG_F(LS_WARNING) << "VE_RUNTIME_PLAY_WARNING"; + } +} + int32_t VoEBaseImpl::RecordedDataIsAvailable( const void* audio_data, const size_t number_of_frames, @@ -175,11 +191,23 @@ int VoEBaseImpl::Init( << "An external ADM implementation will be used in VoiceEngine"; } + // Register the ADM to the process thread, which will drive the error + // callback mechanism + if (shared_->process_thread()) { + shared_->process_thread()->RegisterModule(shared_->audio_device(), + RTC_FROM_HERE); + } + bool available = false; // -------------------- // Reinitialize the ADM + // Register the AudioObserver implementation + if (shared_->audio_device()->RegisterEventObserver(this) != 0) { + LOG(LS_ERROR) << "Init() failed to register event observer for the ADM"; + } + // Register the AudioTransport implementation if (shared_->audio_device()->RegisterAudioCallback(this) != 0) { LOG(LS_ERROR) << "Init() failed to register audio callback for the ADM"; @@ -461,6 +489,9 @@ int32_t VoEBaseImpl::TerminateInternal() { shared_->channel_manager().DestroyAllChannels(); if (shared_->process_thread()) { + if (shared_->audio_device()) { + shared_->process_thread()->DeRegisterModule(shared_->audio_device()); + } shared_->process_thread()->Stop(); } @@ -471,6 +502,10 @@ int32_t VoEBaseImpl::TerminateInternal() { if (shared_->audio_device()->StopRecording() != 0) { LOG(LS_ERROR) << "TerminateInternal() failed to stop recording"; } + if (shared_->audio_device()->RegisterEventObserver(nullptr) != 0) { + LOG(LS_ERROR) << "TerminateInternal() failed to de-register event " + "observer for the ADM"; + } if (shared_->audio_device()->RegisterAudioCallback(nullptr) != 0) { LOG(LS_ERROR) << "TerminateInternal() failed to de-register audio " "callback for the ADM"; diff --git a/voice_engine/voe_base_impl.h b/voice_engine/voe_base_impl.h index a3c4c1f40f..d03a1997c4 100644 --- a/voice_engine/voe_base_impl.h +++ b/voice_engine/voe_base_impl.h @@ -22,7 +22,8 @@ namespace webrtc { class ProcessThread; class VoEBaseImpl : public VoEBase, - public AudioTransport { + public AudioTransport, + public AudioDeviceObserver { public: int Init( AudioDeviceModule* external_adm, @@ -80,6 +81,10 @@ class VoEBaseImpl : public VoEBase, int64_t* elapsed_time_ms, int64_t* ntp_time_ms) override; + // AudioDeviceObserver + void OnErrorIsReported(const ErrorCode error) override; + void OnWarningIsReported(const WarningCode warning) override; + protected: VoEBaseImpl(voe::SharedData* shared); ~VoEBaseImpl() override;