diff options
-rw-r--r-- | content/browser/renderer_host/render_process_host_impl.cc | 1 | ||||
-rw-r--r-- | content/content_renderer.gypi | 2 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | content/public/common/content_switches.cc | 3 | ||||
-rw-r--r-- | content/public/common/content_switches.h | 1 | ||||
-rw-r--r-- | content/renderer/media/audio_renderer_mixer_manager.cc | 73 | ||||
-rw-r--r-- | content/renderer/media/audio_renderer_mixer_manager.h | 79 | ||||
-rw-r--r-- | content/renderer/media/audio_renderer_mixer_manager_unittest.cc | 131 | ||||
-rw-r--r-- | content/renderer/media/render_audiosourceprovider.cc | 22 | ||||
-rw-r--r-- | content/renderer/render_thread_impl.cc | 14 | ||||
-rw-r--r-- | content/renderer/render_thread_impl.h | 8 |
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); }; |