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:
@ -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();
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user