Home | History | Annotate | Download | only in capture
      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
     19   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 
     40 AudioMirroringManager::AudioMirroringManager() {}
     41 
     42 AudioMirroringManager::~AudioMirroringManager() {
     43   DCHECK(diverters_.empty());
     44   DCHECK(sessions_.empty());
     45 }
     46 
     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 
     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 
     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 
    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