Chromium Code Reviews| Index: content/browser/media/audio_debug_recording_impl.cc |
| diff --git a/content/browser/media/audio_debug_recording_impl.cc b/content/browser/media/audio_debug_recording_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..89f4c081297311e3a96ffa7d63d0aac35da6319c |
| --- /dev/null |
| +++ b/content/browser/media/audio_debug_recording_impl.cc |
| @@ -0,0 +1,217 @@ |
| +// Copyright 2015 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/browser/media/audio_debug_recording_impl.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/files/file.h" |
| +#include "base/logging.h" |
| +#include "base/memory/weak_ptr.h" |
| +#include "base/stl_util.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "content/browser/media/webrtc_internals.h" |
| +#include "content/browser/renderer_host/media/audio_input_renderer_host.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/render_process_host.h" |
| +#include "content/public/common/child_process_host.h" |
| +#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" |
| +#include "third_party/mojo/src/mojo/public/cpp/system/handle.h" |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +const base::FilePath::CharType kAudioDebugRecordingFileNameAddition[] = |
|
Henrik Grunell
2015/10/15 12:17:03
kAecDumpFileNameAddition
Anand Mistry (off Chromium)
2015/10/21 03:58:23
Done.
|
| + FILE_PATH_LITERAL("aec_dump"); |
| + |
| +// Creates a file used for diagnostic echo canceller recordings for handing |
| +// over to the renderer. |
| +mojo::ScopedHandle CreateAudioDebugRecordingFile(base::FilePath file_path) { |
|
Henrik Grunell
2015/10/15 12:17:03
CreateAecDumpFile()
Anand Mistry (off Chromium)
2015/10/21 03:58:23
Done.
|
| + DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| + base::File dump_file(file_path, |
| + base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND); |
| + if (!dump_file.IsValid()) { |
| + VLOG(1) << "Could not open AEC dump file, error=" |
| + << dump_file.error_details(); |
| + return mojo::ScopedHandle(); |
| + } |
| + |
| + mojo::embedder::PlatformHandle platform_handle_wrapper( |
| + dump_file.TakePlatformFile()); |
| + mojo::embedder::ScopedPlatformHandle scoped_platform_handle( |
| + platform_handle_wrapper); |
| + MojoHandle mojo_handle = MOJO_HANDLE_INVALID; |
| + MojoResult create_result = mojo::embedder::CreatePlatformHandleWrapper( |
| + scoped_platform_handle.Pass(), &mojo_handle); |
| + if (create_result != MOJO_RESULT_OK) |
| + return mojo::ScopedHandle(); |
| + |
| + return MakeScopedHandle(mojo::Handle(mojo_handle)); |
| +} |
| + |
| +// Does nothing. Just to avoid races between enable and disable. |
| +void DisableAudioDebugRecordingOnFileThread() { |
|
Henrik Grunell
2015/10/15 12:17:02
Would be great if you could remove this and replac
Anand Mistry (off Chromium)
2015/10/21 03:58:23
Done.
|
| + DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| +} |
| + |
| +} // namespace |
| + |
| +class AudioDebugRecordingImpl::DumpObserver { |
| + public: |
| + DumpObserver(AudioDebugRecordingImpl* dump_impl, |
| + int32_t id, |
| + AudioDebugRecordingObserverPtr service) |
| + : dump_impl_(dump_impl), |
| + id_(id), |
| + observer_service_(service.Pass()), |
| + weak_factory_(this) { |
| + observer_service_.set_connection_error_handler( |
| + base::Bind(&DumpObserver::OnConnectionError, base::Unretained(this))); |
| + } |
| + |
| + void Enable(const base::FilePath& file); |
| + |
| + void Disable() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + // Posting on the FILE thread and then replying back on the UI thread is |
| + // only for avoiding races between enable and disable. Nothing is done on |
| + // the FILE thread. |
| + BrowserThread::PostTaskAndReply( |
| + BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&DisableAudioDebugRecordingOnFileThread), |
| + base::Bind(&DumpObserver::SendDisable, weak_factory_.GetWeakPtr())); |
| + } |
| + |
| + private: |
| + void OnConnectionError() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + // Deletes |this|. |
| + dump_impl_->RemoveObserver(this); |
| + } |
| + |
| + void SendEnable(mojo::ScopedHandle handle) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + if (!handle.is_valid()) |
| + return; |
| + observer_service_->Enable(handle.Pass()); |
| + } |
| + |
| + void SendDisable() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + observer_service_->Disable(); |
| + } |
| + |
| + AudioDebugRecordingImpl* dump_impl_; |
|
Henrik Grunell
2015/10/15 12:17:02
Rename to audio_debug_recording_impl_
Anand Mistry (off Chromium)
2015/10/21 03:58:23
Done.
|
| + const int32_t id_; |
| + AudioDebugRecordingObserverPtr observer_service_; |
| + base::ThreadChecker thread_checker_; |
| + |
| + base::WeakPtrFactory<DumpObserver> weak_factory_; |
| +}; |
| + |
| +#if defined(OS_WIN) |
| +#define IntToStringType base::IntToString16 |
| +#else |
| +#define IntToStringType base::IntToString |
| +#endif |
| + |
| +void AudioDebugRecordingImpl::DumpObserver::Enable(const base::FilePath& file) { |
|
Henrik Grunell
2015/10/15 12:17:03
You might as well put the definition with the decl
Anand Mistry (off Chromium)
2015/10/21 03:58:23
Done.
|
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + RenderProcessHost* rph = |
| + RenderProcessHost::FromID(dump_impl_->render_host_id_); |
| + |
| + // Process is dead. We're likely to receive a connection error soon. |
| + if (!rph) |
| + return; |
| + |
| + base::FilePath unique_file = |
| + file.AddExtension(IntToStringType(base::GetProcId(rph->GetHandle()))) |
| + .AddExtension(kAudioDebugRecordingFileNameAddition) |
| + .AddExtension(IntToStringType(id_)); |
| + BrowserThread::PostTaskAndReplyWithResult( |
| + BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&CreateAudioDebugRecordingFile, unique_file), |
| + base::Bind(&DumpObserver::SendEnable, weak_factory_.GetWeakPtr())); |
| +} |
| + |
| +#undef IntToStringType |
| + |
| +AudioDebugRecordingImpl::AudioDebugRecordingImpl( |
| + int render_host_id, |
| + const scoped_refptr<AudioInputRendererHost>& audio_input_renderer_host, |
| + const DisconnectCallback& disconnect_cb, |
| + mojo::InterfaceRequest<AudioDebugRecording> request) |
| + : render_host_id_(render_host_id), |
| + audio_input_renderer_host_(audio_input_renderer_host), |
| + disconnect_cb_(disconnect_cb), |
| + binding_(this) { |
| + DCHECK_NE(render_host_id_, ChildProcessHost::kInvalidUniqueID); |
| + DCHECK(!disconnect_cb_.is_null()); |
| + binding_.Bind(request.Pass()); |
| + binding_.set_connection_error_handler(base::Bind( |
| + &AudioDebugRecordingImpl::OnConnectionError, base::Unretained(this))); |
| +} |
| + |
| +AudioDebugRecordingImpl::~AudioDebugRecordingImpl() { |
| + STLDeleteContainerPointers(observers_.begin(), observers_.end()); |
| +} |
| + |
| +void AudioDebugRecordingImpl::EnableAudioDebugRecording( |
| + const base::FilePath& file) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + for (auto& observer : observers_) |
|
Henrik Grunell
2015/10/15 12:17:02
Add comment that this enables AEC dump.
Anand Mistry (off Chromium)
2015/10/21 03:58:23
Done.
|
| + observer->Enable(file); |
| + |
| + // Enable mic input recording. AudioInputRendererHost is reference counted, so |
| + // its lifetime is guarantueed during the lifetime of the closure. |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&AudioInputRendererHost::EnableDebugRecording, |
| + audio_input_renderer_host_, file)); |
| +} |
| + |
| +void AudioDebugRecordingImpl::DisableAudioDebugRecording() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + for (auto& observer : observers_) |
| + observer->Disable(); |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&AudioInputRendererHost::DisableDebugRecording, |
| + audio_input_renderer_host_)); |
| +} |
| + |
| +void AudioDebugRecordingImpl::RegisterObserver( |
| + int32_t id, |
| + AudioDebugRecordingObserverPtr observer) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + DumpObserver* dump_observer = new DumpObserver(this, id, observer.Pass()); |
| + bool inserted = observers_.insert(dump_observer).second; |
| + DCHECK(inserted); |
| + |
| + if (WebRTCInternals::GetInstance()->IsAudioDebugRecordingsEnabled()) { |
| + dump_observer->Enable( |
| + WebRTCInternals::GetInstance()->GetAudioDebugRecordingsFilePath()); |
| + } |
| +} |
| + |
| +void AudioDebugRecordingImpl::OnConnectionError() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + // May delete |this|. |
| + disconnect_cb_.Run(this); |
| +} |
| + |
| +void AudioDebugRecordingImpl::RemoveObserver( |
| + AudioDebugRecordingImpl::DumpObserver* observer) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + auto it = observers_.find(observer); |
| + DCHECK(it != observers_.end()); |
| + observers_.erase(it); |
| + delete observer; |
| +} |
| + |
| +} // namespace content |