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; |