• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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