mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00

Bug: webrtc:14366 Change-Id: Icd8792a2f9efa5609dd13da2e175042fac101d36 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/272101 Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Markus Handell <handellm@webrtc.org> Auto-Submit: Markus Handell <handellm@webrtc.org> Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#37844}
1018 lines
39 KiB
C++
1018 lines
39 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "modules/audio_device/include/audio_device.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <numeric>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "absl/strings/string_view.h"
|
|
#include "api/scoped_refptr.h"
|
|
#include "api/task_queue/default_task_queue_factory.h"
|
|
#include "api/task_queue/task_queue_factory.h"
|
|
#include "modules/audio_device/android/audio_common.h"
|
|
#include "modules/audio_device/android/audio_manager.h"
|
|
#include "modules/audio_device/android/build_info.h"
|
|
#include "modules/audio_device/android/ensure_initialized.h"
|
|
#include "modules/audio_device/audio_device_impl.h"
|
|
#include "modules/audio_device/include/mock_audio_transport.h"
|
|
#include "rtc_base/arraysize.h"
|
|
#include "rtc_base/event.h"
|
|
#include "rtc_base/synchronization/mutex.h"
|
|
#include "rtc_base/time_utils.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
#include "test/testsupport/file_utils.h"
|
|
|
|
using std::cout;
|
|
using std::endl;
|
|
using ::testing::_;
|
|
using ::testing::AtLeast;
|
|
using ::testing::Gt;
|
|
using ::testing::Invoke;
|
|
using ::testing::NiceMock;
|
|
using ::testing::NotNull;
|
|
using ::testing::Return;
|
|
|
|
// #define ENABLE_DEBUG_PRINTF
|
|
#ifdef ENABLE_DEBUG_PRINTF
|
|
#define PRINTD(...) fprintf(stderr, __VA_ARGS__);
|
|
#else
|
|
#define PRINTD(...) ((void)0)
|
|
#endif
|
|
#define PRINT(...) fprintf(stderr, __VA_ARGS__);
|
|
|
|
namespace webrtc {
|
|
|
|
// Number of callbacks (input or output) the tests waits for before we set
|
|
// an event indicating that the test was OK.
|
|
static const size_t kNumCallbacks = 10;
|
|
// Max amount of time we wait for an event to be set while counting callbacks.
|
|
static constexpr TimeDelta kTestTimeOut = TimeDelta::Seconds(10);
|
|
// Average number of audio callbacks per second assuming 10ms packet size.
|
|
static const size_t kNumCallbacksPerSecond = 100;
|
|
// Play out a test file during this time (unit is in seconds).
|
|
static const int kFilePlayTimeInSec = 5;
|
|
static const size_t kBitsPerSample = 16;
|
|
static const size_t kBytesPerSample = kBitsPerSample / 8;
|
|
// Run the full-duplex test during this time (unit is in seconds).
|
|
// Note that first `kNumIgnoreFirstCallbacks` are ignored.
|
|
static constexpr TimeDelta kFullDuplexTime = TimeDelta::Seconds(5);
|
|
// Wait for the callback sequence to stabilize by ignoring this amount of the
|
|
// initial callbacks (avoids initial FIFO access).
|
|
// Only used in the RunPlayoutAndRecordingInFullDuplex test.
|
|
static const size_t kNumIgnoreFirstCallbacks = 50;
|
|
// Sets the number of impulses per second in the latency test.
|
|
static const int kImpulseFrequencyInHz = 1;
|
|
// Length of round-trip latency measurements. Number of transmitted impulses
|
|
// is kImpulseFrequencyInHz * kMeasureLatencyTime - 1.
|
|
static constexpr TimeDelta kMeasureLatencyTime = TimeDelta::Seconds(11);
|
|
// Utilized in round-trip latency measurements to avoid capturing noise samples.
|
|
static const int kImpulseThreshold = 1000;
|
|
static const char kTag[] = "[..........] ";
|
|
|
|
enum TransportType {
|
|
kPlayout = 0x1,
|
|
kRecording = 0x2,
|
|
};
|
|
|
|
// Interface for processing the audio stream. Real implementations can e.g.
|
|
// run audio in loopback, read audio from a file or perform latency
|
|
// measurements.
|
|
class AudioStreamInterface {
|
|
public:
|
|
virtual void Write(const void* source, size_t num_frames) = 0;
|
|
virtual void Read(void* destination, size_t num_frames) = 0;
|
|
|
|
protected:
|
|
virtual ~AudioStreamInterface() {}
|
|
};
|
|
|
|
// Reads audio samples from a PCM file where the file is stored in memory at
|
|
// construction.
|
|
class FileAudioStream : public AudioStreamInterface {
|
|
public:
|
|
FileAudioStream(size_t num_callbacks,
|
|
absl::string_view file_name,
|
|
int sample_rate)
|
|
: file_size_in_bytes_(0), sample_rate_(sample_rate), file_pos_(0) {
|
|
file_size_in_bytes_ = test::GetFileSize(file_name);
|
|
sample_rate_ = sample_rate;
|
|
EXPECT_GE(file_size_in_callbacks(), num_callbacks)
|
|
<< "Size of test file is not large enough to last during the test.";
|
|
const size_t num_16bit_samples =
|
|
test::GetFileSize(file_name) / kBytesPerSample;
|
|
file_.reset(new int16_t[num_16bit_samples]);
|
|
FILE* audio_file = fopen(std::string(file_name).c_str(), "rb");
|
|
EXPECT_NE(audio_file, nullptr);
|
|
size_t num_samples_read =
|
|
fread(file_.get(), sizeof(int16_t), num_16bit_samples, audio_file);
|
|
EXPECT_EQ(num_samples_read, num_16bit_samples);
|
|
fclose(audio_file);
|
|
}
|
|
|
|
// AudioStreamInterface::Write() is not implemented.
|
|
void Write(const void* source, size_t num_frames) override {}
|
|
|
|
// Read samples from file stored in memory (at construction) and copy
|
|
// `num_frames` (<=> 10ms) to the `destination` byte buffer.
|
|
void Read(void* destination, size_t num_frames) override {
|
|
memcpy(destination, static_cast<int16_t*>(&file_[file_pos_]),
|
|
num_frames * sizeof(int16_t));
|
|
file_pos_ += num_frames;
|
|
}
|
|
|
|
int file_size_in_seconds() const {
|
|
return static_cast<int>(file_size_in_bytes_ /
|
|
(kBytesPerSample * sample_rate_));
|
|
}
|
|
size_t file_size_in_callbacks() const {
|
|
return file_size_in_seconds() * kNumCallbacksPerSecond;
|
|
}
|
|
|
|
private:
|
|
size_t file_size_in_bytes_;
|
|
int sample_rate_;
|
|
std::unique_ptr<int16_t[]> file_;
|
|
size_t file_pos_;
|
|
};
|
|
|
|
// Simple first in first out (FIFO) class that wraps a list of 16-bit audio
|
|
// buffers of fixed size and allows Write and Read operations. The idea is to
|
|
// store recorded audio buffers (using Write) and then read (using Read) these
|
|
// stored buffers with as short delay as possible when the audio layer needs
|
|
// data to play out. The number of buffers in the FIFO will stabilize under
|
|
// normal conditions since there will be a balance between Write and Read calls.
|
|
// The container is a std::list container and access is protected with a lock
|
|
// since both sides (playout and recording) are driven by its own thread.
|
|
class FifoAudioStream : public AudioStreamInterface {
|
|
public:
|
|
explicit FifoAudioStream(size_t frames_per_buffer)
|
|
: frames_per_buffer_(frames_per_buffer),
|
|
bytes_per_buffer_(frames_per_buffer_ * sizeof(int16_t)),
|
|
fifo_(new AudioBufferList),
|
|
largest_size_(0),
|
|
total_written_elements_(0),
|
|
write_count_(0) {
|
|
EXPECT_NE(fifo_.get(), nullptr);
|
|
}
|
|
|
|
~FifoAudioStream() { Flush(); }
|
|
|
|
// Allocate new memory, copy `num_frames` samples from `source` into memory
|
|
// and add pointer to the memory location to end of the list.
|
|
// Increases the size of the FIFO by one element.
|
|
void Write(const void* source, size_t num_frames) override {
|
|
ASSERT_EQ(num_frames, frames_per_buffer_);
|
|
PRINTD("+");
|
|
if (write_count_++ < kNumIgnoreFirstCallbacks) {
|
|
return;
|
|
}
|
|
int16_t* memory = new int16_t[frames_per_buffer_];
|
|
memcpy(static_cast<int16_t*>(&memory[0]), source, bytes_per_buffer_);
|
|
MutexLock lock(&lock_);
|
|
fifo_->push_back(memory);
|
|
const size_t size = fifo_->size();
|
|
if (size > largest_size_) {
|
|
largest_size_ = size;
|
|
PRINTD("(%zu)", largest_size_);
|
|
}
|
|
total_written_elements_ += size;
|
|
}
|
|
|
|
// Read pointer to data buffer from front of list, copy `num_frames` of stored
|
|
// data into `destination` and delete the utilized memory allocation.
|
|
// Decreases the size of the FIFO by one element.
|
|
void Read(void* destination, size_t num_frames) override {
|
|
ASSERT_EQ(num_frames, frames_per_buffer_);
|
|
PRINTD("-");
|
|
MutexLock lock(&lock_);
|
|
if (fifo_->empty()) {
|
|
memset(destination, 0, bytes_per_buffer_);
|
|
} else {
|
|
int16_t* memory = fifo_->front();
|
|
fifo_->pop_front();
|
|
memcpy(destination, static_cast<int16_t*>(&memory[0]), bytes_per_buffer_);
|
|
delete memory;
|
|
}
|
|
}
|
|
|
|
size_t size() const { return fifo_->size(); }
|
|
|
|
size_t largest_size() const { return largest_size_; }
|
|
|
|
size_t average_size() const {
|
|
return (total_written_elements_ == 0)
|
|
? 0.0
|
|
: 0.5 + static_cast<float>(total_written_elements_) /
|
|
(write_count_ - kNumIgnoreFirstCallbacks);
|
|
}
|
|
|
|
private:
|
|
void Flush() {
|
|
for (auto it = fifo_->begin(); it != fifo_->end(); ++it) {
|
|
delete *it;
|
|
}
|
|
fifo_->clear();
|
|
}
|
|
|
|
using AudioBufferList = std::list<int16_t*>;
|
|
Mutex lock_;
|
|
const size_t frames_per_buffer_;
|
|
const size_t bytes_per_buffer_;
|
|
std::unique_ptr<AudioBufferList> fifo_;
|
|
size_t largest_size_;
|
|
size_t total_written_elements_;
|
|
size_t write_count_;
|
|
};
|
|
|
|
// Inserts periodic impulses and measures the latency between the time of
|
|
// transmission and time of receiving the same impulse.
|
|
// Usage requires a special hardware called Audio Loopback Dongle.
|
|
// See http://source.android.com/devices/audio/loopback.html for details.
|
|
class LatencyMeasuringAudioStream : public AudioStreamInterface {
|
|
public:
|
|
explicit LatencyMeasuringAudioStream(size_t frames_per_buffer)
|
|
: frames_per_buffer_(frames_per_buffer),
|
|
bytes_per_buffer_(frames_per_buffer_ * sizeof(int16_t)),
|
|
play_count_(0),
|
|
rec_count_(0),
|
|
pulse_time_(0) {}
|
|
|
|
// Insert periodic impulses in first two samples of `destination`.
|
|
void Read(void* destination, size_t num_frames) override {
|
|
ASSERT_EQ(num_frames, frames_per_buffer_);
|
|
if (play_count_ == 0) {
|
|
PRINT("[");
|
|
}
|
|
play_count_++;
|
|
memset(destination, 0, bytes_per_buffer_);
|
|
if (play_count_ % (kNumCallbacksPerSecond / kImpulseFrequencyInHz) == 0) {
|
|
if (pulse_time_ == 0) {
|
|
pulse_time_ = rtc::TimeMillis();
|
|
}
|
|
PRINT(".");
|
|
const int16_t impulse = std::numeric_limits<int16_t>::max();
|
|
int16_t* ptr16 = static_cast<int16_t*>(destination);
|
|
for (size_t i = 0; i < 2; ++i) {
|
|
ptr16[i] = impulse;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detect received impulses in `source`, derive time between transmission and
|
|
// detection and add the calculated delay to list of latencies.
|
|
void Write(const void* source, size_t num_frames) override {
|
|
ASSERT_EQ(num_frames, frames_per_buffer_);
|
|
rec_count_++;
|
|
if (pulse_time_ == 0) {
|
|
// Avoid detection of new impulse response until a new impulse has
|
|
// been transmitted (sets `pulse_time_` to value larger than zero).
|
|
return;
|
|
}
|
|
const int16_t* ptr16 = static_cast<const int16_t*>(source);
|
|
std::vector<int16_t> vec(ptr16, ptr16 + num_frames);
|
|
// Find max value in the audio buffer.
|
|
int max = *std::max_element(vec.begin(), vec.end());
|
|
// Find index (element position in vector) of the max element.
|
|
int index_of_max =
|
|
std::distance(vec.begin(), std::find(vec.begin(), vec.end(), max));
|
|
if (max > kImpulseThreshold) {
|
|
PRINTD("(%d,%d)", max, index_of_max);
|
|
int64_t now_time = rtc::TimeMillis();
|
|
int extra_delay = IndexToMilliseconds(static_cast<double>(index_of_max));
|
|
PRINTD("[%d]", static_cast<int>(now_time - pulse_time_));
|
|
PRINTD("[%d]", extra_delay);
|
|
// Total latency is the difference between transmit time and detection
|
|
// tome plus the extra delay within the buffer in which we detected the
|
|
// received impulse. It is transmitted at sample 0 but can be received
|
|
// at sample N where N > 0. The term `extra_delay` accounts for N and it
|
|
// is a value between 0 and 10ms.
|
|
latencies_.push_back(now_time - pulse_time_ + extra_delay);
|
|
pulse_time_ = 0;
|
|
} else {
|
|
PRINTD("-");
|
|
}
|
|
}
|
|
|
|
size_t num_latency_values() const { return latencies_.size(); }
|
|
|
|
int min_latency() const {
|
|
if (latencies_.empty())
|
|
return 0;
|
|
return *std::min_element(latencies_.begin(), latencies_.end());
|
|
}
|
|
|
|
int max_latency() const {
|
|
if (latencies_.empty())
|
|
return 0;
|
|
return *std::max_element(latencies_.begin(), latencies_.end());
|
|
}
|
|
|
|
int average_latency() const {
|
|
if (latencies_.empty())
|
|
return 0;
|
|
return 0.5 + static_cast<double>(
|
|
std::accumulate(latencies_.begin(), latencies_.end(), 0)) /
|
|
latencies_.size();
|
|
}
|
|
|
|
void PrintResults() const {
|
|
PRINT("] ");
|
|
for (auto it = latencies_.begin(); it != latencies_.end(); ++it) {
|
|
PRINT("%d ", *it);
|
|
}
|
|
PRINT("\n");
|
|
PRINT("%s[min, max, avg]=[%d, %d, %d] ms\n", kTag, min_latency(),
|
|
max_latency(), average_latency());
|
|
}
|
|
|
|
int IndexToMilliseconds(double index) const {
|
|
return static_cast<int>(10.0 * (index / frames_per_buffer_) + 0.5);
|
|
}
|
|
|
|
private:
|
|
const size_t frames_per_buffer_;
|
|
const size_t bytes_per_buffer_;
|
|
size_t play_count_;
|
|
size_t rec_count_;
|
|
int64_t pulse_time_;
|
|
std::vector<int> latencies_;
|
|
};
|
|
|
|
// Mocks the AudioTransport object and proxies actions for the two callbacks
|
|
// (RecordedDataIsAvailable and NeedMorePlayData) to different implementations
|
|
// of AudioStreamInterface.
|
|
class MockAudioTransportAndroid : public test::MockAudioTransport {
|
|
public:
|
|
explicit MockAudioTransportAndroid(int type)
|
|
: num_callbacks_(0),
|
|
type_(type),
|
|
play_count_(0),
|
|
rec_count_(0),
|
|
audio_stream_(nullptr) {}
|
|
|
|
virtual ~MockAudioTransportAndroid() {}
|
|
|
|
// Set default actions of the mock object. We are delegating to fake
|
|
// implementations (of AudioStreamInterface) here.
|
|
void HandleCallbacks(rtc::Event* test_is_done,
|
|
AudioStreamInterface* audio_stream,
|
|
int num_callbacks) {
|
|
test_is_done_ = test_is_done;
|
|
audio_stream_ = audio_stream;
|
|
num_callbacks_ = num_callbacks;
|
|
if (play_mode()) {
|
|
ON_CALL(*this, NeedMorePlayData(_, _, _, _, _, _, _, _))
|
|
.WillByDefault(
|
|
Invoke(this, &MockAudioTransportAndroid::RealNeedMorePlayData));
|
|
}
|
|
if (rec_mode()) {
|
|
ON_CALL(*this, RecordedDataIsAvailable(_, _, _, _, _, _, _, _, _, _))
|
|
.WillByDefault(Invoke(
|
|
this, &MockAudioTransportAndroid::RealRecordedDataIsAvailable));
|
|
}
|
|
}
|
|
|
|
int32_t RealRecordedDataIsAvailable(const void* audioSamples,
|
|
const size_t nSamples,
|
|
const size_t nBytesPerSample,
|
|
const size_t nChannels,
|
|
const uint32_t samplesPerSec,
|
|
const uint32_t totalDelayMS,
|
|
const int32_t clockDrift,
|
|
const uint32_t currentMicLevel,
|
|
const bool keyPressed,
|
|
uint32_t& newMicLevel) { // NOLINT
|
|
EXPECT_TRUE(rec_mode()) << "No test is expecting these callbacks.";
|
|
rec_count_++;
|
|
// Process the recorded audio stream if an AudioStreamInterface
|
|
// implementation exists.
|
|
if (audio_stream_) {
|
|
audio_stream_->Write(audioSamples, nSamples);
|
|
}
|
|
if (ReceivedEnoughCallbacks()) {
|
|
test_is_done_->Set();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32_t RealNeedMorePlayData(const size_t nSamples,
|
|
const size_t nBytesPerSample,
|
|
const size_t nChannels,
|
|
const uint32_t samplesPerSec,
|
|
void* audioSamples,
|
|
size_t& nSamplesOut, // NOLINT
|
|
int64_t* elapsed_time_ms,
|
|
int64_t* ntp_time_ms) {
|
|
EXPECT_TRUE(play_mode()) << "No test is expecting these callbacks.";
|
|
play_count_++;
|
|
nSamplesOut = nSamples;
|
|
// Read (possibly processed) audio stream samples to be played out if an
|
|
// AudioStreamInterface implementation exists.
|
|
if (audio_stream_) {
|
|
audio_stream_->Read(audioSamples, nSamples);
|
|
}
|
|
if (ReceivedEnoughCallbacks()) {
|
|
test_is_done_->Set();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool ReceivedEnoughCallbacks() {
|
|
bool recording_done = false;
|
|
if (rec_mode())
|
|
recording_done = rec_count_ >= num_callbacks_;
|
|
else
|
|
recording_done = true;
|
|
|
|
bool playout_done = false;
|
|
if (play_mode())
|
|
playout_done = play_count_ >= num_callbacks_;
|
|
else
|
|
playout_done = true;
|
|
|
|
return recording_done && playout_done;
|
|
}
|
|
|
|
bool play_mode() const { return type_ & kPlayout; }
|
|
bool rec_mode() const { return type_ & kRecording; }
|
|
|
|
private:
|
|
rtc::Event* test_is_done_;
|
|
size_t num_callbacks_;
|
|
int type_;
|
|
size_t play_count_;
|
|
size_t rec_count_;
|
|
AudioStreamInterface* audio_stream_;
|
|
std::unique_ptr<LatencyMeasuringAudioStream> latency_audio_stream_;
|
|
};
|
|
|
|
// AudioDeviceTest test fixture.
|
|
class AudioDeviceTest : public ::testing::Test {
|
|
protected:
|
|
AudioDeviceTest() : task_queue_factory_(CreateDefaultTaskQueueFactory()) {
|
|
// One-time initialization of JVM and application context. Ensures that we
|
|
// can do calls between C++ and Java. Initializes both Java and OpenSL ES
|
|
// implementations.
|
|
webrtc::audiodevicemodule::EnsureInitialized();
|
|
// Creates an audio device using a default audio layer.
|
|
audio_device_ = CreateAudioDevice(AudioDeviceModule::kPlatformDefaultAudio);
|
|
EXPECT_NE(audio_device_.get(), nullptr);
|
|
EXPECT_EQ(0, audio_device_->Init());
|
|
playout_parameters_ = audio_manager()->GetPlayoutAudioParameters();
|
|
record_parameters_ = audio_manager()->GetRecordAudioParameters();
|
|
build_info_.reset(new BuildInfo());
|
|
}
|
|
virtual ~AudioDeviceTest() { EXPECT_EQ(0, audio_device_->Terminate()); }
|
|
|
|
int playout_sample_rate() const { return playout_parameters_.sample_rate(); }
|
|
int record_sample_rate() const { return record_parameters_.sample_rate(); }
|
|
size_t playout_channels() const { return playout_parameters_.channels(); }
|
|
size_t record_channels() const { return record_parameters_.channels(); }
|
|
size_t playout_frames_per_10ms_buffer() const {
|
|
return playout_parameters_.frames_per_10ms_buffer();
|
|
}
|
|
size_t record_frames_per_10ms_buffer() const {
|
|
return record_parameters_.frames_per_10ms_buffer();
|
|
}
|
|
|
|
int total_delay_ms() const {
|
|
return audio_manager()->GetDelayEstimateInMilliseconds();
|
|
}
|
|
|
|
rtc::scoped_refptr<AudioDeviceModule> audio_device() const {
|
|
return audio_device_;
|
|
}
|
|
|
|
AudioDeviceModuleImpl* audio_device_impl() const {
|
|
return static_cast<AudioDeviceModuleImpl*>(audio_device_.get());
|
|
}
|
|
|
|
AudioManager* audio_manager() const {
|
|
return audio_device_impl()->GetAndroidAudioManagerForTest();
|
|
}
|
|
|
|
AudioManager* GetAudioManager(AudioDeviceModule* adm) const {
|
|
return static_cast<AudioDeviceModuleImpl*>(adm)
|
|
->GetAndroidAudioManagerForTest();
|
|
}
|
|
|
|
AudioDeviceBuffer* audio_device_buffer() const {
|
|
return audio_device_impl()->GetAudioDeviceBuffer();
|
|
}
|
|
|
|
rtc::scoped_refptr<AudioDeviceModule> CreateAudioDevice(
|
|
AudioDeviceModule::AudioLayer audio_layer) {
|
|
rtc::scoped_refptr<AudioDeviceModule> module(
|
|
AudioDeviceModule::Create(audio_layer, task_queue_factory_.get()));
|
|
return module;
|
|
}
|
|
|
|
// Returns file name relative to the resource root given a sample rate.
|
|
std::string GetFileName(int sample_rate) {
|
|
EXPECT_TRUE(sample_rate == 48000 || sample_rate == 44100);
|
|
char fname[64];
|
|
snprintf(fname, sizeof(fname), "audio_device/audio_short%d",
|
|
sample_rate / 1000);
|
|
std::string file_name(webrtc::test::ResourcePath(fname, "pcm"));
|
|
EXPECT_TRUE(test::FileExists(file_name));
|
|
#ifdef ENABLE_PRINTF
|
|
PRINT("file name: %s\n", file_name.c_str());
|
|
const size_t bytes = test::GetFileSize(file_name);
|
|
PRINT("file size: %zu [bytes]\n", bytes);
|
|
PRINT("file size: %zu [samples]\n", bytes / kBytesPerSample);
|
|
const int seconds =
|
|
static_cast<int>(bytes / (sample_rate * kBytesPerSample));
|
|
PRINT("file size: %d [secs]\n", seconds);
|
|
PRINT("file size: %zu [callbacks]\n", seconds * kNumCallbacksPerSecond);
|
|
#endif
|
|
return file_name;
|
|
}
|
|
|
|
AudioDeviceModule::AudioLayer GetActiveAudioLayer() const {
|
|
AudioDeviceModule::AudioLayer audio_layer;
|
|
EXPECT_EQ(0, audio_device()->ActiveAudioLayer(&audio_layer));
|
|
return audio_layer;
|
|
}
|
|
|
|
int TestDelayOnAudioLayer(
|
|
const AudioDeviceModule::AudioLayer& layer_to_test) {
|
|
rtc::scoped_refptr<AudioDeviceModule> audio_device;
|
|
audio_device = CreateAudioDevice(layer_to_test);
|
|
EXPECT_NE(audio_device.get(), nullptr);
|
|
AudioManager* audio_manager = GetAudioManager(audio_device.get());
|
|
EXPECT_NE(audio_manager, nullptr);
|
|
return audio_manager->GetDelayEstimateInMilliseconds();
|
|
}
|
|
|
|
AudioDeviceModule::AudioLayer TestActiveAudioLayer(
|
|
const AudioDeviceModule::AudioLayer& layer_to_test) {
|
|
rtc::scoped_refptr<AudioDeviceModule> audio_device;
|
|
audio_device = CreateAudioDevice(layer_to_test);
|
|
EXPECT_NE(audio_device.get(), nullptr);
|
|
AudioDeviceModule::AudioLayer active;
|
|
EXPECT_EQ(0, audio_device->ActiveAudioLayer(&active));
|
|
return active;
|
|
}
|
|
|
|
bool DisableTestForThisDevice(absl::string_view model) {
|
|
return (build_info_->GetDeviceModel() == model);
|
|
}
|
|
|
|
// Volume control is currently only supported for the Java output audio layer.
|
|
// For OpenSL ES, the internal stream volume is always on max level and there
|
|
// is no need for this test to set it to max.
|
|
bool AudioLayerSupportsVolumeControl() const {
|
|
return GetActiveAudioLayer() == AudioDeviceModule::kAndroidJavaAudio;
|
|
}
|
|
|
|
void SetMaxPlayoutVolume() {
|
|
if (!AudioLayerSupportsVolumeControl())
|
|
return;
|
|
uint32_t max_volume;
|
|
EXPECT_EQ(0, audio_device()->MaxSpeakerVolume(&max_volume));
|
|
EXPECT_EQ(0, audio_device()->SetSpeakerVolume(max_volume));
|
|
}
|
|
|
|
void DisableBuiltInAECIfAvailable() {
|
|
if (audio_device()->BuiltInAECIsAvailable()) {
|
|
EXPECT_EQ(0, audio_device()->EnableBuiltInAEC(false));
|
|
}
|
|
}
|
|
|
|
void StartPlayout() {
|
|
EXPECT_FALSE(audio_device()->PlayoutIsInitialized());
|
|
EXPECT_FALSE(audio_device()->Playing());
|
|
EXPECT_EQ(0, audio_device()->InitPlayout());
|
|
EXPECT_TRUE(audio_device()->PlayoutIsInitialized());
|
|
EXPECT_EQ(0, audio_device()->StartPlayout());
|
|
EXPECT_TRUE(audio_device()->Playing());
|
|
}
|
|
|
|
void StopPlayout() {
|
|
EXPECT_EQ(0, audio_device()->StopPlayout());
|
|
EXPECT_FALSE(audio_device()->Playing());
|
|
EXPECT_FALSE(audio_device()->PlayoutIsInitialized());
|
|
}
|
|
|
|
void StartRecording() {
|
|
EXPECT_FALSE(audio_device()->RecordingIsInitialized());
|
|
EXPECT_FALSE(audio_device()->Recording());
|
|
EXPECT_EQ(0, audio_device()->InitRecording());
|
|
EXPECT_TRUE(audio_device()->RecordingIsInitialized());
|
|
EXPECT_EQ(0, audio_device()->StartRecording());
|
|
EXPECT_TRUE(audio_device()->Recording());
|
|
}
|
|
|
|
void StopRecording() {
|
|
EXPECT_EQ(0, audio_device()->StopRecording());
|
|
EXPECT_FALSE(audio_device()->Recording());
|
|
}
|
|
|
|
int GetMaxSpeakerVolume() const {
|
|
uint32_t max_volume(0);
|
|
EXPECT_EQ(0, audio_device()->MaxSpeakerVolume(&max_volume));
|
|
return max_volume;
|
|
}
|
|
|
|
int GetMinSpeakerVolume() const {
|
|
uint32_t min_volume(0);
|
|
EXPECT_EQ(0, audio_device()->MinSpeakerVolume(&min_volume));
|
|
return min_volume;
|
|
}
|
|
|
|
int GetSpeakerVolume() const {
|
|
uint32_t volume(0);
|
|
EXPECT_EQ(0, audio_device()->SpeakerVolume(&volume));
|
|
return volume;
|
|
}
|
|
|
|
rtc::Event test_is_done_;
|
|
std::unique_ptr<TaskQueueFactory> task_queue_factory_;
|
|
rtc::scoped_refptr<AudioDeviceModule> audio_device_;
|
|
AudioParameters playout_parameters_;
|
|
AudioParameters record_parameters_;
|
|
std::unique_ptr<BuildInfo> build_info_;
|
|
};
|
|
|
|
TEST_F(AudioDeviceTest, ConstructDestruct) {
|
|
// Using the test fixture to create and destruct the audio device module.
|
|
}
|
|
|
|
// We always ask for a default audio layer when the ADM is constructed. But the
|
|
// ADM will then internally set the best suitable combination of audio layers,
|
|
// for input and output based on if low-latency output and/or input audio in
|
|
// combination with OpenSL ES is supported or not. This test ensures that the
|
|
// correct selection is done.
|
|
TEST_F(AudioDeviceTest, VerifyDefaultAudioLayer) {
|
|
const AudioDeviceModule::AudioLayer audio_layer = GetActiveAudioLayer();
|
|
bool low_latency_output = audio_manager()->IsLowLatencyPlayoutSupported();
|
|
bool low_latency_input = audio_manager()->IsLowLatencyRecordSupported();
|
|
bool aaudio = audio_manager()->IsAAudioSupported();
|
|
AudioDeviceModule::AudioLayer expected_audio_layer;
|
|
if (aaudio) {
|
|
expected_audio_layer = AudioDeviceModule::kAndroidAAudioAudio;
|
|
} else if (low_latency_output && low_latency_input) {
|
|
expected_audio_layer = AudioDeviceModule::kAndroidOpenSLESAudio;
|
|
} else if (low_latency_output && !low_latency_input) {
|
|
expected_audio_layer =
|
|
AudioDeviceModule::kAndroidJavaInputAndOpenSLESOutputAudio;
|
|
} else {
|
|
expected_audio_layer = AudioDeviceModule::kAndroidJavaAudio;
|
|
}
|
|
EXPECT_EQ(expected_audio_layer, audio_layer);
|
|
}
|
|
|
|
// Verify that it is possible to explicitly create the two types of supported
|
|
// ADMs. These two tests overrides the default selection of native audio layer
|
|
// by ignoring if the device supports low-latency output or not.
|
|
TEST_F(AudioDeviceTest, CorrectAudioLayerIsUsedForCombinedJavaOpenSLCombo) {
|
|
AudioDeviceModule::AudioLayer expected_layer =
|
|
AudioDeviceModule::kAndroidJavaInputAndOpenSLESOutputAudio;
|
|
AudioDeviceModule::AudioLayer active_layer =
|
|
TestActiveAudioLayer(expected_layer);
|
|
EXPECT_EQ(expected_layer, active_layer);
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, CorrectAudioLayerIsUsedForJavaInBothDirections) {
|
|
AudioDeviceModule::AudioLayer expected_layer =
|
|
AudioDeviceModule::kAndroidJavaAudio;
|
|
AudioDeviceModule::AudioLayer active_layer =
|
|
TestActiveAudioLayer(expected_layer);
|
|
EXPECT_EQ(expected_layer, active_layer);
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, CorrectAudioLayerIsUsedForOpenSLInBothDirections) {
|
|
AudioDeviceModule::AudioLayer expected_layer =
|
|
AudioDeviceModule::kAndroidOpenSLESAudio;
|
|
AudioDeviceModule::AudioLayer active_layer =
|
|
TestActiveAudioLayer(expected_layer);
|
|
EXPECT_EQ(expected_layer, active_layer);
|
|
}
|
|
|
|
// TODO(bugs.webrtc.org/8914)
|
|
#if !defined(WEBRTC_AUDIO_DEVICE_INCLUDE_ANDROID_AAUDIO)
|
|
#define MAYBE_CorrectAudioLayerIsUsedForAAudioInBothDirections \
|
|
DISABLED_CorrectAudioLayerIsUsedForAAudioInBothDirections
|
|
#else
|
|
#define MAYBE_CorrectAudioLayerIsUsedForAAudioInBothDirections \
|
|
CorrectAudioLayerIsUsedForAAudioInBothDirections
|
|
#endif
|
|
TEST_F(AudioDeviceTest,
|
|
MAYBE_CorrectAudioLayerIsUsedForAAudioInBothDirections) {
|
|
AudioDeviceModule::AudioLayer expected_layer =
|
|
AudioDeviceModule::kAndroidAAudioAudio;
|
|
AudioDeviceModule::AudioLayer active_layer =
|
|
TestActiveAudioLayer(expected_layer);
|
|
EXPECT_EQ(expected_layer, active_layer);
|
|
}
|
|
|
|
// TODO(bugs.webrtc.org/8914)
|
|
#if !defined(WEBRTC_AUDIO_DEVICE_INCLUDE_ANDROID_AAUDIO)
|
|
#define MAYBE_CorrectAudioLayerIsUsedForCombinedJavaAAudioCombo \
|
|
DISABLED_CorrectAudioLayerIsUsedForCombinedJavaAAudioCombo
|
|
#else
|
|
#define MAYBE_CorrectAudioLayerIsUsedForCombinedJavaAAudioCombo \
|
|
CorrectAudioLayerIsUsedForCombinedJavaAAudioCombo
|
|
#endif
|
|
TEST_F(AudioDeviceTest,
|
|
MAYBE_CorrectAudioLayerIsUsedForCombinedJavaAAudioCombo) {
|
|
AudioDeviceModule::AudioLayer expected_layer =
|
|
AudioDeviceModule::kAndroidJavaInputAndAAudioOutputAudio;
|
|
AudioDeviceModule::AudioLayer active_layer =
|
|
TestActiveAudioLayer(expected_layer);
|
|
EXPECT_EQ(expected_layer, active_layer);
|
|
}
|
|
|
|
// The Android ADM supports two different delay reporting modes. One for the
|
|
// low-latency output path (in combination with OpenSL ES), and one for the
|
|
// high-latency output path (Java backends in both directions). These two tests
|
|
// verifies that the audio manager reports correct delay estimate given the
|
|
// selected audio layer. Note that, this delay estimate will only be utilized
|
|
// if the HW AEC is disabled.
|
|
TEST_F(AudioDeviceTest, UsesCorrectDelayEstimateForHighLatencyOutputPath) {
|
|
EXPECT_EQ(kHighLatencyModeDelayEstimateInMilliseconds,
|
|
TestDelayOnAudioLayer(AudioDeviceModule::kAndroidJavaAudio));
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, UsesCorrectDelayEstimateForLowLatencyOutputPath) {
|
|
EXPECT_EQ(kLowLatencyModeDelayEstimateInMilliseconds,
|
|
TestDelayOnAudioLayer(
|
|
AudioDeviceModule::kAndroidJavaInputAndOpenSLESOutputAudio));
|
|
}
|
|
|
|
// Ensure that the ADM internal audio device buffer is configured to use the
|
|
// correct set of parameters.
|
|
TEST_F(AudioDeviceTest, VerifyAudioDeviceBufferParameters) {
|
|
EXPECT_EQ(playout_parameters_.sample_rate(),
|
|
static_cast<int>(audio_device_buffer()->PlayoutSampleRate()));
|
|
EXPECT_EQ(record_parameters_.sample_rate(),
|
|
static_cast<int>(audio_device_buffer()->RecordingSampleRate()));
|
|
EXPECT_EQ(playout_parameters_.channels(),
|
|
audio_device_buffer()->PlayoutChannels());
|
|
EXPECT_EQ(record_parameters_.channels(),
|
|
audio_device_buffer()->RecordingChannels());
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, InitTerminate) {
|
|
// Initialization is part of the test fixture.
|
|
EXPECT_TRUE(audio_device()->Initialized());
|
|
EXPECT_EQ(0, audio_device()->Terminate());
|
|
EXPECT_FALSE(audio_device()->Initialized());
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, Devices) {
|
|
// Device enumeration is not supported. Verify fixed values only.
|
|
EXPECT_EQ(1, audio_device()->PlayoutDevices());
|
|
EXPECT_EQ(1, audio_device()->RecordingDevices());
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, SpeakerVolumeShouldBeAvailable) {
|
|
// The OpenSL ES output audio path does not support volume control.
|
|
if (!AudioLayerSupportsVolumeControl())
|
|
return;
|
|
bool available;
|
|
EXPECT_EQ(0, audio_device()->SpeakerVolumeIsAvailable(&available));
|
|
EXPECT_TRUE(available);
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, MaxSpeakerVolumeIsPositive) {
|
|
// The OpenSL ES output audio path does not support volume control.
|
|
if (!AudioLayerSupportsVolumeControl())
|
|
return;
|
|
StartPlayout();
|
|
EXPECT_GT(GetMaxSpeakerVolume(), 0);
|
|
StopPlayout();
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, MinSpeakerVolumeIsZero) {
|
|
// The OpenSL ES output audio path does not support volume control.
|
|
if (!AudioLayerSupportsVolumeControl())
|
|
return;
|
|
EXPECT_EQ(GetMinSpeakerVolume(), 0);
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, DefaultSpeakerVolumeIsWithinMinMax) {
|
|
// The OpenSL ES output audio path does not support volume control.
|
|
if (!AudioLayerSupportsVolumeControl())
|
|
return;
|
|
const int default_volume = GetSpeakerVolume();
|
|
EXPECT_GE(default_volume, GetMinSpeakerVolume());
|
|
EXPECT_LE(default_volume, GetMaxSpeakerVolume());
|
|
}
|
|
|
|
TEST_F(AudioDeviceTest, SetSpeakerVolumeActuallySetsVolume) {
|
|
// The OpenSL ES output audio path does not support volume control.
|
|
if (!AudioLayerSupportsVolumeControl())
|
|
return;
|
|
const int default_volume = GetSpeakerVolume();
|
|
const int max_volume = GetMaxSpeakerVolume();
|
|
EXPECT_EQ(0, audio_device()->SetSpeakerVolume(max_volume));
|
|
int new_volume = GetSpeakerVolume();
|
|
EXPECT_EQ(new_volume, max_volume);
|
|
EXPECT_EQ(0, audio_device()->SetSpeakerVolume(default_volume));
|
|
}
|
|
|
|
// Tests that playout can be initiated, started and stopped. No audio callback
|
|
// is registered in this test.
|
|
TEST_F(AudioDeviceTest, StartStopPlayout) {
|
|
StartPlayout();
|
|
StopPlayout();
|
|
StartPlayout();
|
|
StopPlayout();
|
|
}
|
|
|
|
// Tests that recording can be initiated, started and stopped. No audio callback
|
|
// is registered in this test.
|
|
TEST_F(AudioDeviceTest, StartStopRecording) {
|
|
StartRecording();
|
|
StopRecording();
|
|
StartRecording();
|
|
StopRecording();
|
|
}
|
|
|
|
// Verify that calling StopPlayout() will leave us in an uninitialized state
|
|
// which will require a new call to InitPlayout(). This test does not call
|
|
// StartPlayout() while being uninitialized since doing so will hit a
|
|
// RTC_DCHECK and death tests are not supported on Android.
|
|
TEST_F(AudioDeviceTest, StopPlayoutRequiresInitToRestart) {
|
|
EXPECT_EQ(0, audio_device()->InitPlayout());
|
|
EXPECT_EQ(0, audio_device()->StartPlayout());
|
|
EXPECT_EQ(0, audio_device()->StopPlayout());
|
|
EXPECT_FALSE(audio_device()->PlayoutIsInitialized());
|
|
}
|
|
|
|
// Verify that calling StopRecording() will leave us in an uninitialized state
|
|
// which will require a new call to InitRecording(). This test does not call
|
|
// StartRecording() while being uninitialized since doing so will hit a
|
|
// RTC_DCHECK and death tests are not supported on Android.
|
|
TEST_F(AudioDeviceTest, StopRecordingRequiresInitToRestart) {
|
|
EXPECT_EQ(0, audio_device()->InitRecording());
|
|
EXPECT_EQ(0, audio_device()->StartRecording());
|
|
EXPECT_EQ(0, audio_device()->StopRecording());
|
|
EXPECT_FALSE(audio_device()->RecordingIsInitialized());
|
|
}
|
|
|
|
// Start playout and verify that the native audio layer starts asking for real
|
|
// audio samples to play out using the NeedMorePlayData callback.
|
|
TEST_F(AudioDeviceTest, StartPlayoutVerifyCallbacks) {
|
|
MockAudioTransportAndroid mock(kPlayout);
|
|
mock.HandleCallbacks(&test_is_done_, nullptr, kNumCallbacks);
|
|
EXPECT_CALL(mock, NeedMorePlayData(playout_frames_per_10ms_buffer(),
|
|
kBytesPerSample, playout_channels(),
|
|
playout_sample_rate(), NotNull(), _, _, _))
|
|
.Times(AtLeast(kNumCallbacks));
|
|
EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
|
|
StartPlayout();
|
|
test_is_done_.Wait(kTestTimeOut);
|
|
StopPlayout();
|
|
}
|
|
|
|
// Start recording and verify that the native audio layer starts feeding real
|
|
// audio samples via the RecordedDataIsAvailable callback.
|
|
// TODO(henrika): investigate if it is possible to perform a sanity check of
|
|
// delay estimates as well (argument #6).
|
|
TEST_F(AudioDeviceTest, StartRecordingVerifyCallbacks) {
|
|
MockAudioTransportAndroid mock(kRecording);
|
|
mock.HandleCallbacks(&test_is_done_, nullptr, kNumCallbacks);
|
|
EXPECT_CALL(
|
|
mock, RecordedDataIsAvailable(NotNull(), record_frames_per_10ms_buffer(),
|
|
kBytesPerSample, record_channels(),
|
|
record_sample_rate(), _, 0, 0, false, _, _))
|
|
.Times(AtLeast(kNumCallbacks));
|
|
|
|
EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
|
|
StartRecording();
|
|
test_is_done_.Wait(kTestTimeOut);
|
|
StopRecording();
|
|
}
|
|
|
|
// Start playout and recording (full-duplex audio) and verify that audio is
|
|
// active in both directions.
|
|
TEST_F(AudioDeviceTest, StartPlayoutAndRecordingVerifyCallbacks) {
|
|
MockAudioTransportAndroid mock(kPlayout | kRecording);
|
|
mock.HandleCallbacks(&test_is_done_, nullptr, kNumCallbacks);
|
|
EXPECT_CALL(mock, NeedMorePlayData(playout_frames_per_10ms_buffer(),
|
|
kBytesPerSample, playout_channels(),
|
|
playout_sample_rate(), NotNull(), _, _, _))
|
|
.Times(AtLeast(kNumCallbacks));
|
|
EXPECT_CALL(
|
|
mock, RecordedDataIsAvailable(NotNull(), record_frames_per_10ms_buffer(),
|
|
kBytesPerSample, record_channels(),
|
|
record_sample_rate(), _, 0, 0, false, _, _))
|
|
.Times(AtLeast(kNumCallbacks));
|
|
EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
|
|
StartPlayout();
|
|
StartRecording();
|
|
test_is_done_.Wait(kTestTimeOut);
|
|
StopRecording();
|
|
StopPlayout();
|
|
}
|
|
|
|
// Start playout and read audio from an external PCM file when the audio layer
|
|
// asks for data to play out. Real audio is played out in this test but it does
|
|
// not contain any explicit verification that the audio quality is perfect.
|
|
TEST_F(AudioDeviceTest, RunPlayoutWithFileAsSource) {
|
|
// TODO(henrika): extend test when mono output is supported.
|
|
EXPECT_EQ(1u, playout_channels());
|
|
NiceMock<MockAudioTransportAndroid> mock(kPlayout);
|
|
const int num_callbacks = kFilePlayTimeInSec * kNumCallbacksPerSecond;
|
|
std::string file_name = GetFileName(playout_sample_rate());
|
|
std::unique_ptr<FileAudioStream> file_audio_stream(
|
|
new FileAudioStream(num_callbacks, file_name, playout_sample_rate()));
|
|
mock.HandleCallbacks(&test_is_done_, file_audio_stream.get(), num_callbacks);
|
|
// SetMaxPlayoutVolume();
|
|
EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
|
|
StartPlayout();
|
|
test_is_done_.Wait(kTestTimeOut);
|
|
StopPlayout();
|
|
}
|
|
|
|
// Start playout and recording and store recorded data in an intermediate FIFO
|
|
// buffer from which the playout side then reads its samples in the same order
|
|
// as they were stored. Under ideal circumstances, a callback sequence would
|
|
// look like: ...+-+-+-+-+-+-+-..., where '+' means 'packet recorded' and '-'
|
|
// means 'packet played'. Under such conditions, the FIFO would only contain
|
|
// one packet on average. However, under more realistic conditions, the size
|
|
// of the FIFO will vary more due to an unbalance between the two sides.
|
|
// This test tries to verify that the device maintains a balanced callback-
|
|
// sequence by running in loopback for ten seconds while measuring the size
|
|
// (max and average) of the FIFO. The size of the FIFO is increased by the
|
|
// recording side and decreased by the playout side.
|
|
// TODO(henrika): tune the final test parameters after running tests on several
|
|
// different devices.
|
|
// Disabling this test on bots since it is difficult to come up with a robust
|
|
// test condition that all worked as intended. The main issue is that, when
|
|
// swarming is used, an initial latency can be built up when the both sides
|
|
// starts at different times. Hence, the test can fail even if audio works
|
|
// as intended. Keeping the test so it can be enabled manually.
|
|
// http://bugs.webrtc.org/7744
|
|
TEST_F(AudioDeviceTest, DISABLED_RunPlayoutAndRecordingInFullDuplex) {
|
|
EXPECT_EQ(record_channels(), playout_channels());
|
|
EXPECT_EQ(record_sample_rate(), playout_sample_rate());
|
|
NiceMock<MockAudioTransportAndroid> mock(kPlayout | kRecording);
|
|
std::unique_ptr<FifoAudioStream> fifo_audio_stream(
|
|
new FifoAudioStream(playout_frames_per_10ms_buffer()));
|
|
mock.HandleCallbacks(&test_is_done_, fifo_audio_stream.get(),
|
|
kFullDuplexTime.seconds() * kNumCallbacksPerSecond);
|
|
SetMaxPlayoutVolume();
|
|
EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
|
|
StartRecording();
|
|
StartPlayout();
|
|
test_is_done_.Wait(std::max(kTestTimeOut, kFullDuplexTime));
|
|
StopPlayout();
|
|
StopRecording();
|
|
|
|
// These thresholds are set rather high to accomodate differences in hardware
|
|
// in several devices, so this test can be used in swarming.
|
|
// See http://bugs.webrtc.org/6464
|
|
EXPECT_LE(fifo_audio_stream->average_size(), 60u);
|
|
EXPECT_LE(fifo_audio_stream->largest_size(), 70u);
|
|
}
|
|
|
|
// Measures loopback latency and reports the min, max and average values for
|
|
// a full duplex audio session.
|
|
// The latency is measured like so:
|
|
// - Insert impulses periodically on the output side.
|
|
// - Detect the impulses on the input side.
|
|
// - Measure the time difference between the transmit time and receive time.
|
|
// - Store time differences in a vector and calculate min, max and average.
|
|
// This test requires a special hardware called Audio Loopback Dongle.
|
|
// See http://source.android.com/devices/audio/loopback.html for details.
|
|
TEST_F(AudioDeviceTest, DISABLED_MeasureLoopbackLatency) {
|
|
EXPECT_EQ(record_channels(), playout_channels());
|
|
EXPECT_EQ(record_sample_rate(), playout_sample_rate());
|
|
NiceMock<MockAudioTransportAndroid> mock(kPlayout | kRecording);
|
|
std::unique_ptr<LatencyMeasuringAudioStream> latency_audio_stream(
|
|
new LatencyMeasuringAudioStream(playout_frames_per_10ms_buffer()));
|
|
mock.HandleCallbacks(&test_is_done_, latency_audio_stream.get(),
|
|
kMeasureLatencyTime.seconds() * kNumCallbacksPerSecond);
|
|
EXPECT_EQ(0, audio_device()->RegisterAudioCallback(&mock));
|
|
SetMaxPlayoutVolume();
|
|
DisableBuiltInAECIfAvailable();
|
|
StartRecording();
|
|
StartPlayout();
|
|
test_is_done_.Wait(std::max(kTestTimeOut, kMeasureLatencyTime));
|
|
StopPlayout();
|
|
StopRecording();
|
|
// Verify that the correct number of transmitted impulses are detected.
|
|
EXPECT_EQ(latency_audio_stream->num_latency_values(),
|
|
static_cast<size_t>(
|
|
kImpulseFrequencyInHz * kMeasureLatencyTime.seconds() - 1));
|
|
latency_audio_stream->PrintResults();
|
|
}
|
|
|
|
} // namespace webrtc
|