From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../modules/audio_device/include/audio_device.h | 194 ++++++++ .../include/audio_device_data_observer.h | 72 +++ .../audio_device/include/audio_device_default.h | 132 +++++ .../audio_device/include/audio_device_defines.h | 177 +++++++ .../audio_device/include/audio_device_factory.cc | 53 ++ .../audio_device/include/audio_device_factory.h | 59 +++ .../audio_device/include/fake_audio_device.h | 33 ++ .../audio_device/include/mock_audio_device.h | 156 ++++++ .../audio_device/include/mock_audio_transport.h | 81 ++++ .../audio_device/include/test_audio_device.cc | 540 +++++++++++++++++++++ .../audio_device/include/test_audio_device.h | 155 ++++++ .../include/test_audio_device_unittest.cc | 528 ++++++++++++++++++++ 12 files changed, 2180 insertions(+) create mode 100644 third_party/libwebrtc/modules/audio_device/include/audio_device.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/audio_device_data_observer.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/audio_device_default.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/audio_device_defines.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/audio_device_factory.cc create mode 100644 third_party/libwebrtc/modules/audio_device/include/audio_device_factory.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/mock_audio_device.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/mock_audio_transport.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/test_audio_device.cc create mode 100644 third_party/libwebrtc/modules/audio_device/include/test_audio_device.h create mode 100644 third_party/libwebrtc/modules/audio_device/include/test_audio_device_unittest.cc (limited to 'third_party/libwebrtc/modules/audio_device/include') diff --git a/third_party/libwebrtc/modules/audio_device/include/audio_device.h b/third_party/libwebrtc/modules/audio_device/include/audio_device.h new file mode 100644 index 0000000000..936ee6cb04 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/audio_device.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2012 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_INCLUDE_AUDIO_DEVICE_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_H_ + +#include "absl/types/optional.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "modules/audio_device/include/audio_device_defines.h" +#include "rtc_base/ref_count.h" + +namespace webrtc { + +class AudioDeviceModuleForTest; + +class AudioDeviceModule : public rtc::RefCountInterface { + public: + enum AudioLayer { + kPlatformDefaultAudio = 0, + kWindowsCoreAudio, + kWindowsCoreAudio2, + kLinuxAlsaAudio, + kLinuxPulseAudio, + kAndroidJavaAudio, + kAndroidOpenSLESAudio, + kAndroidJavaInputAndOpenSLESOutputAudio, + kAndroidAAudioAudio, + kAndroidJavaInputAndAAudioOutputAudio, + kDummyAudio, + }; + + enum WindowsDeviceType { + kDefaultCommunicationDevice = -1, + kDefaultDevice = -2 + }; + + struct Stats { + // The fields below correspond to similarly-named fields in the WebRTC stats + // spec. https://w3c.github.io/webrtc-stats/#playoutstats-dict* + double synthesized_samples_duration_s = 0; + uint64_t synthesized_samples_events = 0; + double total_samples_duration_s = 0; + double total_playout_delay_s = 0; + uint64_t total_samples_count = 0; + }; + + public: + // Creates a default ADM for usage in production code. + static rtc::scoped_refptr Create( + AudioLayer audio_layer, + TaskQueueFactory* task_queue_factory); + // Creates an ADM with support for extra test methods. Don't use this factory + // in production code. + static rtc::scoped_refptr CreateForTest( + AudioLayer audio_layer, + TaskQueueFactory* task_queue_factory); + + // Retrieve the currently utilized audio layer + virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const = 0; + + // Full-duplex transportation of PCM audio + virtual int32_t RegisterAudioCallback(AudioTransport* audioCallback) = 0; + + // Main initialization and termination + virtual int32_t Init() = 0; + virtual int32_t Terminate() = 0; + virtual bool Initialized() const = 0; + + // Device enumeration + virtual int16_t PlayoutDevices() = 0; + virtual int16_t RecordingDevices() = 0; + virtual int32_t PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) = 0; + virtual int32_t RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) = 0; + + // Device selection + virtual int32_t SetPlayoutDevice(uint16_t index) = 0; + virtual int32_t SetPlayoutDevice(WindowsDeviceType device) = 0; + virtual int32_t SetRecordingDevice(uint16_t index) = 0; + virtual int32_t SetRecordingDevice(WindowsDeviceType device) = 0; + + // Audio transport initialization + virtual int32_t PlayoutIsAvailable(bool* available) = 0; + virtual int32_t InitPlayout() = 0; + virtual bool PlayoutIsInitialized() const = 0; + virtual int32_t RecordingIsAvailable(bool* available) = 0; + virtual int32_t InitRecording() = 0; + virtual bool RecordingIsInitialized() const = 0; + + // Audio transport control + virtual int32_t StartPlayout() = 0; + virtual int32_t StopPlayout() = 0; + virtual bool Playing() const = 0; + virtual int32_t StartRecording() = 0; + virtual int32_t StopRecording() = 0; + virtual bool Recording() const = 0; + + // Audio mixer initialization + virtual int32_t InitSpeaker() = 0; + virtual bool SpeakerIsInitialized() const = 0; + virtual int32_t InitMicrophone() = 0; + virtual bool MicrophoneIsInitialized() const = 0; + + // Speaker volume controls + virtual int32_t SpeakerVolumeIsAvailable(bool* available) = 0; + virtual int32_t SetSpeakerVolume(uint32_t volume) = 0; + virtual int32_t SpeakerVolume(uint32_t* volume) const = 0; + virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const = 0; + virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const = 0; + + // Microphone volume controls + virtual int32_t MicrophoneVolumeIsAvailable(bool* available) = 0; + virtual int32_t SetMicrophoneVolume(uint32_t volume) = 0; + virtual int32_t MicrophoneVolume(uint32_t* volume) const = 0; + virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const = 0; + virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const = 0; + + // Speaker mute control + virtual int32_t SpeakerMuteIsAvailable(bool* available) = 0; + virtual int32_t SetSpeakerMute(bool enable) = 0; + virtual int32_t SpeakerMute(bool* enabled) const = 0; + + // Microphone mute control + virtual int32_t MicrophoneMuteIsAvailable(bool* available) = 0; + virtual int32_t SetMicrophoneMute(bool enable) = 0; + virtual int32_t MicrophoneMute(bool* enabled) const = 0; + + // Stereo support + virtual int32_t StereoPlayoutIsAvailable(bool* available) const = 0; + virtual int32_t SetStereoPlayout(bool enable) = 0; + virtual int32_t StereoPlayout(bool* enabled) const = 0; + virtual int32_t StereoRecordingIsAvailable(bool* available) const = 0; + virtual int32_t SetStereoRecording(bool enable) = 0; + virtual int32_t StereoRecording(bool* enabled) const = 0; + + // Playout delay + virtual int32_t PlayoutDelay(uint16_t* delayMS) const = 0; + + // Only supported on Android. + virtual bool BuiltInAECIsAvailable() const = 0; + virtual bool BuiltInAGCIsAvailable() const = 0; + virtual bool BuiltInNSIsAvailable() const = 0; + + // Enables the built-in audio effects. Only supported on Android. + virtual int32_t EnableBuiltInAEC(bool enable) = 0; + virtual int32_t EnableBuiltInAGC(bool enable) = 0; + virtual int32_t EnableBuiltInNS(bool enable) = 0; + + // Play underrun count. Only supported on Android. + // TODO(alexnarest): Make it abstract after upstream projects support it. + virtual int32_t GetPlayoutUnderrunCount() const { return -1; } + + // Used to generate RTC stats. If not implemented, RTCAudioPlayoutStats will + // not be present in the stats. + virtual absl::optional GetStats() const { return absl::nullopt; } + +// Only supported on iOS. +#if defined(WEBRTC_IOS) + virtual int GetPlayoutAudioParameters(AudioParameters* params) const = 0; + virtual int GetRecordAudioParameters(AudioParameters* params) const = 0; +#endif // WEBRTC_IOS + + protected: + ~AudioDeviceModule() override {} +}; + +// Extends the default ADM interface with some extra test methods. +// Intended for usage in tests only and requires a unique factory method. +class AudioDeviceModuleForTest : public AudioDeviceModule { + public: + // Triggers internal restart sequences of audio streaming. Can be used by + // tests to emulate events corresponding to e.g. removal of an active audio + // device or other actions which causes the stream to be disconnected. + virtual int RestartPlayoutInternally() = 0; + virtual int RestartRecordingInternally() = 0; + + virtual int SetPlayoutSampleRate(uint32_t sample_rate) = 0; + virtual int SetRecordingSampleRate(uint32_t sample_rate) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/audio_device_data_observer.h b/third_party/libwebrtc/modules/audio_device/include/audio_device_data_observer.h new file mode 100644 index 0000000000..36dc45f19e --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/audio_device_data_observer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 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_INCLUDE_AUDIO_DEVICE_DATA_OBSERVER_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_DATA_OBSERVER_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "modules/audio_device/include/audio_device.h" + +namespace webrtc { + +// This interface will capture the raw PCM data of both the local captured as +// well as the mixed/rendered remote audio. +class AudioDeviceDataObserver { + public: + virtual void OnCaptureData(const void* audio_samples, + size_t num_samples, + size_t bytes_per_sample, + size_t num_channels, + uint32_t samples_per_sec) = 0; + + virtual void OnRenderData(const void* audio_samples, + size_t num_samples, + size_t bytes_per_sample, + size_t num_channels, + uint32_t samples_per_sec) = 0; + + AudioDeviceDataObserver() = default; + virtual ~AudioDeviceDataObserver() = default; +}; + +// Creates an ADMWrapper around an ADM instance that registers +// the provided AudioDeviceDataObserver. +rtc::scoped_refptr CreateAudioDeviceWithDataObserver( + rtc::scoped_refptr impl, + std::unique_ptr observer); + +// Creates an ADMWrapper around an ADM instance that registers +// the provided AudioDeviceDataObserver. +ABSL_DEPRECATED("") +rtc::scoped_refptr CreateAudioDeviceWithDataObserver( + rtc::scoped_refptr impl, + AudioDeviceDataObserver* observer); + +// Creates an ADM instance with AudioDeviceDataObserver registered. +rtc::scoped_refptr CreateAudioDeviceWithDataObserver( + AudioDeviceModule::AudioLayer audio_layer, + TaskQueueFactory* task_queue_factory, + std::unique_ptr observer); + +// Creates an ADM instance with AudioDeviceDataObserver registered. +ABSL_DEPRECATED("") +rtc::scoped_refptr CreateAudioDeviceWithDataObserver( + AudioDeviceModule::AudioLayer audio_layer, + TaskQueueFactory* task_queue_factory, + AudioDeviceDataObserver* observer); + +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_DATA_OBSERVER_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/audio_device_default.h b/third_party/libwebrtc/modules/audio_device/include/audio_device_default.h new file mode 100644 index 0000000000..3779d6fb3b --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/audio_device_default.h @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_DEFAULT_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_DEFAULT_H_ + +#include "modules/audio_device/include/audio_device.h" + +namespace webrtc { +namespace webrtc_impl { + +// AudioDeviceModuleDefault template adds default implementation for all +// AudioDeviceModule methods to the class, which inherits from +// AudioDeviceModuleDefault. +template +class AudioDeviceModuleDefault : public T { + public: + AudioDeviceModuleDefault() {} + virtual ~AudioDeviceModuleDefault() {} + + int32_t RegisterAudioCallback(AudioTransport* audioCallback) override { + return 0; + } + int32_t Init() override { return 0; } + int32_t InitSpeaker() override { return 0; } + int32_t SetPlayoutDevice(uint16_t index) override { return 0; } + int32_t SetPlayoutDevice( + AudioDeviceModule::WindowsDeviceType device) override { + return 0; + } + int32_t SetStereoPlayout(bool enable) override { return 0; } + int32_t StopPlayout() override { return 0; } + int32_t InitMicrophone() override { return 0; } + int32_t SetRecordingDevice(uint16_t index) override { return 0; } + int32_t SetRecordingDevice( + AudioDeviceModule::WindowsDeviceType device) override { + return 0; + } + int32_t SetStereoRecording(bool enable) override { return 0; } + int32_t StopRecording() override { return 0; } + + int32_t Terminate() override { return 0; } + + int32_t ActiveAudioLayer( + AudioDeviceModule::AudioLayer* audioLayer) const override { + return 0; + } + bool Initialized() const override { return true; } + 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; + } + int32_t PlayoutIsAvailable(bool* available) override { return 0; } + int32_t InitPlayout() override { return 0; } + bool PlayoutIsInitialized() const override { return true; } + int32_t RecordingIsAvailable(bool* available) override { return 0; } + int32_t InitRecording() override { return 0; } + bool RecordingIsInitialized() const override { return true; } + int32_t StartPlayout() override { return 0; } + bool Playing() const override { return false; } + int32_t StartRecording() override { return 0; } + bool Recording() const override { return false; } + bool SpeakerIsInitialized() const override { return true; } + bool MicrophoneIsInitialized() const override { return true; } + 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; } + 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; } + 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 { + *available = false; + return 0; + } + int32_t StereoPlayout(bool* enabled) const override { return 0; } + int32_t StereoRecordingIsAvailable(bool* available) const override { + *available = false; + return 0; + } + int32_t StereoRecording(bool* enabled) const override { return 0; } + int32_t PlayoutDelay(uint16_t* delayMS) const override { + *delayMS = 0; + return 0; + } + bool BuiltInAECIsAvailable() const override { return false; } + int32_t EnableBuiltInAEC(bool enable) override { return -1; } + bool BuiltInAGCIsAvailable() const override { return false; } + int32_t EnableBuiltInAGC(bool enable) override { return -1; } + bool BuiltInNSIsAvailable() const override { return false; } + int32_t EnableBuiltInNS(bool enable) override { return -1; } + + int32_t GetPlayoutUnderrunCount() const override { return -1; } + +#if defined(WEBRTC_IOS) + int GetPlayoutAudioParameters(AudioParameters* params) const override { + return -1; + } + int GetRecordAudioParameters(AudioParameters* params) const override { + return -1; + } +#endif // WEBRTC_IOS +}; + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_DEFAULT_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/audio_device_defines.h b/third_party/libwebrtc/modules/audio_device/include/audio_device_defines.h new file mode 100644 index 0000000000..d677d41f69 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/audio_device_defines.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2011 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_INCLUDE_AUDIO_DEVICE_DEFINES_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_DEFINES_H_ + +#include + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +static const int kAdmMaxDeviceNameSize = 128; +static const int kAdmMaxFileNameSize = 512; +static const int kAdmMaxGuidSize = 128; + +static const int kAdmMinPlayoutBufferSizeMs = 10; +static const int kAdmMaxPlayoutBufferSizeMs = 250; + +// ---------------------------------------------------------------------------- +// AudioTransport +// ---------------------------------------------------------------------------- + +class AudioTransport { + public: + // TODO(bugs.webrtc.org/13620) Deprecate this function + virtual int32_t RecordedDataIsAvailable(const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel) = 0; // NOLINT + + virtual int32_t RecordedDataIsAvailable( + const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel, + absl::optional estimatedCaptureTimeNS) { // NOLINT + // TODO(webrtc:13620) Make the default behaver of the new API to behave as + // the old API. This can be pure virtual if all uses of the old API is + // removed. + return RecordedDataIsAvailable( + audioSamples, nSamples, nBytesPerSample, nChannels, samplesPerSec, + totalDelayMS, clockDrift, currentMicLevel, keyPressed, newMicLevel); + } + + // Implementation has to setup safe values for all specified out parameters. + virtual int32_t NeedMorePlayData(size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, // NOLINT + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) = 0; // NOLINT + + // Method to pull mixed render audio data from all active VoE channels. + // The data will not be passed as reference for audio processing internally. + virtual 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) = 0; + + protected: + virtual ~AudioTransport() {} +}; + +// Helper class for storage of fundamental audio parameters such as sample rate, +// number of channels, native buffer size etc. +// Note that one audio frame can contain more than one channel sample and each +// sample is assumed to be a 16-bit PCM sample. Hence, one audio frame in +// stereo contains 2 * (16/8) = 4 bytes of data. +class AudioParameters { + public: + // This implementation does only support 16-bit PCM samples. + static const size_t kBitsPerSample = 16; + AudioParameters() + : sample_rate_(0), + channels_(0), + frames_per_buffer_(0), + frames_per_10ms_buffer_(0) {} + AudioParameters(int sample_rate, size_t channels, size_t frames_per_buffer) + : sample_rate_(sample_rate), + channels_(channels), + frames_per_buffer_(frames_per_buffer), + frames_per_10ms_buffer_(static_cast(sample_rate / 100)) {} + void reset(int sample_rate, size_t channels, size_t frames_per_buffer) { + sample_rate_ = sample_rate; + channels_ = channels; + frames_per_buffer_ = frames_per_buffer; + frames_per_10ms_buffer_ = static_cast(sample_rate / 100); + } + size_t bits_per_sample() const { return kBitsPerSample; } + void reset(int sample_rate, size_t channels, double buffer_duration) { + reset(sample_rate, channels, + static_cast(sample_rate * buffer_duration + 0.5)); + } + void reset(int sample_rate, size_t channels) { + reset(sample_rate, channels, static_cast(0)); + } + int sample_rate() const { return sample_rate_; } + size_t channels() const { return channels_; } + size_t frames_per_buffer() const { return frames_per_buffer_; } + size_t frames_per_10ms_buffer() const { return frames_per_10ms_buffer_; } + size_t GetBytesPerFrame() const { return channels_ * kBitsPerSample / 8; } + size_t GetBytesPerBuffer() const { + return frames_per_buffer_ * GetBytesPerFrame(); + } + // The WebRTC audio device buffer (ADB) only requires that the sample rate + // and number of channels are configured. Hence, to be "valid", only these + // two attributes must be set. + bool is_valid() const { return ((sample_rate_ > 0) && (channels_ > 0)); } + // Most platforms also require that a native buffer size is defined. + // An audio parameter instance is considered to be "complete" if it is both + // "valid" (can be used by the ADB) and also has a native frame size. + bool is_complete() const { return (is_valid() && (frames_per_buffer_ > 0)); } + size_t GetBytesPer10msBuffer() const { + return frames_per_10ms_buffer_ * GetBytesPerFrame(); + } + double GetBufferSizeInMilliseconds() const { + if (sample_rate_ == 0) + return 0.0; + return frames_per_buffer_ / (sample_rate_ / 1000.0); + } + double GetBufferSizeInSeconds() const { + if (sample_rate_ == 0) + return 0.0; + return static_cast(frames_per_buffer_) / (sample_rate_); + } + std::string ToString() const { + char ss_buf[1024]; + rtc::SimpleStringBuilder ss(ss_buf); + ss << "AudioParameters: "; + ss << "sample_rate=" << sample_rate() << ", channels=" << channels(); + ss << ", frames_per_buffer=" << frames_per_buffer(); + ss << ", frames_per_10ms_buffer=" << frames_per_10ms_buffer(); + ss << ", bytes_per_frame=" << GetBytesPerFrame(); + ss << ", bytes_per_buffer=" << GetBytesPerBuffer(); + ss << ", bytes_per_10ms_buffer=" << GetBytesPer10msBuffer(); + ss << ", size_in_ms=" << GetBufferSizeInMilliseconds(); + return ss.str(); + } + + private: + int sample_rate_; + size_t channels_; + size_t frames_per_buffer_; + size_t frames_per_10ms_buffer_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_DEFINES_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/audio_device_factory.cc b/third_party/libwebrtc/modules/audio_device/include/audio_device_factory.cc new file mode 100644 index 0000000000..130e096e6d --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/audio_device_factory.cc @@ -0,0 +1,53 @@ +/* + * 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/include/audio_device_factory.h" + +#include + +#if defined(WEBRTC_WIN) +#include "modules/audio_device/win/audio_device_module_win.h" +#include "modules/audio_device/win/core_audio_input_win.h" +#include "modules/audio_device/win/core_audio_output_win.h" +#include "modules/audio_device/win/core_audio_utility_win.h" +#endif + +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +rtc::scoped_refptr CreateWindowsCoreAudioAudioDeviceModule( + TaskQueueFactory* task_queue_factory, + bool automatic_restart) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + return CreateWindowsCoreAudioAudioDeviceModuleForTest(task_queue_factory, + automatic_restart); +} + +rtc::scoped_refptr +CreateWindowsCoreAudioAudioDeviceModuleForTest( + TaskQueueFactory* task_queue_factory, + bool automatic_restart) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + // Returns NULL if Core Audio is not supported or if COM has not been + // initialized correctly using ScopedCOMInitializer. + if (!webrtc_win::core_audio_utility::IsSupported()) { + RTC_LOG(LS_ERROR) + << "Unable to create ADM since Core Audio is not supported"; + return nullptr; + } + return CreateWindowsCoreAudioAudioDeviceModuleFromInputAndOutput( + std::make_unique(automatic_restart), + std::make_unique(automatic_restart), + task_queue_factory); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_device/include/audio_device_factory.h b/third_party/libwebrtc/modules/audio_device/include/audio_device_factory.h new file mode 100644 index 0000000000..edd7686b8e --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/audio_device_factory.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_FACTORY_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_FACTORY_H_ + +#include + +#include "api/task_queue/task_queue_factory.h" +#include "modules/audio_device/include/audio_device.h" + +namespace webrtc { + +// Creates an AudioDeviceModule (ADM) for Windows based on the Core Audio API. +// The creating thread must be a COM thread; otherwise nullptr will be returned. +// By default `automatic_restart` is set to true and it results in support for +// automatic restart of audio if e.g. the existing device is removed. If set to +// false, no attempt to restart audio is performed under these conditions. +// +// Example (assuming webrtc namespace): +// +// public: +// rtc::scoped_refptr CreateAudioDevice() { +// task_queue_factory_ = CreateDefaultTaskQueueFactory(); +// // Tell COM that this thread shall live in the MTA. +// com_initializer_ = std::make_unique( +// ScopedCOMInitializer::kMTA); +// if (!com_initializer_->Succeeded()) { +// return nullptr; +// } +// // Create the ADM with support for automatic restart if devices are +// // unplugged. +// return CreateWindowsCoreAudioAudioDeviceModule( +// task_queue_factory_.get()); +// } +// +// private: +// std::unique_ptr com_initializer_; +// std::unique_ptr task_queue_factory_; +// +rtc::scoped_refptr CreateWindowsCoreAudioAudioDeviceModule( + TaskQueueFactory* task_queue_factory, + bool automatic_restart = true); + +rtc::scoped_refptr +CreateWindowsCoreAudioAudioDeviceModuleForTest( + TaskQueueFactory* task_queue_factory, + bool automatic_restart = true); + +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_FACTORY_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h b/third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h new file mode 100644 index 0000000000..2322ce0263 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 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_INCLUDE_FAKE_AUDIO_DEVICE_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_FAKE_AUDIO_DEVICE_H_ + +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_device/include/audio_device_default.h" + +namespace webrtc { + +class FakeAudioDeviceModule + : public webrtc_impl::AudioDeviceModuleDefault { + public: + // TODO(bugs.webrtc.org/12701): Fix all users of this class to managed + // references using scoped_refptr. Current code doesn't always use refcounting + // for this class. + void AddRef() const override {} + rtc::RefCountReleaseStatus Release() const override { + return rtc::RefCountReleaseStatus::kDroppedLastRef; + } +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_FAKE_AUDIO_DEVICE_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/mock_audio_device.h b/third_party/libwebrtc/modules/audio_device/include/mock_audio_device.h new file mode 100644 index 0000000000..73fbdd547d --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/mock_audio_device.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015 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_INCLUDE_MOCK_AUDIO_DEVICE_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_MOCK_AUDIO_DEVICE_H_ + +#include + +#include "api/make_ref_counted.h" +#include "modules/audio_device/include/audio_device.h" +#include "test/gmock.h" + +namespace webrtc { +namespace test { + +class MockAudioDeviceModule : public AudioDeviceModule { + public: + static rtc::scoped_refptr CreateNice() { + return rtc::make_ref_counted<::testing::NiceMock>(); + } + static rtc::scoped_refptr CreateStrict() { + return rtc::make_ref_counted< + ::testing::StrictMock>(); + } + + // AudioDeviceModule. + MOCK_METHOD(int32_t, + ActiveAudioLayer, + (AudioLayer * audioLayer), + (const, override)); + MOCK_METHOD(int32_t, + RegisterAudioCallback, + (AudioTransport * audioCallback), + (override)); + MOCK_METHOD(int32_t, Init, (), (override)); + MOCK_METHOD(int32_t, Terminate, (), (override)); + MOCK_METHOD(bool, Initialized, (), (const, override)); + MOCK_METHOD(int16_t, PlayoutDevices, (), (override)); + MOCK_METHOD(int16_t, RecordingDevices, (), (override)); + MOCK_METHOD(int32_t, + PlayoutDeviceName, + (uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]), + (override)); + MOCK_METHOD(int32_t, + RecordingDeviceName, + (uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]), + (override)); + MOCK_METHOD(int32_t, SetPlayoutDevice, (uint16_t index), (override)); + MOCK_METHOD(int32_t, + SetPlayoutDevice, + (WindowsDeviceType device), + (override)); + MOCK_METHOD(int32_t, SetRecordingDevice, (uint16_t index), (override)); + MOCK_METHOD(int32_t, + SetRecordingDevice, + (WindowsDeviceType device), + (override)); + MOCK_METHOD(int32_t, PlayoutIsAvailable, (bool* available), (override)); + MOCK_METHOD(int32_t, InitPlayout, (), (override)); + MOCK_METHOD(bool, PlayoutIsInitialized, (), (const, override)); + MOCK_METHOD(int32_t, RecordingIsAvailable, (bool* available), (override)); + MOCK_METHOD(int32_t, InitRecording, (), (override)); + MOCK_METHOD(bool, RecordingIsInitialized, (), (const, override)); + MOCK_METHOD(int32_t, StartPlayout, (), (override)); + MOCK_METHOD(int32_t, StopPlayout, (), (override)); + MOCK_METHOD(bool, Playing, (), (const, override)); + MOCK_METHOD(int32_t, StartRecording, (), (override)); + MOCK_METHOD(int32_t, StopRecording, (), (override)); + MOCK_METHOD(bool, Recording, (), (const, override)); + MOCK_METHOD(int32_t, InitSpeaker, (), (override)); + MOCK_METHOD(bool, SpeakerIsInitialized, (), (const, override)); + MOCK_METHOD(int32_t, InitMicrophone, (), (override)); + MOCK_METHOD(bool, MicrophoneIsInitialized, (), (const, override)); + MOCK_METHOD(int32_t, SpeakerVolumeIsAvailable, (bool* available), (override)); + MOCK_METHOD(int32_t, SetSpeakerVolume, (uint32_t volume), (override)); + MOCK_METHOD(int32_t, SpeakerVolume, (uint32_t * volume), (const, override)); + MOCK_METHOD(int32_t, + MaxSpeakerVolume, + (uint32_t * maxVolume), + (const, override)); + MOCK_METHOD(int32_t, + MinSpeakerVolume, + (uint32_t * minVolume), + (const, override)); + MOCK_METHOD(int32_t, + MicrophoneVolumeIsAvailable, + (bool* available), + (override)); + MOCK_METHOD(int32_t, SetMicrophoneVolume, (uint32_t volume), (override)); + MOCK_METHOD(int32_t, + MicrophoneVolume, + (uint32_t * volume), + (const, override)); + MOCK_METHOD(int32_t, + MaxMicrophoneVolume, + (uint32_t * maxVolume), + (const, override)); + MOCK_METHOD(int32_t, + MinMicrophoneVolume, + (uint32_t * minVolume), + (const, override)); + MOCK_METHOD(int32_t, SpeakerMuteIsAvailable, (bool* available), (override)); + MOCK_METHOD(int32_t, SetSpeakerMute, (bool enable), (override)); + MOCK_METHOD(int32_t, SpeakerMute, (bool* enabled), (const, override)); + MOCK_METHOD(int32_t, + MicrophoneMuteIsAvailable, + (bool* available), + (override)); + MOCK_METHOD(int32_t, SetMicrophoneMute, (bool enable), (override)); + MOCK_METHOD(int32_t, MicrophoneMute, (bool* enabled), (const, override)); + MOCK_METHOD(int32_t, + StereoPlayoutIsAvailable, + (bool* available), + (const, override)); + MOCK_METHOD(int32_t, SetStereoPlayout, (bool enable), (override)); + MOCK_METHOD(int32_t, StereoPlayout, (bool* enabled), (const, override)); + MOCK_METHOD(int32_t, + StereoRecordingIsAvailable, + (bool* available), + (const, override)); + MOCK_METHOD(int32_t, SetStereoRecording, (bool enable), (override)); + MOCK_METHOD(int32_t, StereoRecording, (bool* enabled), (const, override)); + MOCK_METHOD(int32_t, PlayoutDelay, (uint16_t * delayMS), (const, override)); + MOCK_METHOD(bool, BuiltInAECIsAvailable, (), (const, override)); + MOCK_METHOD(bool, BuiltInAGCIsAvailable, (), (const, override)); + MOCK_METHOD(bool, BuiltInNSIsAvailable, (), (const, override)); + MOCK_METHOD(int32_t, EnableBuiltInAEC, (bool enable), (override)); + MOCK_METHOD(int32_t, EnableBuiltInAGC, (bool enable), (override)); + MOCK_METHOD(int32_t, EnableBuiltInNS, (bool enable), (override)); + MOCK_METHOD(int32_t, GetPlayoutUnderrunCount, (), (const, override)); +#if defined(WEBRTC_IOS) + MOCK_METHOD(int, + GetPlayoutAudioParameters, + (AudioParameters * params), + (const, override)); + MOCK_METHOD(int, + GetRecordAudioParameters, + (AudioParameters * params), + (const, override)); +#endif // WEBRTC_IOS +}; +} // namespace test +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_MOCK_AUDIO_DEVICE_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/mock_audio_transport.h b/third_party/libwebrtc/modules/audio_device/include/mock_audio_transport.h new file mode 100644 index 0000000000..b886967319 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/mock_audio_transport.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 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_INCLUDE_MOCK_AUDIO_TRANSPORT_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_MOCK_AUDIO_TRANSPORT_H_ + +#include "modules/audio_device/include/audio_device_defines.h" +#include "test/gmock.h" + +namespace webrtc { +namespace test { + +class MockAudioTransport : public AudioTransport { + public: + MockAudioTransport() {} + ~MockAudioTransport() {} + + MOCK_METHOD(int32_t, + RecordedDataIsAvailable, + (const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel), + (override)); + + MOCK_METHOD(int32_t, + RecordedDataIsAvailable, + (const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel, + absl::optional estimated_capture_time_ns), + (override)); + + MOCK_METHOD(int32_t, + NeedMorePlayData, + (size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms), + (override)); + + MOCK_METHOD(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)); +}; + +} // namespace test +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_MOCK_AUDIO_TRANSPORT_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/test_audio_device.cc b/third_party/libwebrtc/modules/audio_device/include/test_audio_device.cc new file mode 100644 index 0000000000..4c29c98f2c --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/test_audio_device.cc @@ -0,0 +1,540 @@ +/* + * 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/include/test_audio_device.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/make_ref_counted.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/test_audio_device_impl.h" +#include "rtc_base/buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/platform_thread.h" +#include "rtc_base/random.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/task_queue.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +namespace { + +constexpr int kFrameLengthUs = 10000; +constexpr int kFramesPerSecond = rtc::kNumMicrosecsPerSec / kFrameLengthUs; + +class TestAudioDeviceModuleImpl : public AudioDeviceModuleImpl { + public: + TestAudioDeviceModuleImpl( + TaskQueueFactory* task_queue_factory, + std::unique_ptr capturer, + std::unique_ptr renderer, + float speed = 1) + : AudioDeviceModuleImpl( + AudioLayer::kDummyAudio, + std::make_unique(task_queue_factory, + std::move(capturer), + std::move(renderer), + speed), + task_queue_factory, + /*create_detached=*/true) {} + + ~TestAudioDeviceModuleImpl() override = default; +}; + +// A fake capturer that generates pulses with random samples between +// -max_amplitude and +max_amplitude. +class PulsedNoiseCapturerImpl final + : public TestAudioDeviceModule::PulsedNoiseCapturer { + public: + // Assuming 10ms audio packets. + PulsedNoiseCapturerImpl(int16_t max_amplitude, + int sampling_frequency_in_hz, + int num_channels) + : sampling_frequency_in_hz_(sampling_frequency_in_hz), + fill_with_zero_(false), + random_generator_(1), + max_amplitude_(max_amplitude), + num_channels_(num_channels) { + RTC_DCHECK_GT(max_amplitude, 0); + } + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Capture(rtc::BufferT* buffer) override { + fill_with_zero_ = !fill_with_zero_; + int16_t max_amplitude; + { + MutexLock lock(&lock_); + max_amplitude = max_amplitude_; + } + buffer->SetData( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz_) * + num_channels_, + [&](rtc::ArrayView data) { + if (fill_with_zero_) { + std::fill(data.begin(), data.end(), 0); + } else { + std::generate(data.begin(), data.end(), [&]() { + return random_generator_.Rand(-max_amplitude, max_amplitude); + }); + } + return data.size(); + }); + return true; + } + + void SetMaxAmplitude(int16_t amplitude) override { + MutexLock lock(&lock_); + max_amplitude_ = amplitude; + } + + private: + int sampling_frequency_in_hz_; + bool fill_with_zero_; + Random random_generator_; + Mutex lock_; + int16_t max_amplitude_ RTC_GUARDED_BY(lock_); + const int num_channels_; +}; + +class WavFileReader final : public TestAudioDeviceModule::Capturer { + public: + WavFileReader(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels, + bool repeat) + : WavFileReader(std::make_unique(filename), + sampling_frequency_in_hz, + num_channels, + repeat) {} + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Capture(rtc::BufferT* buffer) override { + buffer->SetData( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz_) * + num_channels_, + [&](rtc::ArrayView data) { + size_t read = wav_reader_->ReadSamples(data.size(), data.data()); + if (read < data.size() && repeat_) { + do { + wav_reader_->Reset(); + size_t delta = wav_reader_->ReadSamples( + data.size() - read, data.subview(read).data()); + RTC_CHECK_GT(delta, 0) << "No new data read from file"; + read += delta; + } while (read < data.size()); + } + return read; + }); + return buffer->size() > 0; + } + + private: + WavFileReader(std::unique_ptr wav_reader, + int sampling_frequency_in_hz, + int num_channels, + bool repeat) + : sampling_frequency_in_hz_(sampling_frequency_in_hz), + num_channels_(num_channels), + wav_reader_(std::move(wav_reader)), + repeat_(repeat) { + RTC_CHECK_EQ(wav_reader_->sample_rate(), sampling_frequency_in_hz); + RTC_CHECK_EQ(wav_reader_->num_channels(), num_channels); + } + + const int sampling_frequency_in_hz_; + const int num_channels_; + std::unique_ptr wav_reader_; + const bool repeat_; +}; + +class WavFileWriter final : public TestAudioDeviceModule::Renderer { + public: + WavFileWriter(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels) + : WavFileWriter(std::make_unique(filename, + sampling_frequency_in_hz, + num_channels), + sampling_frequency_in_hz, + num_channels) {} + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Render(rtc::ArrayView data) override { + wav_writer_->WriteSamples(data.data(), data.size()); + return true; + } + + private: + WavFileWriter(std::unique_ptr wav_writer, + int sampling_frequency_in_hz, + int num_channels) + : sampling_frequency_in_hz_(sampling_frequency_in_hz), + wav_writer_(std::move(wav_writer)), + num_channels_(num_channels) {} + + int sampling_frequency_in_hz_; + std::unique_ptr wav_writer_; + const int num_channels_; +}; + +class BoundedWavFileWriter : public TestAudioDeviceModule::Renderer { + public: + BoundedWavFileWriter(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels) + : sampling_frequency_in_hz_(sampling_frequency_in_hz), + wav_writer_(filename, sampling_frequency_in_hz, num_channels), + num_channels_(num_channels), + silent_audio_( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz) * + num_channels, + 0), + started_writing_(false), + trailing_zeros_(0) {} + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Render(rtc::ArrayView data) override { + const int16_t kAmplitudeThreshold = 5; + + const int16_t* begin = data.begin(); + const int16_t* end = data.end(); + if (!started_writing_) { + // Cut off silence at the beginning. + while (begin < end) { + if (std::abs(*begin) > kAmplitudeThreshold) { + started_writing_ = true; + break; + } + ++begin; + } + } + if (started_writing_) { + // Cut off silence at the end. + while (begin < end) { + if (*(end - 1) != 0) { + break; + } + --end; + } + if (begin < end) { + // If it turns out that the silence was not final, need to write all the + // skipped zeros and continue writing audio. + while (trailing_zeros_ > 0) { + const size_t zeros_to_write = + std::min(trailing_zeros_, silent_audio_.size()); + wav_writer_.WriteSamples(silent_audio_.data(), zeros_to_write); + trailing_zeros_ -= zeros_to_write; + } + wav_writer_.WriteSamples(begin, end - begin); + } + // Save the number of zeros we skipped in case this needs to be restored. + trailing_zeros_ += data.end() - end; + } + return true; + } + + private: + int sampling_frequency_in_hz_; + WavWriter wav_writer_; + const int num_channels_; + std::vector silent_audio_; + bool started_writing_; + size_t trailing_zeros_; +}; + +class DiscardRenderer final : public TestAudioDeviceModule::Renderer { + public: + explicit DiscardRenderer(int sampling_frequency_in_hz, int num_channels) + : sampling_frequency_in_hz_(sampling_frequency_in_hz), + num_channels_(num_channels) {} + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Render(rtc::ArrayView data) override { return true; } + + private: + int sampling_frequency_in_hz_; + const int num_channels_; +}; + +class RawFileReader final : public TestAudioDeviceModule::Capturer { + public: + RawFileReader(absl::string_view input_file_name, + int sampling_frequency_in_hz, + int num_channels, + bool repeat) + : input_file_name_(input_file_name), + sampling_frequency_in_hz_(sampling_frequency_in_hz), + num_channels_(num_channels), + repeat_(repeat), + read_buffer_( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz) * + num_channels * 2, + 0) { + input_file_ = FileWrapper::OpenReadOnly(input_file_name_); + RTC_CHECK(input_file_.is_open()) + << "Failed to open audio input file: " << input_file_name_; + } + + ~RawFileReader() override { input_file_.Close(); } + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Capture(rtc::BufferT* buffer) override { + buffer->SetData( + TestAudioDeviceModule::SamplesPerFrame(SamplingFrequency()) * + NumChannels(), + [&](rtc::ArrayView data) { + rtc::ArrayView read_buffer_view = ReadBufferView(); + size_t size = data.size() * 2; + size_t read = input_file_.Read(read_buffer_view.data(), size); + if (read < size && repeat_) { + do { + input_file_.Rewind(); + size_t delta = input_file_.Read( + read_buffer_view.subview(read).data(), size - read); + RTC_CHECK_GT(delta, 0) << "No new data to read from file"; + read += delta; + } while (read < size); + } + memcpy(data.data(), read_buffer_view.data(), size); + return read / 2; + }); + return buffer->size() > 0; + } + + private: + rtc::ArrayView ReadBufferView() { return read_buffer_; } + + const std::string input_file_name_; + const int sampling_frequency_in_hz_; + const int num_channels_; + const bool repeat_; + FileWrapper input_file_; + std::vector read_buffer_; +}; + +class RawFileWriter : public TestAudioDeviceModule::Renderer { + public: + RawFileWriter(absl::string_view output_file_name, + int sampling_frequency_in_hz, + int num_channels) + : output_file_name_(output_file_name), + sampling_frequency_in_hz_(sampling_frequency_in_hz), + num_channels_(num_channels), + silent_audio_( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz) * + num_channels * 2, + 0), + write_buffer_( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz) * + num_channels * 2, + 0), + started_writing_(false), + trailing_zeros_(0) { + output_file_ = FileWrapper::OpenWriteOnly(output_file_name_); + RTC_CHECK(output_file_.is_open()) + << "Failed to open playout file" << output_file_name_; + } + ~RawFileWriter() override { output_file_.Close(); } + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Render(rtc::ArrayView data) override { + const int16_t kAmplitudeThreshold = 5; + + const int16_t* begin = data.begin(); + const int16_t* end = data.end(); + if (!started_writing_) { + // Cut off silence at the beginning. + while (begin < end) { + if (std::abs(*begin) > kAmplitudeThreshold) { + started_writing_ = true; + break; + } + ++begin; + } + } + if (started_writing_) { + // Cut off silence at the end. + while (begin < end) { + if (*(end - 1) != 0) { + break; + } + --end; + } + if (begin < end) { + // If it turns out that the silence was not final, need to write all the + // skipped zeros and continue writing audio. + while (trailing_zeros_ > 0) { + const size_t zeros_to_write = + std::min(trailing_zeros_, silent_audio_.size()); + output_file_.Write(silent_audio_.data(), zeros_to_write * 2); + trailing_zeros_ -= zeros_to_write; + } + WriteInt16(begin, end); + } + // Save the number of zeros we skipped in case this needs to be restored. + trailing_zeros_ += data.end() - end; + } + return true; + } + + private: + void WriteInt16(const int16_t* begin, const int16_t* end) { + int size = (end - begin) * sizeof(int16_t); + memcpy(write_buffer_.data(), begin, size); + output_file_.Write(write_buffer_.data(), size); + } + + const std::string output_file_name_; + const int sampling_frequency_in_hz_; + const int num_channels_; + FileWrapper output_file_; + std::vector silent_audio_; + std::vector write_buffer_; + bool started_writing_; + size_t trailing_zeros_; +}; + +} // namespace + +size_t TestAudioDeviceModule::SamplesPerFrame(int sampling_frequency_in_hz) { + return rtc::CheckedDivExact(sampling_frequency_in_hz, kFramesPerSecond); +} + +rtc::scoped_refptr TestAudioDeviceModule::Create( + TaskQueueFactory* task_queue_factory, + std::unique_ptr capturer, + std::unique_ptr renderer, + float speed) { + auto audio_device = rtc::make_ref_counted( + 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::CreatePulsedNoiseCapturer(int16_t max_amplitude, + int sampling_frequency_in_hz, + int num_channels) { + return std::make_unique( + max_amplitude, sampling_frequency_in_hz, num_channels); +} + +std::unique_ptr +TestAudioDeviceModule::CreateDiscardRenderer(int sampling_frequency_in_hz, + int num_channels) { + return std::make_unique(sampling_frequency_in_hz, + num_channels); +} + +std::unique_ptr +TestAudioDeviceModule::CreateWavFileReader(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels) { + return std::make_unique(filename, sampling_frequency_in_hz, + num_channels, false); +} + +std::unique_ptr +TestAudioDeviceModule::CreateWavFileReader(absl::string_view filename, + bool repeat) { + WavReader reader(filename); + int sampling_frequency_in_hz = reader.sample_rate(); + int num_channels = rtc::checked_cast(reader.num_channels()); + return std::make_unique(filename, sampling_frequency_in_hz, + num_channels, repeat); +} + +std::unique_ptr +TestAudioDeviceModule::CreateWavFileWriter(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels) { + return std::make_unique(filename, sampling_frequency_in_hz, + num_channels); +} + +std::unique_ptr +TestAudioDeviceModule::CreateBoundedWavFileWriter(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels) { + return std::make_unique( + filename, sampling_frequency_in_hz, num_channels); +} + +std::unique_ptr +TestAudioDeviceModule::CreateRawFileReader(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels, + bool repeat) { + return std::make_unique(filename, sampling_frequency_in_hz, + num_channels, repeat); +} + +std::unique_ptr +TestAudioDeviceModule::CreateRawFileWriter(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels) { + return std::make_unique(filename, sampling_frequency_in_hz, + num_channels); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_device/include/test_audio_device.h b/third_party/libwebrtc/modules/audio_device/include/test_audio_device.h new file mode 100644 index 0000000000..4b2d755ae1 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/test_audio_device.h @@ -0,0 +1,155 @@ +/* + * 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. + */ +#ifndef MODULES_AUDIO_DEVICE_INCLUDE_TEST_AUDIO_DEVICE_H_ +#define MODULES_AUDIO_DEVICE_INCLUDE_TEST_AUDIO_DEVICE_H_ + +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_device/include/audio_device_defines.h" +#include "rtc_base/buffer.h" + +namespace webrtc { + +// This is test API and is in development, so it can be changed/removed without +// notice. + +// This class exists for historical reasons. For now it only contains static +// methods to create test AudioDeviceModule. Implementation details of that +// module are considered private. This class isn't intended to be instantiated. +class TestAudioDeviceModule { + public: + // Returns the number of samples that Capturers and Renderers with this + // sampling frequency will work with every time Capture or Render is called. + static size_t SamplesPerFrame(int sampling_frequency_in_hz); + + class Capturer { + public: + virtual ~Capturer() {} + // Returns the sampling frequency in Hz of the audio data that this + // capturer produces. + virtual int SamplingFrequency() const = 0; + // Returns the number of channels of captured audio data. + virtual int NumChannels() const = 0; + // Replaces the contents of `buffer` with 10ms of captured audio data + // (see TestAudioDeviceModule::SamplesPerFrame). Returns true if the + // capturer can keep producing data, or false when the capture finishes. + virtual bool Capture(rtc::BufferT* buffer) = 0; + }; + + class Renderer { + public: + virtual ~Renderer() {} + // Returns the sampling frequency in Hz of the audio data that this + // renderer receives. + virtual int SamplingFrequency() const = 0; + // Returns the number of channels of audio data to be required. + virtual int NumChannels() const = 0; + // Renders the passed audio data and returns true if the renderer wants + // to keep receiving data, or false otherwise. + virtual bool Render(rtc::ArrayView data) = 0; + }; + + // A fake capturer that generates pulses with random samples between + // -max_amplitude and +max_amplitude. + class PulsedNoiseCapturer : public Capturer { + public: + ~PulsedNoiseCapturer() override {} + + virtual void SetMaxAmplitude(int16_t amplitude) = 0; + }; + + // Creates a new TestAudioDeviceModule. 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. + // Use one of the Create... functions to get these instances. + static rtc::scoped_refptr Create( + TaskQueueFactory* task_queue_factory, + std::unique_ptr capturer, + std::unique_ptr renderer, + float speed = 1); + + // Returns a Capturer instance that generates a signal of `num_channels` + // channels where every second frame is zero and every second frame is evenly + // distributed random noise with max amplitude `max_amplitude`. + static std::unique_ptr CreatePulsedNoiseCapturer( + int16_t max_amplitude, + int sampling_frequency_in_hz, + int num_channels = 1); + + // Returns a Renderer instance that does nothing with the audio data. + static std::unique_ptr CreateDiscardRenderer( + int sampling_frequency_in_hz, + int num_channels = 1); + + // WavReader and WavWriter creation based on file name. + + // Returns a Capturer instance that gets its data from a WAV file. The sample + // rate and channels will be checked against the Wav file. + static std::unique_ptr CreateWavFileReader( + absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels = 1); + + // Returns a Capturer instance that gets its data from a file. + // Automatically detects sample rate and num of channels. + // `repeat` - if true, the file will be replayed from the start when we reach + // the end of file. + static std::unique_ptr CreateWavFileReader( + absl::string_view filename, + bool repeat = false); + + // Returns a Renderer instance that writes its data to a file. + static std::unique_ptr CreateWavFileWriter( + absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels = 1); + + // Returns a Renderer instance that writes its data to a WAV file, cutting + // off silence at the beginning (not necessarily perfect silence, see + // kAmplitudeThreshold) and at the end (only actual 0 samples in this case). + static std::unique_ptr CreateBoundedWavFileWriter( + absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels = 1); + + // Returns a Capturer instance that gets its data from a raw file (*.raw). + static std::unique_ptr CreateRawFileReader( + absl::string_view filename, + int sampling_frequency_in_hz = 48000, + int num_channels = 2, + bool repeat = true); + + // Returns a Renderer instance that writes its data to a raw file (*.raw), + // cutting off silence at the beginning (not necessarily perfect silence, see + // kAmplitudeThreshold) and at the end (only actual 0 samples in this case). + static std::unique_ptr CreateRawFileWriter( + absl::string_view filename, + int sampling_frequency_in_hz = 48000, + int num_channels = 2); + + private: + TestAudioDeviceModule() = default; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_DEVICE_INCLUDE_TEST_AUDIO_DEVICE_H_ diff --git a/third_party/libwebrtc/modules/audio_device/include/test_audio_device_unittest.cc b/third_party/libwebrtc/modules/audio_device/include/test_audio_device_unittest.cc new file mode 100644 index 0000000000..7a122ca84b --- /dev/null +++ b/third_party/libwebrtc/modules/audio_device/include/test_audio_device_unittest.cc @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2017 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/include/test_audio_device.h" + +#include +#include +#include +#include + +#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 "api/units/timestamp.h" +#include "common_audio/wav_file.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/synchronization/mutex.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" +#include "test/time_controller/simulated_time_controller.h" + +namespace webrtc { +namespace { + +void RunWavTest(const std::vector& input_samples, + const std::vector& expected_samples) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + const std::string output_filename = + test::OutputPath() + "BoundedWavFileWriterTest_" + test_info->name() + + "_" + std::to_string(std::rand()) + ".wav"; + + static const size_t kSamplesPerFrame = 8; + static const int kSampleRate = kSamplesPerFrame * 100; + EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), + kSamplesPerFrame); + + // Test through file name API. + { + std::unique_ptr writer = + TestAudioDeviceModule::CreateBoundedWavFileWriter(output_filename, 800); + + for (size_t i = 0; i < input_samples.size(); i += kSamplesPerFrame) { + EXPECT_TRUE(writer->Render(rtc::ArrayView( + &input_samples[i], + std::min(kSamplesPerFrame, input_samples.size() - i)))); + } + } + + { + WavReader reader(output_filename); + std::vector read_samples(expected_samples.size()); + EXPECT_EQ(expected_samples.size(), + reader.ReadSamples(read_samples.size(), read_samples.data())); + EXPECT_EQ(expected_samples, read_samples); + + EXPECT_EQ(0u, reader.ReadSamples(read_samples.size(), read_samples.data())); + } + + remove(output_filename.c_str()); +} + +TEST(BoundedWavFileWriterTest, NoSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 3, 88, + 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples = kInputSamples; + RunWavTest(kInputSamples, kExpectedSamples); +} + +TEST(BoundedWavFileWriterTest, SomeStartSilence) { + static const std::vector kInputSamples = { + 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 10, + kInputSamples.end()); + RunWavTest(kInputSamples, kExpectedSamples); +} + +TEST(BoundedWavFileWriterTest, NegativeStartSilence) { + static const std::vector kInputSamples = { + 0, -4, -6, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 2, + kInputSamples.end()); + RunWavTest(kInputSamples, kExpectedSamples); +} + +TEST(BoundedWavFileWriterTest, SomeEndSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 9); + RunWavTest(kInputSamples, kExpectedSamples); +} + +TEST(BoundedWavFileWriterTest, DoubleEndSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 0, 0, + 0, -1213, -13222, -7, -3525, 5787, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 2); + RunWavTest(kInputSamples, kExpectedSamples); +} + +TEST(BoundedWavFileWriterTest, DoubleSilence) { + static const std::vector kInputSamples = {0, -1213, -13222, -7, + -3525, 5787, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 1, + kInputSamples.end() - 2); + RunWavTest(kInputSamples, kExpectedSamples); +} + +TEST(BoundedWavFileWriterTest, EndSilenceCutoff) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 4); + RunWavTest(kInputSamples, kExpectedSamples); +} + +TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) { + static const std::vector kInputSamples = {75, 1234, 243, -1231, + -22222, 0, 3, 88}; + static const rtc::BufferT kExpectedSamples(kInputSamples.data(), + kInputSamples.size()); + + const std::string output_filename = test::OutputPath() + + "WavFileReaderTest_RepeatedTrue_" + + std::to_string(std::rand()) + ".wav"; + + static const size_t kSamplesPerFrame = 8; + static const int kSampleRate = kSamplesPerFrame * 100; + EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), + kSamplesPerFrame); + + // Create raw file to read. + { + std::unique_ptr writer = + TestAudioDeviceModule::CreateWavFileWriter(output_filename, 800); + + for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) { + EXPECT_TRUE(writer->Render(rtc::ArrayView( + &kInputSamples[i], + std::min(kSamplesPerFrame, kInputSamples.size() - i)))); + } + } + + { + std::unique_ptr reader = + TestAudioDeviceModule::CreateWavFileReader(output_filename, true); + rtc::BufferT buffer(kExpectedSamples.size()); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(kExpectedSamples, buffer); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(kExpectedSamples, buffer); + } + + remove(output_filename.c_str()); +} + +void RunRawTestNoRepeat(const std::vector& input_samples, + const std::vector& expected_samples) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + const std::string output_filename = test::OutputPath() + "RawFileTest_" + + test_info->name() + "_" + + std::to_string(std::rand()) + ".raw"; + + static const size_t kSamplesPerFrame = 8; + static const int kSampleRate = kSamplesPerFrame * 100; + EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), + kSamplesPerFrame); + + // Test through file name API. + { + std::unique_ptr writer = + TestAudioDeviceModule::CreateRawFileWriter( + output_filename, /*sampling_frequency_in_hz=*/800); + + for (size_t i = 0; i < input_samples.size(); i += kSamplesPerFrame) { + EXPECT_TRUE(writer->Render(rtc::ArrayView( + &input_samples[i], + std::min(kSamplesPerFrame, input_samples.size() - i)))); + } + } + + { + std::unique_ptr reader = + TestAudioDeviceModule::CreateRawFileReader( + output_filename, /*sampling_frequency_in_hz=*/800, + /*num_channels=*/2, /*repeat=*/false); + rtc::BufferT buffer(expected_samples.size()); + rtc::BufferT expected_buffer(expected_samples.size()); + expected_buffer.SetData(expected_samples); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(expected_buffer, buffer); + EXPECT_FALSE(reader->Capture(&buffer)); + EXPECT_TRUE(buffer.empty()); + } + + remove(output_filename.c_str()); +} + +TEST(RawFileWriterTest, NoSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 3, 88, + 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples = kInputSamples; + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, SomeStartSilence) { + static const std::vector kInputSamples = { + 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 10, + kInputSamples.end()); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, NegativeStartSilence) { + static const std::vector kInputSamples = { + 0, -4, -6, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 2, + kInputSamples.end()); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, SomeEndSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 9); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, DoubleEndSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 0, 0, + 0, -1213, -13222, -7, -3525, 5787, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 2); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, DoubleSilence) { + static const std::vector kInputSamples = {0, -1213, -13222, -7, + -3525, 5787, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 1, + kInputSamples.end() - 2); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, EndSilenceCutoff) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 4); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, Repeat) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 3, 88, + 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; + static const rtc::BufferT kExpectedSamples(kInputSamples.data(), + kInputSamples.size()); + + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + const std::string output_filename = test::OutputPath() + "RawFileTest_" + + test_info->name() + "_" + + std::to_string(std::rand()) + ".raw"; + + static const size_t kSamplesPerFrame = 8; + static const int kSampleRate = kSamplesPerFrame * 100; + EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), + kSamplesPerFrame); + + // Test through file name API. + { + std::unique_ptr writer = + TestAudioDeviceModule::CreateRawFileWriter( + output_filename, /*sampling_frequency_in_hz=*/800); + + for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) { + EXPECT_TRUE(writer->Render(rtc::ArrayView( + &kInputSamples[i], + std::min(kSamplesPerFrame, kInputSamples.size() - i)))); + } + } + + { + std::unique_ptr reader = + TestAudioDeviceModule::CreateRawFileReader( + output_filename, /*sampling_frequency_in_hz=*/800, + /*num_channels=*/2, /*repeat=*/true); + rtc::BufferT buffer(kExpectedSamples.size()); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(kExpectedSamples, buffer); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(kExpectedSamples, buffer); + } + + remove(output_filename.c_str()); +} + +TEST(PulsedNoiseCapturerTest, SetMaxAmplitude) { + const int16_t kAmplitude = 50; + std::unique_ptr capturer = + TestAudioDeviceModule::CreatePulsedNoiseCapturer( + kAmplitude, /*sampling_frequency_in_hz=*/8000); + rtc::BufferT recording_buffer; + + // Verify that the capturer doesn't create entries louder than than + // kAmplitude. Since the pulse generator alternates between writing + // zeroes and actual entries, we need to do the capturing twice. + capturer->Capture(&recording_buffer); + capturer->Capture(&recording_buffer); + int16_t max_sample = + *std::max_element(recording_buffer.begin(), recording_buffer.end()); + EXPECT_LE(max_sample, kAmplitude); + + // Increase the amplitude and verify that the samples can now be louder + // than the previous max. + capturer->SetMaxAmplitude(kAmplitude * 2); + capturer->Capture(&recording_buffer); + capturer->Capture(&recording_buffer); + max_sample = + *std::max_element(recording_buffer.begin(), recording_buffer.end()); + 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 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 samples_per_channel() const { + MutexLock lock(&mutex_); + return samples_per_channel_; + } + std::vector number_of_channels() const { + MutexLock lock(&mutex_); + return number_of_channels_; + } + std::vector bytes_per_sample() const { + MutexLock lock(&mutex_); + return bytes_per_sample_; + } + std::vector samples_per_second() const { + MutexLock lock(&mutex_); + return samples_per_second_; + } + + private: + const Mode mode_; + + mutable Mutex mutex_; + std::vector samples_per_channel_ RTC_GUARDED_BY(mutex_); + std::vector number_of_channels_ RTC_GUARDED_BY(mutex_); + std::vector bytes_per_sample_ RTC_GUARDED_BY(mutex_); + std::vector samples_per_second_ RTC_GUARDED_BY(mutex_); +}; + +TEST(TestAudioDeviceModuleTest, CreatedADMCanRecord) { + GlobalSimulatedTimeController time_controller(kStartTime); + TestAudioTransport audio_transport(TestAudioTransport::Mode::kRecording); + std::unique_ptr capturer = + TestAudioDeviceModule::CreatePulsedNoiseCapturer( + /*max_amplitude=*/1000, + /*sampling_frequency_in_hz=*/48000, /*num_channels=*/2); + + rtc::scoped_refptr 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 renderer = + TestAudioDeviceModule::CreateDiscardRenderer( + /*sampling_frequency_in_hz=*/48000, /*num_channels=*/2); + + rtc::scoped_refptr 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 -- cgit v1.2.3