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 <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/lazy_instance.h"
     12 
     13 namespace content {
     14 
     15 namespace {
     16 
     17 base::LazyInstance<AudioMirroringManager>::Leaky g_audio_mirroring_manager =
     18     LAZY_INSTANCE_INITIALIZER;
     19 
     20 }  // namespace
     21 
     22 // static
     23 AudioMirroringManager* AudioMirroringManager::GetInstance() {
     24   return g_audio_mirroring_manager.Pointer();
     25 }
     26 
     27 AudioMirroringManager::AudioMirroringManager() {
     28   // Only *after* construction, check that AudioMirroringManager is being
     29   // invoked on the same single thread.
     30   thread_checker_.DetachFromThread();
     31 }
     32 
     33 AudioMirroringManager::~AudioMirroringManager() {}
     34 
     35 void AudioMirroringManager::AddDiverter(
     36     int render_process_id, int render_frame_id, Diverter* diverter) {
     37   DCHECK(thread_checker_.CalledOnValidThread());
     38   DCHECK(diverter);
     39 
     40   // DCHECK(diverter not already in routes_)
     41 #ifndef NDEBUG
     42   for (StreamRoutes::const_iterator it = routes_.begin();
     43        it != routes_.end(); ++it) {
     44     DCHECK_NE(diverter, it->diverter);
     45   }
     46 #endif
     47   routes_.push_back(StreamRoutingState(
     48       SourceFrameRef(render_process_id, render_frame_id),
     49       diverter));
     50 
     51   // Query existing destinations to see whether to immediately start diverting
     52   // the stream.
     53   std::set<SourceFrameRef> candidates;
     54   candidates.insert(routes_.back().source_render_frame);
     55   InitiateQueriesToFindNewDestination(NULL, candidates);
     56 }
     57 
     58 void AudioMirroringManager::RemoveDiverter(Diverter* diverter) {
     59   DCHECK(thread_checker_.CalledOnValidThread());
     60 
     61   // Find and remove the entry from the routing table.  If the stream is being
     62   // diverted, it is stopped.
     63   for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) {
     64     if (it->diverter == diverter) {
     65       ChangeRoute(&(*it), NULL);
     66       routes_.erase(it);
     67       return;
     68     }
     69   }
     70   NOTREACHED();
     71 }
     72 
     73 void AudioMirroringManager::StartMirroring(MirroringDestination* destination) {
     74   DCHECK(thread_checker_.CalledOnValidThread());
     75   DCHECK(destination);
     76 
     77   // Insert an entry into the set of active mirroring sessions, if this is a
     78   // previously-unknown destination.
     79   if (std::find(sessions_.begin(), sessions_.end(), destination) ==
     80           sessions_.end()) {
     81     sessions_.push_back(destination);
     82   }
     83 
     84   // Query the MirroringDestination to see which of the audio streams should be
     85   // diverted.
     86   std::set<SourceFrameRef> candidates;
     87   for (StreamRoutes::const_iterator it = routes_.begin(); it != routes_.end();
     88        ++it) {
     89     if (!it->destination || it->destination == destination)
     90       candidates.insert(it->source_render_frame);
     91   }
     92   if (!candidates.empty()) {
     93     destination->QueryForMatches(
     94         candidates,
     95         base::Bind(&AudioMirroringManager::UpdateRoutesToDestination,
     96                    base::Unretained(this),
     97                    destination,
     98                    false));
     99   }
    100 }
    101 
    102 void AudioMirroringManager::StopMirroring(MirroringDestination* destination) {
    103   DCHECK(thread_checker_.CalledOnValidThread());
    104 
    105   // Stop diverting each audio stream in the mirroring session being stopped.
    106   // Each stopped stream becomes a candidate to be diverted to another
    107   // destination.
    108   std::set<SourceFrameRef> redivert_candidates;
    109   for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) {
    110     if (it->destination == destination) {
    111       ChangeRoute(&(*it), NULL);
    112       redivert_candidates.insert(it->source_render_frame);
    113     }
    114   }
    115   if (!redivert_candidates.empty())
    116     InitiateQueriesToFindNewDestination(destination, redivert_candidates);
    117 
    118   // Remove the entry from the set of active mirroring sessions.
    119   const Destinations::iterator dest_it =
    120       std::find(sessions_.begin(), sessions_.end(), destination);
    121   if (dest_it == sessions_.end()) {
    122     NOTREACHED();
    123     return;
    124   }
    125   sessions_.erase(dest_it);
    126 }
    127 
    128 void AudioMirroringManager::InitiateQueriesToFindNewDestination(
    129     MirroringDestination* old_destination,
    130     const std::set<SourceFrameRef>& candidates) {
    131   DCHECK(thread_checker_.CalledOnValidThread());
    132 
    133   for (Destinations::const_iterator it = sessions_.begin();
    134        it != sessions_.end(); ++it) {
    135     if (*it != old_destination) {
    136       (*it)->QueryForMatches(
    137           candidates,
    138           base::Bind(&AudioMirroringManager::UpdateRoutesToDestination,
    139                      base::Unretained(this),
    140                      *it,
    141                      true));
    142     }
    143   }
    144 }
    145 
    146 void AudioMirroringManager::UpdateRoutesToDestination(
    147     MirroringDestination* destination,
    148     bool add_only,
    149     const std::set<SourceFrameRef>& matches) {
    150   DCHECK(thread_checker_.CalledOnValidThread());
    151 
    152   if (std::find(sessions_.begin(), sessions_.end(), destination) ==
    153           sessions_.end()) {
    154     return;  // Query result callback invoked after StopMirroring().
    155   }
    156 
    157   DVLOG(1) << (add_only ? "Add " : "Replace with ") << matches.size()
    158            << " routes to MirroringDestination@" << destination;
    159 
    160   // Start/stop diverting based on |matches|.  Any stopped stream becomes a
    161   // candidate to be diverted to another destination.
    162   std::set<SourceFrameRef> redivert_candidates;
    163   for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) {
    164     if (matches.find(it->source_render_frame) != matches.end()) {
    165       // Only change the route if the stream is not already being diverted.
    166       if (!it->destination)
    167         ChangeRoute(&(*it), destination);
    168     } else if (!add_only) {
    169       // Only stop diverting if the stream is currently routed to |destination|.
    170       if (it->destination == destination) {
    171         ChangeRoute(&(*it), NULL);
    172         redivert_candidates.insert(it->source_render_frame);
    173       }
    174     }
    175   }
    176   if (!redivert_candidates.empty())
    177     InitiateQueriesToFindNewDestination(destination, redivert_candidates);
    178 }
    179 
    180 // static
    181 void AudioMirroringManager::ChangeRoute(
    182     StreamRoutingState* route, MirroringDestination* new_destination) {
    183   if (route->destination == new_destination)
    184     return;  // No change.
    185 
    186   if (route->destination) {
    187     DVLOG(1) << "Stop diverting render_process_id:render_frame_id="
    188              << route->source_render_frame.first << ':'
    189              << route->source_render_frame.second
    190              << " --> MirroringDestination@" << route->destination;
    191     route->diverter->StopDiverting();
    192     route->destination = NULL;
    193   }
    194 
    195   if (new_destination) {
    196       DVLOG(1) << "Start diverting of render_process_id:render_frame_id="
    197                << route->source_render_frame.first << ':'
    198                << route->source_render_frame.second
    199                << " --> MirroringDestination@" << new_destination;
    200     route->diverter->StartDiverting(
    201         new_destination->AddInput(route->diverter->GetAudioParameters()));
    202     route->destination = new_destination;
    203   }
    204 }
    205 
    206 AudioMirroringManager::StreamRoutingState::StreamRoutingState(
    207     const SourceFrameRef& source_frame, Diverter* stream_diverter)
    208   : source_render_frame(source_frame),
    209     diverter(stream_diverter),
    210     destination(NULL) {}
    211 
    212 AudioMirroringManager::StreamRoutingState::~StreamRoutingState() {}
    213 
    214 }  // namespace content
    215