Refactor FakeAudioDevice to have separate methods for starting recording and playout.

Also, change FakeAudioDevice to generate a sine tone instead of using a file.

TBR=henrika@webrtc.org, stefan@webrtc.org

BUG=webrtc:7080

Review-Url: https://codereview.webrtc.org/2652803002
Cr-Commit-Position: refs/heads/master@{#16385}
This commit is contained in:
perkj
2017-01-31 13:32:49 -08:00
committed by Commit bot
parent 1c056254a6
commit ac61b745df
6 changed files with 142 additions and 134 deletions

View File

@ -146,11 +146,7 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec,
metrics::Reset();
VoiceEngine* voice_engine = VoiceEngine::Create();
VoEBase* voe_base = VoEBase::GetInterface(voice_engine);
const std::string audio_filename =
test::ResourcePath("voice_engine/audio_long16", "pcm");
ASSERT_STRNE("", audio_filename.c_str());
FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(), audio_filename,
audio_rtp_speed);
FakeAudioDevice fake_audio_device(audio_rtp_speed, 48000, 256);
EXPECT_EQ(0, voe_base->Init(&fake_audio_device, nullptr, decoder_factory_));
VoEBase::ChannelConfig config;
config.enable_voice_pacing = true;
@ -264,16 +260,14 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec,
Start();
fake_audio_device.Start();
audio_send_stream->Start();
audio_receive_stream->Start();
EXPECT_EQ(0, voe_base->StartSend(send_channel_id));
EXPECT_TRUE(observer.Wait())
<< "Timed out while waiting for audio and video to be synchronized.";
EXPECT_EQ(0, voe_base->StopSend(send_channel_id));
EXPECT_EQ(0, voe_base->StopPlayout(recv_channel_id));
fake_audio_device.Stop();
audio_send_stream->Stop();
audio_receive_stream->Stop();
Stop();
video_send_transport.StopSending();

View File

