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}
diff --git a/webrtc/call/call_perf_tests.cc b/webrtc/call/call_perf_tests.cc
index 782b45c..b6a8fe6 100644
--- a/webrtc/call/call_perf_tests.cc
+++ b/webrtc/call/call_perf_tests.cc
@@ -146,11 +146,7 @@
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 @@
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();
diff --git a/webrtc/modules/audio_device/include/fake_audio_device.h b/webrtc/modules/audio_device/include/fake_audio_device.h
index 98fd2f4..a28ad86 100644
--- a/webrtc/modules/audio_device/include/fake_audio_device.h
+++ b/webrtc/modules/audio_device/include/fake_audio_device.h
@@ -21,6 +21,8 @@
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 @@
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(
diff --git a/webrtc/test/BUILD.gn b/webrtc/test/BUILD.gn
index 9893814..352eb37 100644
--- a/webrtc/test/BUILD.gn
+++ b/webrtc/test/BUILD.gn
@@ -332,7 +332,6 @@
"../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",
diff --git a/webrtc/test/call_test.cc b/webrtc/test/call_test.cc
index c1c7355..87bbea7 100644
--- a/webrtc/test/call_test.cc
+++ b/webrtc/test/call_test.cc
@@ -140,16 +140,10 @@
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 @@
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::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() {
diff --git a/webrtc/test/fake_audio_device.cc b/webrtc/test/fake_audio_device.cc
index 9e5e95f..623ff51 100644
--- a/webrtc/test/fake_audio_device.cc
+++ b/webrtc/test/fake_audio_device.cc
@@ -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::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::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
diff --git a/webrtc/test/fake_audio_device.h b/webrtc/test/fake_audio_device.h
index 77a74ba..4daeab4 100644
--- a/webrtc/test/fake_audio_device.h
+++ b/webrtc/test/fake_audio_device.h
@@ -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);
+ // 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;
- virtual ~FakeAudioDevice();
-
+ 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