| /* | |
| * Copyright (c) 2018 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 "modules/audio_device/win/audio_device_module_win.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "api/audio/audio_device.h" | |
| #include "api/environment/environment.h" | |
| #include "api/make_ref_counted.h" | |
| #include "api/sequence_checker.h" | |
| #include "modules/audio_device/audio_device_buffer.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/logging.h" | |
| #include "rtc_base/string_utils.h" | |
| namespace webrtc { | |
| namespace webrtc_win { | |
| namespace { | |
| #define RETURN_IF_OUTPUT_RESTARTS(...) \ | |
| do { \ | |
| if (output_->Restarting()) { \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (0) | |
| #define RETURN_IF_INPUT_RESTARTS(...) \ | |
| do { \ | |
| if (input_->Restarting()) { \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (0) | |
| #define RETURN_IF_OUTPUT_IS_INITIALIZED(...) \ | |
| do { \ | |
| if (output_->PlayoutIsInitialized()) { \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (0) | |
| #define RETURN_IF_INPUT_IS_INITIALIZED(...) \ | |
| do { \ | |
| if (input_->RecordingIsInitialized()) { \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (0) | |
| #define RETURN_IF_OUTPUT_IS_ACTIVE(...) \ | |
| do { \ | |
| if (output_->Playing()) { \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (0) | |
| #define RETURN_IF_INPUT_IS_ACTIVE(...) \ | |
| do { \ | |
| if (input_->Recording()) { \ | |
| return __VA_ARGS__; \ | |
| } \ | |
| } while (0) | |
| // This class combines a generic instance of an AudioInput and a generic | |
| // instance of an AudioOutput to create an AudioDeviceModule. This is mostly | |
| // done by delegating to the audio input/output with some glue code. This class | |
| // also directly implements some of the AudioDeviceModule methods with dummy | |
| // implementations. | |
| // | |
| // An instance must be created, destroyed and used on one and the same thread, | |
| // i.e., all public methods must also be called on the same thread. A thread | |
| // checker will RTC_DCHECK if any method is called on an invalid thread. | |
| // TODO(henrika): is thread checking needed in AudioInput and AudioOutput? | |
| class WindowsAudioDeviceModule : public AudioDeviceModuleForTest { | |
| public: | |
| enum class InitStatus { | |
| OK = 0, | |
| PLAYOUT_ERROR = 1, | |
| RECORDING_ERROR = 2, | |
| OTHER_ERROR = 3, | |
| NUM_STATUSES = 4 | |
| }; | |
| WindowsAudioDeviceModule(const Environment& env, | |
| std::unique_ptr<AudioInput> audio_input, | |
| std::unique_ptr<AudioOutput> audio_output) | |
| : env_(env), | |
| input_(std::move(audio_input)), | |
| output_(std::move(audio_output)) { | |
| RTC_CHECK(input_); | |
| RTC_CHECK(output_); | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| } | |
| ~WindowsAudioDeviceModule() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| Terminate(); | |
| } | |
| WindowsAudioDeviceModule(const WindowsAudioDeviceModule&) = delete; | |
| WindowsAudioDeviceModule& operator=(const WindowsAudioDeviceModule&) = delete; | |
| int32_t ActiveAudioLayer( | |
| AudioDeviceModule::AudioLayer* audioLayer) const override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| // TODO(henrika): it might be possible to remove this unique signature. | |
| *audioLayer = AudioDeviceModule::kWindowsCoreAudio2; | |
| return 0; | |
| } | |
| int32_t RegisterAudioCallback(AudioTransport* audioCallback) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK(audio_device_buffer_); | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return audio_device_buffer_->RegisterAudioCallback(audioCallback); | |
| } | |
| int32_t Init() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| RETURN_IF_INPUT_RESTARTS(0); | |
| if (initialized_) { | |
| return 0; | |
| } | |
| audio_device_buffer_ = std::make_unique<AudioDeviceBuffer>(env_); | |
| AttachAudioBuffer(); | |
| InitStatus status; | |
| if (output_->Init() != 0) { | |
| status = InitStatus::PLAYOUT_ERROR; | |
| } else if (input_->Init() != 0) { | |
| output_->Terminate(); | |
| status = InitStatus::RECORDING_ERROR; | |
| } else { | |
| initialized_ = true; | |
| status = InitStatus::OK; | |
| } | |
| if (status != InitStatus::OK) { | |
| RTC_LOG(LS_ERROR) << "Audio device initialization failed"; | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int32_t Terminate() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| RETURN_IF_INPUT_RESTARTS(0); | |
| if (!initialized_) | |
| return 0; | |
| int32_t err = input_->Terminate(); | |
| err |= output_->Terminate(); | |
| initialized_ = false; | |
| RTC_DCHECK_EQ(err, 0); | |
| return err; | |
| } | |
| bool Initialized() const override { | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return initialized_; | |
| } | |
| int16_t PlayoutDevices() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| return output_->NumDevices(); | |
| } | |
| int16_t RecordingDevices() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_INPUT_RESTARTS(0); | |
| return input_->NumDevices(); | |
| } | |
| int32_t PlayoutDeviceName(uint16_t index, | |
| char name[kAdmMaxDeviceNameSize], | |
| char guid[kAdmMaxGuidSize]) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| std::string name_str, guid_str; | |
| int ret = -1; | |
| if (guid != nullptr) { | |
| ret = output_->DeviceName(index, &name_str, &guid_str); | |
| webrtc::strcpyn(guid, kAdmMaxGuidSize, guid_str.c_str()); | |
| } else { | |
| ret = output_->DeviceName(index, &name_str, nullptr); | |
| } | |
| webrtc::strcpyn(name, kAdmMaxDeviceNameSize, name_str.c_str()); | |
| return ret; | |
| } | |
| int32_t RecordingDeviceName(uint16_t index, | |
| char name[kAdmMaxDeviceNameSize], | |
| char guid[kAdmMaxGuidSize]) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_INPUT_RESTARTS(0); | |
| std::string name_str, guid_str; | |
| int ret = -1; | |
| if (guid != nullptr) { | |
| ret = input_->DeviceName(index, &name_str, &guid_str); | |
| webrtc::strcpyn(guid, kAdmMaxGuidSize, guid_str.c_str()); | |
| } else { | |
| ret = input_->DeviceName(index, &name_str, nullptr); | |
| } | |
| webrtc::strcpyn(name, kAdmMaxDeviceNameSize, name_str.c_str()); | |
| return ret; | |
| } | |
| int32_t SetPlayoutDevice(uint16_t index) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| return output_->SetDevice(index); | |
| } | |
| int32_t SetPlayoutDevice( | |
| AudioDeviceModule::WindowsDeviceType device) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| return output_->SetDevice(device); | |
| } | |
| int32_t SetRecordingDevice(uint16_t index) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return input_->SetDevice(index); | |
| } | |
| int32_t SetRecordingDevice( | |
| AudioDeviceModule::WindowsDeviceType device) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return input_->SetDevice(device); | |
| } | |
| int32_t PlayoutIsAvailable(bool* available) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *available = true; | |
| return 0; | |
| } | |
| int32_t InitPlayout() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| RETURN_IF_OUTPUT_IS_INITIALIZED(0); | |
| return output_->InitPlayout(); | |
| } | |
| bool PlayoutIsInitialized() const override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(true); | |
| return output_->PlayoutIsInitialized(); | |
| } | |
| int32_t RecordingIsAvailable(bool* available) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *available = true; | |
| return 0; | |
| } | |
| int32_t InitRecording() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_INPUT_RESTARTS(0); | |
| RETURN_IF_INPUT_IS_INITIALIZED(0); | |
| return input_->InitRecording(); | |
| } | |
| bool RecordingIsInitialized() const override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_INPUT_RESTARTS(true); | |
| return input_->RecordingIsInitialized(); | |
| } | |
| int32_t StartPlayout() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| RETURN_IF_OUTPUT_IS_ACTIVE(0); | |
| return output_->StartPlayout(); | |
| } | |
| int32_t StopPlayout() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(-1); | |
| return output_->StopPlayout(); | |
| } | |
| bool Playing() const override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(true); | |
| return output_->Playing(); | |
| } | |
| int32_t StartRecording() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_INPUT_RESTARTS(0); | |
| RETURN_IF_INPUT_IS_ACTIVE(0); | |
| return input_->StartRecording(); | |
| } | |
| int32_t StopRecording() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_INPUT_RESTARTS(-1); | |
| return input_->StopRecording(); | |
| } | |
| bool Recording() const override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RETURN_IF_INPUT_RESTARTS(true); | |
| return input_->Recording(); | |
| } | |
| int32_t InitSpeaker() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RTC_DLOG(LS_WARNING) << "This method has no effect"; | |
| return initialized_ ? 0 : -1; | |
| } | |
| bool SpeakerIsInitialized() const override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RTC_DLOG(LS_WARNING) << "This method has no effect"; | |
| return initialized_; | |
| } | |
| int32_t InitMicrophone() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RTC_DLOG(LS_WARNING) << "This method has no effect"; | |
| return initialized_ ? 0 : -1; | |
| } | |
| bool MicrophoneIsInitialized() const override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RTC_DLOG(LS_WARNING) << "This method has no effect"; | |
| return initialized_; | |
| } | |
| int32_t SpeakerVolumeIsAvailable(bool* available) override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *available = false; | |
| return 0; | |
| } | |
| int32_t SetSpeakerVolume(uint32_t volume) override { return 0; } | |
| int32_t SpeakerVolume(uint32_t* volume) const override { return 0; } | |
| int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override { return 0; } | |
| int32_t MinSpeakerVolume(uint32_t* minVolume) const override { return 0; } | |
| int32_t MicrophoneVolumeIsAvailable(bool* available) override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *available = false; | |
| return 0; | |
| } | |
| int32_t SetMicrophoneVolume(uint32_t volume) override { return 0; } | |
| int32_t MicrophoneVolume(uint32_t* volume) const override { return 0; } | |
| int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override { return 0; } | |
| int32_t MinMicrophoneVolume(uint32_t* minVolume) const override { return 0; } | |
| int32_t SpeakerMuteIsAvailable(bool* available) override { return 0; } | |
| int32_t SetSpeakerMute(bool enable) override { return 0; } | |
| int32_t SpeakerMute(bool* enabled) const override { return 0; } | |
| int32_t MicrophoneMuteIsAvailable(bool* available) override { return 0; } | |
| int32_t SetMicrophoneMute(bool enable) override { return 0; } | |
| int32_t MicrophoneMute(bool* enabled) const override { return 0; } | |
| int32_t StereoPlayoutIsAvailable(bool* available) const override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *available = true; | |
| return 0; | |
| } | |
| int32_t SetStereoPlayout(bool enable) override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return 0; | |
| } | |
| int32_t StereoPlayout(bool* enabled) const override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *enabled = true; | |
| return 0; | |
| } | |
| int32_t StereoRecordingIsAvailable(bool* available) const override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *available = true; | |
| return 0; | |
| } | |
| int32_t SetStereoRecording(bool enable) override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return 0; | |
| } | |
| int32_t StereoRecording(bool* enabled) const override { | |
| // TODO(henrika): improve support. | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| *enabled = true; | |
| return 0; | |
| } | |
| int32_t PlayoutDelay(uint16_t* delayMS) const override { return 0; } | |
| bool BuiltInAECIsAvailable() const override { return false; } | |
| bool BuiltInAGCIsAvailable() const override { return false; } | |
| bool BuiltInNSIsAvailable() const override { return false; } | |
| int32_t EnableBuiltInAEC(bool enable) override { return 0; } | |
| int32_t EnableBuiltInAGC(bool enable) override { return 0; } | |
| int32_t EnableBuiltInNS(bool enable) override { return 0; } | |
| int32_t AttachAudioBuffer() { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| output_->AttachAudioBuffer(audio_device_buffer_.get()); | |
| input_->AttachAudioBuffer(audio_device_buffer_.get()); | |
| return 0; | |
| } | |
| int RestartPlayoutInternally() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| RETURN_IF_OUTPUT_RESTARTS(0); | |
| return output_->RestartPlayout(); | |
| } | |
| int RestartRecordingInternally() override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return input_->RestartRecording(); | |
| } | |
| int SetPlayoutSampleRate(uint32_t sample_rate) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return output_->SetSampleRate(sample_rate); | |
| } | |
| int SetRecordingSampleRate(uint32_t sample_rate) override { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| RTC_DCHECK_RUN_ON(&thread_checker_); | |
| return input_->SetSampleRate(sample_rate); | |
| } | |
| private: | |
| const Environment env_; | |
| // Ensures that the class is used on the same thread as it is constructed | |
| // and destroyed on. | |
| SequenceChecker thread_checker_; | |
| // Implements the AudioInput interface and deals with audio capturing parts. | |
| const std::unique_ptr<AudioInput> input_; | |
| // Implements the AudioOutput interface and deals with audio rendering parts. | |
| const std::unique_ptr<AudioOutput> output_; | |
| // The AudioDeviceBuffer (ADB) instance is needed for sending/receiving audio | |
| // to/from the WebRTC layer. Created and owned by this object. Used by | |
| // both `input_` and `output_` but they use orthogonal parts of the ADB. | |
| std::unique_ptr<AudioDeviceBuffer> audio_device_buffer_; | |
| // Set to true after a successful call to Init(). Cleared by Terminate(). | |
| bool initialized_ = false; | |
| }; | |
| } // namespace | |
| webrtc::scoped_refptr<AudioDeviceModuleForTest> | |
| CreateWindowsCoreAudioAudioDeviceModuleFromInputAndOutput( | |
| const Environment& env, | |
| std::unique_ptr<AudioInput> audio_input, | |
| std::unique_ptr<AudioOutput> audio_output) { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| return make_ref_counted<WindowsAudioDeviceModule>(env, std::move(audio_input), | |
| std::move(audio_output)); | |
| } | |
| } // namespace webrtc_win | |
| } // namespace webrtc |