diff options
author | grunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-18 19:39:04 +0000 |
---|---|---|
committer | grunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-18 19:39:04 +0000 |
commit | da9f30ae881fe63b37b17197d6a18b305d2154f8 (patch) | |
tree | 67f2679ee38e5c002b57a83dcdc2f3350cb1c099 | |
parent | fe2962f4b16746d1d26b9bb7ba078b136ed5ce6d (diff) | |
download | chromium_src-da9f30ae881fe63b37b17197d6a18b305d2154f8.zip chromium_src-da9f30ae881fe63b37b17197d6a18b305d2154f8.tar.gz chromium_src-da9f30ae881fe63b37b17197d6a18b305d2154f8.tar.bz2 |
Support multiple files for AEC dump.
* This adds support for AEC dump files in multiple tabs and if multiple getUserMedia calls are made in one tab.
* Each AEC dump consumer registers with the browser to recieve a file handle when AEC dump is enabled.
* Add dedicated aec dump message filter an messages file.
* Works with and without track-processing.
NOTRY=true
BUG=350347
Review URL: https://codereview.chromium.org/334743006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278148 0039d316-1c4b-4281-b951-d872f2087c98
21 files changed, 545 insertions, 165 deletions
diff --git a/content/browser/media/webrtc_browsertest.cc b/content/browser/media/webrtc_browsertest.cc index e26510b..f5bb216 100644 --- a/content/browser/media/webrtc_browsertest.cc +++ b/content/browser/media/webrtc_browsertest.cc @@ -4,6 +4,7 @@ #include "base/command_line.h" #include "base/file_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/threading/platform_thread.h" #include "base/values.h" @@ -367,6 +368,12 @@ IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, CallAndVerifyVideoMutingWorks) { MakeTypicalPeerConnectionCall("callAndEnsureVideoTrackMutingWorks();"); } +#if defined(OS_WIN) +#define IntToStringType base::IntToString16 +#else +#define IntToStringType base::IntToString +#endif + #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) // Timing out on ARM linux bot: http://crbug.com/238490 #define MAYBE_CallWithAecDump DISABLED_CallWithAecDump @@ -398,6 +405,17 @@ IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CallWithAecDump) { DisableOpusIfOnAndroid(); ExecuteJavascriptAndWaitForOk("call({video: true, audio: true});"); + // Get the ID for the render process host. There should only be one. + RenderProcessHost::iterator it( + content::RenderProcessHost::AllHostsIterator()); + int render_process_host_id = it.GetCurrentValue()->GetID(); + EXPECT_GE(render_process_host_id, 0); + + // Add file extensions that we expect to be added. + static const int kExpectedConsumerId = 0; + dump_file = dump_file.AddExtension(IntToStringType(render_process_host_id)) + .AddExtension(IntToStringType(kExpectedConsumerId)); + EXPECT_TRUE(base::PathExists(dump_file)); int64 file_size = 0; EXPECT_TRUE(base::GetFileSize(dump_file, &file_size)); @@ -406,6 +424,9 @@ IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CallWithAecDump) { base::DeleteFile(dump_file, false); } +// TODO(grunell): Add test for multiple dumps when re-use of +// MediaStreamAudioProcessor in AudioCapturer has been removed. + #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) // Timing out on ARM linux bot: http://crbug.com/238490 #define MAYBE_CallWithAecDumpEnabledThenDisabled DISABLED_CallWithAecDumpEnabledThenDisabled diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 890c390..efe852b 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -30,6 +30,7 @@ #include "base/path_service.h" #include "base/rand_util.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/supports_user_data.h" #include "base/sys_info.h" @@ -173,6 +174,7 @@ #include "content/browser/media/webrtc_internals.h" #include "content/browser/renderer_host/media/media_stream_track_metrics_host.h" #include "content/browser/renderer_host/media/webrtc_identity_service_host.h" +#include "content/common/media/aec_dump_messages.h" #include "content/common/media/media_stream_messages.h" #endif @@ -1368,6 +1370,12 @@ bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) { ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer, OnAllocateGpuMemoryBuffer) IPC_MESSAGE_HANDLER(ViewHostMsg_Close_ACK, OnCloseACK) +#if defined(ENABLE_WEBRTC) + IPC_MESSAGE_HANDLER(AecDumpMsg_RegisterAecDumpConsumer, + OnRegisterAecDumpConsumer) + IPC_MESSAGE_HANDLER(AecDumpMsg_UnregisterAecDumpConsumer, + OnUnregisterAecDumpConsumer) +#endif // Adding single handlers for your service here is fine, but once your // service needs more than one handler, please extract them into a new // message filter and add that filter to CreateMessageFilters(). @@ -1538,11 +1546,11 @@ void RenderProcessHostImpl::FilterURL(bool empty_allowed, GURL* url) { #if defined(ENABLE_WEBRTC) void RenderProcessHostImpl::EnableAecDump(const base::FilePath& file) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::FILE, FROM_HERE, - base::Bind(&CreateAecDumpFileForProcess, file, GetHandle()), - base::Bind(&RenderProcessHostImpl::SendAecDumpFileToRenderer, - weak_factory_.GetWeakPtr())); + // Enable AEC dump for each registered consumer. + for (std::vector<int>::iterator it = aec_dump_consumers_.begin(); + it != aec_dump_consumers_.end(); ++it) { + EnableAecDumpForId(file, *it); + } } void RenderProcessHostImpl::DisableAecDump() { @@ -2154,15 +2162,78 @@ void RenderProcessHostImpl::OnGpuSwitching() { } #if defined(ENABLE_WEBRTC) +void RenderProcessHostImpl::OnRegisterAecDumpConsumer(int id) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind( + &RenderProcessHostImpl::RegisterAecDumpConsumerOnUIThread, + weak_factory_.GetWeakPtr(), + id)); +} + +void RenderProcessHostImpl::OnUnregisterAecDumpConsumer(int id) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind( + &RenderProcessHostImpl::UnregisterAecDumpConsumerOnUIThread, + weak_factory_.GetWeakPtr(), + id)); +} + +void RenderProcessHostImpl::RegisterAecDumpConsumerOnUIThread(int id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + aec_dump_consumers_.push_back(id); + if (WebRTCInternals::GetInstance()->aec_dump_enabled()) { + EnableAecDumpForId(WebRTCInternals::GetInstance()->aec_dump_file_path(), + id); + } +} + +void RenderProcessHostImpl::UnregisterAecDumpConsumerOnUIThread(int id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + for (std::vector<int>::iterator it = aec_dump_consumers_.begin(); + it != aec_dump_consumers_.end(); ++it) { + if (*it == id) { + aec_dump_consumers_.erase(it); + break; + } + } +} + +#if defined(OS_WIN) +#define IntToStringType base::IntToString16 +#else +#define IntToStringType base::IntToString +#endif + +void RenderProcessHostImpl::EnableAecDumpForId(const base::FilePath& file, + int id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + base::FilePath unique_file = + file.AddExtension(IntToStringType(GetID())) + .AddExtension(IntToStringType(id)); + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::FILE, FROM_HERE, + base::Bind(&CreateAecDumpFileForProcess, unique_file, GetHandle()), + base::Bind(&RenderProcessHostImpl::SendAecDumpFileToRenderer, + weak_factory_.GetWeakPtr(), + id)); +} + +#undef IntToStringType + void RenderProcessHostImpl::SendAecDumpFileToRenderer( + int id, IPC::PlatformFileForTransit file_for_transit) { if (file_for_transit == IPC::InvalidPlatformFileForTransit()) return; - Send(new MediaStreamMsg_EnableAecDump(file_for_transit)); + Send(new AecDumpMsg_EnableAecDump(id, file_for_transit)); } void RenderProcessHostImpl::SendDisableAecDumpToRenderer() { - Send(new MediaStreamMsg_DisableAecDump()); + Send(new AecDumpMsg_DisableAecDump()); } #endif diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index 32e342d..2728626 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h @@ -324,8 +324,14 @@ class CONTENT_EXPORT RenderProcessHostImpl virtual void OnGpuSwitching() OVERRIDE; #if defined(ENABLE_WEBRTC) + void OnRegisterAecDumpConsumer(int id); + void OnUnregisterAecDumpConsumer(int id); + void RegisterAecDumpConsumerOnUIThread(int id); + void UnregisterAecDumpConsumerOnUIThread(int id); + void EnableAecDumpForId(const base::FilePath& file, int id); // Sends |file_for_transit| to the render process. - void SendAecDumpFileToRenderer(IPC::PlatformFileForTransit file_for_transit); + void SendAecDumpFileToRenderer(int id, + IPC::PlatformFileForTransit file_for_transit); void SendDisableAecDumpToRenderer(); #endif @@ -444,6 +450,9 @@ class CONTENT_EXPORT RenderProcessHostImpl scoped_refptr<P2PSocketDispatcherHost> p2p_socket_dispatcher_host_; + // Must be accessed on UI thread. + std::vector<int> aec_dump_consumers_; + WebRtcStopRtpDumpCallback stop_rtp_dump_callback_; #endif diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h index dd0f096..e18ddfe 100644 --- a/content/common/content_message_generator.h +++ b/content/common/content_message_generator.h @@ -31,6 +31,7 @@ #include "content/common/indexed_db/indexed_db_messages.h" #include "content/common/input_messages.h" #include "content/common/java_bridge_messages.h" +#include "content/common/media/aec_dump_messages.h" #include "content/common/media/audio_messages.h" // TODO(xhwang): Move this to a new ifdef block. #include "content/common/media/cdm_messages.h" diff --git a/content/common/media/aec_dump_messages.h b/content/common/media/aec_dump_messages.h new file mode 100644 index 0000000..0f98f27 --- /dev/null +++ b/content/common/media/aec_dump_messages.h @@ -0,0 +1,36 @@ +// Copyright 2014 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. + +// IPC messages for the AEC dump. +// Multiply-included message file, hence no include guard. + +#include "content/common/content_export.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_platform_file.h" + +#undef IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_EXPORT CONTENT_EXPORT +#define IPC_MESSAGE_START AecDumpMsgStart + +// Messages sent from the browser to the renderer. + +// The browser hands over a file handle to the consumer in the renderer +// identified by |id| to use for AEC dump. +IPC_MESSAGE_CONTROL2(AecDumpMsg_EnableAecDump, + int /* id */, + IPC::PlatformFileForTransit /* file_handle */) + +// Tell the renderer to disable AEC dump in all consumers. +IPC_MESSAGE_CONTROL0(AecDumpMsg_DisableAecDump) + +// Messages sent from the renderer to the browser. + +// Registers a consumer with the browser. The consumer will then get a file +// handle when the dump is enabled. +IPC_MESSAGE_CONTROL1(AecDumpMsg_RegisterAecDumpConsumer, + int /* id */) + +// Unregisters a consumer with the browser. +IPC_MESSAGE_CONTROL1(AecDumpMsg_UnregisterAecDumpConsumer, + int /* id */) diff --git a/content/common/media/media_stream_messages.h b/content/common/media/media_stream_messages.h index 0afa9ec..695776c 100644 --- a/content/common/media/media_stream_messages.h +++ b/content/common/media/media_stream_messages.h @@ -101,15 +101,6 @@ IPC_MESSAGE_CONTROL2(MediaStreamMsg_GetSourcesACK, int /* request id */, content::StreamDeviceInfoArray /* device_list */) -// The browser hands over a file handle to the renderer to use for AEC dump. -// TODO(grunell): This should not belong to media stream. Change when -// refactoring MediaStreamDependencyFactory. -IPC_MESSAGE_CONTROL1(MediaStreamMsg_EnableAecDump, - IPC::PlatformFileForTransit /* file_handle */) - -// Tell the renderer to disable AEC dump. -IPC_MESSAGE_CONTROL0(MediaStreamMsg_DisableAecDump) - // Messages sent from the renderer to the browser. // Request a new media stream. diff --git a/content/content_common.gypi b/content/content_common.gypi index 2c54e77..240c001 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -348,6 +348,7 @@ 'common/mac/font_descriptor.mm', 'common/mac/font_loader.h', 'common/mac/font_loader.mm', + 'common/media/aec_dump_messages.h', 'common/media/audio_messages.h', 'common/media/cdm_messages.h', 'common/media/cdm_messages_enums.h', diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 67dd628..444a665 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -249,6 +249,8 @@ 'renderer/java/java_bridge_dispatcher.h', 'renderer/media/active_loader.cc', 'renderer/media/active_loader.h', + 'renderer/media/aec_dump_message_filter.cc', + 'renderer/media/aec_dump_message_filter.h', 'renderer/media/android/audio_decoder_android.cc', 'renderer/media/android/audio_decoder_android.h', 'renderer/media/android/media_info_loader.cc', diff --git a/content/renderer/media/aec_dump_message_filter.cc b/content/renderer/media/aec_dump_message_filter.cc new file mode 100644 index 0000000..ed6aa4a --- /dev/null +++ b/content/renderer/media/aec_dump_message_filter.cc @@ -0,0 +1,191 @@ +// Copyright 2014 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/aec_dump_message_filter.h" + +#include "base/message_loop/message_loop_proxy.h" +#include "content/common/media/aec_dump_messages.h" +#include "content/renderer/media/webrtc_logging.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_sender.h" + +namespace { +const int kInvalidDelegateId = -1; +} + +namespace content { + +AecDumpMessageFilter* AecDumpMessageFilter::g_filter = NULL; + +AecDumpMessageFilter::AecDumpMessageFilter( + const scoped_refptr<base::MessageLoopProxy>& io_message_loop, + const scoped_refptr<base::MessageLoopProxy>& main_message_loop) + : sender_(NULL), + delegate_id_counter_(0), + io_message_loop_(io_message_loop), + main_message_loop_(main_message_loop) { + DCHECK(!g_filter); + g_filter = this; +} + +AecDumpMessageFilter::~AecDumpMessageFilter() { + DCHECK_EQ(g_filter, this); + g_filter = NULL; +} + +// static +scoped_refptr<AecDumpMessageFilter> AecDumpMessageFilter::Get() { + return g_filter; +} + +void AecDumpMessageFilter::AddDelegate( + AecDumpMessageFilter::AecDumpDelegate* delegate) { + DCHECK(main_message_loop_->BelongsToCurrentThread()); + DCHECK(delegate); + DCHECK_EQ(kInvalidDelegateId, GetIdForDelegate(delegate)); + + int id = delegate_id_counter_++; + delegates_[id] = delegate; + + io_message_loop_->PostTask( + FROM_HERE, + base::Bind( + &AecDumpMessageFilter::RegisterAecDumpConsumer, + this, + id)); +} + +void AecDumpMessageFilter::RemoveDelegate( + AecDumpMessageFilter::AecDumpDelegate* delegate) { + DCHECK(main_message_loop_->BelongsToCurrentThread()); + DCHECK(delegate); + + int id = GetIdForDelegate(delegate); + DCHECK_NE(kInvalidDelegateId, id); + delegates_.erase(id); + + io_message_loop_->PostTask( + FROM_HERE, + base::Bind( + &AecDumpMessageFilter::UnregisterAecDumpConsumer, + this, + id)); +} + +void AecDumpMessageFilter::Send(IPC::Message* message) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + if (sender_) + sender_->Send(message); + else + delete message; +} + +void AecDumpMessageFilter::RegisterAecDumpConsumer(int id) { + Send(new AecDumpMsg_RegisterAecDumpConsumer(id)); +} + +void AecDumpMessageFilter::UnregisterAecDumpConsumer(int id) { + Send(new AecDumpMsg_UnregisterAecDumpConsumer(id)); +} + +bool AecDumpMessageFilter::OnMessageReceived(const IPC::Message& message) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(AecDumpMessageFilter, message) + IPC_MESSAGE_HANDLER(AecDumpMsg_EnableAecDump, OnEnableAecDump) + IPC_MESSAGE_HANDLER(AecDumpMsg_DisableAecDump, OnDisableAecDump) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void AecDumpMessageFilter::OnFilterAdded(IPC::Sender* sender) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + sender_ = sender; +} + +void AecDumpMessageFilter::OnFilterRemoved() { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + + // Once removed, a filter will not be used again. At this time the + // observer must be notified so it releases its reference. + OnChannelClosing(); +} + +void AecDumpMessageFilter::OnChannelClosing() { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + sender_ = NULL; + main_message_loop_->PostTask( + FROM_HERE, + base::Bind( + &AecDumpMessageFilter::DoChannelClosingOnDelegates, + this)); +} + +void AecDumpMessageFilter::OnEnableAecDump( + int id, + IPC::PlatformFileForTransit file_handle) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + main_message_loop_->PostTask( + FROM_HERE, + base::Bind( + &AecDumpMessageFilter::DoEnableAecDump, + this, + id, + file_handle)); +} + +void AecDumpMessageFilter::OnDisableAecDump() { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + main_message_loop_->PostTask( + FROM_HERE, + base::Bind( + &AecDumpMessageFilter::DoDisableAecDump, + this)); +} + +void AecDumpMessageFilter::DoEnableAecDump( + int id, + IPC::PlatformFileForTransit file_handle) { + DCHECK(main_message_loop_->BelongsToCurrentThread()); + DelegateMap::iterator it = delegates_.find(id); + if (it != delegates_.end()) { + it->second->OnAecDumpFile(file_handle); + } else { + // Delegate has been removed, we must close the file. + base::File file = IPC::PlatformFileForTransitToFile(file_handle); + DCHECK(file.IsValid()); + file.Close(); + } +} + +void AecDumpMessageFilter::DoDisableAecDump() { + DCHECK(main_message_loop_->BelongsToCurrentThread()); + for (DelegateMap::iterator it = delegates_.begin(); + it != delegates_.end(); ++it) { + it->second->OnDisableAecDump(); + } +} + +void AecDumpMessageFilter::DoChannelClosingOnDelegates() { + DCHECK(main_message_loop_->BelongsToCurrentThread()); + for (DelegateMap::iterator it = delegates_.begin(); + it != delegates_.end(); ++it) { + it->second->OnIpcClosing(); + } + delegates_.clear(); +} + +int AecDumpMessageFilter::GetIdForDelegate( + AecDumpMessageFilter::AecDumpDelegate* delegate) { + DCHECK(main_message_loop_->BelongsToCurrentThread()); + for (DelegateMap::iterator it = delegates_.begin(); + it != delegates_.end(); ++it) { + if (it->second == delegate) + return it->first; + } + return kInvalidDelegateId; +} + +} // namespace content diff --git a/content/renderer/media/aec_dump_message_filter.h b/content/renderer/media/aec_dump_message_filter.h new file mode 100644 index 0000000..7268132 --- /dev/null +++ b/content/renderer/media/aec_dump_message_filter.h @@ -0,0 +1,108 @@ +// Copyright 2014 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_AEC_DUMP_MESSAGE_FILTER_H_ +#define CONTENT_RENDERER_MEDIA_AEC_DUMP_MESSAGE_FILTER_H_ + +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/renderer/render_thread_impl.h" +#include "ipc/ipc_platform_file.h" +#include "ipc/message_filter.h" + +namespace base { +class MessageLoopProxy; +} + +namespace content { + +// MessageFilter that handles AEC dump messages and forwards them to an +// observer. +class CONTENT_EXPORT AecDumpMessageFilter : public IPC::MessageFilter { + public: + class AecDumpDelegate { + public: + virtual void OnAecDumpFile( + const IPC::PlatformFileForTransit& file_handle) = 0; + virtual void OnDisableAecDump() = 0; + virtual void OnIpcClosing() = 0; + }; + + AecDumpMessageFilter( + const scoped_refptr<base::MessageLoopProxy>& io_message_loop, + const scoped_refptr<base::MessageLoopProxy>& main_message_loop); + + // Getter for the one AecDumpMessageFilter object. + static scoped_refptr<AecDumpMessageFilter> Get(); + + // Adds a delegate that receives the enable and disable notifications. + void AddDelegate(AecDumpMessageFilter::AecDumpDelegate* delegate); + + // Removes a delegate. + void RemoveDelegate(AecDumpMessageFilter::AecDumpDelegate* delegate); + + // IO message loop associated with this message filter. + scoped_refptr<base::MessageLoopProxy> io_message_loop() const { + return io_message_loop_; + } + + protected: + virtual ~AecDumpMessageFilter(); + + private: + // Sends an IPC message using |sender_|. + void Send(IPC::Message* message); + + // Registers a consumer of AEC dump in the browser process. This consumer will + // get a file handle when the AEC dump is enabled and a notification when it + // is disabled. + void RegisterAecDumpConsumer(int id); + + // Unregisters a consumer of AEC dump in the browser process. + void UnregisterAecDumpConsumer(int id); + + // IPC::MessageFilter override. Called on |io_message_loop|. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE; + virtual void OnFilterRemoved() OVERRIDE; + virtual void OnChannelClosing() OVERRIDE; + + // Accessed on |io_message_loop|. + void OnEnableAecDump(int id, IPC::PlatformFileForTransit file_handle); + void OnDisableAecDump(); + + // Accessed on |main_message_loop_|. + void DoEnableAecDump(int id, IPC::PlatformFileForTransit file_handle); + void DoDisableAecDump(); + void DoChannelClosingOnDelegates(); + int GetIdForDelegate(AecDumpMessageFilter::AecDumpDelegate* delegate); + + // Accessed on |io_message_loop_|. + IPC::Sender* sender_; + + // The delgates for this filter. Must only be accessed on + // |main_message_loop_|. + typedef std::map<int, AecDumpMessageFilter::AecDumpDelegate*> DelegateMap; + DelegateMap delegates_; + + // Counter for generating unique IDs to delegates. Accessed on + // |main_message_loop_|. + int delegate_id_counter_; + + // Message loop on which IPC calls are driven. + const scoped_refptr<base::MessageLoopProxy> io_message_loop_; + + // Main message loop. + const scoped_refptr<base::MessageLoopProxy> main_message_loop_; + + // The singleton instance for this filter. + static AecDumpMessageFilter* g_filter; + + DISALLOW_COPY_AND_ASSIGN(AecDumpMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_AEC_DUMP_MESSAGE_FILTER_H_ diff --git a/content/renderer/media/media_stream_audio_processor.cc b/content/renderer/media/media_stream_audio_processor.cc index f929b14..12e8d34 100644 --- a/content/renderer/media/media_stream_audio_processor.cc +++ b/content/renderer/media/media_stream_audio_processor.cc @@ -177,10 +177,22 @@ MediaStreamAudioProcessor::MediaStreamAudioProcessor( capture_thread_checker_.DetachFromThread(); render_thread_checker_.DetachFromThread(); InitializeAudioProcessingModule(constraints, effects); + if (IsAudioTrackProcessingEnabled()) { + aec_dump_message_filter_ = AecDumpMessageFilter::Get(); + // In unit tests not creating a message filter, |aec_dump_message_filter_| + // will be NULL. We can just ignore that. Other unit tests and browser tests + // ensure that we do get the filter when we should. + if (aec_dump_message_filter_) + aec_dump_message_filter_->AddDelegate(this); + } } MediaStreamAudioProcessor::~MediaStreamAudioProcessor() { DCHECK(main_thread_checker_.CalledOnValidThread()); + if (aec_dump_message_filter_) { + aec_dump_message_filter_->RemoveDelegate(this); + aec_dump_message_filter_ = NULL; + } StopAudioProcessing(); } @@ -238,16 +250,30 @@ const media::AudioParameters& MediaStreamAudioProcessor::OutputFormat() const { return capture_converter_->sink_parameters(); } -void MediaStreamAudioProcessor::StartAecDump(base::File aec_dump_file) { +void MediaStreamAudioProcessor::OnAecDumpFile( + const IPC::PlatformFileForTransit& file_handle) { + DCHECK(main_thread_checker_.CalledOnValidThread()); + + base::File file = IPC::PlatformFileForTransitToFile(file_handle); + DCHECK(file.IsValid()); + if (audio_processing_) - StartEchoCancellationDump(audio_processing_.get(), aec_dump_file.Pass()); + StartEchoCancellationDump(audio_processing_.get(), file.Pass()); + else + file.Close(); } -void MediaStreamAudioProcessor::StopAecDump() { +void MediaStreamAudioProcessor::OnDisableAecDump() { + DCHECK(main_thread_checker_.CalledOnValidThread()); if (audio_processing_) StopEchoCancellationDump(audio_processing_.get()); } +void MediaStreamAudioProcessor::OnIpcClosing() { + DCHECK(main_thread_checker_.CalledOnValidThread()); + aec_dump_message_filter_ = NULL; +} + void MediaStreamAudioProcessor::OnPlayoutData(media::AudioBus* audio_bus, int sample_rate, int audio_delay_milliseconds) { @@ -494,7 +520,7 @@ void MediaStreamAudioProcessor::StopAudioProcessing() { if (!audio_processing_.get()) return; - StopAecDump(); + StopEchoCancellationDump(audio_processing_.get()); if (playout_data_source_) playout_data_source_->RemovePlayoutSink(this); diff --git a/content/renderer/media/media_stream_audio_processor.h b/content/renderer/media/media_stream_audio_processor.h index b97cb395..2dddff5 100644 --- a/content/renderer/media/media_stream_audio_processor.h +++ b/content/renderer/media/media_stream_audio_processor.h @@ -11,6 +11,7 @@ #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "content/common/content_export.h" +#include "content/renderer/media/aec_dump_message_filter.h" #include "content/renderer/media/webrtc_audio_device_impl.h" #include "media/base/audio_converter.h" #include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" @@ -44,7 +45,8 @@ using webrtc::AudioProcessorInterface; // of 10 ms data chunk. class CONTENT_EXPORT MediaStreamAudioProcessor : NON_EXPORTED_BASE(public WebRtcPlayoutDataSource::Sink), - NON_EXPORTED_BASE(public AudioProcessorInterface) { + NON_EXPORTED_BASE(public AudioProcessorInterface), + NON_EXPORTED_BASE(public AecDumpMessageFilter::AecDumpDelegate) { public: // Returns false if |kDisableAudioTrackProcessing| is set to true, otherwise // returns true. @@ -96,11 +98,12 @@ class CONTENT_EXPORT MediaStreamAudioProcessor : // Accessor to check if the audio processing is enabled or not. bool has_audio_processing() const { return audio_processing_ != NULL; } - // Starts/Stops the Aec dump on the |audio_processing_|. + // AecDumpMessageFilter::AecDumpDelegate implementation. // Called on the main render thread. - // This method takes the ownership of |aec_dump_file|. - void StartAecDump(base::File aec_dump_file); - void StopAecDump(); + virtual void OnAecDumpFile( + const IPC::PlatformFileForTransit& file_handle) OVERRIDE; + virtual void OnDisableAecDump() OVERRIDE; + virtual void OnIpcClosing() OVERRIDE; protected: friend class base::RefCountedThreadSafe<MediaStreamAudioProcessor>; @@ -191,6 +194,9 @@ class CONTENT_EXPORT MediaStreamAudioProcessor : // It can be accessed by the capture audio thread and by the libjingle thread // which calls GetStats(). base::subtle::Atomic32 typing_detected_; + + // Communication with browser for AEC dump. + scoped_refptr<AecDumpMessageFilter> aec_dump_message_filter_; }; } // namespace content diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc index a9652e7..f2f7f1d1 100644 --- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc +++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc @@ -172,6 +172,8 @@ PeerConnectionDependencyFactory::PeerConnectionDependencyFactory( PeerConnectionDependencyFactory::~PeerConnectionDependencyFactory() { CleanupPeerConnectionFactory(); + if (aec_dump_message_filter_) + aec_dump_message_filter_->RemoveDelegate(this); } blink::WebRTCPeerConnectionHandler* @@ -347,9 +349,16 @@ void PeerConnectionDependencyFactory::CreatePeerConnectionFactory() { cmd_line->HasSwitch(switches::kDisableWebRtcEncryption); pc_factory_->SetOptions(factory_options); - // |aec_dump_file| will be invalid when dump is not enabled. - if (aec_dump_file_.IsValid()) - StartAecDump(aec_dump_file_.Pass()); + // TODO(xians): Remove the following code after kDisableAudioTrackProcessing + // is removed. + if (!MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled()) { + aec_dump_message_filter_ = AecDumpMessageFilter::Get(); + // In unit tests not creating a message filter, |aec_dump_message_filter_| + // will be NULL. We can just ignore that. Other unit tests and browser tests + // ensure that we do get the filter when we should. + if (aec_dump_message_filter_) + aec_dump_message_filter_->AddDelegate(this); + } } bool PeerConnectionDependencyFactory::PeerConnectionFactoryCreated() { @@ -612,56 +621,30 @@ PeerConnectionDependencyFactory::GetWebRtcWorkerThread() const { return chrome_worker_thread_.message_loop_proxy(); } -bool PeerConnectionDependencyFactory::OnControlMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PeerConnectionDependencyFactory, message) - IPC_MESSAGE_HANDLER(MediaStreamMsg_EnableAecDump, OnAecDumpFile) - IPC_MESSAGE_HANDLER(MediaStreamMsg_DisableAecDump, OnDisableAecDump) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - void PeerConnectionDependencyFactory::OnAecDumpFile( - IPC::PlatformFileForTransit file_handle) { - DCHECK(!aec_dump_file_.IsValid()); + const IPC::PlatformFileForTransit& file_handle) { + DCHECK(CalledOnValidThread()); + DCHECK(!MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled()); + DCHECK(PeerConnectionFactoryCreated()); + base::File file = IPC::PlatformFileForTransitToFile(file_handle); DCHECK(file.IsValid()); - if (MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled()) { - EnsureWebRtcAudioDeviceImpl(); - GetWebRtcAudioDevice()->EnableAecDump(file.Pass()); - return; - } - - // TODO(xians): Remove the following code after kDisableAudioTrackProcessing - // is removed. - if (PeerConnectionFactoryCreated()) - StartAecDump(file.Pass()); - else - aec_dump_file_ = file.Pass(); + // |pc_factory_| always takes ownership of |aec_dump_file|. If StartAecDump() + // fails, |aec_dump_file| will be closed. + if (!GetPcFactory()->StartAecDump(file.TakePlatformFile())) + VLOG(1) << "Could not start AEC dump."; } void PeerConnectionDependencyFactory::OnDisableAecDump() { - if (MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled()) { - // Do nothing if OnAecDumpFile() has never been called. - if (GetWebRtcAudioDevice()) - GetWebRtcAudioDevice()->DisableAecDump(); - return; - } - - // TODO(xians): Remove the following code after kDisableAudioTrackProcessing - // is removed. - if (aec_dump_file_.IsValid()) - aec_dump_file_.Close(); + DCHECK(CalledOnValidThread()); + DCHECK(!MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled()); + // Do nothing. We never disable AEC dump for non-track-processing case. } -void PeerConnectionDependencyFactory::StartAecDump(base::File aec_dump_file) { - // |pc_factory_| always takes ownership of |aec_dump_file|. If StartAecDump() - // fails, |aec_dump_file| will be closed. - if (!GetPcFactory()->StartAecDump(aec_dump_file.TakePlatformFile())) - VLOG(1) << "Could not start AEC dump."; +void PeerConnectionDependencyFactory::OnIpcClosing() { + DCHECK(CalledOnValidThread()); + aec_dump_message_filter_ = NULL; } void PeerConnectionDependencyFactory::EnsureWebRtcAudioDeviceImpl() { diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.h b/content/renderer/media/webrtc/peer_connection_dependency_factory.h index 969fb6b..5169935 100644 --- a/content/renderer/media/webrtc/peer_connection_dependency_factory.h +++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.h @@ -12,6 +12,7 @@ #include "base/threading/thread.h" #include "content/common/content_export.h" #include "content/public/renderer/render_process_observer.h" +#include "content/renderer/media/aec_dump_message_filter.h" #include "content/renderer/p2p/socket_dispatcher.h" #include "ipc/ipc_platform_file.h" #include "third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h" @@ -55,7 +56,7 @@ struct StreamDeviceInfo; // Object factory for RTC PeerConnections. class CONTENT_EXPORT PeerConnectionDependencyFactory : NON_EXPORTED_BASE(public base::NonThreadSafe), - public RenderProcessObserver { + NON_EXPORTED_BASE(public AecDumpMessageFilter::AecDumpDelegate) { public: PeerConnectionDependencyFactory( P2PSocketDispatcher* p2p_socket_dispatcher); @@ -129,6 +130,14 @@ class CONTENT_EXPORT PeerConnectionDependencyFactory scoped_refptr<base::MessageLoopProxy> GetWebRtcWorkerThread() const; + // AecDumpMessageFilter::AecDumpDelegate implementation. + // TODO(xians): Remove when option to disable audio track processing is + // removed. + virtual void OnAecDumpFile( + const IPC::PlatformFileForTransit& file_handle) OVERRIDE; + virtual void OnDisableAecDump() OVERRIDE; + virtual void OnIpcClosing() OVERRIDE; + protected: // Asks the PeerConnection factory to create a Local Audio Source. virtual scoped_refptr<webrtc::AudioSourceInterface> @@ -177,14 +186,6 @@ class CONTENT_EXPORT PeerConnectionDependencyFactory void DeleteIpcNetworkManager(); void CleanupPeerConnectionFactory(); - // RenderProcessObserver implementation. - virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE; - - void OnAecDumpFile(IPC::PlatformFileForTransit file_handle); - void OnDisableAecDump(); - - void StartAecDump(base::File aec_dump_file); - // Helper method to create a WebRtcAudioDeviceImpl. void EnsureWebRtcAudioDeviceImpl(); @@ -198,14 +199,17 @@ class CONTENT_EXPORT PeerConnectionDependencyFactory scoped_refptr<P2PSocketDispatcher> p2p_socket_dispatcher_; scoped_refptr<WebRtcAudioDeviceImpl> audio_device_; + // This is only used if audio track processing is disabled. + // TODO(xians): Remove when option to disable audio track processing is + // removed. + scoped_refptr<AecDumpMessageFilter> aec_dump_message_filter_; + // PeerConnection threads. signaling_thread_ is created from the // "current" chrome thread. talk_base::Thread* signaling_thread_; talk_base::Thread* worker_thread_; base::Thread chrome_worker_thread_; - base::File aec_dump_file_; - DISALLOW_COPY_AND_ASSIGN(PeerConnectionDependencyFactory); }; diff --git a/content/renderer/media/webrtc_audio_capturer.cc b/content/renderer/media/webrtc_audio_capturer.cc index 2ff0d43..6212a8e 100644 --- a/content/renderer/media/webrtc_audio_capturer.cc +++ b/content/renderer/media/webrtc_audio_capturer.cc @@ -417,9 +417,6 @@ void WebRtcAudioCapturer::Stop() { if (audio_device_) audio_device_->RemoveAudioCapturer(this); - // Stop the Aec dump. - StopAecDump(); - for (TrackList::ItemList::const_iterator it = tracks.begin(); it != tracks.end(); ++it) { @@ -615,15 +612,4 @@ void WebRtcAudioCapturer::SetCapturerSourceForTesting( static_cast<float>(params.sample_rate())); } -void WebRtcAudioCapturer::StartAecDump(base::File aec_dump_file) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(aec_dump_file.IsValid()); - audio_processor_->StartAecDump(aec_dump_file.Pass()); -} - -void WebRtcAudioCapturer::StopAecDump() { - DCHECK(thread_checker_.CalledOnValidThread()); - audio_processor_->StopAecDump(); -} - } // namespace content diff --git a/content/renderer/media/webrtc_audio_capturer.h b/content/renderer/media/webrtc_audio_capturer.h index f433a1a..89ac7a9 100644 --- a/content/renderer/media/webrtc_audio_capturer.h +++ b/content/renderer/media/webrtc_audio_capturer.h @@ -117,9 +117,6 @@ class CONTENT_EXPORT WebRtcAudioCapturer const scoped_refptr<media::AudioCapturerSource>& source, media::AudioParameters params); - void StartAecDump(base::File aec_dump_file); - void StopAecDump(); - protected: friend class base::RefCountedThreadSafe<WebRtcAudioCapturer>; virtual ~WebRtcAudioCapturer(); diff --git a/content/renderer/media/webrtc_audio_device_impl.cc b/content/renderer/media/webrtc_audio_device_impl.cc index 293ff36..1339a6f 100644 --- a/content/renderer/media/webrtc_audio_device_impl.cc +++ b/content/renderer/media/webrtc_audio_device_impl.cc @@ -249,8 +249,6 @@ int32_t WebRtcAudioDeviceImpl::Terminate() { DCHECK(!renderer_.get() || !renderer_->IsStarted()) << "The shared audio renderer shouldn't be running"; - DisableAecDump(); - // Stop all the capturers to ensure no further OnData() and // RemoveAudioCapturer() callback. // Cache the capturers in a local list since WebRtcAudioCapturer::Stop() @@ -477,11 +475,6 @@ void WebRtcAudioDeviceImpl::AddAudioCapturer( capturers_.end()); capturers_.push_back(capturer); } - - // Start the Aec dump if the Aec dump has been enabled and has not been - // started. - if (aec_dump_file_.IsValid()) - MaybeStartAecDump(); } void WebRtcAudioDeviceImpl::RemoveAudioCapturer( @@ -533,48 +526,4 @@ bool WebRtcAudioDeviceImpl::GetAuthorizedDeviceInfoForAudioRenderer( session_id, output_sample_rate, output_frames_per_buffer); } -void WebRtcAudioDeviceImpl::EnableAecDump(base::File aec_dump_file) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(aec_dump_file.IsValid()); - - // Close the previous AEC dump file description if it has not been consumed. - // This can happen if no getUserMedia has been made yet. - // TODO(xians): DCHECK(!aec_dump_file_.IsValid()) after the browser - // guarantees it won't call EnableAecDump() more than once in a row. - if (aec_dump_file_.IsValid()) - aec_dump_file_.Close(); - - aec_dump_file_ = aec_dump_file.Pass(); - MaybeStartAecDump(); -} - -void WebRtcAudioDeviceImpl::DisableAecDump() { - DCHECK(thread_checker_.CalledOnValidThread()); - // Simply invalidate the |aec_dump_file_| if we have not pass the ownership - // to WebRtc. - if (aec_dump_file_.IsValid()) { - aec_dump_file_.Close(); - return; - } - - // We might have call StartAecDump() on one of the capturer. Loop - // through all the capturers and call StopAecDump() on each of them. - for (CapturerList::const_iterator iter = capturers_.begin(); - iter != capturers_.end(); ++iter) { - (*iter)->StopAecDump(); - } -} - -void WebRtcAudioDeviceImpl::MaybeStartAecDump() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(aec_dump_file_.IsValid()); - - // Start the Aec dump on the current default capturer. - scoped_refptr<WebRtcAudioCapturer> default_capturer(GetDefaultCapturer()); - if (!default_capturer) - return; - - default_capturer->StartAecDump(aec_dump_file_.Pass()); -} - } // namespace content diff --git a/content/renderer/media/webrtc_audio_device_impl.h b/content/renderer/media/webrtc_audio_device_impl.h index d182acf..e53125d 100644 --- a/content/renderer/media/webrtc_audio_device_impl.h +++ b/content/renderer/media/webrtc_audio_device_impl.h @@ -353,15 +353,6 @@ class CONTENT_EXPORT WebRtcAudioDeviceImpl return renderer_; } - // Enables the Aec dump. If the default capturer exists, it will call - // StartAecDump() on the capturer and pass the ownership of the file to - // WebRtc. Otherwise it will hold the file until a capturer is added. - void EnableAecDump(base::File aec_dump_file); - - // Disables the Aec dump. When this method is called, the ongoing Aec dump - // on WebRtc will be stopped. - void DisableAecDump(); - private: typedef std::list<scoped_refptr<WebRtcAudioCapturer> > CapturerList; typedef std::list<WebRtcPlayoutDataSource::Sink*> PlayoutDataSinkList; @@ -401,9 +392,6 @@ class CONTENT_EXPORT WebRtcAudioDeviceImpl virtual void AddPlayoutSink(WebRtcPlayoutDataSource::Sink* sink) OVERRIDE; virtual void RemovePlayoutSink(WebRtcPlayoutDataSource::Sink* sink) OVERRIDE; - // Helper to start the Aec dump if the default capturer exists. - void MaybeStartAecDump(); - // Used to DCHECK that we are called on the correct thread. base::ThreadChecker thread_checker_; @@ -452,9 +440,6 @@ class CONTENT_EXPORT WebRtcAudioDeviceImpl // It is only accessed by the audio render thread. std::vector<int16> render_buffer_; - // Used for start the Aec dump on the default capturer. - base::File aec_dump_file_; - // Flag to tell if audio processing is enabled in MediaStreamAudioProcessor. const bool is_audio_track_processing_enabled_; diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 2fcaab4..8934a31 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -74,6 +74,7 @@ #include "content/renderer/gpu/gpu_benchmarking_extension.h" #include "content/renderer/input/input_event_filter.h" #include "content/renderer/input/input_handler_manager.h" +#include "content/renderer/media/aec_dump_message_filter.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" @@ -381,9 +382,13 @@ void RenderThreadImpl::Init() { webrtc_identity_service_.reset(new WebRTCIdentityService()); + aec_dump_message_filter_ = + new AecDumpMessageFilter(GetIOMessageLoopProxy(), + message_loop()->message_loop_proxy()); + AddFilter(aec_dump_message_filter_.get()); + peer_connection_factory_.reset(new PeerConnectionDependencyFactory( p2p_socket_dispatcher_.get())); - AddObserver(peer_connection_factory_.get()); #endif // defined(ENABLE_WEBRTC) audio_input_message_filter_ = diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index 3659394..4afe3ed 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h @@ -74,6 +74,7 @@ class GrContextForWebGraphicsContext3D; namespace content { class AppCacheDispatcher; +class AecDumpMessageFilter; class AudioInputMessageFilter; class AudioMessageFilter; class AudioRendererMixerManager; @@ -489,6 +490,12 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, // Used on the render thread. scoped_ptr<VideoCaptureImplManager> vc_manager_; + // Used for communicating registering AEC dump consumers with the browser and + // receving AEC dump file handles when AEC dump is enabled. An AEC dump is + // diagnostic audio data for WebRTC stored locally when enabled by the user in + // chrome://webrtc-internals. + scoped_refptr<AecDumpMessageFilter> aec_dump_message_filter_; + // The count of RenderWidgets running through this thread. int widget_count_; diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index 74d7463..8d813a2 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h @@ -106,6 +106,7 @@ enum IPCMessageStart { GinJavaBridgeMsgStart, BatteryStatusMsgStart, UtilityPrintingMsgStart, + AecDumpMsgStart, LastIPCMsgStart // Must come last. }; |