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
13namespace content {
14
15namespace {
16
17base::LazyInstance<AudioMirroringManager>::Leaky g_audio_mirroring_manager =
18    LAZY_INSTANCE_INITIALIZER;
19
20}  // namespace
21
22// static
23AudioMirroringManager* AudioMirroringManager::GetInstance() {
24  return g_audio_mirroring_manager.Pointer();
25}
26
27AudioMirroringManager::AudioMirroringManager() {
28  // Only *after* construction, check that AudioMirroringManager is being
29  // invoked on the same single thread.
30  thread_checker_.DetachFromThread();
31}
32
33AudioMirroringManager::~AudioMirroringManager() {}
34
35void 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
58void 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
73void 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
102void 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
128void 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
146void 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
181void 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
206AudioMirroringManager::StreamRoutingState::StreamRoutingState(
207    const SourceFrameRef& source_frame, Diverter* stream_diverter)
208  : source_render_frame(source_frame),
209    diverter(stream_diverter),
210    destination(NULL) {}
211
212AudioMirroringManager::StreamRoutingState::~StreamRoutingState() {}
213
214}  // namespace content
215