/* * 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 #include #include "api/make_ref_counted.h" #include "api/sequence_checker.h" #include "modules/audio_device/audio_device_buffer.h" #include "modules/audio_device/include/audio_device.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(std::unique_ptr audio_input, std::unique_ptr audio_output, TaskQueueFactory* task_queue_factory) : input_(std::move(audio_input)), output_(std::move(audio_output)), task_queue_factory_(task_queue_factory) { 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(task_queue_factory_); 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); rtc::strcpyn(guid, kAdmMaxGuidSize, guid_str.c_str()); } else { ret = output_->DeviceName(index, &name_str, nullptr); } rtc::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); rtc::strcpyn(guid, kAdmMaxGuidSize, guid_str.c_str()); } else { ret = input_->DeviceName(index, &name_str, nullptr); } rtc::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: // 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 input_; // Implements the AudioOutput interface and deals with audio rendering parts. const std::unique_ptr output_; TaskQueueFactory* const task_queue_factory_; // 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 audio_device_buffer_; // Set to true after a successful call to Init(). Cleared by Terminate(). bool initialized_ = false; }; } // namespace rtc::scoped_refptr CreateWindowsCoreAudioAudioDeviceModuleFromInputAndOutput( std::unique_ptr audio_input, std::unique_ptr audio_output, TaskQueueFactory* task_queue_factory) { RTC_DLOG(LS_INFO) << __FUNCTION__; return rtc::make_ref_counted( std::move(audio_input), std::move(audio_output), task_queue_factory); } } // namespace webrtc_win } // namespace webrtc