Commit 1492075f authored by hongchan's avatar hongchan Committed by Commit bot

Add WebThread in AudioDestination to support AudioWorkletThread

This CL to introduce a WebThread to run WebAudio graph rendering.

FYI: with the box 2d demo on my local machine (MacPro), the release
build of this patch was able to render 50 cubes concurrently before
glitch, which is similar to the current stable build that runs only on the
audio device thread. No significant performance regression is observed.

Design doc: https://docs.google.com/document/d/1WnqZHcA2TQjS1aKvzx2nnJ2y2tWK1Xp_0BhFGpsryaQ/edit#

BUG=706133

Review-Url: https://codereview.chromium.org/2777903005
Cr-Commit-Position: refs/heads/master@{#467819}
parent a0d005f4
......@@ -2207,6 +2207,11 @@ source_set("unit_tests") {
# TODO(toyoshim): Remove Platform dependency and move to loader/BUILD.gn
"loader/fetch/ResourceResponseTest.cpp",
# TODO(hongchan): Platform::Current()->CreateThread() returns nullptr
# without adding the test file into this section. Remove platform dependency
# when creating a valid thread becomes possible in the Blink unit test.
"audio/PushPullFIFOMultithreadTest.cpp",
]
configs += [
......
......@@ -29,7 +29,9 @@
#include "platform/audio/AudioDestination.h"
#include <memory>
#include "platform/CrossThreadFunctional.h"
#include "platform/Histogram.h"
#include "platform/WebTaskRunner.h"
#include "platform/audio/AudioUtilities.h"
#include "platform/audio/PushPullFIFO.h"
#include "platform/weborigin/SecurityOrigin.h"
......@@ -37,6 +39,7 @@
#include "public/platform/Platform.h"
#include "public/platform/WebAudioLatencyHint.h"
#include "public/platform/WebSecurityOrigin.h"
#include "public/platform/WebThread.h"
namespace blink {
......@@ -64,14 +67,16 @@ AudioDestination::AudioDestination(AudioIOCallback& callback,
PassRefPtr<SecurityOrigin> security_origin)
: number_of_output_channels_(number_of_output_channels),
is_playing_(false),
callback_(callback),
rendering_thread_(WTF::WrapUnique(
Platform::Current()->CreateThread("WebAudio Rendering Thread"))),
fifo_(WTF::WrapUnique(
new PushPullFIFO(number_of_output_channels, kFIFOSize))),
output_bus_(AudioBus::Create(number_of_output_channels,
AudioUtilities::kRenderQuantumFrames,
false)),
render_bus_(AudioBus::Create(number_of_output_channels,
AudioUtilities::kRenderQuantumFrames)),
fifo_(WTF::WrapUnique(
new PushPullFIFO(number_of_output_channels, kFIFOSize))),
callback_(callback),
frames_elapsed_(0) {
// Create WebAudioDevice. blink::WebAudioDevice is designed to support the
// local input (e.g. loopback from OS audio system), but Chromium's media
......@@ -97,6 +102,9 @@ void AudioDestination::Render(const WebVector<float*>& destination_data,
double delay,
double delay_timestamp,
size_t prior_frames_skipped) {
// This method is called by AudioDeviceThread.
DCHECK(!IsRenderingThread());
CHECK_EQ(destination_data.size(), number_of_output_channels_);
CHECK_EQ(number_of_frames, callback_buffer_size_);
......@@ -106,25 +114,36 @@ void AudioDestination::Render(const WebVector<float*>& destination_data,
if (!fifo_ || fifo_->length() < number_of_frames)
return;
frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped);
double output_position =
frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) -
delay;
output_position_.position = output_position;
output_position_.timestamp = delay_timestamp;
output_position_received_timestamp_ = base::TimeTicks::Now();
// Associate the destination data array with the output bus then fill the
// FIFO.
for (unsigned i = 0; i < number_of_output_channels_; ++i)
output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames);
// Number of frames to render via WebAudio graph. |framesToRender > 0| means
// the frames in FIFO is not enough to fulfill the requested frames from the
// audio device.
size_t frames_to_render = number_of_frames > fifo_->FramesAvailable()
? number_of_frames - fifo_->FramesAvailable()
: 0;
size_t frames_to_render = fifo_->Pull(output_bus_.Get(), number_of_frames);
rendering_thread_->GetWebTaskRunner()->PostTask(
BLINK_FROM_HERE,
CrossThreadBind(&AudioDestination::RequestRenderOnWebThread,
CrossThreadUnretained(this),
number_of_frames, frames_to_render,
delay, delay_timestamp, prior_frames_skipped));
}
void AudioDestination::RequestRenderOnWebThread(size_t frames_requested,
size_t frames_to_render,
double delay,
double delay_timestamp,
size_t prior_frames_skipped) {
// This method is called by WebThread.
DCHECK(IsRenderingThread());
frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped);
AudioIOPosition output_position;
output_position.position =
frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) -
delay;
output_position.timestamp = delay_timestamp;
base::TimeTicks received_timestamp = base::TimeTicks::Now();
for (size_t pushed_frames = 0; pushed_frames < frames_to_render;
pushed_frames += AudioUtilities::kRenderQuantumFrames) {
......@@ -132,27 +151,23 @@ void AudioDestination::Render(const WebVector<float*>& destination_data,
// we do not want output position to get stuck so we promote it
// using the elapsed time from the moment it was initially obtained.
if (callback_buffer_size_ > AudioUtilities::kRenderQuantumFrames * 2) {
double delta =
(base::TimeTicks::Now() - output_position_received_timestamp_)
.InSecondsF();
output_position_.position += delta;
output_position_.timestamp += delta;
double delta = (base::TimeTicks::Now() - received_timestamp).InSecondsF();
output_position.position += delta;
output_position.timestamp += delta;
}
// Some implementations give only rough estimation of |delay| so
// we might have negative estimation |outputPosition| value.
if (output_position_.position < 0.0)
output_position_.position = 0.0;
if (output_position.position < 0.0)
output_position.position = 0.0;
// Process WebAudio graph and push the rendered output to FIFO.
callback_.Render(nullptr, render_bus_.Get(),
AudioUtilities::kRenderQuantumFrames, output_position_);
AudioUtilities::kRenderQuantumFrames, output_position);
fifo_->Push(render_bus_.Get());
}
fifo_->Pull(output_bus_.Get(), number_of_frames);
frames_elapsed_ += number_of_frames;
frames_elapsed_ += frames_requested;
}
void AudioDestination::Start() {
......@@ -204,4 +219,9 @@ bool AudioDestination::CheckBufferSize() {
return is_buffer_size_valid;
}
bool AudioDestination::IsRenderingThread() {
return static_cast<ThreadIdentifier>(rendering_thread_->ThreadId()) ==
CurrentThread();
}
} // namespace blink
......@@ -43,6 +43,7 @@ namespace blink {
class PushPullFIFO;
class SecurityOrigin;
class WebAudioLatencyHint;
class WebThread;
// The AudioDestination class is an audio sink interface between the media
// renderer and the Blink's WebAudio module. It has a FIFO to adapt the
......@@ -73,6 +74,14 @@ class PLATFORM_EXPORT AudioDestination : public WebAudioDevice::RenderCallback {
double delay_timestamp,
size_t prior_frames_skipped) override;
// The actual render request to the WebAudio destination node. This triggers
// the WebAudio rendering pipe line on the web thread.
void RequestRenderOnWebThread(size_t frames_requested,
size_t frames_to_render,
double delay,
double delay_timestamp,
size_t prior_frames_skipped);
virtual void Start();
virtual void Stop();
......@@ -90,32 +99,38 @@ class PLATFORM_EXPORT AudioDestination : public WebAudioDevice::RenderCallback {
static unsigned long MaxChannelCount();
private:
// Check if the buffer size chosen by the WebAudioDevice is too large.
bool CheckBufferSize();
size_t HardwareBufferSize();
bool IsRenderingThread();
std::unique_ptr<WebAudioDevice> web_audio_device_;
unsigned number_of_output_channels_;
const unsigned number_of_output_channels_;
size_t callback_buffer_size_;
bool is_playing_;
// The render callback function of WebAudio engine. (i.e. DestinationNode)
AudioIOCallback& callback_;
// Rendering thread for WebAudio graph.
std::unique_ptr<WebThread> rendering_thread_;
// To pass the data from FIFO to the audio device callback.
// Accessed by both threads: resolves the buffer size mismatch between the
// WebAudio engine and the callback function from the actual audio device.
std::unique_ptr<PushPullFIFO> fifo_;
// Accessed by device thread: to pass the data from FIFO to the device.
RefPtr<AudioBus> output_bus_;
// To push the rendered result from WebAudio graph into the FIFO.
// Accessed by rendering thread: to push the rendered result from WebAudio
// graph into the FIFO.
RefPtr<AudioBus> render_bus_;
// Resolves the buffer size mismatch between the WebAudio engine and
// the callback function from the actual audio device.
std::unique_ptr<PushPullFIFO> fifo_;
// Accessed by rendering thread: the render callback function of WebAudio
// engine. (i.e. DestinationNode)
AudioIOCallback& callback_;
// Accessed by rendering thread.
size_t frames_elapsed_;
AudioIOPosition output_position_;
base::TimeTicks output_position_received_timestamp_;
// Check if the buffer size chosen by the WebAudioDevice is too large.
bool CheckBufferSize();
size_t HardwareBufferSize();
};
} // namespace blink
......
......@@ -19,21 +19,18 @@ const unsigned kMaxMessagesToLog = 100;
const size_t PushPullFIFO::kMaxFIFOLength = 65536;
PushPullFIFO::PushPullFIFO(unsigned number_of_channels, size_t fifo_length)
: fifo_length_(fifo_length),
frames_available_(0),
index_read_(0),
index_write_(0),
overflow_count_(0),
underflow_count_(0) {
: fifo_length_(fifo_length) {
CHECK_LE(fifo_length_, kMaxFIFOLength);
fifo_bus_ = AudioBus::Create(number_of_channels, fifo_length_);
}
PushPullFIFO::~PushPullFIFO() {}
// Push the data from |inputBus| to FIFO. The size of push is determined by
// the length of |inputBus|.
// Push the data from |input_bus| to FIFO. The size of push is determined by
// the length of |input_bus|.
void PushPullFIFO::Push(const AudioBus* input_bus) {
MutexLocker locker(lock_);
CHECK(input_bus);
CHECK_EQ(input_bus->length(), AudioUtilities::kRenderQuantumFrames);
SECURITY_CHECK(input_bus->length() <= fifo_length_);
......@@ -61,8 +58,8 @@ void PushPullFIFO::Push(const AudioBus* input_bus) {
// Update the write index; wrap it around if necessary.
index_write_ = (index_write_ + input_bus_length) % fifo_length_;
// In case of overflow, move the |indexRead| to the updated |indexWrite| to
// avoid reading overwritten frames by the next pull.
// In case of overflow, move the |index_read_| to the updated |index_write_|
// to avoid reading overwritten frames by the next pull.
if (input_bus_length > fifo_length_ - frames_available_) {
index_read_ = index_write_;
if (++overflow_count_ < kMaxMessagesToLog) {
......@@ -80,16 +77,18 @@ void PushPullFIFO::Push(const AudioBus* input_bus) {
DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_);
}
// Pull the data out of FIFO to |outputBus|. If remaining frame in the FIFO
// Pull the data out of FIFO to |output_bus|. If remaining frame in the FIFO
// is less than the frames to pull, provides remaining frame plus the silence.
void PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) {
size_t PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) {
MutexLocker locker(lock_);
#if OS(ANDROID)
if (!output_bus) {
// Log when outputBus or FIFO object is invalid. (crbug.com/692423)
LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
<< ">] |outputBus| is invalid.";
// Silently return to avoid crash.
return;
return 0;
}
// The following checks are in place to catch the inexplicable crash.
......@@ -110,6 +109,7 @@ void PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) {
<< " >= " << fifo_length_ << ")";
}
#endif
CHECK(output_bus);
SECURITY_CHECK(frames_requested <= output_bus->length());
SECURITY_CHECK(frames_requested <= fifo_length_);
......@@ -162,10 +162,16 @@ void PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) {
// Update the number of frames in FIFO.
frames_available_ -= frames_to_fill;
DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_);
// |frames_requested > frames_available_| means the frames in FIFO is not
// enough to fulfill the requested frames from the audio device.
return frames_requested > frames_available_
? frames_requested - frames_available_
: 0;
}
const PushPullFIFOStateForTest PushPullFIFO::GetStateForTest() const {
return {length(), NumberOfChannels(), FramesAvailable(), index_read_,
return {length(), NumberOfChannels(), frames_available_, index_read_,
index_write_, overflow_count_, underflow_count_};
}
......
......@@ -7,6 +7,9 @@
#include "platform/audio/AudioBus.h"
#include "platform/wtf/Allocator.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/Threading.h"
#include "platform/wtf/ThreadingPrimitives.h"
#include "public/platform/WebCommon.h"
namespace blink {
......@@ -26,6 +29,14 @@ struct PushPullFIFOStateForTest {
// Blink-WebAudio and the renderer. The renderer's hardware callback buffer size
// varies on the platform, but the WebAudio always renders 128 frames (render
// quantum, RQ) thus FIFO is needed to handle the general case.
//
// Note that this object is concurrently accessed by two threads; WebAudio
// rendering thread (WebThread) in Blink and the audio device thread
// (AudioDeviceThread) from the media renderer. The push/pull operations touch
// most of variables in the class (index_write_, index_read_, frames_available_,
// and fifo_Bus_) so the thread safety must be handled with care.
//
// TODO(hongchan): add a unit test for multi-thread access.
class BLINK_PLATFORM_EXPORT PushPullFIFO {
USING_FAST_MALLOC(PushPullFIFO);
WTF_MAKE_NONCOPYABLE(PushPullFIFO);
......@@ -34,50 +45,53 @@ class BLINK_PLATFORM_EXPORT PushPullFIFO {
// Maximum FIFO length. (512 render quanta)
static const size_t kMaxFIFOLength;
// |fifoLength| cannot exceed |kMaxFIFOLength|. Otherwise it crashes.
// |fifo_length| cannot exceed |kMaxFIFOLength|. Otherwise it crashes.
explicit PushPullFIFO(unsigned number_of_channels, size_t fifo_length);
~PushPullFIFO();
// Pushes the rendered frames by WebAudio engine.
// - The |inputBus| length is 128 frames (1 render quantum), fixed.
// - The |input_bus| length is 128 frames (1 render quantum), fixed.
// - In case of overflow (FIFO full while push), the existing frames in FIFO
// will be overwritten and |indexRead| will be forcibly moved to
// |indexWrite| to avoid reading overwritten frames.
// will be overwritten and |index_read_| will be forcibly moved to
// |index_write_| to avoid reading overwritten frames.
void Push(const AudioBus* input_bus);
// Pulling |framesRequested| by the audio device thread.
// - If |framesRequested| is bigger than the length of |outputBus|, it
// Pulls |frames_requested| by the audio device thread and returns the actual
// number of frames to be rendered by the source. (i.e. WebAudio graph)
// - If |frames_requested| is bigger than the length of |output_bus|, it
// violates SECURITY_CHECK().
// - If |framesRequested| is bigger than FIFO length, it violates
// - If |frames_requested| is bigger than FIFO length, it violates
// SECURITY_CHECK().
// - In case of underflow (FIFO empty while pull), the remaining space in the
// requested output bus will be filled with silence. Thus it will fulfill
// the request from the consumer without causing error, but with a glitch.
void Pull(AudioBus* output_bus, size_t frames_requested);
size_t Pull(AudioBus* output_bus, size_t frames_requested);
size_t FramesAvailable() const { return frames_available_; }
size_t length() const { return fifo_length_; }
unsigned NumberOfChannels() const { return fifo_bus_->NumberOfChannels(); }
AudioBus* Bus() const { return fifo_bus_.Get(); }
// For unit test. Get the current configuration that consists of FIFO length,
// number of channels, read/write index position and under/overflow count.
// TODO(hongchan): For single thread unit test only. Consider refactoring.
AudioBus* GetFIFOBusForTest() const { return fifo_bus_.Get(); }
// For single thread unit test only. Get the current configuration that
// consists of FIFO length, number of channels, read/write index position and
// under/overflow count.
const PushPullFIFOStateForTest GetStateForTest() const;
private:
// The size of the FIFO.
const size_t fifo_length_ = 0;
RefPtr<AudioBus> fifo_bus_;
unsigned overflow_count_ = 0;
unsigned underflow_count_ = 0;
// This lock protects variables below.
Mutex lock_;
// The number of frames in the FIFO actually available for pulling.
size_t frames_available_;
size_t index_read_;
size_t index_write_;
unsigned overflow_count_;
unsigned underflow_count_;
size_t frames_available_ = 0;
size_t index_read_ = 0;
size_t index_write_ = 0;
RefPtr<AudioBus> fifo_bus_;
};
} // namespace blink
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/audio/PushPullFIFO.h"
#include <memory>
#include <vector>
#include "platform/CrossThreadFunctional.h"
#include "platform/WaitableEvent.h"
#include "platform/WebTaskRunner.h"
#include "platform/audio/AudioUtilities.h"
#include "platform/testing/UnitTestHelpers.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/PtrUtil.h"
#include "public/platform/Platform.h"
#include "public/platform/WebThread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
// To forcibly stop the message loop.
// TODO(hongchan): move this hack into Test class when the solution is found.
void FinishTest() {
LOG(INFO) << "FinishTest";
testing::ExitRunLoop();
}
// To wait for spawned threads to finish their tasks.
// TODO(hongchan): move this hack into Test class when the solution is found.
void HoldTestForDuration(double duration_ms) {
LOG(INFO) << "HoldTestForDuration";
Platform::Current()->CurrentThread()->GetWebTaskRunner()->PostDelayedTask(
BLINK_FROM_HERE,
WTF::Bind(&FinishTest),
duration_ms);
testing::EnterRunLoop();
}
// Base FIFOClient with an extra thread for looping and jitter control. The
// child class must define a specific task to run on the thread.
class FIFOClient {
public:
FIFOClient(PushPullFIFO* fifo, size_t bus_length, size_t jitter_range_ms)
: fifo_(fifo),
bus_(AudioBus::Create(fifo->NumberOfChannels(), bus_length)),
client_thread_(WTF::WrapUnique(
Platform::Current()->CreateThread("client thread"))),
jitter_range_ms_(jitter_range_ms) {}
void Start(double duration_ms, double interval_ms) {
duration_ms_ = duration_ms;
interval_ms_ = interval_ms;
client_thread_->GetWebTaskRunner()->PostTask(
BLINK_FROM_HERE,
CrossThreadBind(&FIFOClient::RunTaskOnOwnThread,
CrossThreadUnretained(this)));
}
virtual void Stop(int callback_counter) = 0;
virtual void RunTask() = 0;
void Pull(size_t frames_to_pull) {
fifo_->Pull(bus_.Get(), frames_to_pull);
}
void Push() {
fifo_->Push(bus_.Get());
}
private:
void RunTaskOnOwnThread() {
double interval_with_jitter = interval_ms_
+ (static_cast<double>(std::rand()) / RAND_MAX) * jitter_range_ms_;
elapsed_ms_ += interval_with_jitter;
++counter_;
RunTask();
if (elapsed_ms_ < duration_ms_) {
client_thread_->GetWebTaskRunner()->PostDelayedTask(
BLINK_FROM_HERE,
CrossThreadBind(&FIFOClient::RunTaskOnOwnThread,
CrossThreadUnretained(this)),
interval_with_jitter);
} else {
Stop(counter_);
}
}
PushPullFIFO* fifo_;
RefPtr<AudioBus> bus_;
std::unique_ptr<WebThread> client_thread_;
// Test duration.
double duration_ms_;
// Interval between each callback.
double interval_ms_;
// Jitter added to the regular pushing/pulling interval.
// (where j is 0 < j < jitter_range_ms)
double jitter_range_ms_;
// Elapsed test duration.
double elapsed_ms_ = 0;
// Counter variable for the total number of callbacks invoked.
int counter_ = 0;
};
// FIFO-pulling client (consumer). This mimics the audio device thread.
// |frames_to_pull| is variable.
class PullClient : public FIFOClient {
public:
PullClient(PushPullFIFO* fifo, size_t frames_to_pull, double jitter_range_ms)
: FIFOClient(fifo, frames_to_pull, jitter_range_ms),
frames_to_pull_(frames_to_pull) {
}
void RunTask() override {
Pull(frames_to_pull_);
}
void Stop(int callback_counter) override {
LOG(INFO) << "PullClient stopped. (" << callback_counter << " calls)";
}
private:
size_t frames_to_pull_;
};
// FIFO-pushing client (producer). This mimics the WebAudio rendering thread.
// The frames to push are static as 128 frames.
class PushClient : public FIFOClient {
public:
PushClient(PushPullFIFO* fifo, size_t frames_to_push, double jitter_range_ms)
: FIFOClient(fifo, frames_to_push, jitter_range_ms) {}
void RunTask() override {
Push();
}
void Stop(int callback_counter) override {
LOG(INFO) << "PushClient stopped. (" << callback_counter << " calls)";
}
};
struct FIFOSmokeTestParam {
const double sample_rate;
const unsigned number_of_channels;
const size_t fifo_length;
const double test_duration_ms;
// Buffer size for pulling. Equivalent of |callback_buffer_size|.
const size_t pull_buffer_size;
// Jitter range for the pulling interval.
const double pull_jitter_range_ms;
// Buffer size for pushing. Equivalent of WebAudio render quantum.
const size_t push_buffer_size;
// Jitter range for the pushing interval.
const double push_jitter_range_ms;
};
class PushPullFIFOSmokeTest
: public ::testing::TestWithParam<FIFOSmokeTestParam> {};
TEST_P(PushPullFIFOSmokeTest, SmokeTests) {
const FIFOSmokeTestParam param = GetParam();
const double sample_rate = param.sample_rate * 4;
const double pull_interval_ms =
param.pull_buffer_size / sample_rate * 1000;
const double push_interval_ms =
param.push_buffer_size / sample_rate * 1000;
std::unique_ptr<PushPullFIFO> test_fifo = WTF::WrapUnique(
new PushPullFIFO(param.number_of_channels, param.fifo_length));
std::unique_ptr<PullClient> pull_client = WTF::WrapUnique(new PullClient(
test_fifo.get(), param.pull_buffer_size, param.pull_jitter_range_ms));
std::unique_ptr<PushClient> push_client = WTF::WrapUnique(new PushClient(
test_fifo.get(), param.push_buffer_size, param.push_jitter_range_ms));
LOG(INFO) << "PushPullFIFOSmokeTest - Start";
pull_client->Start(param.test_duration_ms, pull_interval_ms);
push_client->Start(param.test_duration_ms, push_interval_ms);
// If the operation does not cause a crash for the test period, it's passed.
// Also give a bit more time to finish the tear-down process.
HoldTestForDuration(param.test_duration_ms + 150);
}
FIFOSmokeTestParam smoke_test_params[] = {
// Test case 0 (OSX): 256 Pull, 128 Push, Minimal jitter.
// WebThread's priority is lower than the device thread, so its jitter range
// is slightly bigger than the other.
{48000, 2, 8192, 1000, 256, 1, 128, 2},
// Test case 1 (Windows): 441 Pull, 128 Push. Moderate Jitter.
// Windows' audio callback is known to be ~10ms and UMA data shows the
// evidence for it. The jitter range was determined speculatively.
{44100, 2, 8192, 1000, 441, 2, 128, 3},
// Test case 2 (Ubuntu/Linux): 512 Pull, 128 Push. Unstable callback, but
// fast CPU. A typical configuration for Ubuntu + PulseAudio setup.
// PulseAudio's callback is known to be rather unstable.
{48000, 2, 8192, 1000, 512, 8, 128, 1},
// Test case 3 (Android-Reference): 512 Pull, 128 Push. Similar to Linux, but
// low profile CPU.
{44100, 2, 8192, 1000, 512, 8, 128, 3},
// Test case 4 (Android-ExternalA): 441 Pull, 128 Push. Extreme jitter with
// low profile CPU.
{44100, 2, 8192, 1000, 441, 24, 128, 8},
// Test case 5 (Android-ExternalB): 5768 Pull, 128 Push. Huge callback with
// large jitter. Low profile CPU.
{44100, 2, 8192, 1000, 5768, 120, 128, 12},
// Test case 6 (User-specified buffer size): 960 Pull, 128 Push. Minimal
// Jitter. 960 frames = 20ms at 48KHz.
{48000, 2, 8192, 1000, 960, 1, 128, 1},
// Test case 7 (Longer test duration): 256 Pull, 128 Push. 10 seconds.
{48000, 2, 8192, 10000, 256, 0, 128, 1}
};
INSTANTIATE_TEST_CASE_P(PushPullFIFOSmokeTest,
PushPullFIFOSmokeTest,
::testing::ValuesIn(smoke_test_params));
} // namespace
} // namespace blink
......@@ -6,51 +6,57 @@
#include <memory>
#include <vector>
#include "platform/CrossThreadFunctional.h"
#include "platform/WebTaskRunner.h"
#include "platform/audio/AudioUtilities.h"
#include "platform/testing/TestingPlatformSupport.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/PtrUtil.h"
#include "public/platform/Platform.h"
#include "public/platform/WebThread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
// Check the basic contract of FIFO.
// Check the basic contract of FIFO. This test only covers the single thread
// scenario.
TEST(PushPullFIFOBasicTest, BasicTests) {
// This suppresses the multi-thread warning for GTest. Potently it increases
// the test execution time, but this specific test is very short and simple.
::testing::FLAGS_gtest_death_test_style = "threadsafe";
// FIFO length exceeding the maximum length allowed will cause crash.
// i.e.) m_fifoLength <= kMaxFIFOLength
// i.e.) fifo_length_ <= kMaxFIFOLength
EXPECT_DEATH(new PushPullFIFO(2, PushPullFIFO::kMaxFIFOLength + 1), "");
std::unique_ptr<PushPullFIFO> test_fifo =
WTF::WrapUnique(new PushPullFIFO(2, 1024));
// The input bus length must be |AudioUtilities::kRenderQuantumFrames|.
// i.e.) inputBus->length() == kRenderQuantumFrames
RefPtr<AudioBus> input_bus_of129_frames =
// i.e.) input_bus->length() == kRenderQuantumFrames
RefPtr<AudioBus> input_bus_129_frames =
AudioBus::Create(2, AudioUtilities::kRenderQuantumFrames + 1);
EXPECT_DEATH(test_fifo->Push(input_bus_of129_frames.Get()), "");
RefPtr<AudioBus> input_bus_of127_frames =
EXPECT_DEATH(test_fifo->Push(input_bus_129_frames.Get()), "");
RefPtr<AudioBus> input_bus_127_frames =
AudioBus::Create(2, AudioUtilities::kRenderQuantumFrames - 1);
EXPECT_DEATH(test_fifo->Push(input_bus_of127_frames.Get()), "");
EXPECT_DEATH(test_fifo->Push(input_bus_127_frames.Get()), "");
// Pull request frames cannot exceed the length of output bus.
// i.e.) framesRequested <= outputBus->length()
RefPtr<AudioBus> output_bus_of512_frames = AudioBus::Create(2, 512);
EXPECT_DEATH(test_fifo->Pull(output_bus_of512_frames.Get(), 513), "");
// i.e.) frames_requested <= output_bus->length()
RefPtr<AudioBus> output_bus_512_frames = AudioBus::Create(2, 512);
EXPECT_DEATH(test_fifo->Pull(output_bus_512_frames.Get(), 513), "");
// Pull request frames cannot exceed the length of FIFO.
// i.e.) framesRequested <= m_fifoLength
RefPtr<AudioBus> output_bus_of1025_frames = AudioBus::Create(2, 1025);
EXPECT_DEATH(test_fifo->Pull(output_bus_of1025_frames.Get(), 1025), "");
// i.e.) frames_requested <= fifo_length_
RefPtr<AudioBus> output_bus_1025_frames = AudioBus::Create(2, 1025);
EXPECT_DEATH(test_fifo->Pull(output_bus_1025_frames.Get(), 1025), "");
}
// Fills each AudioChannel in an AudioBus with a series of linearly increasing
// values starting from |startingValue| and incrementing by 1. Then return value
// will be |startingValue| + |bus_length|.
// values starting from |starting_value| and incrementing by 1. Then return
// value will be |starting_value| + |bus_length|.
size_t FillBusWithLinearRamp(AudioBus* target_bus, size_t starting_value) {
for (unsigned c = 0; c < target_bus->NumberOfChannels(); ++c) {
float* bus_channel = target_bus->Channel(c)->MutableData();
......@@ -169,7 +175,8 @@ TEST_P(PushPullFIFOFeatureTest, FeatureTests) {
// Verify in-FIFO samples.
for (const auto& sample : expected_state.fifo_samples) {
EXPECT_TRUE(VerifyBusValueAtIndex(fifo->Bus(), sample.index, sample.value));
EXPECT_TRUE(VerifyBusValueAtIndex(fifo->GetFIFOBusForTest(),
sample.index, sample.value));
}
// Verify samples from the most recent output bus.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment