Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(805)

Unified Diff: content/browser/renderer_host/media/audio_renderer_host.cc

Issue 11413078: Tab Audio Capture: Browser-side connect/disconnect functionality. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed WCAIS threading issue (methods called on audio thread). Addressed hclam's review comments. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/media/audio_renderer_host.cc
diff --git a/content/browser/renderer_host/media/audio_renderer_host.cc b/content/browser/renderer_host/media/audio_renderer_host.cc
index c043c2cb33317c31064f20ea215d70bfd5e4ad59..43d315d066cf05662cf881bbd4ee22461d1907ee 100644
--- a/content/browser/renderer_host/media/audio_renderer_host.cc
+++ b/content/browser/renderer_host/media/audio_renderer_host.cc
@@ -5,13 +5,16 @@
#include "content/browser/renderer_host/media/audio_renderer_host.h"
#include "base/bind.h"
+#include "base/lazy_instance.h"
#include "base/metrics/histogram.h"
#include "base/process.h"
#include "base/shared_memory.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/renderer_host/media/audio_sync_reader.h"
+#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
#include "content/common/media/audio_messages.h"
#include "content/public/browser/media_observer.h"
+#include "media/audio/audio_io.h"
#include "media/audio/shared_memory_util.h"
#include "media/base/audio_bus.h"
#include "media/base/limits.h"
@@ -30,6 +33,9 @@ struct AudioRendererHost::AudioEntry {
// The audio stream ID.
int stream_id;
+ // The routing ID of the source render view.
+ int render_view_id;
+
// Shared memory for transmission of the audio data.
base::SharedMemory shared_memory;
@@ -37,27 +43,69 @@ struct AudioRendererHost::AudioEntry {
// ownership of the reader.
scoped_ptr<media::AudioOutputController::SyncReader> reader;
+ // When non-NULL, normal audio output is being diverted for audio mirroring.
+ scoped_ptr<media::DivertedAudioOutputStream> diverted_stream;
+
// Set to true after we called Close() for the controller.
bool pending_close;
};
AudioRendererHost::AudioEntry::AudioEntry()
: stream_id(0),
+ render_view_id(MSG_ROUTING_NONE),
pending_close(false) {
}
AudioRendererHost::AudioEntry::~AudioEntry() {}
+namespace {
+
+struct GlobalLookup {
+ base::Lock lock;
+ std::map<int, AudioRendererHost*> by_process_id;
+};
+
+base::LazyInstance<GlobalLookup>::Leaky g_lookup = LAZY_INSTANCE_INITIALIZER;
+
+void RegisterAudioRendererHost(int render_process_id, AudioRendererHost* arh) {
+ GlobalLookup& lookup = g_lookup.Get();
Alpha Left Google 2012/11/26 22:59:59 Make sure RegisterARG and DeregisterARH are called
miu 2012/11/28 05:05:01 Done.
+ base::AutoLock guard(lookup.lock);
+ lookup.by_process_id.insert(std::make_pair(render_process_id, arh));
+}
+
+void DeregisterAudioRendererHost(int render_process_id) {
+ GlobalLookup& lookup = g_lookup.Get();
+ base::AutoLock guard(lookup.lock);
+ lookup.by_process_id.erase(render_process_id);
+}
+
+} // namespace
+
+// static
+scoped_refptr<AudioRendererHost> AudioRendererHost::FromRenderProcessID(
+ int render_process_id) {
+ GlobalLookup& lookup = g_lookup.Get();
+ base::AutoLock guard(lookup.lock);
+ std::map<int, AudioRendererHost*>::const_iterator it =
+ lookup.by_process_id.find(render_process_id);
+ return (it != lookup.by_process_id.end()) ? it->second : NULL;
+}
+
///////////////////////////////////////////////////////////////////////////////
// AudioRendererHost implementations.
AudioRendererHost::AudioRendererHost(
+ int render_process_id,
media::AudioManager* audio_manager, MediaObserver* media_observer)
- : audio_manager_(audio_manager),
+ : render_process_id_(render_process_id),
+ audio_manager_(audio_manager),
media_observer_(media_observer) {
+ RegisterAudioRendererHost(render_process_id_, this);
Alpha Left Google 2012/11/26 22:59:59 Call register in OnChannelConnected() which is cal
miu 2012/11/28 05:05:01 Done.
}
AudioRendererHost::~AudioRendererHost() {
DCHECK(audio_entries_.empty());
+ DCHECK(mirror_sessions_.empty());
+ DeregisterAudioRendererHost(render_process_id_);
Alpha Left Google 2012/11/26 22:59:59 Do it in OnChannelClosing().
miu 2012/11/28 05:05:01 Done.
}
void AudioRendererHost::OnChannelClosing() {
@@ -214,6 +262,75 @@ bool AudioRendererHost::OnMessageReceived(const IPC::Message& message,
return handled;
}
+void AudioRendererHost::StartMirroring(
+ int render_view_id,
+ const scoped_refptr<WebContentsAudioInputStream>& destination) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&AudioRendererHost::DoStartMirroring, this,
+ render_view_id, destination));
+}
+
+void AudioRendererHost::DoStartMirroring(
+ int render_view_id,
+ const scoped_refptr<WebContentsAudioInputStream>& destination) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(destination);
+
+ MirrorSessionMap::iterator session_it = mirror_sessions_.find(render_view_id);
+ if (session_it != mirror_sessions_.end()) {
+ DVLOG(1) << "Forcing StopMirroring(" << render_view_id
+ << ") on existing mirroring session (@" << session_it->second
+ << ").";
+ DoStopMirroring(render_view_id, session_it->second);
+ }
+
+ DVLOG(1) << "Start mirroring RenderView " << render_view_id
+ << " --> WebContentsAudioInputStream@" << destination;
+
+ mirror_sessions_.insert(std::make_pair(render_view_id, destination));
+ for (AudioEntryMap::const_iterator it = audio_entries_.begin();
+ it != audio_entries_.end(); ++it) {
+ if (it->second->render_view_id == render_view_id) {
+ it->second->diverted_stream.reset(it->second->controller->Divert());
+ destination->AddAudioOutputStream(it->second->diverted_stream.get());
+ }
+ }
+}
+
+void AudioRendererHost::StopMirroring(
+ int render_view_id,
+ const scoped_refptr<WebContentsAudioInputStream>& destination) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&AudioRendererHost::DoStopMirroring, this,
+ render_view_id, destination));
+}
+
+void AudioRendererHost::DoStopMirroring(
+ int render_view_id,
+ const scoped_refptr<WebContentsAudioInputStream>& destination) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ MirrorSessionMap::iterator session_it = mirror_sessions_.find(render_view_id);
+ if (session_it == mirror_sessions_.end() ||
+ destination != session_it->second) {
+ return;
+ }
+
+ DVLOG(1) << "Stop mirroring RenderView " << render_view_id
+ << " --> WebContentsAudioInputStream@" << destination;
+
+ for (AudioEntryMap::const_iterator it = audio_entries_.begin();
+ it != audio_entries_.end(); ++it) {
+ if (it->second->render_view_id == render_view_id) {
+ destination->RemoveAudioOutputStream(it->second->diverted_stream.get());
+ it->second->diverted_stream.reset();
+ }
+ }
+ mirror_sessions_.erase(session_it);
+}
+
void AudioRendererHost::OnCreateStream(
int stream_id, const media::AudioParameters& params, int input_channels) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -281,6 +398,46 @@ void AudioRendererHost::OnCreateStream(
media_observer_->OnSetAudioStreamStatus(this, stream_id, "created");
}
+void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id,
+ int render_view_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* const entry = LookupById(stream_id);
+ if (!entry) {
+ SendErrorMessage(stream_id);
+ return;
+ }
+
+ if (entry->render_view_id == render_view_id)
+ return; // No change.
+
+ // If this stream is currently being mirrored, remove it from that mirroring
+ // session.
+ MirrorSessionMap::const_iterator prev_session_it =
+ mirror_sessions_.find(entry->render_view_id);
+ scoped_ptr<media::DivertedAudioOutputStream> diverted_stream;
+ if (prev_session_it != mirror_sessions_.end()) {
+ prev_session_it->second->
+ RemoveAudioOutputStream(entry->diverted_stream.get());
+ diverted_stream.swap(entry->diverted_stream);
+ }
+
+ entry->render_view_id = render_view_id;
+
+ // If a mirroring session is already active for the RenderView, add this
+ // stream to that mirroring session.
+ MirrorSessionMap::const_iterator next_session_it =
+ mirror_sessions_.find(render_view_id);
+ if (next_session_it != mirror_sessions_.end()) {
+ if (diverted_stream) {
+ entry->diverted_stream.swap(diverted_stream);
+ } else {
+ entry->diverted_stream.reset(entry->controller->Divert());
+ }
+ next_session_it->second->AddAudioOutputStream(entry->diverted_stream.get());
+ }
+}
+
void AudioRendererHost::OnPlayStream(int stream_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -370,6 +527,15 @@ void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!entry->pending_close) {
+ // If this stream is currently being mirrored, remove it from the mirroring
+ // session.
+ MirrorSessionMap::const_iterator session_it =
+ mirror_sessions_.find(entry->render_view_id);
+ if (session_it != mirror_sessions_.end()) {
+ session_it->second->RemoveAudioOutputStream(entry->diverted_stream.get());
+ entry->diverted_stream.reset();
+ }
+
entry->controller->Close(
base::Bind(&AudioRendererHost::DeleteEntry, this, entry));
entry->pending_close = true;

Powered by Google App Engine
This is Rietveld 408576698