Index: content/browser/renderer_host/media/audio_mirroring_manager.cc |
diff --git a/content/browser/renderer_host/media/audio_mirroring_manager.cc b/content/browser/renderer_host/media/audio_mirroring_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ec7d9641dab0b4bfcbfb972adfc3888e82593210 |
--- /dev/null |
+++ b/content/browser/renderer_host/media/audio_mirroring_manager.cc |
@@ -0,0 +1,166 @@ |
+// Copyright (c) 2013 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/renderer_host/media/audio_mirroring_manager.h" |
+ |
+#include "content/public/browser/browser_thread.h" |
+ |
+namespace content { |
+ |
+namespace { |
+ |
+// Debug utility to make sure methods of AudioMirroringManager are not invoked |
+// more than once in a single call stack. In release builds, this compiles to |
+// nothing and gets completely optimized out. |
+class ReentrancyGuard { |
+ public: |
+#ifdef NDEBUG |
+ ReentrancyGuard() {} |
+ ~ReentrancyGuard() {} |
+#else |
+ ReentrancyGuard() { |
+ DCHECK(!inside_a_method_); |
+ inside_a_method_ = true; |
+ } |
+ ~ReentrancyGuard() { |
+ inside_a_method_ = false; |
+ } |
+ |
+ static bool inside_a_method_; // Safe to be static, since AMM is a singleton. |
+#endif |
+}; |
+ |
+#ifndef NDEBUG |
+bool ReentrancyGuard::inside_a_method_ = false; |
+#endif |
+ |
+} // namespace |
+ |
+AudioMirroringManager::AudioMirroringManager() {} |
+ |
+AudioMirroringManager::~AudioMirroringManager() { |
+ DCHECK(diverters_.empty()); |
+ DCHECK(sessions_.empty()); |
+} |
+ |
+void AudioMirroringManager::AddDiverter( |
+ int render_process_id, int render_view_id, Diverter* diverter) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ ReentrancyGuard guard; |
+ DCHECK(diverter); |
+ |
+ // DCHECK(diverter not already in diverters_ under any key) |
+#ifndef NDEBUG |
+ for (DiverterMap::const_iterator it = diverters_.begin(); |
+ it != diverters_.end(); ++it) { |
+ DCHECK_NE(diverter, it->second); |
+ } |
+#endif |
+ |
+ // Add the diverter to the set of active diverters. |
+ const Target target(render_process_id, render_view_id); |
+ diverters_.insert(std::make_pair(target, diverter)); |
+ |
+ // If a mirroring session is active, start diverting the audio stream |
+ // immediately. |
+ SessionMap::iterator session_it = sessions_.find(target); |
+ if (session_it != sessions_.end()) { |
+ diverter->StartDiverting( |
+ session_it->second->AddInput(diverter->GetAudioParameters())); |
+ } |
+} |
+ |
+void AudioMirroringManager::RemoveDiverter( |
+ int render_process_id, int render_view_id, Diverter* diverter) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ ReentrancyGuard guard; |
+ |
+ // Stop diverting the audio stream if a mirroring session is active. |
+ const Target target(render_process_id, render_view_id); |
+ SessionMap::iterator session_it = sessions_.find(target); |
+ if (session_it != sessions_.end()) |
+ diverter->StopDiverting(); |
+ |
+ // Remove the diverter from the set of active diverters. |
+ for (DiverterMap::iterator it = diverters_.lower_bound(target); |
+ it != diverters_.end() && it->first == target; ++it) { |
+ if (it->second == diverter) { |
+ diverters_.erase(it); |
+ break; |
+ } |
+ } |
+} |
+ |
+void AudioMirroringManager::StartMirroring( |
+ int render_process_id, int render_view_id, |
+ MirroringDestination* destination) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ ReentrancyGuard guard; |
+ DCHECK(destination); |
+ |
+ // Insert an entry into the set of active mirroring sessions. If a mirroring |
+ // session is already active for |render_process_id| + |render_view_id|, |
+ // replace the entry. |
+ const Target target(render_process_id, render_view_id); |
+ SessionMap::iterator session_it = sessions_.find(target); |
+ MirroringDestination* old_destination; |
+ if (session_it == sessions_.end()) { |
+ old_destination = NULL; |
+ sessions_.insert(std::make_pair(target, destination)); |
+ |
+ DVLOG(1) << "Start mirroring render_process_id:render_view_id=" |
+ << render_process_id << ':' << render_view_id |
+ << " --> MirroringDestination@" << destination; |
+ } else { |
+ old_destination = session_it->second; |
+ session_it->second = destination; |
+ |
+ DVLOG(1) << "Switch mirroring of render_process_id:render_view_id=" |
+ << render_process_id << ':' << render_view_id |
+ << " MirroringDestination@" << old_destination |
+ << " --> MirroringDestination@" << destination; |
+ } |
+ |
+ // Divert audio streams coming from |target| to |destination|. If streams |
+ // were already diverted to the |old_destination|, remove them. |
+ for (DiverterMap::iterator it = diverters_.lower_bound(target); |
+ it != diverters_.end() && it->first == target; ++it) { |
+ Diverter* const diverter = it->second; |
+ if (old_destination) |
+ diverter->StopDiverting(); |
+ diverter->StartDiverting( |
+ destination->AddInput(diverter->GetAudioParameters())); |
+ } |
+} |
+ |
+void AudioMirroringManager::StopMirroring( |
+ int render_process_id, int render_view_id, |
+ MirroringDestination* destination) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ ReentrancyGuard guard; |
+ |
+ // Stop mirroring if there is an active session *and* the destination |
+ // matches. |
+ const Target target(render_process_id, render_view_id); |
+ SessionMap::iterator session_it = sessions_.find(target); |
+ if (session_it == sessions_.end() || destination != session_it->second) |
+ return; |
+ |
+ DVLOG(1) << "Stop mirroring render_process_id:render_view_id=" |
+ << render_process_id << ':' << render_view_id |
+ << " --> MirroringDestination@" << destination; |
+ |
+ // Stop diverting each audio stream in the mirroring session being stopped. |
+ for (DiverterMap::iterator it = diverters_.lower_bound(target); |
+ it != diverters_.end() && it->first == target; ++it) { |
+ it->second->StopDiverting(); |
+ } |
+ |
+ // Remove the entry from the set of active mirroring sessions. |
+ sessions_.erase(session_it); |
+} |
+ |
+AudioMirroringManager::MirroringDestination::~MirroringDestination() {} |
+ |
+} // namespace content |