@ -21,6 +21,8 @@ class FakeAudioDeviceModule : public AudioDeviceModule {
virtual ~FakeAudioDeviceModule() {}
virtual int32_t AddRef() const { return 0; }
virtual int32_t Release() const { return 0; }
private:
virtual int32_t RegisterEventObserver(AudioDeviceObserver* eventCallback) {
return 0;
}
@ -121,7 +123,10 @@ class FakeAudioDeviceModule : public AudioDeviceModule {
virtual int32_t PlayoutBuffer(BufferType* type, uint16_t* sizeMS) const {
return 0;
}
virtual int32_t PlayoutDelay(uint16_t* delayMS) const { return 0; }
virtual int32_t PlayoutDelay(uint16_t* delayMS) const {
*delayMS = 0;
return 0;
}
virtual int32_t RecordingDelay(uint16_t* delayMS) const { return 0; }
virtual int32_t CPULoad(uint16_t* load) const { return 0; }
virtual int32_t StartRawOutputFileRecording(

View File

@ -332,7 +332,6 @@ rtc_source_set("test_common") {
"../modules/audio_device:mock_audio_device",
"../modules/audio_mixer:audio_mixer_impl",
"../modules/audio_processing",
"../modules/media_file",
"../modules/video_capture:video_capture",
"../modules/video_capture:video_capture_module",
"../video",

View File

@ -140,16 +140,10 @@ void CallTest::Start() {
for (VideoReceiveStream* video_recv_stream : video_receive_streams_)
video_recv_stream->Start();
if (audio_send_stream_) {
fake_send_audio_device_->Start();
audio_send_stream_->Start();
EXPECT_EQ(0, voe_send_.base->StartSend(voe_send_.channel_id));
}
for (AudioReceiveStream* audio_recv_stream : audio_receive_streams_)
audio_recv_stream->Start();
if (!audio_receive_streams_.empty()) {
fake_recv_audio_device_->Start();
EXPECT_EQ(0, voe_recv_.base->StartPlayout(voe_recv_.channel_id));
}
for (FlexfecReceiveStream* flexfec_recv_stream : flexfec_receive_streams_)
flexfec_recv_stream->Start();
if (frame_generator_capturer_.get() != NULL)
@ -161,15 +155,9 @@ void CallTest::Stop() {
frame_generator_capturer_->Stop();
for (FlexfecReceiveStream* flexfec_recv_stream : flexfec_receive_streams_)
flexfec_recv_stream->Stop();
if (!audio_receive_streams_.empty()) {
fake_recv_audio_device_->Stop();
EXPECT_EQ(0, voe_recv_.base->StopPlayout(voe_recv_.channel_id));
}
for (AudioReceiveStream* audio_recv_stream : audio_receive_streams_)
audio_recv_stream->Stop();
if (audio_send_stream_) {
fake_send_audio_device_->Stop();
EXPECT_EQ(0, voe_send_.base->StopSend(voe_send_.channel_id));
audio_send_stream_->Stop();
}
for (VideoReceiveStream* video_recv_stream : video_receive_streams_)
@ -309,12 +297,8 @@ void CallTest::CreateFrameGeneratorCapturer(int framerate,
}
void CallTest::CreateFakeAudioDevices() {
fake_send_audio_device_.reset(new FakeAudioDevice(
clock_, test::ResourcePath("voice_engine/audio_long16", "pcm"),
DriftingClock::kNoDrift));
fake_recv_audio_device_.reset(new FakeAudioDevice(
clock_, test::ResourcePath("voice_engine/audio_long16", "pcm"),
DriftingClock::kNoDrift));
fake_send_audio_device_.reset(new FakeAudioDevice(1.f, 48000, 256));
fake_recv_audio_device_.reset(new FakeAudioDevice(1.f, 48000, 256));
}
void CallTest::CreateVideoStreams() {

View File

@ -12,49 +12,104 @@
#include <algorithm>
#include "webrtc/base/platform_thread.h"
#include "webrtc/modules/media_file/media_file_utility.h"
#include "webrtc/system_wrappers/include/clock.h"
#include "webrtc/base/array_view.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/random.h"
#include "webrtc/system_wrappers/include/event_wrapper.h"
#include "webrtc/system_wrappers/include/file_wrapper.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace {
constexpr int kFrameLengthMs = 10;
constexpr int kFramesPerSecond = 1000 / kFrameLengthMs;
} // namespace
namespace test {
FakeAudioDevice::FakeAudioDevice(Clock* clock,
const std::string& filename,
float speed)
: audio_callback_(NULL),
capturing_(false),
captured_audio_(),
playout_buffer_(),
// Assuming 10ms audio packets..
class FakeAudioDevice::PulsedNoiseCapturer {
public:
PulsedNoiseCapturer(size_t num_samples_per_frame, int16_t max_amplitude)
: fill_with_zero_(false),
random_generator_(1),
max_amplitude_(max_amplitude),
random_audio_(num_samples_per_frame),
silent_audio_(num_samples_per_frame, 0) {
RTC_DCHECK_GT(max_amplitude, 0);
}
rtc::ArrayView<const int16_t> Capture() {
fill_with_zero_ = !fill_with_zero_;
if (!fill_with_zero_) {
std::generate(random_audio_.begin(), random_audio_.end(), [&]() {
return random_generator_.Rand(-max_amplitude_, max_amplitude_);
});
}
return fill_with_zero_ ? silent_audio_ : random_audio_;
}
private:
bool fill_with_zero_;
Random random_generator_;
const int16_t max_amplitude_;
std::vector<int16_t> random_audio_;
std::vector<int16_t> silent_audio_;
};
FakeAudioDevice::FakeAudioDevice(float speed,
int sampling_frequency_in_hz,
int16_t max_amplitude)
: sampling_frequency_in_hz_(sampling_frequency_in_hz),
num_samples_per_frame_(
rtc::CheckedDivExact(sampling_frequency_in_hz_, kFramesPerSecond)),
speed_(speed),
last_playout_ms_(-1),
clock_(clock, speed),
audio_callback_(nullptr),
rendering_(false),
capturing_(false),
capturer_(new FakeAudioDevice::PulsedNoiseCapturer(num_samples_per_frame_,
max_amplitude)),
playout_buffer_(num_samples_per_frame_, 0),
tick_(EventTimerWrapper::Create()),
thread_(FakeAudioDevice::Run, this, "FakeAudioDevice"),
file_utility_(new ModuleFileUtility(0)),
input_stream_(FileWrapper::Create()) {
memset(captured_audio_, 0, sizeof(captured_audio_));
memset(playout_buffer_, 0, sizeof(playout_buffer_));
// Open audio input file as read-only and looping.
EXPECT_TRUE(input_stream_->OpenFile(filename.c_str(), true)) << filename;
thread_(FakeAudioDevice::Run, this, "FakeAudioDevice") {
RTC_DCHECK(
sampling_frequency_in_hz == 8000 || sampling_frequency_in_hz == 16000 ||
sampling_frequency_in_hz == 32000 || sampling_frequency_in_hz == 44100 ||
sampling_frequency_in_hz == 48000);
}
FakeAudioDevice::~FakeAudioDevice() {
Stop();
StopPlayout();
StopRecording();
thread_.Stop();
}
int32_t FakeAudioDevice::Init() {
int32_t FakeAudioDevice::StartPlayout() {
rtc::CritScope cs(&lock_);
if (file_utility_->InitPCMReading(*input_stream_.get()) != 0)
return -1;
rendering_ = true;
return 0;
}
if (!tick_->StartTimer(true, 10 / speed_))
return -1;
int32_t FakeAudioDevice::StopPlayout() {
rtc::CritScope cs(&lock_);
rendering_ = false;
return 0;
}
int32_t FakeAudioDevice::StartRecording() {
rtc::CritScope cs(&lock_);
capturing_ = true;
return 0;
}
int32_t FakeAudioDevice::StopRecording() {
rtc::CritScope cs(&lock_);
capturing_ = false;
return 0;
}
int32_t FakeAudioDevice::Init() {
RTC_CHECK(tick_->StartTimer(true, kFrameLengthMs / speed_));
thread_.Start();
thread_.SetPriority(rtc::kHighPriority);
return 0;
@ -62,18 +117,14 @@ int32_t FakeAudioDevice::Init() {
int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) {
rtc::CritScope cs(&lock_);
RTC_DCHECK(callback || audio_callback_ != nullptr);
audio_callback_ = callback;
return 0;
}
bool FakeAudioDevice::Playing() const {
rtc::CritScope cs(&lock_);
return capturing_;
}
int32_t FakeAudioDevice::PlayoutDelay(uint16_t* delay_ms) const {
*delay_ms = 0;
return 0;
return rendering_;
}
bool FakeAudioDevice::Recording() const {
@ -82,65 +133,33 @@ bool FakeAudioDevice::Recording() const {
}
bool FakeAudioDevice::Run(void* obj) {
static_cast<FakeAudioDevice*>(obj)->CaptureAudio();
static_cast<FakeAudioDevice*>(obj)->ProcessAudio();
return true;
}
void FakeAudioDevice::CaptureAudio() {
void FakeAudioDevice::ProcessAudio() {
{
rtc::CritScope cs(&lock_);
if (capturing_) {
int bytes_read = file_utility_->ReadPCMData(
*input_stream_.get(), captured_audio_, kBufferSizeBytes);
if (bytes_read <= 0)
return;
// 2 bytes per sample.
size_t num_samples = static_cast<size_t>(bytes_read / 2);
uint32_t new_mic_level;
EXPECT_EQ(0,
audio_callback_->RecordedDataIsAvailable(captured_audio_,
num_samples,
2,
1,
kFrequencyHz,
0,
0,
0,
false,
new_mic_level));
size_t samples_needed = kFrequencyHz / 100;
int64_t now_ms = clock_.TimeInMilliseconds();
uint32_t time_since_last_playout_ms = now_ms - last_playout_ms_;
if (last_playout_ms_ > 0 && time_since_last_playout_ms > 0) {
samples_needed = std::min(
static_cast<size_t>(kFrequencyHz / time_since_last_playout_ms),
kBufferSizeBytes / 2);
}
// Capture 10ms of audio. 2 bytes per sample.
rtc::ArrayView<const int16_t> audio_data = capturer_->Capture();
uint32_t new_mic_level = 0;
audio_callback_->RecordedDataIsAvailable(
audio_data.data(), audio_data.size(), 2, 1, sampling_frequency_in_hz_,
0, 0, 0, false, new_mic_level);
}
if (rendering_) {
size_t samples_out = 0;
int64_t elapsed_time_ms = -1;
int64_t ntp_time_ms = -1;
EXPECT_EQ(0,
audio_callback_->NeedMorePlayData(samples_needed,
2,
1,
kFrequencyHz,
playout_buffer_,
samples_out,
&elapsed_time_ms,
&ntp_time_ms));
audio_callback_->NeedMorePlayData(
num_samples_per_frame_, 2, 1, sampling_frequency_in_hz_,
playout_buffer_.data(), samples_out, &elapsed_time_ms, &ntp_time_ms);
}
}
tick_->Wait(WEBRTC_EVENT_INFINITE);
}
void FakeAudioDevice::Start() {
rtc::CritScope cs(&lock_);
capturing_ = true;
}
void FakeAudioDevice::Stop() {
rtc::CritScope cs(&lock_);
capturing_ = false;
}
} // namespace test
} // namespace webrtc

View File

@ -12,58 +12,65 @@
#include <memory>
#include <string>
#include <vector>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/modules/audio_device/include/fake_audio_device.h"
#include "webrtc/test/drifting_clock.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class Clock;
class EventTimerWrapper;
class FileWrapper;
class ModuleFileUtility;
namespace test {
// FakeAudioDevice implements an AudioDevice module that can act both as a
// capturer and a renderer. It will use 10ms audio frames.
class FakeAudioDevice : public FakeAudioDeviceModule {
public:
FakeAudioDevice(Clock* clock, const std::string& filename, float speed);
virtual ~FakeAudioDevice();
// Creates a new FakeAudioDevice. When capturing or playing, 10 ms audio
// frames will be processed every 100ms / |speed|.
// |sampling_frequency_in_hz| can be 8, 16, 32, 44.1 or 48kHz.
// When recording is started, it will generates a signal where every second
// frame is zero and every second frame is evenly distributed random noise
// with max amplitude |max_amplitude|.
FakeAudioDevice(float speed,
int sampling_frequency_in_hz,
int16_t max_amplitude);
~FakeAudioDevice() override;
private:
int32_t Init() override;
int32_t RegisterAudioCallback(AudioTransport* callback) override;
int32_t StartPlayout() override;
int32_t StopPlayout() override;
int32_t StartRecording() override;
int32_t StopRecording() override;
bool Playing() const override;
int32_t PlayoutDelay(uint16_t* delay_ms) const override;
bool Recording() const override;
void Start();
void Stop();
private:
static bool Run(void* obj);
void CaptureAudio();
void ProcessAudio();
static const uint32_t kFrequencyHz = 16000;
static const size_t kBufferSizeBytes = 2 * kFrequencyHz;
AudioTransport* audio_callback_;
bool capturing_;
int8_t captured_audio_[kBufferSizeBytes];
int8_t playout_buffer_[kBufferSizeBytes];
const int sampling_frequency_in_hz_;
const size_t num_samples_per_frame_;
const float speed_;
int64_t last_playout_ms_;
DriftingClock clock_;
std::unique_ptr<EventTimerWrapper> tick_;
rtc::CriticalSection lock_;
AudioTransport* audio_callback_ GUARDED_BY(lock_);
bool rendering_ GUARDED_BY(lock_);
bool capturing_ GUARDED_BY(lock_);
class PulsedNoiseCapturer;
const std::unique_ptr<PulsedNoiseCapturer> capturer_ GUARDED_BY(lock_);
std::vector<int16_t> playout_buffer_ GUARDED_BY(lock_);
std::unique_ptr<EventTimerWrapper> tick_;
rtc::PlatformThread thread_;
std::unique_ptr<ModuleFileUtility> file_utility_;
std::unique_ptr<FileWrapper> input_stream_;
};
} // namespace test
} // namespace webrtc