summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc1
-rw-r--r--content/content_renderer.gypi2
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/common/content_switches.cc3
-rw-r--r--content/public/common/content_switches.h1
-rw-r--r--content/renderer/media/audio_renderer_mixer_manager.cc73
-rw-r--r--content/renderer/media/audio_renderer_mixer_manager.h79
-rw-r--r--content/renderer/media/audio_renderer_mixer_manager_unittest.cc131
-rw-r--r--content/renderer/media/render_audiosourceprovider.cc22
-rw-r--r--content/renderer/render_thread_impl.cc14
-rw-r--r--content/renderer/render_thread_impl.h8
11 files changed, 330 insertions, 5 deletions
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index a8d2772..54fc2b8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -764,6 +764,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kEnablePartialSwap,
switches::kEnablePeerConnection,
switches::kEnablePerTilePainting,
+ switches::kEnableRendererSideMixing,
switches::kEnableShadowDOM,
switches::kEnableStrictSiteIsolation,
switches::kEnableStyleScoped,
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 570e48c..2d4a777 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -108,6 +108,8 @@
'renderer/media/audio_input_message_filter.h',
'renderer/media/audio_message_filter.cc',
'renderer/media/audio_message_filter.h',
+ 'renderer/media/audio_renderer_mixer_manager.cc',
+ 'renderer/media/audio_renderer_mixer_manager.h',
'renderer/media/capture_video_decoder.cc',
'renderer/media/capture_video_decoder.h',
'renderer/media/media_stream_center.h',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 8cc1a88..77b8f47 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -325,6 +325,7 @@
'renderer/gpu/input_event_filter_unittest.cc',
'renderer/media/audio_device_unittest.cc',
'renderer/media/audio_message_filter_unittest.cc',
+ 'renderer/media/audio_renderer_mixer_manager_unittest.cc',
'renderer/media/capture_video_decoder_unittest.cc',
'renderer/media/video_capture_impl_unittest.cc',
'renderer/media/video_capture_message_filter_unittest.cc',
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 7aa4439..6fd3cad 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -312,6 +312,9 @@ const char kEnablePrivilegedWebGLExtensions[] =
const char kEnablePruneGpuCommandBuffers[] =
"enable-prune-gpu-command-buffers";
+// Enable renderer side mixing and low latency audio path for media elements.
+const char kEnableRendererSideMixing[] = "enable-renderer-side-mixing";
+
// Enables TLS cached info extension.
const char kEnableSSLCachedInfo[] = "enable-ssl-cached-info";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 983c9f5..1f7197c 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -104,6 +104,7 @@ extern const char kEnablePinch[];
extern const char kEnablePreparsedJsCaching[];
CONTENT_EXPORT extern const char kEnablePrivilegedWebGLExtensions[];
extern const char kEnablePruneGpuCommandBuffers[];
+extern const char kEnableRendererSideMixing[];
extern const char kEnableSSLCachedInfo[];
extern const char kEnableSandboxLogging[];
extern const char kEnableSeccompSandbox[];
diff --git a/content/renderer/media/audio_renderer_mixer_manager.cc b/content/renderer/media/audio_renderer_mixer_manager.cc
new file mode 100644
index 0000000..03e638e
--- /dev/null
+++ b/content/renderer/media/audio_renderer_mixer_manager.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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 "content/renderer/media/audio_renderer_mixer_manager.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "content/renderer/media/audio_device_factory.h"
+#include "media/base/audio_renderer_mixer.h"
+#include "media/base/audio_renderer_mixer_input.h"
+
+namespace content {
+
+AudioRendererMixerManager::AudioRendererMixerManager(int hardware_sample_rate,
+ int hardware_buffer_size)
+ : hardware_sample_rate_(hardware_sample_rate),
+ hardware_buffer_size_(hardware_buffer_size) {
+}
+
+AudioRendererMixerManager::~AudioRendererMixerManager() {
+ DCHECK(mixers_.empty());
+}
+
+media::AudioRendererMixerInput* AudioRendererMixerManager::CreateInput() {
+ return new media::AudioRendererMixerInput(
+ base::Bind(
+ &AudioRendererMixerManager::GetMixer, base::Unretained(this)),
+ base::Bind(
+ &AudioRendererMixerManager::RemoveMixer, base::Unretained(this)));
+}
+
+media::AudioRendererMixer* AudioRendererMixerManager::GetMixer(
+ const media::AudioParameters& params) {
+ base::AutoLock auto_lock(mixers_lock_);
+
+ AudioRendererMixerMap::iterator it = mixers_.find(params);
+ if (it != mixers_.end()) {
+ it->second.ref_count++;
+ return it->second.mixer;
+ }
+
+ // Create output parameters based on the audio hardware configuration for
+ // passing on to the output sink. Force to 16-bit output for now since we
+ // know that works well for WebAudio and WebRTC.
+ media::AudioParameters output_params(
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY, params.channel_layout(),
+ hardware_sample_rate_, 16, hardware_buffer_size_);
+
+ media::AudioRendererMixer* mixer = new media::AudioRendererMixer(
+ params, output_params, AudioDeviceFactory::Create());
+
+ AudioRendererMixerReference mixer_reference = { mixer, 1 };
+ mixers_[params] = mixer_reference;
+ return mixer;
+}
+
+void AudioRendererMixerManager::RemoveMixer(
+ const media::AudioParameters& params) {
+ base::AutoLock auto_lock(mixers_lock_);
+
+ AudioRendererMixerMap::iterator it = mixers_.find(params);
+ DCHECK(it != mixers_.end());
+
+ // Only remove the mixer if AudioRendererMixerManager is the last owner.
+ it->second.ref_count--;
+ if (it->second.ref_count == 0) {
+ delete it->second.mixer;
+ mixers_.erase(it);
+ }
+}
+
+} // namespace content
diff --git a/content/renderer/media/audio_renderer_mixer_manager.h b/content/renderer/media/audio_renderer_mixer_manager.h
new file mode 100644
index 0000000..ab713f96
--- /dev/null
+++ b/content/renderer/media/audio_renderer_mixer_manager.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_RENDERER_MEDIA_AUDIO_RENDERER_MIXER_MANAGER_H_
+#define CONTENT_RENDERER_MEDIA_AUDIO_RENDERER_MIXER_MANAGER_H_
+
+#include <map>
+
+#include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
+#include "media/audio/audio_parameters.h"
+
+namespace media {
+class AudioRendererMixer;
+class AudioRendererMixerInput;
+}
+
+namespace content {
+
+// Manages sharing of an AudioRendererMixer among AudioRendererMixerInputs based
+// on their AudioParameters configuration. Inputs with the same AudioParameters
+// configuration will share a mixer while a new AudioRendererMixer will be
+// lazily created if one with the exact AudioParameters does not exist.
+//
+// There should only be one instance of AudioRendererMixerManager per render
+// thread.
+//
+// TODO(dalecurtis): Right now we require AudioParameters to be an exact match
+// when we should be able to ignore bits per channel since we're only dealing
+// with floats. However, bits per channel is currently used to interleave the
+// audio data by AudioDevice::AudioThreadCallback::Process for consumption via
+// the shared memory. See http://crbug.com/114700.
+class CONTENT_EXPORT AudioRendererMixerManager {
+ public:
+ // Construct an instance using the given audio hardware configuration.
+ AudioRendererMixerManager(int hardware_sample_rate, int hardware_buffer_size);
+ ~AudioRendererMixerManager();
+
+ // Creates an AudioRendererMixerInput with the proper callbacks necessary to
+ // retrieve an AudioRendererMixer instance from AudioRendererMixerManager.
+ // Caller must ensure AudioRendererMixerManager outlives the returned input.
+ media::AudioRendererMixerInput* CreateInput();
+
+ private:
+ friend class AudioRendererMixerManagerTest;
+
+ // Returns a mixer instance based on AudioParameters; an existing one if one
+ // with the provided AudioParameters exists or a new one if not.
+ media::AudioRendererMixer* GetMixer(const media::AudioParameters& params);
+
+ // Remove a mixer instance given a mixer if the only other reference is held
+ // by AudioRendererMixerManager. Every AudioRendererMixer owner must call
+ // this method when it's done with a mixer.
+ void RemoveMixer(const media::AudioParameters& params);
+
+ // Map of AudioParameters to <AudioRendererMixer, Count>. Count allows
+ // AudioRendererMixerManager to keep track explicitly (v.s. RefCounted which
+ // is implicit) of the number of outstanding AudioRendererMixers.
+ struct AudioRendererMixerReference {
+ media::AudioRendererMixer* mixer;
+ int ref_count;
+ };
+ typedef std::map<media::AudioParameters, AudioRendererMixerReference,
+ media::AudioParameters::Compare> AudioRendererMixerMap;
+ AudioRendererMixerMap mixers_;
+ base::Lock mixers_lock_;
+
+ // Audio hardware configuration. Used to construct output AudioParameters for
+ // each AudioRendererMixer instance.
+ int hardware_sample_rate_;
+ int hardware_buffer_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererMixerManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_MEDIA_AUDIO_RENDERER_MIXER_MANAGER_H_
diff --git a/content/renderer/media/audio_renderer_mixer_manager_unittest.cc b/content/renderer/media/audio_renderer_mixer_manager_unittest.cc
new file mode 100644
index 0000000..92a44d4
--- /dev/null
+++ b/content/renderer/media/audio_renderer_mixer_manager_unittest.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 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 "base/memory/scoped_ptr.h"
+#include "content/renderer/media/audio_device_factory.h"
+#include "content/renderer/media/audio_renderer_mixer_manager.h"
+#include "media/base/audio_renderer_mixer.h"
+#include "media/base/audio_renderer_mixer_input.h"
+#include "media/base/fake_audio_render_callback.h"
+#include "media/base/mock_audio_renderer_sink.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+static const int kBitsPerChannel = 16;
+static const int kSampleRate = 48000;
+static const int kBufferSize = 8192;
+static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
+
+// By sub-classing AudioDeviceFactory we've overridden the factory to use our
+// CreateAudioDevice() method globally.
+class MockAudioRenderSinkFactory : public AudioDeviceFactory {
+ public:
+ MockAudioRenderSinkFactory() {}
+ virtual ~MockAudioRenderSinkFactory() {}
+
+ protected:
+ virtual media::MockAudioRendererSink* CreateAudioDevice() {
+ media::MockAudioRendererSink* sink = new media::MockAudioRendererSink();
+ EXPECT_CALL(*sink, Start());
+ EXPECT_CALL(*sink, Stop());
+ return sink;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(MockAudioRenderSinkFactory);
+};
+
+class AudioRendererMixerManagerTest : public testing::Test {
+ public:
+ AudioRendererMixerManagerTest() {
+ // We don't want to deal with instantiating a real AudioDevice since it's
+ // not important to our testing, so use a mock AudioDeviceFactory.
+ mock_sink_factory_.reset(new MockAudioRenderSinkFactory());
+ manager_.reset(new AudioRendererMixerManager(kSampleRate, kBufferSize));
+ }
+
+ media::AudioRendererMixer* GetMixer(const media::AudioParameters& params) {
+ return manager_->GetMixer(params);
+ }
+
+ void RemoveMixer(const media::AudioParameters& params) {
+ return manager_->RemoveMixer(params);
+ }
+
+ // Number of instantiated mixers.
+ int mixer_count() {
+ return manager_->mixers_.size();
+ }
+
+ protected:
+ scoped_ptr<MockAudioRenderSinkFactory> mock_sink_factory_;
+ scoped_ptr<AudioRendererMixerManager> manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererMixerManagerTest);
+};
+
+// Verify GetMixer() and RemoveMixer() both work as expected; particularly with
+// respect to the explicit ref counting done.
+TEST_F(AudioRendererMixerManagerTest, GetRemoveMixer) {
+ // There should be no mixers outstanding to start with.
+ EXPECT_EQ(mixer_count(), 0);
+
+ media::AudioParameters params1(
+ media::AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate,
+ kBitsPerChannel, kBufferSize);
+
+ media::AudioRendererMixer* mixer1 = GetMixer(params1);
+ ASSERT_TRUE(mixer1);
+ EXPECT_EQ(mixer_count(), 1);
+
+ // The same parameters should return the same mixer1.
+ EXPECT_EQ(mixer1, GetMixer(params1));
+ EXPECT_EQ(mixer_count(), 1);
+
+ // Remove the extra mixer we just acquired.
+ RemoveMixer(params1);
+ EXPECT_EQ(mixer_count(), 1);
+
+ media::AudioParameters params2(
+ media::AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate * 2,
+ kBitsPerChannel, kBufferSize * 2);
+ media::AudioRendererMixer* mixer2 = GetMixer(params2);
+ ASSERT_TRUE(mixer2);
+ EXPECT_EQ(mixer_count(), 2);
+
+ // Different parameters should result in a different mixer1.
+ EXPECT_NE(mixer1, mixer2);
+
+ // Remove both outstanding mixers.
+ RemoveMixer(params1);
+ EXPECT_EQ(mixer_count(), 1);
+ RemoveMixer(params2);
+ EXPECT_EQ(mixer_count(), 0);
+}
+
+// Verify CreateInput() provides AudioRendererMixerInput with the appropriate
+// callbacks and they are working as expected.
+TEST_F(AudioRendererMixerManagerTest, CreateInput) {
+ media::AudioParameters params(
+ media::AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate,
+ kBitsPerChannel, kBufferSize);
+
+ // Create a mixer input and ensure it doesn't instantiate a mixer yet.
+ EXPECT_EQ(mixer_count(), 0);
+ scoped_refptr<media::AudioRendererMixerInput> input(manager_->CreateInput());
+ EXPECT_EQ(mixer_count(), 0);
+
+ // Implicitly test that AudioRendererMixerInput was provided with the expected
+ // callbacks needed to acquire an AudioRendererMixer and remove it.
+ media::FakeAudioRenderCallback callback(0);
+ input->Initialize(params, &callback);
+ EXPECT_EQ(mixer_count(), 1);
+
+ // Destroying the input should destroy the mixer.
+ input = NULL;
+ EXPECT_EQ(mixer_count(), 0);
+}
+
+} // namespace content
diff --git a/content/renderer/media/render_audiosourceprovider.cc b/content/renderer/media/render_audiosourceprovider.cc
index c0a0580..da1e341 100644
--- a/content/renderer/media/render_audiosourceprovider.cc
+++ b/content/renderer/media/render_audiosourceprovider.cc
@@ -5,11 +5,17 @@
#include "content/renderer/media/render_audiosourceprovider.h"
#include "base/basictypes.h"
+#include "base/command_line.h"
#include "base/logging.h"
+#include "content/public/common/content_switches.h"
#include "content/renderer/media/audio_device_factory.h"
+#include "content/renderer/media/audio_renderer_mixer_manager.h"
+#include "content/renderer/render_thread_impl.h"
+#include "media/base/audio_renderer_mixer_input.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebAudioSourceProviderClient.h"
using content::AudioDeviceFactory;
+using content::AudioRendererMixerManager;
using std::vector;
using WebKit::WebVector;
@@ -20,11 +26,17 @@ RenderAudioSourceProvider::RenderAudioSourceProvider()
is_running_(false),
renderer_(NULL),
client_(NULL) {
- // We create the AudioDevice here using the factory. But we don't yet know
- // the audio format (sample-rate, etc.) at this point. Later, when
- // Initialize() is called, we have the audio format information and call
- // the AudioDevice::Initialize() method to fully initialize it.
- default_sink_ = AudioDeviceFactory::Create();
+ // We create an AudioRendererSink here, but we don't yet know the audio format
+ // (sample-rate, etc.) at this point. Later, when Initialize() is called, we
+ // have the audio format information and call AudioRendererSink::Initialize()
+ // to fully initialize it.
+ const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ if (cmd_line->HasSwitch(switches::kEnableRendererSideMixing)) {
+ default_sink_ = RenderThreadImpl::current()->
+ GetAudioRendererMixerManager()->CreateInput();
+ } else {
+ default_sink_ = AudioDeviceFactory::Create();
+ }
}
void RenderAudioSourceProvider::setClient(
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 99988be..0cb9136 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -55,8 +55,10 @@
#include "content/renderer/dom_storage/webstoragenamespace_impl.h"
#include "content/renderer/gpu/compositor_thread.h"
#include "content/renderer/gpu/gpu_benchmarking_extension.h"
+#include "content/renderer/media/audio_hardware.h"
#include "content/renderer/media/audio_input_message_filter.h"
#include "content/renderer/media/audio_message_filter.h"
+#include "content/renderer/media/audio_renderer_mixer_manager.h"
#include "content/renderer/media/media_stream_center.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/media/video_capture_message_filter.h"
@@ -111,6 +113,7 @@ using WebKit::WebScriptController;
using WebKit::WebSecurityPolicy;
using WebKit::WebString;
using WebKit::WebView;
+using content::AudioRendererMixerManager;
using content::RenderProcessObserver;
namespace {
@@ -750,6 +753,17 @@ RenderThreadImpl::GetGpuVDAContext3D() {
return gpu_vda_context3d_->AsWeakPtr();
}
+content::AudioRendererMixerManager*
+RenderThreadImpl::GetAudioRendererMixerManager() {
+ if (!audio_renderer_mixer_manager_.get()) {
+ audio_renderer_mixer_manager_.reset(new AudioRendererMixerManager(
+ audio_hardware::GetOutputSampleRate(),
+ audio_hardware::GetOutputBufferSize()));
+ }
+
+ return audio_renderer_mixer_manager_.get();
+}
+
#if defined(OS_WIN)
void RenderThreadImpl::PreCacheFont(const LOGFONT& log_font) {
Send(new ChildProcessHostMsg_PreCacheFont(log_font));
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 8857120..d3048d7 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -53,6 +53,7 @@ class ScopedCOMInitializer;
}
namespace content {
+class AudioRendererMixerManager;
class BrowserPluginChannelManager;
class BrowserPluginRegistry;
class MediaStreamCenter;
@@ -223,6 +224,11 @@ class CONTENT_EXPORT RenderThreadImpl : public content::RenderThread,
// threaded compositing is enabled.
base::WeakPtr<WebGraphicsContext3DCommandBufferImpl> GetGpuVDAContext3D();
+ // AudioRendererMixerManager instance which manages renderer side mixer
+ // instances shared based on configured audio parameters. Lazily created on
+ // first call.
+ content::AudioRendererMixerManager* GetAudioRendererMixerManager();
+
private:
virtual bool OnControlMessageReceived(const IPC::Message& msg) OVERRIDE;
@@ -303,6 +309,8 @@ class CONTENT_EXPORT RenderThreadImpl : public content::RenderThread,
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> gpu_vda_context3d_;
+ scoped_ptr<content::AudioRendererMixerManager> audio_renderer_mixer_manager_;
+
DISALLOW_COPY_AND_ASSIGN(RenderThreadImpl);
};