mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00

Bug: webrtc:15874 Change-Id: I5bdb19d5e710838b41e6ca283d406c9f1f21286b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/348060 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Henrik Andreassson <henrika@webrtc.org> Commit-Queue: Florent Castelli <orphis@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42137}
522 lines
16 KiB
C++
522 lines
16 KiB
C++
/*
|
|
* 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/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(std::unique_ptr<AudioInput> audio_input,
|
|
std::unique_ptr<AudioOutput> 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<AudioDeviceBuffer>(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<AudioInput> input_;
|
|
|
|
// Implements the AudioOutput interface and deals with audio rendering parts.
|
|
const std::unique_ptr<AudioOutput> 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<AudioDeviceBuffer> audio_device_buffer_;
|
|
|
|
// Set to true after a successful call to Init(). Cleared by Terminate().
|
|
bool initialized_ = false;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
rtc::scoped_refptr<AudioDeviceModuleForTest>
|
|
CreateWindowsCoreAudioAudioDeviceModuleFromInputAndOutput(
|
|
std::unique_ptr<AudioInput> audio_input,
|
|
std::unique_ptr<AudioOutput> audio_output,
|
|
TaskQueueFactory* task_queue_factory) {
|
|
RTC_DLOG(LS_INFO) << __FUNCTION__;
|
|
return rtc::make_ref_counted<WindowsAudioDeviceModule>(
|
|
std::move(audio_input), std::move(audio_output), task_queue_factory);
|
|
}
|
|
|
|
} // namespace webrtc_win
|
|
} // namespace webrtc
|