diff --git a/webrtc/media/BUILD.gn b/webrtc/media/BUILD.gn index a518562917..6b18392ee1 100644 --- a/webrtc/media/BUILD.gn +++ b/webrtc/media/BUILD.gn @@ -121,6 +121,8 @@ rtc_static_library("rtc_media") { libs = [] deps = [] sources = [ + "engine/adm_helpers.cc", + "engine/adm_helpers.h", "engine/apm_helpers.cc", "engine/apm_helpers.h", "engine/internaldecoderfactory.cc", diff --git a/webrtc/media/engine/adm_helpers.cc b/webrtc/media/engine/adm_helpers.cc new file mode 100644 index 0000000000..d8409f0df8 --- /dev/null +++ b/webrtc/media/engine/adm_helpers.cc @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/media/engine/adm_helpers.h" + +#include "webrtc/base/logging.h" +#include "webrtc/modules/audio_device/include/audio_device.h" + +namespace webrtc { +namespace adm_helpers { + +// On Windows Vista and newer, Microsoft introduced the concept of "Default +// Communications Device". This means that there are two types of default +// devices (old Wave Audio style default and Default Communications Device). +// +// On Windows systems which only support Wave Audio style default, uses either +// -1 or 0 to select the default device. +// +// Using a #define for AUDIO_DEVICE since we will call *different* versions of +// the ADM functions, depending on the ID type. +#if defined(WEBRTC_WIN) +#define AUDIO_DEVICE_ID \ + (AudioDeviceModule::WindowsDeviceType::kDefaultCommunicationDevice) +#else +#define AUDIO_DEVICE_ID (0u) +#endif // defined(WEBRTC_WIN) + +void SetRecordingDevice(AudioDeviceModule* adm) { + RTC_DCHECK(adm); + + // Save recording status and stop recording. + const bool was_recording = adm->Recording(); + if (was_recording && adm->StopRecording() != 0) { + LOG(LS_ERROR) << "Unable to stop recording."; + return; + } + + // Set device and stereo mode. + if (adm->SetRecordingChannel(AudioDeviceModule::kChannelBoth) != 0) { + LOG(LS_ERROR) << "Unable to set recording channel to kChannelBoth."; + } + if (adm->SetRecordingDevice(AUDIO_DEVICE_ID) != 0) { + LOG(LS_ERROR) << "Unable to set recording device."; + return; + } + + // Init microphone, so user can do volume settings etc. + if (adm->InitMicrophone() != 0) { + LOG(LS_ERROR) << "Unable to access microphone."; + } + + // Set number of channels + bool available = false; + if (adm->StereoRecordingIsAvailable(&available) != 0) { + LOG(LS_ERROR) << "Failed to query stereo recording."; + } + if (adm->SetStereoRecording(available) != 0) { + LOG(LS_ERROR) << "Failed to set stereo recording mode."; + } + + // Restore recording if it was enabled already when calling this function. + if (was_recording) { + if (adm->InitRecording() != 0) { + LOG(LS_ERROR) << "Failed to initialize recording."; + return; + } + if (adm->StartRecording() != 0) { + LOG(LS_ERROR) << "Failed to start recording."; + return; + } + } + + LOG(LS_INFO) << "Set recording device."; +} + +void SetPlayoutDevice(AudioDeviceModule* adm) { + RTC_DCHECK(adm); + + // Save playing status and stop playout. + const bool was_playing = adm->Playing(); + if (was_playing && adm->StopPlayout() != 0) { + LOG(LS_ERROR) << "Unable to stop playout."; + } + + // Set device. + if (adm->SetPlayoutDevice(AUDIO_DEVICE_ID) != 0) { + LOG(LS_ERROR) << "Unable to set playout device."; + return; + } + + // Init speaker, so user can do volume settings etc. + if (adm->InitSpeaker() != 0) { + LOG(LS_ERROR) << "Unable to access speaker."; + } + + // Set number of channels + bool available = false; + if (adm->StereoPlayoutIsAvailable(&available) != 0) { + LOG(LS_ERROR) << "Failed to query stereo playout."; + } + if (adm->SetStereoPlayout(available) != 0) { + LOG(LS_ERROR) << "Failed to set stereo playout mode."; + } + + // Restore recording if it was enabled already when calling this function. + if (was_playing) { + if (adm->InitPlayout() != 0) { + LOG(LS_ERROR) << "Failed to initialize playout."; + return; + } + if (adm->StartPlayout() != 0) { + LOG(LS_ERROR) << "Failed to start playout."; + return; + } + } + + LOG(LS_INFO) << "Set playout device."; +} + +} // namespace adm_helpers +} // namespace webrtc diff --git a/webrtc/media/engine/adm_helpers.h b/webrtc/media/engine/adm_helpers.h new file mode 100644 index 0000000000..1a3eb45960 --- /dev/null +++ b/webrtc/media/engine/adm_helpers.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MEDIA_ENGINE_ADM_HELPERS_H_ +#define WEBRTC_MEDIA_ENGINE_ADM_HELPERS_H_ + +#include "webrtc/common_types.h" + +namespace webrtc { + +class AudioDeviceModule; + +namespace adm_helpers { + +void SetRecordingDevice(AudioDeviceModule* adm); +void SetPlayoutDevice(AudioDeviceModule* adm); + +} // namespace adm_helpers +} // namespace webrtc + +#endif // WEBRTC_MEDIA_ENGINE_ADM_HELPERS_H_ diff --git a/webrtc/media/engine/fakewebrtcvoiceengine.h b/webrtc/media/engine/fakewebrtcvoiceengine.h index e44e44da75..e7d95d09e7 100644 --- a/webrtc/media/engine/fakewebrtcvoiceengine.h +++ b/webrtc/media/engine/fakewebrtcvoiceengine.h @@ -59,8 +59,7 @@ static const int kOpusBandwidthFb = 20000; #define WEBRTC_FUNC(method, args) int method args override class FakeWebRtcVoiceEngine - : public webrtc::VoEBase, public webrtc::VoECodec, - public webrtc::VoEHardware { + : public webrtc::VoEBase, public webrtc::VoECodec { public: struct Channel { std::vector recv_codecs; @@ -203,26 +202,6 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(SetOpusMaxPlaybackRate, (int channel, int frequency_hz)); WEBRTC_STUB(SetOpusDtx, (int channel, bool enable_dtx)); - // webrtc::VoEHardware - WEBRTC_STUB(GetNumOfRecordingDevices, (int& num)); - WEBRTC_STUB(GetNumOfPlayoutDevices, (int& num)); - WEBRTC_STUB(GetRecordingDeviceName, (int i, char* name, char* guid)); - WEBRTC_STUB(GetPlayoutDeviceName, (int i, char* name, char* guid)); - WEBRTC_STUB(SetRecordingDevice, (int, webrtc::StereoChannel)); - WEBRTC_STUB(SetPlayoutDevice, (int)); - WEBRTC_STUB(SetAudioDeviceLayer, (webrtc::AudioLayers)); - WEBRTC_STUB(GetAudioDeviceLayer, (webrtc::AudioLayers&)); - WEBRTC_STUB(SetRecordingSampleRate, (unsigned int samples_per_sec)); - WEBRTC_STUB_CONST(RecordingSampleRate, (unsigned int* samples_per_sec)); - WEBRTC_STUB(SetPlayoutSampleRate, (unsigned int samples_per_sec)); - WEBRTC_STUB_CONST(PlayoutSampleRate, (unsigned int* samples_per_sec)); - WEBRTC_STUB(EnableBuiltInAEC, (bool enable)); - bool BuiltInAECIsAvailable() const override { return false; } - WEBRTC_STUB(EnableBuiltInAGC, (bool enable)); - bool BuiltInAGCIsAvailable() const override { return false; } - WEBRTC_STUB(EnableBuiltInNS, (bool enable)); - bool BuiltInNSIsAvailable() const override { return false; } - size_t GetNetEqCapacity() const { auto ch = channels_.find(last_channel_); RTC_DCHECK(ch != channels_.end()); diff --git a/webrtc/media/engine/webrtcvoe.h b/webrtc/media/engine/webrtcvoe.h index 6d99b5cbf1..9547e6a7fb 100644 --- a/webrtc/media/engine/webrtcvoe.h +++ b/webrtc/media/engine/webrtcvoe.h @@ -20,7 +20,6 @@ #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_codec.h" #include "webrtc/voice_engine/include/voe_errors.h" -#include "webrtc/voice_engine/include/voe_hardware.h" namespace cricket { // automatically handles lifetime of WebRtc VoiceEngine @@ -77,28 +76,24 @@ class VoEWrapper { public: VoEWrapper() : engine_(webrtc::VoiceEngine::Create()), - base_(engine_), codec_(engine_), hw_(engine_) { + base_(engine_), codec_(engine_) { } VoEWrapper(webrtc::VoEBase* base, - webrtc::VoECodec* codec, - webrtc::VoEHardware* hw) + webrtc::VoECodec* codec) : engine_(NULL), base_(base), - codec_(codec), - hw_(hw) { + codec_(codec) { } ~VoEWrapper() {} webrtc::VoiceEngine* engine() const { return engine_.get(); } webrtc::VoEBase* base() const { return base_.get(); } webrtc::VoECodec* codec() const { return codec_.get(); } - webrtc::VoEHardware* hw() const { return hw_.get(); } int error() { return base_->LastError(); } private: scoped_voe_engine engine_; scoped_voe_ptr base_; scoped_voe_ptr codec_; - scoped_voe_ptr hw_; }; } // namespace cricket diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc index 4b59a816d8..c95d7582a9 100644 --- a/webrtc/media/engine/webrtcvoiceengine.cc +++ b/webrtc/media/engine/webrtcvoiceengine.cc @@ -32,6 +32,7 @@ #include "webrtc/media/base/audiosource.h" #include "webrtc/media/base/mediaconstants.h" #include "webrtc/media/base/streamparams.h" +#include "webrtc/media/engine/adm_helpers.h" #include "webrtc/media/engine/apm_helpers.h" #include "webrtc/media/engine/payload_type_mapper.h" #include "webrtc/media/engine/webrtcmediaengine.h" @@ -55,18 +56,6 @@ const int kDefaultTraceFilter = webrtc::kTraceNone | webrtc::kTraceTerseInfo | const int kElevatedTraceFilter = kDefaultTraceFilter | webrtc::kTraceStateInfo | webrtc::kTraceInfo; -// On Windows Vista and newer, Microsoft introduced the concept of "Default -// Communications Device". This means that there are two types of default -// devices (old Wave Audio style default and Default Communications Device). -// -// On Windows systems which only support Wave Audio style default, uses either -// -1 or 0 to select the default device. -#ifdef WIN32 -const int kDefaultAudioDeviceId = -1; -#elif !defined(WEBRTC_IOS) -const int kDefaultAudioDeviceId = 0; -#endif - constexpr int kNackRtpHistoryMs = 5000; // Check to verify that the define for the intelligibility enhancer is properly @@ -655,7 +644,12 @@ WebRtcVoiceEngine::WebRtcVoiceEngine( RTC_DCHECK(error); } - SetDefaultDevices(); + // Set default audio devices. +#if !defined(WEBRTC_IOS) + webrtc::adm_helpers::SetRecordingDevice(adm_); + apm()->Initialize(); + webrtc::adm_helpers::SetPlayoutDevice(adm_); +#endif // !WEBRTC_IOS } WebRtcVoiceEngine::~WebRtcVoiceEngine() { @@ -934,34 +928,6 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { return true; } -void WebRtcVoiceEngine::SetDefaultDevices() { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); -#if !defined(WEBRTC_IOS) - int in_id = kDefaultAudioDeviceId; - int out_id = kDefaultAudioDeviceId; - LOG(LS_INFO) << "Setting microphone to (id=" << in_id - << ") and speaker to (id=" << out_id << ")"; - - bool ret = true; - if (voe_wrapper_->hw()->SetRecordingDevice(in_id) == -1) { - LOG_RTCERR1(SetRecordingDevice, in_id); - ret = false; - } - - apm()->Initialize(); - - if (voe_wrapper_->hw()->SetPlayoutDevice(out_id) == -1) { - LOG_RTCERR1(SetPlayoutDevice, out_id); - ret = false; - } - - if (ret) { - LOG(LS_INFO) << "Set microphone to (id=" << in_id - << ") and speaker to (id=" << out_id << ")"; - } -#endif // !WEBRTC_IOS -} - // TODO(solenberg): Remove, once AudioMonitor is gone. int WebRtcVoiceEngine::GetInputLevel() { RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); diff --git a/webrtc/media/engine/webrtcvoiceengine.h b/webrtc/media/engine/webrtcvoiceengine.h index 33e53f1f39..bac336c017 100644 --- a/webrtc/media/engine/webrtcvoiceengine.h +++ b/webrtc/media/engine/webrtcvoiceengine.h @@ -102,7 +102,6 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { // ignored. This allows us to selectively turn on and off different options // easily at any time. bool ApplyOptions(const AudioOptions& options); - void SetDefaultDevices(); // webrtc::TraceCallback: void Print(webrtc::TraceLevel level, const char* trace, int length) override; diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc index 5809a08eaa..2d41ecd43b 100644 --- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc +++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc @@ -64,8 +64,7 @@ class FakeVoEWrapper : public cricket::VoEWrapper { public: explicit FakeVoEWrapper(cricket::FakeWebRtcVoiceEngine* engine) : cricket::VoEWrapper(engine, // base - engine, // codec - engine) { // hw + engine) { // codec } }; @@ -76,17 +75,50 @@ class MockTransmitMixer : public webrtc::voe::TransmitMixer { MOCK_METHOD1(EnableStereoChannelSwapping, void(bool enable)); }; + +void AdmSetupExpectations(webrtc::test::MockAudioDeviceModule* adm) { + RTC_DCHECK(adm); + EXPECT_CALL(*adm, AddRef()).WillOnce(Return(0)); + EXPECT_CALL(*adm, Release()).WillOnce(Return(0)); +#if !defined(WEBRTC_IOS) + EXPECT_CALL(*adm, Recording()).WillOnce(Return(false)); + EXPECT_CALL(*adm, SetRecordingChannel(webrtc::AudioDeviceModule:: + ChannelType::kChannelBoth)).WillOnce(Return(0)); +#if defined(WEBRTC_WIN) + EXPECT_CALL(*adm, SetRecordingDevice( + testing::Matcher( + webrtc::AudioDeviceModule::kDefaultCommunicationDevice))) + .WillOnce(Return(0)); +#else + EXPECT_CALL(*adm, SetRecordingDevice(0)).WillOnce(Return(0)); +#endif // #if defined(WEBRTC_WIN) + EXPECT_CALL(*adm, InitMicrophone()).WillOnce(Return(0)); + EXPECT_CALL(*adm, StereoRecordingIsAvailable(testing::_)).WillOnce(Return(0)); + EXPECT_CALL(*adm, SetStereoRecording(false)).WillOnce(Return(0)); + EXPECT_CALL(*adm, Playing()).WillOnce(Return(false)); +#if defined(WEBRTC_WIN) + EXPECT_CALL(*adm, SetPlayoutDevice( + testing::Matcher( + webrtc::AudioDeviceModule::kDefaultCommunicationDevice))) + .WillOnce(Return(0)); +#else + EXPECT_CALL(*adm, SetPlayoutDevice(0)).WillOnce(Return(0)); +#endif // #if defined(WEBRTC_WIN) + EXPECT_CALL(*adm, InitSpeaker()).WillOnce(Return(0)); + EXPECT_CALL(*adm, StereoPlayoutIsAvailable(testing::_)).WillOnce(Return(0)); + EXPECT_CALL(*adm, SetStereoPlayout(false)).WillOnce(Return(0)); +#endif // #if !defined(WEBRTC_IOS) + EXPECT_CALL(*adm, BuiltInAECIsAvailable()).WillOnce(Return(false)); + EXPECT_CALL(*adm, BuiltInAGCIsAvailable()).WillOnce(Return(false)); + EXPECT_CALL(*adm, BuiltInNSIsAvailable()).WillOnce(Return(false)); + EXPECT_CALL(*adm, SetAGC(true)).WillOnce(Return(0)); +} } // namespace // Tests that our stub library "works". TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) { StrictMock adm; - EXPECT_CALL(adm, AddRef()).WillOnce(Return(0)); - EXPECT_CALL(adm, Release()).WillOnce(Return(0)); - EXPECT_CALL(adm, BuiltInAECIsAvailable()).WillOnce(Return(false)); - EXPECT_CALL(adm, BuiltInAGCIsAvailable()).WillOnce(Return(false)); - EXPECT_CALL(adm, BuiltInNSIsAvailable()).WillOnce(Return(false)); - EXPECT_CALL(adm, SetAGC(true)).WillOnce(Return(0)); + AdmSetupExpectations(&adm); StrictMock apm; EXPECT_CALL(apm, ApplyConfig(testing::_)); EXPECT_CALL(apm, SetExtraOptions(testing::_)); @@ -123,12 +155,7 @@ class WebRtcVoiceEngineTestFake : public testing::Test { call_(webrtc::Call::Config(&event_log_)), voe_(&apm_, &transmit_mixer_), override_field_trials_(field_trials) { // AudioDeviceModule. - EXPECT_CALL(adm_, AddRef()).WillOnce(Return(0)); - EXPECT_CALL(adm_, Release()).WillOnce(Return(0)); - EXPECT_CALL(adm_, BuiltInAECIsAvailable()).WillOnce(Return(false)); - EXPECT_CALL(adm_, BuiltInAGCIsAvailable()).WillOnce(Return(false)); - EXPECT_CALL(adm_, BuiltInNSIsAvailable()).WillOnce(Return(false)); - EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + AdmSetupExpectations(&adm_); // AudioProcessing. EXPECT_CALL(apm_, ApplyConfig(testing::_)); EXPECT_CALL(apm_, SetExtraOptions(testing::_)); diff --git a/webrtc/test/mock_voice_engine.h b/webrtc/test/mock_voice_engine.h index cdb6a783da..ca3fe4f466 100644 --- a/webrtc/test/mock_voice_engine.h +++ b/webrtc/test/mock_voice_engine.h @@ -175,29 +175,6 @@ class MockVoiceEngine : public VoiceEngineImpl { int(OutStream* stream, CodecInst* compression)); MOCK_METHOD0(StopRecordingMicrophone, int()); - // VoEHardware - MOCK_METHOD1(GetNumOfRecordingDevices, int(int& devices)); - MOCK_METHOD1(GetNumOfPlayoutDevices, int(int& devices)); - MOCK_METHOD3(GetRecordingDeviceName, - int(int index, char strNameUTF8[128], char strGuidUTF8[128])); - MOCK_METHOD3(GetPlayoutDeviceName, - int(int index, char strNameUTF8[128], char strGuidUTF8[128])); - MOCK_METHOD2(SetRecordingDevice, - int(int index, StereoChannel recordingChannel)); - MOCK_METHOD1(SetPlayoutDevice, int(int index)); - MOCK_METHOD1(SetAudioDeviceLayer, int(AudioLayers audioLayer)); - MOCK_METHOD1(GetAudioDeviceLayer, int(AudioLayers& audioLayer)); - MOCK_METHOD1(SetRecordingSampleRate, int(unsigned int samples_per_sec)); - MOCK_CONST_METHOD1(RecordingSampleRate, int(unsigned int* samples_per_sec)); - MOCK_METHOD1(SetPlayoutSampleRate, int(unsigned int samples_per_sec)); - MOCK_CONST_METHOD1(PlayoutSampleRate, int(unsigned int* samples_per_sec)); - MOCK_CONST_METHOD0(BuiltInAECIsAvailable, bool()); - MOCK_METHOD1(EnableBuiltInAEC, int(bool enable)); - MOCK_CONST_METHOD0(BuiltInAGCIsAvailable, bool()); - MOCK_METHOD1(EnableBuiltInAGC, int(bool enable)); - MOCK_CONST_METHOD0(BuiltInNSIsAvailable, bool()); - MOCK_METHOD1(EnableBuiltInNS, int(bool enable)); - // VoENetwork MOCK_METHOD2(RegisterExternalTransport, int(int channel, Transport& transport));