mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 06:10:40 +01:00
Migrate TestAudioDeviceModule on AudioDeviceModuleImpl
Bug: b/272350185 Change-Id: Ia3d85d6fa3b0d4809e987a39d60d3eb022687132 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/300363 Commit-Queue: Artem Titov <titovartem@webrtc.org> Reviewed-by: Henrik Andreassson <henrika@webrtc.org> Cr-Commit-Position: refs/heads/main@{#39877}
This commit is contained in:
parent
59d09aeeee
commit
e42bf81486
11 changed files with 969 additions and 182 deletions
|
@ -192,6 +192,7 @@ rtc_library("audio_device_impl") {
|
||||||
"../../api:scoped_refptr",
|
"../../api:scoped_refptr",
|
||||||
"../../api:sequence_checker",
|
"../../api:sequence_checker",
|
||||||
"../../api/task_queue",
|
"../../api/task_queue",
|
||||||
|
"../../api/units:time_delta",
|
||||||
"../../common_audio",
|
"../../common_audio",
|
||||||
"../../common_audio:common_audio_c",
|
"../../common_audio:common_audio_c",
|
||||||
"../../rtc_base:buffer",
|
"../../rtc_base:buffer",
|
||||||
|
@ -217,6 +218,7 @@ rtc_library("audio_device_impl") {
|
||||||
absl_deps = [
|
absl_deps = [
|
||||||
"//third_party/abseil-cpp/absl/base:core_headers",
|
"//third_party/abseil-cpp/absl/base:core_headers",
|
||||||
"//third_party/abseil-cpp/absl/strings:strings",
|
"//third_party/abseil-cpp/absl/strings:strings",
|
||||||
|
"//third_party/abseil-cpp/absl/types:optional",
|
||||||
]
|
]
|
||||||
if (rtc_include_internal_audio_device && is_ios) {
|
if (rtc_include_internal_audio_device && is_ios) {
|
||||||
deps += [ "../../sdk:audio_device" ]
|
deps += [ "../../sdk:audio_device" ]
|
||||||
|
@ -228,8 +230,6 @@ rtc_library("audio_device_impl") {
|
||||||
"dummy/file_audio_device.cc",
|
"dummy/file_audio_device.cc",
|
||||||
"dummy/file_audio_device.h",
|
"dummy/file_audio_device.h",
|
||||||
"include/fake_audio_device.h",
|
"include/fake_audio_device.h",
|
||||||
"include/test_audio_device.cc",
|
|
||||||
"include/test_audio_device.h",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if (build_with_mozilla) {
|
if (build_with_mozilla) {
|
||||||
|
@ -253,6 +253,10 @@ rtc_library("audio_device_impl") {
|
||||||
"audio_device_impl.cc",
|
"audio_device_impl.cc",
|
||||||
"audio_device_impl.h",
|
"audio_device_impl.h",
|
||||||
"include/audio_device_data_observer.h",
|
"include/audio_device_data_observer.h",
|
||||||
|
"include/test_audio_device.cc",
|
||||||
|
"include/test_audio_device.h",
|
||||||
|
"test_audio_device_impl.cc",
|
||||||
|
"test_audio_device_impl.h",
|
||||||
]
|
]
|
||||||
if (is_android) {
|
if (is_android) {
|
||||||
sources += [
|
sources += [
|
||||||
|
@ -414,10 +418,12 @@ if (rtc_include_tests && !build_with_chromium) {
|
||||||
sources = [
|
sources = [
|
||||||
"fine_audio_buffer_unittest.cc",
|
"fine_audio_buffer_unittest.cc",
|
||||||
"include/test_audio_device_unittest.cc",
|
"include/test_audio_device_unittest.cc",
|
||||||
|
"test_audio_device_impl_test.cc",
|
||||||
]
|
]
|
||||||
deps = [
|
deps = [
|
||||||
":audio_device",
|
":audio_device",
|
||||||
":audio_device_buffer",
|
":audio_device_buffer",
|
||||||
|
":audio_device_generic",
|
||||||
":audio_device_impl",
|
":audio_device_impl",
|
||||||
":mock_audio_device",
|
":mock_audio_device",
|
||||||
"../../api:array_view",
|
"../../api:array_view",
|
||||||
|
@ -425,6 +431,8 @@ if (rtc_include_tests && !build_with_chromium) {
|
||||||
"../../api:sequence_checker",
|
"../../api:sequence_checker",
|
||||||
"../../api/task_queue",
|
"../../api/task_queue",
|
||||||
"../../api/task_queue:default_task_queue_factory",
|
"../../api/task_queue:default_task_queue_factory",
|
||||||
|
"../../api/units:time_delta",
|
||||||
|
"../../api/units:timestamp",
|
||||||
"../../common_audio",
|
"../../common_audio",
|
||||||
"../../rtc_base:buffer",
|
"../../rtc_base:buffer",
|
||||||
"../../rtc_base:checks",
|
"../../rtc_base:checks",
|
||||||
|
@ -439,6 +447,7 @@ if (rtc_include_tests && !build_with_chromium) {
|
||||||
"../../system_wrappers",
|
"../../system_wrappers",
|
||||||
"../../test:fileutils",
|
"../../test:fileutils",
|
||||||
"../../test:test_support",
|
"../../test:test_support",
|
||||||
|
"../../test/time_controller",
|
||||||
]
|
]
|
||||||
absl_deps = [
|
absl_deps = [
|
||||||
"//third_party/abseil-cpp/absl/strings",
|
"//third_party/abseil-cpp/absl/strings",
|
||||||
|
|
|
@ -41,7 +41,8 @@ static const size_t kMinValidCallTimeTimeInMilliseconds =
|
||||||
static const double k2Pi = 6.28318530717959;
|
static const double k2Pi = 6.28318530717959;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AudioDeviceBuffer::AudioDeviceBuffer(TaskQueueFactory* task_queue_factory)
|
AudioDeviceBuffer::AudioDeviceBuffer(TaskQueueFactory* task_queue_factory,
|
||||||
|
bool create_detached)
|
||||||
: task_queue_(task_queue_factory->CreateTaskQueue(
|
: task_queue_(task_queue_factory->CreateTaskQueue(
|
||||||
kTimerQueueName,
|
kTimerQueueName,
|
||||||
TaskQueueFactory::Priority::NORMAL)),
|
TaskQueueFactory::Priority::NORMAL)),
|
||||||
|
@ -67,6 +68,9 @@ AudioDeviceBuffer::AudioDeviceBuffer(TaskQueueFactory* task_queue_factory)
|
||||||
phase_ = 0.0;
|
phase_ = 0.0;
|
||||||
RTC_LOG(LS_WARNING) << "AUDIO_DEVICE_PLAYS_SINUS_TONE is defined!";
|
RTC_LOG(LS_WARNING) << "AUDIO_DEVICE_PLAYS_SINUS_TONE is defined!";
|
||||||
#endif
|
#endif
|
||||||
|
if (create_detached) {
|
||||||
|
main_thread_checker_.Detach();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioDeviceBuffer::~AudioDeviceBuffer() {
|
AudioDeviceBuffer::~AudioDeviceBuffer() {
|
||||||
|
|
|
@ -78,7 +78,11 @@ class AudioDeviceBuffer {
|
||||||
int16_t max_play_level = 0;
|
int16_t max_play_level = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit AudioDeviceBuffer(TaskQueueFactory* task_queue_factory);
|
// If `create_detached` is true, created buffer can be used on another
|
||||||
|
// thread compared to the one on which it was created. It's useful for
|
||||||
|
// testing.
|
||||||
|
explicit AudioDeviceBuffer(TaskQueueFactory* task_queue_factory,
|
||||||
|
bool create_detached = false);
|
||||||
virtual ~AudioDeviceBuffer();
|
virtual ~AudioDeviceBuffer();
|
||||||
|
|
||||||
int32_t RegisterAudioCallback(AudioTransport* audio_callback);
|
int32_t RegisterAudioCallback(AudioTransport* audio_callback);
|
||||||
|
|
|
@ -121,6 +121,17 @@ AudioDeviceModuleImpl::AudioDeviceModuleImpl(
|
||||||
RTC_DLOG(LS_INFO) << __FUNCTION__;
|
RTC_DLOG(LS_INFO) << __FUNCTION__;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioDeviceModuleImpl::AudioDeviceModuleImpl(
|
||||||
|
AudioLayer audio_layer,
|
||||||
|
std::unique_ptr<AudioDeviceGeneric> audio_device,
|
||||||
|
TaskQueueFactory* task_queue_factory,
|
||||||
|
bool create_detached)
|
||||||
|
: audio_layer_(audio_layer),
|
||||||
|
audio_device_buffer_(task_queue_factory, create_detached),
|
||||||
|
audio_device_(std::move(audio_device)) {
|
||||||
|
RTC_DLOG(LS_INFO) << __FUNCTION__;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t AudioDeviceModuleImpl::CheckPlatform() {
|
int32_t AudioDeviceModuleImpl::CheckPlatform() {
|
||||||
RTC_DLOG(LS_INFO) << __FUNCTION__;
|
RTC_DLOG(LS_INFO) << __FUNCTION__;
|
||||||
// Ensure that the current platform is supported
|
// Ensure that the current platform is supported
|
||||||
|
@ -140,6 +151,9 @@ int32_t AudioDeviceModuleImpl::CheckPlatform() {
|
||||||
#elif defined(WEBRTC_MAC)
|
#elif defined(WEBRTC_MAC)
|
||||||
platform = kPlatformMac;
|
platform = kPlatformMac;
|
||||||
RTC_LOG(LS_INFO) << "current platform is Mac";
|
RTC_LOG(LS_INFO) << "current platform is Mac";
|
||||||
|
#elif defined(WEBRTC_FUCHSIA)
|
||||||
|
platform = kPlatformFuchsia;
|
||||||
|
RTC_LOG(LS_INFO) << "current platform is Fuchsia";
|
||||||
#endif
|
#endif
|
||||||
if (platform == kPlatformNotSupported) {
|
if (platform == kPlatformNotSupported) {
|
||||||
RTC_LOG(LS_ERROR)
|
RTC_LOG(LS_ERROR)
|
||||||
|
@ -153,6 +167,10 @@ int32_t AudioDeviceModuleImpl::CheckPlatform() {
|
||||||
|
|
||||||
int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() {
|
int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() {
|
||||||
RTC_LOG(LS_INFO) << __FUNCTION__;
|
RTC_LOG(LS_INFO) << __FUNCTION__;
|
||||||
|
if (audio_device_ != nullptr) {
|
||||||
|
RTC_LOG(LS_INFO) << "Reusing provided audio device";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// Dummy ADM implementations if build flags are set.
|
// Dummy ADM implementations if build flags are set.
|
||||||
#if defined(WEBRTC_DUMMY_AUDIO_BUILD)
|
#if defined(WEBRTC_DUMMY_AUDIO_BUILD)
|
||||||
audio_device_.reset(new AudioDeviceDummy());
|
audio_device_.reset(new AudioDeviceDummy());
|
||||||
|
|
|
@ -35,7 +35,12 @@ class AudioDeviceModuleImpl : public AudioDeviceModuleForTest {
|
||||||
kPlatformLinux = 3,
|
kPlatformLinux = 3,
|
||||||
kPlatformMac = 4,
|
kPlatformMac = 4,
|
||||||
kPlatformAndroid = 5,
|
kPlatformAndroid = 5,
|
||||||
kPlatformIOS = 6
|
kPlatformIOS = 6,
|
||||||
|
// Fuchsia isn't fully supported, as there is no implementation for
|
||||||
|
// AudioDeviceGeneric which will be created for Fuchsia, so
|
||||||
|
// `CreatePlatformSpecificObjects()` call will fail unless usable
|
||||||
|
// implementation will be provided by the user.
|
||||||
|
kPlatformFuchsia = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
int32_t CheckPlatform();
|
int32_t CheckPlatform();
|
||||||
|
@ -44,6 +49,12 @@ class AudioDeviceModuleImpl : public AudioDeviceModuleForTest {
|
||||||
|
|
||||||
AudioDeviceModuleImpl(AudioLayer audio_layer,
|
AudioDeviceModuleImpl(AudioLayer audio_layer,
|
||||||
TaskQueueFactory* task_queue_factory);
|
TaskQueueFactory* task_queue_factory);
|
||||||
|
// If `create_detached` is true, created ADM can be used on another thread
|
||||||
|
// compared to the one on which it was created. It's useful for testing.
|
||||||
|
AudioDeviceModuleImpl(AudioLayer audio_layer,
|
||||||
|
std::unique_ptr<AudioDeviceGeneric> audio_device,
|
||||||
|
TaskQueueFactory* task_queue_factory,
|
||||||
|
bool create_detached);
|
||||||
~AudioDeviceModuleImpl() override;
|
~AudioDeviceModuleImpl() override;
|
||||||
|
|
||||||
// Retrieve the currently utilized audio layer
|
// Retrieve the currently utilized audio layer
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
#include "api/array_view.h"
|
#include "api/array_view.h"
|
||||||
#include "api/make_ref_counted.h"
|
#include "api/make_ref_counted.h"
|
||||||
#include "common_audio/wav_file.h"
|
#include "common_audio/wav_file.h"
|
||||||
|
#include "modules/audio_device/audio_device_impl.h"
|
||||||
#include "modules/audio_device/include/audio_device_default.h"
|
#include "modules/audio_device/include/audio_device_default.h"
|
||||||
|
#include "modules/audio_device/test_audio_device_impl.h"
|
||||||
#include "rtc_base/buffer.h"
|
#include "rtc_base/buffer.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/event.h"
|
#include "rtc_base/event.h"
|
||||||
|
@ -43,164 +45,23 @@ namespace {
|
||||||
constexpr int kFrameLengthUs = 10000;
|
constexpr int kFrameLengthUs = 10000;
|
||||||
constexpr int kFramesPerSecond = rtc::kNumMicrosecsPerSec / kFrameLengthUs;
|
constexpr int kFramesPerSecond = rtc::kNumMicrosecsPerSec / kFrameLengthUs;
|
||||||
|
|
||||||
// TestAudioDeviceModule implements an AudioDevice module that can act both as a
|
class TestAudioDeviceModuleImpl : public AudioDeviceModuleImpl {
|
||||||
// capturer and a renderer. It will use 10ms audio frames.
|
|
||||||
class TestAudioDeviceModuleImpl
|
|
||||||
: public webrtc_impl::AudioDeviceModuleDefault<TestAudioDeviceModule> {
|
|
||||||
public:
|
public:
|
||||||
// Creates a new TestAudioDeviceModule. When capturing or playing, 10 ms audio
|
TestAudioDeviceModuleImpl(
|
||||||
// frames will be processed every 10ms / `speed`.
|
TaskQueueFactory* task_queue_factory,
|
||||||
// `capturer` is an object that produces audio data. Can be nullptr if this
|
std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,
|
||||||
// device is never used for recording.
|
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer,
|
||||||
// `renderer` is an object that receives audio data that would have been
|
|
||||||
// played out. Can be nullptr if this device is never used for playing.
|
|
||||||
// Use one of the Create... functions to get these instances.
|
|
||||||
TestAudioDeviceModuleImpl(TaskQueueFactory* task_queue_factory,
|
|
||||||
std::unique_ptr<Capturer> capturer,
|
|
||||||
std::unique_ptr<Renderer> renderer,
|
|
||||||
float speed = 1)
|
float speed = 1)
|
||||||
: task_queue_factory_(task_queue_factory),
|
: AudioDeviceModuleImpl(
|
||||||
capturer_(std::move(capturer)),
|
AudioLayer::kDummyAudio,
|
||||||
renderer_(std::move(renderer)),
|
std::make_unique<TestAudioDevice>(task_queue_factory,
|
||||||
process_interval_us_(kFrameLengthUs / speed),
|
std::move(capturer),
|
||||||
audio_callback_(nullptr),
|
std::move(renderer),
|
||||||
rendering_(false),
|
speed),
|
||||||
capturing_(false) {
|
task_queue_factory,
|
||||||
auto good_sample_rate = [](int sr) {
|
/*create_detached=*/true) {}
|
||||||
return sr == 8000 || sr == 16000 || sr == 32000 || sr == 44100 ||
|
|
||||||
sr == 48000;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (renderer_) {
|
~TestAudioDeviceModuleImpl() override = default;
|
||||||
const int sample_rate = renderer_->SamplingFrequency();
|
|
||||||
playout_buffer_.resize(
|
|
||||||
SamplesPerFrame(sample_rate) * renderer_->NumChannels(), 0);
|
|
||||||
RTC_CHECK(good_sample_rate(sample_rate));
|
|
||||||
}
|
|
||||||
if (capturer_) {
|
|
||||||
RTC_CHECK(good_sample_rate(capturer_->SamplingFrequency()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~TestAudioDeviceModuleImpl() override {
|
|
||||||
StopPlayout();
|
|
||||||
StopRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t Init() override {
|
|
||||||
task_queue_ =
|
|
||||||
std::make_unique<rtc::TaskQueue>(task_queue_factory_->CreateTaskQueue(
|
|
||||||
"TestAudioDeviceModuleImpl", TaskQueueFactory::Priority::NORMAL));
|
|
||||||
|
|
||||||
RepeatingTaskHandle::Start(task_queue_->Get(), [this]() {
|
|
||||||
ProcessAudio();
|
|
||||||
return TimeDelta::Micros(process_interval_us_);
|
|
||||||
});
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t RegisterAudioCallback(AudioTransport* callback) override {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
RTC_DCHECK(callback || audio_callback_);
|
|
||||||
audio_callback_ = callback;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t StartPlayout() override {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
RTC_CHECK(renderer_);
|
|
||||||
rendering_ = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t StopPlayout() override {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
rendering_ = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t StartRecording() override {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
RTC_CHECK(capturer_);
|
|
||||||
capturing_ = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t StopRecording() override {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
capturing_ = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Playing() const override {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
return rendering_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Recording() const override {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
return capturing_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocks forever until the Recorder stops producing data.
|
|
||||||
void WaitForRecordingEnd() override {
|
|
||||||
done_capturing_.Wait(rtc::Event::kForever);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void ProcessAudio() {
|
|
||||||
MutexLock lock(&lock_);
|
|
||||||
if (capturing_) {
|
|
||||||
// Capture 10ms of audio. 2 bytes per sample.
|
|
||||||
const bool keep_capturing = capturer_->Capture(&recording_buffer_);
|
|
||||||
uint32_t new_mic_level = 0;
|
|
||||||
if (recording_buffer_.size() > 0) {
|
|
||||||
audio_callback_->RecordedDataIsAvailable(
|
|
||||||
recording_buffer_.data(),
|
|
||||||
recording_buffer_.size() / capturer_->NumChannels(),
|
|
||||||
2 * capturer_->NumChannels(), capturer_->NumChannels(),
|
|
||||||
capturer_->SamplingFrequency(), /*totalDelayMS=*/0,
|
|
||||||
/*clockDrift=*/0,
|
|
||||||
/*currentMicLevel=*/0, /*keyPressed=*/false, new_mic_level,
|
|
||||||
absl::make_optional(rtc::TimeNanos()));
|
|
||||||
}
|
|
||||||
if (!keep_capturing) {
|
|
||||||
capturing_ = false;
|
|
||||||
done_capturing_.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rendering_) {
|
|
||||||
size_t samples_out = 0;
|
|
||||||
int64_t elapsed_time_ms = -1;
|
|
||||||
int64_t ntp_time_ms = -1;
|
|
||||||
const int sampling_frequency = renderer_->SamplingFrequency();
|
|
||||||
audio_callback_->NeedMorePlayData(
|
|
||||||
SamplesPerFrame(sampling_frequency), 2 * renderer_->NumChannels(),
|
|
||||||
renderer_->NumChannels(), sampling_frequency, playout_buffer_.data(),
|
|
||||||
samples_out, &elapsed_time_ms, &ntp_time_ms);
|
|
||||||
const bool keep_rendering = renderer_->Render(
|
|
||||||
rtc::ArrayView<const int16_t>(playout_buffer_.data(), samples_out));
|
|
||||||
if (!keep_rendering) {
|
|
||||||
rendering_ = false;
|
|
||||||
done_rendering_.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TaskQueueFactory* const task_queue_factory_;
|
|
||||||
const std::unique_ptr<Capturer> capturer_ RTC_GUARDED_BY(lock_);
|
|
||||||
const std::unique_ptr<Renderer> renderer_ RTC_GUARDED_BY(lock_);
|
|
||||||
const int64_t process_interval_us_;
|
|
||||||
|
|
||||||
mutable Mutex lock_;
|
|
||||||
AudioTransport* audio_callback_ RTC_GUARDED_BY(lock_);
|
|
||||||
bool rendering_ RTC_GUARDED_BY(lock_);
|
|
||||||
bool capturing_ RTC_GUARDED_BY(lock_);
|
|
||||||
rtc::Event done_rendering_;
|
|
||||||
rtc::Event done_capturing_;
|
|
||||||
|
|
||||||
std::vector<int16_t> playout_buffer_ RTC_GUARDED_BY(lock_);
|
|
||||||
rtc::BufferT<int16_t> recording_buffer_ RTC_GUARDED_BY(lock_);
|
|
||||||
std::unique_ptr<rtc::TaskQueue> task_queue_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A fake capturer that generates pulses with random samples between
|
// A fake capturer that generates pulses with random samples between
|
||||||
|
@ -444,8 +305,26 @@ rtc::scoped_refptr<AudioDeviceModule> TestAudioDeviceModule::Create(
|
||||||
std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,
|
std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,
|
||||||
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer,
|
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer,
|
||||||
float speed) {
|
float speed) {
|
||||||
return rtc::make_ref_counted<TestAudioDeviceModuleImpl>(
|
auto audio_device = rtc::make_ref_counted<TestAudioDeviceModuleImpl>(
|
||||||
task_queue_factory, std::move(capturer), std::move(renderer), speed);
|
task_queue_factory, std::move(capturer), std::move(renderer), speed);
|
||||||
|
|
||||||
|
// Ensure that the current platform is supported.
|
||||||
|
if (audio_device->CheckPlatform() == -1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the platform-dependent implementation.
|
||||||
|
if (audio_device->CreatePlatformSpecificObjects() == -1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the generic audio buffer can communicate with the platform
|
||||||
|
// specific parts.
|
||||||
|
if (audio_device->AttachAudioBuffer() == -1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return audio_device;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer>
|
std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer>
|
||||||
|
|
|
@ -29,9 +29,10 @@ namespace webrtc {
|
||||||
// This is test API and is in development, so it can be changed/removed without
|
// This is test API and is in development, so it can be changed/removed without
|
||||||
// notice.
|
// notice.
|
||||||
|
|
||||||
// TestAudioDeviceModule implements an AudioDevice module that can act both as a
|
// This class exists for historical reasons. For now it only contains static
|
||||||
// capturer and a renderer. It will use 10ms audio frames.
|
// methods to create test AudioDeviceModule. Implementation details of that
|
||||||
class TestAudioDeviceModule : public AudioDeviceModule {
|
// module are considered private. This class isn't intended to be instantiated.
|
||||||
|
class TestAudioDeviceModule {
|
||||||
public:
|
public:
|
||||||
// Returns the number of samples that Capturers and Renderers with this
|
// Returns the number of samples that Capturers and Renderers with this
|
||||||
// sampling frequency will work with every time Capture or Render is called.
|
// sampling frequency will work with every time Capture or Render is called.
|
||||||
|
@ -73,8 +74,6 @@ class TestAudioDeviceModule : public AudioDeviceModule {
|
||||||
virtual void SetMaxAmplitude(int16_t amplitude) = 0;
|
virtual void SetMaxAmplitude(int16_t amplitude) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
~TestAudioDeviceModule() override {}
|
|
||||||
|
|
||||||
// Creates a new TestAudioDeviceModule. When capturing or playing, 10 ms audio
|
// Creates a new TestAudioDeviceModule. When capturing or playing, 10 ms audio
|
||||||
// frames will be processed every 10ms / `speed`.
|
// frames will be processed every 10ms / `speed`.
|
||||||
// `capturer` is an object that produces audio data. Can be nullptr if this
|
// `capturer` is an object that produces audio data. Can be nullptr if this
|
||||||
|
@ -132,19 +131,8 @@ class TestAudioDeviceModule : public AudioDeviceModule {
|
||||||
int sampling_frequency_in_hz,
|
int sampling_frequency_in_hz,
|
||||||
int num_channels = 1);
|
int num_channels = 1);
|
||||||
|
|
||||||
int32_t Init() override = 0;
|
private:
|
||||||
int32_t RegisterAudioCallback(AudioTransport* callback) override = 0;
|
TestAudioDeviceModule() = default;
|
||||||
|
|
||||||
int32_t StartPlayout() override = 0;
|
|
||||||
int32_t StopPlayout() override = 0;
|
|
||||||
int32_t StartRecording() override = 0;
|
|
||||||
int32_t StopRecording() override = 0;
|
|
||||||
|
|
||||||
bool Playing() const override = 0;
|
|
||||||
bool Recording() const override = 0;
|
|
||||||
|
|
||||||
// Blocks forever until the Recorder stops producing data.
|
|
||||||
virtual void WaitForRecordingEnd() = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
|
@ -12,17 +12,26 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
#include "api/array_view.h"
|
#include "api/array_view.h"
|
||||||
|
#include "api/task_queue/task_queue_factory.h"
|
||||||
|
#include "api/units/time_delta.h"
|
||||||
|
#include "api/units/timestamp.h"
|
||||||
#include "common_audio/wav_file.h"
|
#include "common_audio/wav_file.h"
|
||||||
#include "common_audio/wav_header.h"
|
#include "common_audio/wav_header.h"
|
||||||
|
#include "modules/audio_device/include/audio_device_defines.h"
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
#include "rtc_base/synchronization/mutex.h"
|
||||||
#include "test/gmock.h"
|
#include "test/gmock.h"
|
||||||
#include "test/gtest.h"
|
#include "test/gtest.h"
|
||||||
#include "test/testsupport/file_utils.h"
|
#include "test/testsupport/file_utils.h"
|
||||||
|
#include "test/time_controller/simulated_time_controller.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void RunTest(const std::vector<int16_t>& input_samples,
|
void RunTest(const std::vector<int16_t>& input_samples,
|
||||||
|
@ -64,7 +73,6 @@ void RunTest(const std::vector<int16_t>& input_samples,
|
||||||
|
|
||||||
remove(output_filename.c_str());
|
remove(output_filename.c_str());
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST(BoundedWavFileWriterTest, NoSilence) {
|
TEST(BoundedWavFileWriterTest, NoSilence) {
|
||||||
static const std::vector<int16_t> kInputSamples = {
|
static const std::vector<int16_t> kInputSamples = {
|
||||||
|
@ -189,4 +197,185 @@ TEST(PulsedNoiseCapturerTest, SetMaxAmplitude) {
|
||||||
EXPECT_GT(max_sample, kAmplitude);
|
EXPECT_GT(max_sample, kAmplitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
|
||||||
|
constexpr Timestamp kStartTime = Timestamp::Millis(10000);
|
||||||
|
|
||||||
|
class TestAudioTransport : public AudioTransport {
|
||||||
|
public:
|
||||||
|
enum class Mode { kPlaying, kRecording };
|
||||||
|
|
||||||
|
explicit TestAudioTransport(Mode mode) : mode_(mode) {}
|
||||||
|
~TestAudioTransport() override = default;
|
||||||
|
|
||||||
|
int32_t RecordedDataIsAvailable(
|
||||||
|
const void* audioSamples,
|
||||||
|
size_t samples_per_channel,
|
||||||
|
size_t bytes_per_sample,
|
||||||
|
size_t number_of_channels,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
uint32_t total_delay_ms,
|
||||||
|
int32_t clock_drift,
|
||||||
|
uint32_t current_mic_level,
|
||||||
|
bool key_pressed,
|
||||||
|
uint32_t& new_mic_level,
|
||||||
|
absl::optional<int64_t> estimated_capture_time_ns) override {
|
||||||
|
new_mic_level = 1;
|
||||||
|
|
||||||
|
if (mode_ != Mode::kRecording) {
|
||||||
|
EXPECT_TRUE(false)
|
||||||
|
<< "NeedMorePlayData mustn't be called when mode isn't kRecording";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
samples_per_channel_.push_back(samples_per_channel);
|
||||||
|
number_of_channels_.push_back(number_of_channels);
|
||||||
|
bytes_per_sample_.push_back(bytes_per_sample);
|
||||||
|
samples_per_second_.push_back(samples_per_second);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t NeedMorePlayData(size_t samples_per_channel,
|
||||||
|
size_t bytes_per_sample,
|
||||||
|
size_t number_of_channels,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
void* audio_samples,
|
||||||
|
size_t& samples_out,
|
||||||
|
int64_t* elapsed_time_ms,
|
||||||
|
int64_t* ntp_time_ms) override {
|
||||||
|
const size_t num_bytes = samples_per_channel * number_of_channels;
|
||||||
|
std::memset(audio_samples, 1, num_bytes);
|
||||||
|
samples_out = samples_per_channel * number_of_channels;
|
||||||
|
*elapsed_time_ms = 0;
|
||||||
|
*ntp_time_ms = 0;
|
||||||
|
|
||||||
|
if (mode_ != Mode::kPlaying) {
|
||||||
|
EXPECT_TRUE(false)
|
||||||
|
<< "NeedMorePlayData mustn't be called when mode isn't kPlaying";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
samples_per_channel_.push_back(samples_per_channel);
|
||||||
|
number_of_channels_.push_back(number_of_channels);
|
||||||
|
bytes_per_sample_.push_back(bytes_per_sample);
|
||||||
|
samples_per_second_.push_back(samples_per_second);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RecordedDataIsAvailable(const void* audio_samples,
|
||||||
|
size_t samples_per_channel,
|
||||||
|
size_t bytes_per_sample,
|
||||||
|
size_t number_of_channels,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
uint32_t total_delay_ms,
|
||||||
|
int32_t clockDrift,
|
||||||
|
uint32_t current_mic_level,
|
||||||
|
bool key_pressed,
|
||||||
|
uint32_t& new_mic_level) override {
|
||||||
|
RTC_CHECK(false) << "This methods should be never executed";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PullRenderData(int bits_per_sample,
|
||||||
|
int sample_rate,
|
||||||
|
size_t number_of_channels,
|
||||||
|
size_t number_of_frames,
|
||||||
|
void* audio_data,
|
||||||
|
int64_t* elapsed_time_ms,
|
||||||
|
int64_t* ntp_time_ms) override {
|
||||||
|
RTC_CHECK(false) << "This methods should be never executed";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> samples_per_channel() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return samples_per_channel_;
|
||||||
|
}
|
||||||
|
std::vector<size_t> number_of_channels() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return number_of_channels_;
|
||||||
|
}
|
||||||
|
std::vector<size_t> bytes_per_sample() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return bytes_per_sample_;
|
||||||
|
}
|
||||||
|
std::vector<size_t> samples_per_second() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return samples_per_second_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Mode mode_;
|
||||||
|
|
||||||
|
mutable Mutex mutex_;
|
||||||
|
std::vector<size_t> samples_per_channel_ RTC_GUARDED_BY(mutex_);
|
||||||
|
std::vector<size_t> number_of_channels_ RTC_GUARDED_BY(mutex_);
|
||||||
|
std::vector<size_t> bytes_per_sample_ RTC_GUARDED_BY(mutex_);
|
||||||
|
std::vector<size_t> samples_per_second_ RTC_GUARDED_BY(mutex_);
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceModuleTest, CreatedADMCanRecord) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
TestAudioTransport audio_transport(TestAudioTransport::Mode::kRecording);
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer> capturer =
|
||||||
|
TestAudioDeviceModule::CreatePulsedNoiseCapturer(
|
||||||
|
/*max_amplitude=*/1000,
|
||||||
|
/*sampling_frequency_in_hz=*/48000, /*num_channels=*/2);
|
||||||
|
|
||||||
|
rtc::scoped_refptr<AudioDeviceModule> adm = TestAudioDeviceModule::Create(
|
||||||
|
time_controller.GetTaskQueueFactory(), std::move(capturer),
|
||||||
|
/*renderer=*/nullptr);
|
||||||
|
|
||||||
|
ASSERT_EQ(adm->RegisterAudioCallback(&audio_transport), 0);
|
||||||
|
ASSERT_EQ(adm->Init(), 0);
|
||||||
|
|
||||||
|
EXPECT_FALSE(adm->RecordingIsInitialized());
|
||||||
|
ASSERT_EQ(adm->InitRecording(), 0);
|
||||||
|
EXPECT_TRUE(adm->RecordingIsInitialized());
|
||||||
|
ASSERT_EQ(adm->StartRecording(), 0);
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_TRUE(adm->Recording());
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_EQ(adm->StopRecording(), 0);
|
||||||
|
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_channel(),
|
||||||
|
ElementsAre(480, 480, 480));
|
||||||
|
EXPECT_THAT(audio_transport.number_of_channels(), ElementsAre(2, 2, 2));
|
||||||
|
EXPECT_THAT(audio_transport.bytes_per_sample(), ElementsAre(4, 4, 4));
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_second(),
|
||||||
|
ElementsAre(48000, 48000, 48000));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceModuleTest, CreatedADMCanPlay) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
TestAudioTransport audio_transport(TestAudioTransport::Mode::kPlaying);
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer =
|
||||||
|
TestAudioDeviceModule::CreateDiscardRenderer(
|
||||||
|
/*sampling_frequency_in_hz=*/48000, /*num_channels=*/2);
|
||||||
|
|
||||||
|
rtc::scoped_refptr<AudioDeviceModule> adm =
|
||||||
|
TestAudioDeviceModule::Create(time_controller.GetTaskQueueFactory(),
|
||||||
|
/*capturer=*/nullptr, std::move(renderer));
|
||||||
|
|
||||||
|
ASSERT_EQ(adm->RegisterAudioCallback(&audio_transport), 0);
|
||||||
|
ASSERT_EQ(adm->Init(), 0);
|
||||||
|
|
||||||
|
EXPECT_FALSE(adm->PlayoutIsInitialized());
|
||||||
|
ASSERT_EQ(adm->InitPlayout(), 0);
|
||||||
|
EXPECT_TRUE(adm->PlayoutIsInitialized());
|
||||||
|
ASSERT_EQ(adm->StartPlayout(), 0);
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_TRUE(adm->Playing());
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_EQ(adm->StopPlayout(), 0);
|
||||||
|
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_channel(),
|
||||||
|
ElementsAre(480, 480, 480));
|
||||||
|
EXPECT_THAT(audio_transport.number_of_channels(), ElementsAre(2, 2, 2));
|
||||||
|
EXPECT_THAT(audio_transport.bytes_per_sample(), ElementsAre(4, 4, 4));
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_second(),
|
||||||
|
ElementsAre(48000, 48000, 48000));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
212
modules/audio_device/test_audio_device_impl.cc
Normal file
212
modules/audio_device/test_audio_device_impl.cc
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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/test_audio_device_impl.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include "api/array_view.h"
|
||||||
|
#include "api/task_queue/task_queue_factory.h"
|
||||||
|
#include "api/units/time_delta.h"
|
||||||
|
#include "modules/audio_device/include/test_audio_device.h"
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
|
#include "rtc_base/synchronization/mutex.h"
|
||||||
|
#include "rtc_base/task_queue.h"
|
||||||
|
#include "rtc_base/task_utils/repeating_task.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int kFrameLengthUs = 10000;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TestAudioDevice::TestAudioDevice(
|
||||||
|
TaskQueueFactory* task_queue_factory,
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer,
|
||||||
|
float speed)
|
||||||
|
: task_queue_factory_(task_queue_factory),
|
||||||
|
capturer_(std::move(capturer)),
|
||||||
|
renderer_(std::move(renderer)),
|
||||||
|
process_interval_us_(kFrameLengthUs / speed),
|
||||||
|
audio_buffer_(nullptr),
|
||||||
|
rendering_(false),
|
||||||
|
capturing_(false) {
|
||||||
|
auto good_sample_rate = [](int sr) {
|
||||||
|
return sr == 8000 || sr == 16000 || sr == 32000 || sr == 44100 ||
|
||||||
|
sr == 48000;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (renderer_) {
|
||||||
|
const int sample_rate = renderer_->SamplingFrequency();
|
||||||
|
playout_buffer_.resize(TestAudioDeviceModule::SamplesPerFrame(sample_rate) *
|
||||||
|
renderer_->NumChannels(),
|
||||||
|
0);
|
||||||
|
RTC_CHECK(good_sample_rate(sample_rate));
|
||||||
|
}
|
||||||
|
if (capturer_) {
|
||||||
|
RTC_CHECK(good_sample_rate(capturer_->SamplingFrequency()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioDeviceGeneric::InitStatus TestAudioDevice::Init() {
|
||||||
|
task_queue_ =
|
||||||
|
std::make_unique<rtc::TaskQueue>(task_queue_factory_->CreateTaskQueue(
|
||||||
|
"TestAudioDeviceModuleImpl", TaskQueueFactory::Priority::NORMAL));
|
||||||
|
|
||||||
|
RepeatingTaskHandle::Start(task_queue_->Get(), [this]() {
|
||||||
|
ProcessAudio();
|
||||||
|
return TimeDelta::Micros(process_interval_us_);
|
||||||
|
});
|
||||||
|
return InitStatus::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::PlayoutIsAvailable(bool& available) {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
available = renderer_ != nullptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::InitPlayout() {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
|
||||||
|
if (rendering_) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audio_buffer_ != nullptr && renderer_ != nullptr) {
|
||||||
|
// Update webrtc audio buffer with the selected parameters
|
||||||
|
audio_buffer_->SetPlayoutSampleRate(renderer_->SamplingFrequency());
|
||||||
|
audio_buffer_->SetPlayoutChannels(renderer_->NumChannels());
|
||||||
|
}
|
||||||
|
rendering_initialized_ = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestAudioDevice::PlayoutIsInitialized() const {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
return rendering_initialized_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::StartPlayout() {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
RTC_CHECK(renderer_);
|
||||||
|
rendering_ = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::StopPlayout() {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
rendering_ = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::RecordingIsAvailable(bool& available) {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
available = capturer_ != nullptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::InitRecording() {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
|
||||||
|
if (capturing_) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audio_buffer_ != nullptr && capturer_ != nullptr) {
|
||||||
|
// Update webrtc audio buffer with the selected parameters
|
||||||
|
audio_buffer_->SetRecordingSampleRate(capturer_->SamplingFrequency());
|
||||||
|
audio_buffer_->SetRecordingChannels(capturer_->NumChannels());
|
||||||
|
}
|
||||||
|
capturing_initialized_ = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestAudioDevice::RecordingIsInitialized() const {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
return capturing_initialized_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::StartRecording() {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
RTC_CHECK(capturer_);
|
||||||
|
capturing_ = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t TestAudioDevice::StopRecording() {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
capturing_ = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestAudioDevice::Playing() const {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
return rendering_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestAudioDevice::Recording() const {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
return capturing_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestAudioDevice::ProcessAudio() {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
if (audio_buffer_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (capturing_) {
|
||||||
|
// Capture 10ms of audio. 2 bytes per sample.
|
||||||
|
const bool keep_capturing = capturer_->Capture(&recording_buffer_);
|
||||||
|
if (recording_buffer_.size() > 0) {
|
||||||
|
audio_buffer_->SetRecordedBuffer(
|
||||||
|
recording_buffer_.data(),
|
||||||
|
recording_buffer_.size() / capturer_->NumChannels(),
|
||||||
|
absl::make_optional(rtc::TimeNanos()));
|
||||||
|
audio_buffer_->DeliverRecordedData();
|
||||||
|
}
|
||||||
|
if (!keep_capturing) {
|
||||||
|
capturing_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rendering_) {
|
||||||
|
const int sampling_frequency = renderer_->SamplingFrequency();
|
||||||
|
int32_t samples_per_channel = audio_buffer_->RequestPlayoutData(
|
||||||
|
TestAudioDeviceModule::SamplesPerFrame(sampling_frequency));
|
||||||
|
audio_buffer_->GetPlayoutData(playout_buffer_.data());
|
||||||
|
size_t samples_out = samples_per_channel * renderer_->NumChannels();
|
||||||
|
RTC_CHECK_LE(samples_out, playout_buffer_.size());
|
||||||
|
const bool keep_rendering = renderer_->Render(
|
||||||
|
rtc::ArrayView<const int16_t>(playout_buffer_.data(), samples_out));
|
||||||
|
if (!keep_rendering) {
|
||||||
|
rendering_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestAudioDevice::AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) {
|
||||||
|
MutexLock lock(&lock_);
|
||||||
|
RTC_DCHECK(audio_buffer || audio_buffer_);
|
||||||
|
audio_buffer_ = audio_buffer;
|
||||||
|
|
||||||
|
if (renderer_ != nullptr) {
|
||||||
|
audio_buffer_->SetPlayoutSampleRate(renderer_->SamplingFrequency());
|
||||||
|
audio_buffer_->SetPlayoutChannels(renderer_->NumChannels());
|
||||||
|
}
|
||||||
|
if (capturer_ != nullptr) {
|
||||||
|
audio_buffer_->SetRecordingSampleRate(capturer_->SamplingFrequency());
|
||||||
|
audio_buffer_->SetRecordingChannels(capturer_->NumChannels());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
198
modules/audio_device/test_audio_device_impl.h
Normal file
198
modules/audio_device/test_audio_device_impl.h
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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 MODULES_AUDIO_DEVICE_TEST_AUDIO_DEVICE_IMPL_H_
|
||||||
|
#define MODULES_AUDIO_DEVICE_TEST_AUDIO_DEVICE_IMPL_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/task_queue/task_queue_factory.h"
|
||||||
|
#include "modules/audio_device/audio_device_buffer.h"
|
||||||
|
#include "modules/audio_device/audio_device_generic.h"
|
||||||
|
#include "modules/audio_device/include/audio_device.h"
|
||||||
|
#include "modules/audio_device/include/audio_device_defines.h"
|
||||||
|
#include "modules/audio_device/include/test_audio_device.h"
|
||||||
|
#include "rtc_base/buffer.h"
|
||||||
|
#include "rtc_base/synchronization/mutex.h"
|
||||||
|
#include "rtc_base/task_queue.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class TestAudioDevice : public AudioDeviceGeneric {
|
||||||
|
public:
|
||||||
|
// Creates a new TestAudioDevice. When capturing or playing, 10 ms audio
|
||||||
|
// frames will be processed every 10ms / `speed`.
|
||||||
|
// `capturer` is an object that produces audio data. Can be nullptr if this
|
||||||
|
// device is never used for recording.
|
||||||
|
// `renderer` is an object that receives audio data that would have been
|
||||||
|
// played out. Can be nullptr if this device is never used for playing.
|
||||||
|
TestAudioDevice(TaskQueueFactory* task_queue_factory,
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer,
|
||||||
|
float speed = 1);
|
||||||
|
TestAudioDevice(const TestAudioDevice&) = delete;
|
||||||
|
TestAudioDevice& operator=(const TestAudioDevice&) = delete;
|
||||||
|
~TestAudioDevice() override = default;
|
||||||
|
|
||||||
|
// Retrieve the currently utilized audio layer
|
||||||
|
int32_t ActiveAudioLayer(
|
||||||
|
AudioDeviceModule::AudioLayer& audioLayer) const override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main initializaton and termination
|
||||||
|
InitStatus Init() override;
|
||||||
|
int32_t Terminate() override { return 0; }
|
||||||
|
bool Initialized() const override { return true; }
|
||||||
|
|
||||||
|
// Device enumeration
|
||||||
|
int16_t PlayoutDevices() override { return 0; }
|
||||||
|
int16_t RecordingDevices() override { return 0; }
|
||||||
|
int32_t PlayoutDeviceName(uint16_t index,
|
||||||
|
char name[kAdmMaxDeviceNameSize],
|
||||||
|
char guid[kAdmMaxGuidSize]) override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int32_t RecordingDeviceName(uint16_t index,
|
||||||
|
char name[kAdmMaxDeviceNameSize],
|
||||||
|
char guid[kAdmMaxGuidSize]) override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device selection
|
||||||
|
int32_t SetPlayoutDevice(uint16_t index) override { return 0; }
|
||||||
|
int32_t SetPlayoutDevice(
|
||||||
|
AudioDeviceModule::WindowsDeviceType device) override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int32_t SetRecordingDevice(uint16_t index) override { return 0; }
|
||||||
|
int32_t SetRecordingDevice(
|
||||||
|
AudioDeviceModule::WindowsDeviceType device) override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio transport initialization
|
||||||
|
int32_t PlayoutIsAvailable(bool& available) override;
|
||||||
|
int32_t InitPlayout() override;
|
||||||
|
bool PlayoutIsInitialized() const override;
|
||||||
|
int32_t RecordingIsAvailable(bool& available) override;
|
||||||
|
int32_t InitRecording() override;
|
||||||
|
bool RecordingIsInitialized() const override;
|
||||||
|
|
||||||
|
// Audio transport control
|
||||||
|
int32_t StartPlayout() override;
|
||||||
|
int32_t StopPlayout() override;
|
||||||
|
bool Playing() const override;
|
||||||
|
int32_t StartRecording() override;
|
||||||
|
int32_t StopRecording() override;
|
||||||
|
bool Recording() const override;
|
||||||
|
|
||||||
|
// Audio mixer initialization
|
||||||
|
int32_t InitSpeaker() override { return 0; }
|
||||||
|
bool SpeakerIsInitialized() const override { return true; }
|
||||||
|
int32_t InitMicrophone() override { return 0; }
|
||||||
|
bool MicrophoneIsInitialized() const override { return true; }
|
||||||
|
|
||||||
|
// Speaker volume controls
|
||||||
|
int32_t SpeakerVolumeIsAvailable(bool& available) override { 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; }
|
||||||
|
|
||||||
|
// Microphone volume controls
|
||||||
|
int32_t MicrophoneVolumeIsAvailable(bool& available) override { 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; }
|
||||||
|
|
||||||
|
// Speaker mute control
|
||||||
|
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; }
|
||||||
|
|
||||||
|
// Microphone mute control
|
||||||
|
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; }
|
||||||
|
|
||||||
|
// Stereo support
|
||||||
|
int32_t StereoPlayoutIsAvailable(bool& available) override {
|
||||||
|
available = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int32_t SetStereoPlayout(bool enable) override { return 0; }
|
||||||
|
int32_t StereoPlayout(bool& enabled) const override { return 0; }
|
||||||
|
int32_t StereoRecordingIsAvailable(bool& available) override {
|
||||||
|
available = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int32_t SetStereoRecording(bool enable) override { return 0; }
|
||||||
|
int32_t StereoRecording(bool& enabled) const override { return 0; }
|
||||||
|
|
||||||
|
// Delay information and control
|
||||||
|
int32_t PlayoutDelay(uint16_t& delayMS) const override {
|
||||||
|
delayMS = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Android only
|
||||||
|
bool BuiltInAECIsAvailable() const override { return false; }
|
||||||
|
bool BuiltInAGCIsAvailable() const override { return false; }
|
||||||
|
bool BuiltInNSIsAvailable() const override { return false; }
|
||||||
|
|
||||||
|
// Windows Core Audio and Android only.
|
||||||
|
int32_t EnableBuiltInAEC(bool enable) override { return -1; }
|
||||||
|
int32_t EnableBuiltInAGC(bool enable) override { return -1; }
|
||||||
|
int32_t EnableBuiltInNS(bool enable) override { return -1; }
|
||||||
|
|
||||||
|
// Play underrun count.
|
||||||
|
int32_t GetPlayoutUnderrunCount() const override { return -1; }
|
||||||
|
|
||||||
|
// iOS only.
|
||||||
|
// TODO(henrika): add Android support.
|
||||||
|
#if defined(WEBRTC_IOS)
|
||||||
|
int GetPlayoutAudioParameters(AudioParameters* params) const override {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int GetRecordAudioParameters(AudioParameters* params) const override {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif // WEBRTC_IOS
|
||||||
|
|
||||||
|
void AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ProcessAudio();
|
||||||
|
|
||||||
|
TaskQueueFactory* const task_queue_factory_;
|
||||||
|
const std::unique_ptr<TestAudioDeviceModule::Capturer> capturer_
|
||||||
|
RTC_GUARDED_BY(lock_);
|
||||||
|
const std::unique_ptr<TestAudioDeviceModule::Renderer> renderer_
|
||||||
|
RTC_GUARDED_BY(lock_);
|
||||||
|
const int64_t process_interval_us_;
|
||||||
|
|
||||||
|
mutable Mutex lock_;
|
||||||
|
AudioDeviceBuffer* audio_buffer_ RTC_GUARDED_BY(lock_) = nullptr;
|
||||||
|
bool rendering_ RTC_GUARDED_BY(lock_) = false;
|
||||||
|
bool capturing_ RTC_GUARDED_BY(lock_) = false;
|
||||||
|
bool rendering_initialized_ RTC_GUARDED_BY(lock_) = false;
|
||||||
|
bool capturing_initialized_ RTC_GUARDED_BY(lock_) = false;
|
||||||
|
|
||||||
|
std::vector<int16_t> playout_buffer_ RTC_GUARDED_BY(lock_);
|
||||||
|
rtc::BufferT<int16_t> recording_buffer_ RTC_GUARDED_BY(lock_);
|
||||||
|
std::unique_ptr<rtc::TaskQueue> task_queue_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // MODULES_AUDIO_DEVICE_TEST_AUDIO_DEVICE_IMPL_H_
|
275
modules/audio_device/test_audio_device_impl_test.cc
Normal file
275
modules/audio_device/test_audio_device_impl_test.cc
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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/test_audio_device_impl.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include "api/task_queue/task_queue_factory.h"
|
||||||
|
#include "api/units/time_delta.h"
|
||||||
|
#include "api/units/timestamp.h"
|
||||||
|
#include "modules/audio_device/audio_device_buffer.h"
|
||||||
|
#include "modules/audio_device/audio_device_generic.h"
|
||||||
|
#include "modules/audio_device/include/audio_device.h"
|
||||||
|
#include "modules/audio_device/include/audio_device_defines.h"
|
||||||
|
#include "modules/audio_device/include/test_audio_device.h"
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
|
#include "rtc_base/synchronization/mutex.h"
|
||||||
|
#include "test/gmock.h"
|
||||||
|
#include "test/gtest.h"
|
||||||
|
#include "test/time_controller/simulated_time_controller.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
|
||||||
|
constexpr Timestamp kStartTime = Timestamp::Millis(10000);
|
||||||
|
|
||||||
|
class TestAudioTransport : public AudioTransport {
|
||||||
|
public:
|
||||||
|
enum class Mode { kPlaying, kRecording };
|
||||||
|
|
||||||
|
explicit TestAudioTransport(Mode mode) : mode_(mode) {}
|
||||||
|
~TestAudioTransport() override = default;
|
||||||
|
|
||||||
|
int32_t RecordedDataIsAvailable(
|
||||||
|
const void* audioSamples,
|
||||||
|
size_t samples_per_channel,
|
||||||
|
size_t bytes_per_sample,
|
||||||
|
size_t number_of_channels,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
uint32_t total_delay_ms,
|
||||||
|
int32_t clock_drift,
|
||||||
|
uint32_t current_mic_level,
|
||||||
|
bool key_pressed,
|
||||||
|
uint32_t& new_mic_level,
|
||||||
|
absl::optional<int64_t> estimated_capture_time_ns) override {
|
||||||
|
new_mic_level = 1;
|
||||||
|
|
||||||
|
if (mode_ != Mode::kRecording) {
|
||||||
|
EXPECT_TRUE(false)
|
||||||
|
<< "NeedMorePlayData mustn't be called when mode isn't kRecording";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
samples_per_channel_.push_back(samples_per_channel);
|
||||||
|
number_of_channels_.push_back(number_of_channels);
|
||||||
|
bytes_per_sample_.push_back(bytes_per_sample);
|
||||||
|
samples_per_second_.push_back(samples_per_second);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t NeedMorePlayData(size_t samples_per_channel,
|
||||||
|
size_t bytes_per_sample,
|
||||||
|
size_t number_of_channels,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
void* audio_samples,
|
||||||
|
size_t& samples_out,
|
||||||
|
int64_t* elapsed_time_ms,
|
||||||
|
int64_t* ntp_time_ms) override {
|
||||||
|
const size_t num_bytes = samples_per_channel * number_of_channels;
|
||||||
|
std::memset(audio_samples, 1, num_bytes);
|
||||||
|
samples_out = samples_per_channel * number_of_channels;
|
||||||
|
*elapsed_time_ms = 0;
|
||||||
|
*ntp_time_ms = 0;
|
||||||
|
|
||||||
|
if (mode_ != Mode::kPlaying) {
|
||||||
|
EXPECT_TRUE(false)
|
||||||
|
<< "NeedMorePlayData mustn't be called when mode isn't kPlaying";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
samples_per_channel_.push_back(samples_per_channel);
|
||||||
|
number_of_channels_.push_back(number_of_channels);
|
||||||
|
bytes_per_sample_.push_back(bytes_per_sample);
|
||||||
|
samples_per_second_.push_back(samples_per_second);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RecordedDataIsAvailable(const void* audio_samples,
|
||||||
|
size_t samples_per_channel,
|
||||||
|
size_t bytes_per_sample,
|
||||||
|
size_t number_of_channels,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
uint32_t total_delay_ms,
|
||||||
|
int32_t clockDrift,
|
||||||
|
uint32_t current_mic_level,
|
||||||
|
bool key_pressed,
|
||||||
|
uint32_t& new_mic_level) override {
|
||||||
|
RTC_CHECK(false) << "This methods should be never executed";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PullRenderData(int bits_per_sample,
|
||||||
|
int sample_rate,
|
||||||
|
size_t number_of_channels,
|
||||||
|
size_t number_of_frames,
|
||||||
|
void* audio_data,
|
||||||
|
int64_t* elapsed_time_ms,
|
||||||
|
int64_t* ntp_time_ms) override {
|
||||||
|
RTC_CHECK(false) << "This methods should be never executed";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> samples_per_channel() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return samples_per_channel_;
|
||||||
|
}
|
||||||
|
std::vector<size_t> number_of_channels() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return number_of_channels_;
|
||||||
|
}
|
||||||
|
std::vector<size_t> bytes_per_sample() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return bytes_per_sample_;
|
||||||
|
}
|
||||||
|
std::vector<size_t> samples_per_second() const {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
return samples_per_second_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Mode mode_;
|
||||||
|
|
||||||
|
mutable Mutex mutex_;
|
||||||
|
std::vector<size_t> samples_per_channel_ RTC_GUARDED_BY(mutex_);
|
||||||
|
std::vector<size_t> number_of_channels_ RTC_GUARDED_BY(mutex_);
|
||||||
|
std::vector<size_t> bytes_per_sample_ RTC_GUARDED_BY(mutex_);
|
||||||
|
std::vector<size_t> samples_per_second_ RTC_GUARDED_BY(mutex_);
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceTest, EnablingRecordingProducesAudio) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
TestAudioTransport audio_transport(TestAudioTransport::Mode::kRecording);
|
||||||
|
AudioDeviceBuffer audio_buffer(time_controller.GetTaskQueueFactory());
|
||||||
|
ASSERT_EQ(audio_buffer.RegisterAudioCallback(&audio_transport), 0);
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer> capturer =
|
||||||
|
TestAudioDeviceModule::CreatePulsedNoiseCapturer(
|
||||||
|
/*max_amplitude=*/1000,
|
||||||
|
/*sampling_frequency_in_hz=*/48000, /*num_channels=*/2);
|
||||||
|
|
||||||
|
TestAudioDevice audio_device(time_controller.GetTaskQueueFactory(),
|
||||||
|
std::move(capturer),
|
||||||
|
/*renderer=*/nullptr);
|
||||||
|
ASSERT_EQ(audio_device.Init(), AudioDeviceGeneric::InitStatus::OK);
|
||||||
|
audio_device.AttachAudioBuffer(&audio_buffer);
|
||||||
|
|
||||||
|
EXPECT_FALSE(audio_device.RecordingIsInitialized());
|
||||||
|
ASSERT_EQ(audio_device.InitRecording(), 0);
|
||||||
|
EXPECT_TRUE(audio_device.RecordingIsInitialized());
|
||||||
|
audio_buffer.StartRecording();
|
||||||
|
ASSERT_EQ(audio_device.StartRecording(), 0);
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_TRUE(audio_device.Recording());
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_EQ(audio_device.StopRecording(), 0);
|
||||||
|
audio_buffer.StopRecording();
|
||||||
|
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_channel(),
|
||||||
|
ElementsAre(480, 480, 480));
|
||||||
|
EXPECT_THAT(audio_transport.number_of_channels(), ElementsAre(2, 2, 2));
|
||||||
|
EXPECT_THAT(audio_transport.bytes_per_sample(), ElementsAre(4, 4, 4));
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_second(),
|
||||||
|
ElementsAre(48000, 48000, 48000));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceTest, RecordingIsAvailableWhenCapturerIsSet) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer> capturer =
|
||||||
|
TestAudioDeviceModule::CreatePulsedNoiseCapturer(
|
||||||
|
/*max_amplitude=*/1000,
|
||||||
|
/*sampling_frequency_in_hz=*/48000, /*num_channels=*/2);
|
||||||
|
|
||||||
|
TestAudioDevice audio_device(time_controller.GetTaskQueueFactory(),
|
||||||
|
std::move(capturer),
|
||||||
|
/*renderer=*/nullptr);
|
||||||
|
ASSERT_EQ(audio_device.Init(), AudioDeviceGeneric::InitStatus::OK);
|
||||||
|
|
||||||
|
bool available;
|
||||||
|
EXPECT_EQ(audio_device.RecordingIsAvailable(available), 0);
|
||||||
|
EXPECT_TRUE(available);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceTest, RecordingIsNotAvailableWhenCapturerIsNotSet) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
TestAudioDevice audio_device(time_controller.GetTaskQueueFactory(),
|
||||||
|
/*capturer=*/nullptr,
|
||||||
|
/*renderer=*/nullptr);
|
||||||
|
ASSERT_EQ(audio_device.Init(), AudioDeviceGeneric::InitStatus::OK);
|
||||||
|
|
||||||
|
bool available;
|
||||||
|
EXPECT_EQ(audio_device.RecordingIsAvailable(available), 0);
|
||||||
|
EXPECT_FALSE(available);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceTest, EnablingPlayoutProducesAudio) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
TestAudioTransport audio_transport(TestAudioTransport::Mode::kPlaying);
|
||||||
|
AudioDeviceBuffer audio_buffer(time_controller.GetTaskQueueFactory());
|
||||||
|
ASSERT_EQ(audio_buffer.RegisterAudioCallback(&audio_transport), 0);
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer =
|
||||||
|
TestAudioDeviceModule::CreateDiscardRenderer(
|
||||||
|
/*sampling_frequency_in_hz=*/48000, /*num_channels=*/2);
|
||||||
|
|
||||||
|
TestAudioDevice audio_device(time_controller.GetTaskQueueFactory(),
|
||||||
|
/*capturer=*/nullptr, std::move(renderer));
|
||||||
|
ASSERT_EQ(audio_device.Init(), AudioDeviceGeneric::InitStatus::OK);
|
||||||
|
audio_device.AttachAudioBuffer(&audio_buffer);
|
||||||
|
|
||||||
|
EXPECT_FALSE(audio_device.PlayoutIsInitialized());
|
||||||
|
ASSERT_EQ(audio_device.InitPlayout(), 0);
|
||||||
|
EXPECT_TRUE(audio_device.PlayoutIsInitialized());
|
||||||
|
audio_buffer.StartPlayout();
|
||||||
|
ASSERT_EQ(audio_device.StartPlayout(), 0);
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_TRUE(audio_device.Playing());
|
||||||
|
time_controller.AdvanceTime(TimeDelta::Millis(10));
|
||||||
|
ASSERT_EQ(audio_device.StopPlayout(), 0);
|
||||||
|
audio_buffer.StopPlayout();
|
||||||
|
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_channel(),
|
||||||
|
ElementsAre(480, 480, 480));
|
||||||
|
EXPECT_THAT(audio_transport.number_of_channels(), ElementsAre(2, 2, 2));
|
||||||
|
EXPECT_THAT(audio_transport.bytes_per_sample(), ElementsAre(4, 4, 4));
|
||||||
|
EXPECT_THAT(audio_transport.samples_per_second(),
|
||||||
|
ElementsAre(48000, 48000, 48000));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceTest, PlayoutIsAvailableWhenRendererIsSet) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer =
|
||||||
|
TestAudioDeviceModule::CreateDiscardRenderer(
|
||||||
|
/*sampling_frequency_in_hz=*/48000, /*num_channels=*/2);
|
||||||
|
|
||||||
|
TestAudioDevice audio_device(time_controller.GetTaskQueueFactory(),
|
||||||
|
/*capturer=*/nullptr, std::move(renderer));
|
||||||
|
ASSERT_EQ(audio_device.Init(), AudioDeviceGeneric::InitStatus::OK);
|
||||||
|
|
||||||
|
bool available;
|
||||||
|
EXPECT_EQ(audio_device.PlayoutIsAvailable(available), 0);
|
||||||
|
EXPECT_TRUE(available);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAudioDeviceTest, PlayoutIsNotAvailableWhenRendererIsNotSet) {
|
||||||
|
GlobalSimulatedTimeController time_controller(kStartTime);
|
||||||
|
TestAudioDevice audio_device(time_controller.GetTaskQueueFactory(),
|
||||||
|
/*capturer=*/nullptr,
|
||||||
|
/*renderer=*/nullptr);
|
||||||
|
ASSERT_EQ(audio_device.Init(), AudioDeviceGeneric::InitStatus::OK);
|
||||||
|
|
||||||
|
bool available;
|
||||||
|
EXPECT_EQ(audio_device.PlayoutIsAvailable(available), 0);
|
||||||
|
EXPECT_FALSE(available);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace webrtc
|
Loading…
Reference in a new issue