1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/media/capture/audio_mirroring_manager.h"
6
7 #include "content/public/browser/browser_thread.h"
8
9 namespace content {
10
11 namespace {
12
13 // Debug utility to make sure methods of AudioMirroringManager are not invoked
14 // more than once in a single call stack. In release builds, this compiles to
15 // nothing and gets completely optimized out.
16 class ReentrancyGuard {
17 public:
18 #ifdef NDEBUG
ReentrancyGuard()19 ReentrancyGuard() {}
~ReentrancyGuard()20 ~ReentrancyGuard() {}
21 #else
22 ReentrancyGuard() {
23 DCHECK(!inside_a_method_);
24 inside_a_method_ = true;
25 }
26 ~ReentrancyGuard() {
27 inside_a_method_ = false;
28 }
29
30 static bool inside_a_method_; // Safe to be static, since AMM is a singleton.
31 #endif
32 };
33
34 #ifndef NDEBUG
35 bool ReentrancyGuard::inside_a_method_ = false;
36 #endif
37
38 } // namespace
39
AudioMirroringManager()40 AudioMirroringManager::AudioMirroringManager() {}
41
~AudioMirroringManager()42 AudioMirroringManager::~AudioMirroringManager() {
43 DCHECK(diverters_.empty());
44 DCHECK(sessions_.empty());
45 }
46
AddDiverter(int render_process_id,int render_view_id,Diverter * diverter)47 void AudioMirroringManager::AddDiverter(
48 int render_process_id, int render_view_id, Diverter* diverter) {
49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
50 ReentrancyGuard guard;
51 DCHECK(diverter);
52
53 // DCHECK(diverter not already in diverters_ under any key)
54 #ifndef NDEBUG
55 for (DiverterMap::const_iterator it = diverters_.begin();
56 it != diverters_.end(); ++it) {
57 DCHECK_NE(diverter, it->second);
58 }
59 #endif
60
61 // Add the diverter to the set of active diverters.
62 const Target target(render_process_id, render_view_id);
63 diverters_.insert(std::make_pair(target, diverter));
64
65 // If a mirroring session is active, start diverting the audio stream
66 // immediately.
67 SessionMap::iterator session_it = sessions_.find(target);
68 if (session_it != sessions_.end()) {
69 diverter->StartDiverting(
70 session_it->second->AddInput(diverter->GetAudioParameters()));
71 }
72 }
73
RemoveDiverter(int render_process_id,int render_view_id,Diverter * diverter)74 void AudioMirroringManager::RemoveDiverter(
75 int render_process_id, int render_view_id, Diverter* diverter) {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
77 ReentrancyGuard guard;
78
79 // Stop diverting the audio stream if a mirroring session is active.
80 const Target target(render_process_id, render_view_id);
81 SessionMap::iterator session_it = sessions_.find(target);
82 if (session_it != sessions_.end())
83 diverter->StopDiverting();
84
85 // Remove the diverter from the set of active diverters.
86 for (DiverterMap::iterator it = diverters_.lower_bound(target);
87 it != diverters_.end() && it->first == target; ++it) {
88 if (it->second == diverter) {
89 diverters_.erase(it);
90 break;
91 }
92 }
93 }
94
StartMirroring(int render_process_id,int render_view_id,MirroringDestination * destination)95 void AudioMirroringManager::StartMirroring(
96 int render_process_id, int render_view_id,
97 MirroringDestination* destination) {
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
99 ReentrancyGuard guard;
100 DCHECK(destination);
101
102 // Insert an entry into the set of active mirroring sessions. If a mirroring
103 // session is already active for |render_process_id| + |render_view_id|,
104 // replace the entry.
105 const Target target(render_process_id, render_view_id);
106 SessionMap::iterator session_it = sessions_.find(target);
107 MirroringDestination* old_destination;
108 if (session_it == sessions_.end()) {
109 old_destination = NULL;
110 sessions_.insert(std::make_pair(target, destination));
111
112 DVLOG(1) << "Start mirroring render_process_id:render_view_id="
113 << render_process_id << ':' << render_view_id
114 << " --> MirroringDestination@" << destination;
115 } else {
116 old_destination = session_it->second;
117 session_it->second = destination;
118
119 DVLOG(1) << "Switch mirroring of render_process_id:render_view_id="
120 << render_process_id << ':' << render_view_id
121 << " MirroringDestination@" << old_destination
122 << " --> MirroringDestination@" << destination;
123 }
124
125 // Divert audio streams coming from |target| to |destination|. If streams
126 // were already diverted to the |old_destination|, remove them.
127 for (DiverterMap::iterator it = diverters_.lower_bound(target);
128 it != diverters_.end() && it->first == target; ++it) {
129 Diverter* const diverter = it->second;
130 if (old_destination)
131 diverter->StopDiverting();
132 diverter->StartDiverting(
133 destination->AddInput(diverter->GetAudioParameters()));
134 }
135 }
136
StopMirroring(int render_process_id,int render_view_id,MirroringDestination * destination)137 void AudioMirroringManager::StopMirroring(
138 int render_process_id, int render_view_id,
139 MirroringDestination* destination) {
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
141 ReentrancyGuard guard;
142
143 // Stop mirroring if there is an active session *and* the destination
144 // matches.
145 const Target target(render_process_id, render_view_id);
146 SessionMap::iterator session_it = sessions_.find(target);
147 if (session_it == sessions_.end() || destination != session_it->second)
148 return;
149
150 DVLOG(1) << "Stop mirroring render_process_id:render_view_id="
151 << render_process_id << ':' << render_view_id
152 << " --> MirroringDestination@" << destination;
153
154 // Stop diverting each audio stream in the mirroring session being stopped.
155 for (DiverterMap::iterator it = diverters_.lower_bound(target);
156 it != diverters_.end() && it->first == target; ++it) {
157 it->second->StopDiverting();
158 }
159
160 // Remove the entry from the set of active mirroring sessions.
161 sessions_.erase(session_it);
162 }
163
164 } // namespace content
165