| /* |
| * 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/audio/audio_device.h" |
| #include "api/audio/audio_device_defines.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/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) << "RecordedDataIsAvailable 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 |