Chromium Code Reviews| Index: third_party/WebKit/Source/platform/audio/AudioDestination.cpp |
| diff --git a/third_party/WebKit/Source/platform/audio/AudioDestination.cpp b/third_party/WebKit/Source/platform/audio/AudioDestination.cpp |
| index 72444184e89d65df7c8a29bf9fb77efc2bc4af9b..cbd943024f75218e17a774a7125c2a855dd39770 100644 |
| --- a/third_party/WebKit/Source/platform/audio/AudioDestination.cpp |
| +++ b/third_party/WebKit/Source/platform/audio/AudioDestination.cpp |
| @@ -29,7 +29,6 @@ |
| #include "platform/audio/AudioDestination.h" |
| #include "platform/Histogram.h" |
| -#include "platform/audio/AudioFIFO.h" |
| #include "platform/audio/AudioPullFIFO.h" |
| #include "platform/audio/AudioUtilities.h" |
| #include "platform/weborigin/SecurityOrigin.h" |
| @@ -40,138 +39,72 @@ |
| namespace blink { |
| -// Size of the FIFO |
| -const size_t fifoSize = 8192; |
| +// FIFO Size. |
| +// |
| +// TODO(hongchan): This was estimated based on the largest callback buffer size |
|
Raymond Toy
2016/12/22 21:46:07
crbug.com/670747
|
| +// that we would ever need. The current UMA stats indicates that this is, in |
| +// fact, probably too small. There are Android devices out there with a size of |
| +// 8000 or so. We might need to make this larger. |
| +const size_t kFIFOSize = 8192; |
| -// Factory method: Chromium-implementation |
| std::unique_ptr<AudioDestination> AudioDestination::create( |
| AudioIOCallback& callback, |
| - const String& inputDeviceId, |
| - unsigned numberOfInputChannels, |
| unsigned numberOfOutputChannels, |
| float sampleRate, |
| PassRefPtr<SecurityOrigin> securityOrigin) { |
| return WTF::wrapUnique(new AudioDestination( |
| - callback, inputDeviceId, numberOfInputChannels, numberOfOutputChannels, |
| - sampleRate, std::move(securityOrigin))); |
| + callback, numberOfOutputChannels, sampleRate, std::move(securityOrigin))); |
| } |
| AudioDestination::AudioDestination(AudioIOCallback& callback, |
| - const String& inputDeviceId, |
| - unsigned numberOfInputChannels, |
| unsigned numberOfOutputChannels, |
| float sampleRate, |
| PassRefPtr<SecurityOrigin> securityOrigin) |
| - : m_callback(callback), |
| - m_numberOfOutputChannels(numberOfOutputChannels), |
| - m_renderBus(AudioBus::create(numberOfOutputChannels, |
| - AudioUtilities::kRenderQuantumFrames, |
| - false)), |
| + : m_numberOfOutputChannels(numberOfOutputChannels), |
| m_sampleRate(sampleRate), |
| m_isPlaying(false), |
| - m_framesElapsed(0), |
| - m_outputPosition() { |
| - // Histogram for audioHardwareBufferSize |
| - DEFINE_STATIC_LOCAL(SparseHistogram, hardwareBufferSizeHistogram, |
| - ("WebAudio.AudioDestination.HardwareBufferSize")); |
| - // Histogram for the actual callback size used. Typically, this is the same |
| - // as audioHardwareBufferSize, but can be adjusted depending on some |
| - // heuristics below. |
| - DEFINE_STATIC_LOCAL(SparseHistogram, callbackBufferSizeHistogram, |
| - ("WebAudio.AudioDestination.CallbackBufferSize")); |
| - |
| - // Use the optimal buffer size recommended by the audio backend. |
| - size_t recommendedHardwareBufferSize = |
| - Platform::current()->audioHardwareBufferSize(); |
| - m_callbackBufferSize = recommendedHardwareBufferSize; |
| - |
| -#if OS(ANDROID) |
| - // The optimum low-latency hardware buffer size is usually too small on |
| - // Android for WebAudio to render without glitching. So, if it is small, use |
| - // a larger size. If it was already large, use the requested size. |
| - // |
| - // Since WebAudio renders in 128-frame blocks, the small buffer sizes (144 |
| - // for a Galaxy Nexus), cause significant processing jitter. Sometimes |
| - // multiple blocks will processed, but other times will not be since the FIFO |
| - // can satisfy the request. By using a larger callbackBufferSize, we smooth |
| - // out the jitter. |
| - const size_t kSmallBufferSize = 1024; |
| - const size_t kDefaultCallbackBufferSize = 2048; |
| - |
| - if (m_callbackBufferSize <= kSmallBufferSize) |
| - m_callbackBufferSize = kDefaultCallbackBufferSize; |
| - |
| - LOG(INFO) << "audioHardwareBufferSize = " << recommendedHardwareBufferSize; |
| - LOG(INFO) << "callbackBufferSize = " << m_callbackBufferSize; |
| -#endif |
| - |
| - // Quick exit if the requested size is too large. |
| - DCHECK_LE(m_callbackBufferSize + AudioUtilities::kRenderQuantumFrames, |
| - fifoSize); |
| - if (m_callbackBufferSize + AudioUtilities::kRenderQuantumFrames > fifoSize) |
| - return; |
| - |
| - m_audioDevice = WTF::wrapUnique(Platform::current()->createAudioDevice( |
| - m_callbackBufferSize, numberOfInputChannels, numberOfOutputChannels, |
| - sampleRate, this, inputDeviceId, std::move(securityOrigin))); |
| - ASSERT(m_audioDevice); |
| - |
| - // Record the sizes if we successfully created an output device. |
| - hardwareBufferSizeHistogram.sample(recommendedHardwareBufferSize); |
| - callbackBufferSizeHistogram.sample(m_callbackBufferSize); |
| - |
| - // Create a FIFO to handle the possibility of the callback size |
| - // not being a multiple of the render size. If the FIFO already |
| - // contains enough data, the data will be provided directly. |
| - // Otherwise, the FIFO will call the provider enough times to |
| - // satisfy the request for data. |
| - m_fifo = |
| - WTF::wrapUnique(new AudioPullFIFO(*this, numberOfOutputChannels, fifoSize, |
| - AudioUtilities::kRenderQuantumFrames)); |
| + m_callback(callback), |
| + m_outputBus(AudioBus::create(numberOfOutputChannels, |
| + AudioUtilities::kRenderQuantumFrames, |
| + false)), |
| + m_framesElapsed(0) { |
| + // Calculate the optimum buffer size first. |
| + bool isBufferSizeValid = calculateBufferSize(); |
| + DCHECK(isBufferSizeValid); |
| + |
| + if (isBufferSizeValid) { |
| + // Create WebAudioDevice. blink::WebAudioDevice is designed to support the |
| + // local input (e.g. loopback from OS audio system), but Chromium's media |
| + // renderer does not support it currently. Thus, we use zero for the number |
| + // of input channels. |
| + m_webAudioDevice = WTF::wrapUnique(Platform::current()->createAudioDevice( |
| + m_callbackBufferSize, 0, numberOfOutputChannels, sampleRate, this, |
| + String(), std::move(securityOrigin))); |
| + DCHECK(m_webAudioDevice); |
| + |
| + // Create a FIFO. |
| + m_fifo = WTF::wrapUnique( |
| + new AudioPullFIFO(*this, numberOfOutputChannels, kFIFOSize, |
| + AudioUtilities::kRenderQuantumFrames)); |
| + } |
| } |
| AudioDestination::~AudioDestination() { |
| stop(); |
| } |
| -void AudioDestination::start() { |
| - if (!m_isPlaying && m_audioDevice) { |
| - m_audioDevice->start(); |
| - m_isPlaying = true; |
| - } |
| -} |
| - |
| -void AudioDestination::stop() { |
| - if (m_isPlaying && m_audioDevice) { |
| - m_audioDevice->stop(); |
| - m_isPlaying = false; |
| - } |
| -} |
| - |
| -float AudioDestination::hardwareSampleRate() { |
| - return static_cast<float>(Platform::current()->audioHardwareSampleRate()); |
| -} |
| - |
| -unsigned long AudioDestination::maxChannelCount() { |
| - return static_cast<float>(Platform::current()->audioHardwareOutputChannels()); |
| -} |
| - |
| -void AudioDestination::render(const WebVector<float*>& audioData, |
| +void AudioDestination::render(const WebVector<float*>& destinationData, |
| size_t numberOfFrames, |
| double delay, |
| double delayTimestamp, |
| size_t priorFramesSkipped) { |
| - bool isNumberOfChannelsGood = audioData.size() == m_numberOfOutputChannels; |
| - if (!isNumberOfChannelsGood) { |
| - ASSERT_NOT_REACHED(); |
| + DCHECK_EQ(destinationData.size(), m_numberOfOutputChannels); |
| + if (destinationData.size() != m_numberOfOutputChannels) |
| return; |
| - } |
| - bool isBufferSizeGood = numberOfFrames == m_callbackBufferSize; |
| - if (!isBufferSizeGood) { |
| - ASSERT_NOT_REACHED(); |
| + DCHECK_EQ(numberOfFrames, m_callbackBufferSize); |
| + if (numberOfFrames != m_callbackBufferSize) |
| return; |
| - } |
| m_framesElapsed -= std::min(m_framesElapsed, priorFramesSkipped); |
| double outputPosition = |
| @@ -180,18 +113,20 @@ void AudioDestination::render(const WebVector<float*>& audioData, |
| m_outputPosition.timestamp = delayTimestamp; |
| m_outputPositionReceivedTimestamp = base::TimeTicks::Now(); |
| + // Associate the destination data array with the output bus then fill the |
| + // FIFO. |
| for (unsigned i = 0; i < m_numberOfOutputChannels; ++i) |
| - m_renderBus->setChannelMemory(i, audioData[i], numberOfFrames); |
| - |
| - m_fifo->consume(m_renderBus.get(), numberOfFrames); |
| + m_outputBus->setChannelMemory(i, destinationData[i], numberOfFrames); |
| + m_fifo->consume(m_outputBus.get(), numberOfFrames); |
| m_framesElapsed += numberOfFrames; |
| } |
| -void AudioDestination::provideInput(AudioBus* bus, size_t framesToProcess) { |
| +void AudioDestination::provideInput(AudioBus* outputBus, |
| + size_t framesToProcess) { |
| AudioIOPosition outputPosition = m_outputPosition; |
| - // If platfrom buffer is more than two times longer than |framesToProcess| |
| + // If platform buffer is more than two times longer than |framesToProcess| |
| // 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 (m_callbackBufferSize > framesToProcess * 2) { |
| @@ -206,7 +141,83 @@ void AudioDestination::provideInput(AudioBus* bus, size_t framesToProcess) { |
| if (outputPosition.position < 0.0) |
| outputPosition.position = 0.0; |
| - m_callback.render(nullptr, bus, framesToProcess, outputPosition); |
| + // To fill the FIFO, start the render call chain of the destination node. |
| + m_callback.render(nullptr, outputBus, framesToProcess, outputPosition); |
| +} |
| + |
| +void AudioDestination::start() { |
| + if (m_webAudioDevice && !m_isPlaying) { |
| + m_webAudioDevice->start(); |
| + m_isPlaying = true; |
| + } |
| +} |
| + |
| +void AudioDestination::stop() { |
| + if (m_webAudioDevice && m_isPlaying) { |
| + m_webAudioDevice->stop(); |
| + m_isPlaying = false; |
| + } |
| +} |
| + |
| +size_t AudioDestination::hardwareBufferSize() { |
| + return Platform::current()->audioHardwareBufferSize(); |
| +} |
| + |
| +float AudioDestination::hardwareSampleRate() { |
| + return static_cast<float>(Platform::current()->audioHardwareSampleRate()); |
| +} |
| + |
| +unsigned long AudioDestination::maxChannelCount() { |
| + return static_cast<unsigned long>( |
| + Platform::current()->audioHardwareOutputChannels()); |
| +} |
| + |
| +bool AudioDestination::calculateBufferSize() { |
| + // Use the optimal buffer size recommended by the audio backend. |
| + size_t recommendedHardwareBufferSize = hardwareBufferSize(); |
| + m_callbackBufferSize = recommendedHardwareBufferSize; |
| + |
| +#if OS(ANDROID) |
| + // The optimum low-latency hardware buffer size is usually too small on |
| + // Android for WebAudio to render without glitching. So, if it is small, use a |
| + // larger size. If it was already large, use the requested size. |
| + // |
| + // Since WebAudio renders in 128-frame blocks, the small buffer sizes (144 for |
| + // a Galaxy Nexus), cause significant processing jitter. Sometimes multiple |
| + // blocks will processed, but other times will not be since the FIFO can |
| + // satisfy the request. By using a larger callbackBufferSize, we smooth out |
| + // the jitter. |
| + const size_t kSmallBufferSize = 1024; |
| + const size_t kDefaultCallbackBufferSize = 2048; |
| + |
| + if (m_callbackBufferSize <= kSmallBufferSize) |
| + m_callbackBufferSize = kDefaultCallbackBufferSize; |
| + |
| + LOG(INFO) << "audioHardwareBufferSize = " << recommendedHardwareBufferSize; |
| + LOG(INFO) << "callbackBufferSize = " << m_callbackBufferSize; |
| +#endif |
| + |
| + // Histogram for audioHardwareBufferSize |
| + DEFINE_STATIC_LOCAL(SparseHistogram, hardwareBufferSizeHistogram, |
| + ("WebAudio.AudioDestination.HardwareBufferSize")); |
| + |
| + // Histogram for the actual callback size used. Typically, this is the same |
| + // as audioHardwareBufferSize, but can be adjusted depending on some |
| + // heuristics below. |
| + DEFINE_STATIC_LOCAL(SparseHistogram, callbackBufferSizeHistogram, |
| + ("WebAudio.AudioDestination.CallbackBufferSize")); |
| + |
| + // Record the sizes if we successfully created an output device. |
| + hardwareBufferSizeHistogram.sample(recommendedHardwareBufferSize); |
| + callbackBufferSizeHistogram.sample(m_callbackBufferSize); |
| + |
| + // Quick exit if the requested size is too large. |
| + DCHECK_LE(m_callbackBufferSize + AudioUtilities::kRenderQuantumFrames, |
| + kFIFOSize); |
| + if (m_callbackBufferSize + AudioUtilities::kRenderQuantumFrames > kFIFOSize) |
| + return false; |
| + |
| + return true; |
|
Raymond Toy
2016/12/22 21:46:06
Instead of lines 217-220, maybe just do
return m_
hongchan
2017/01/03 18:50:26
Done.
|
| } |
| } // namespace blink |