blob: 8a2bc4b0b5c69c80a656e23c99334f40d5759fbd [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
#include "content/public/browser/browser_thread.h"
namespace content {
namespace {
// Debug utility to make sure methods of AudioMirroringManager are not invoked
// more than once in a single call stack. In release builds, this compiles to
// nothing and gets completely optimized out.
class ReentrancyGuard {
public:
#ifdef NDEBUG
ReentrancyGuard() {}
~ReentrancyGuard() {}
#else
ReentrancyGuard() {
DCHECK(!inside_a_method_);
inside_a_method_ = true;
}
~ReentrancyGuard() {
inside_a_method_ = false;
}
static bool inside_a_method_; // Safe to be static, since AMM is a singleton.
#endif
};
#ifndef NDEBUG
bool ReentrancyGuard::inside_a_method_ = false;
#endif
} // namespace
AudioMirroringManager::AudioMirroringManager() {}
AudioMirroringManager::~AudioMirroringManager() {
DCHECK(diverters_.empty());
DCHECK(sessions_.empty());
}
void AudioMirroringManager::AddDiverter(
int render_process_id, int render_view_id, Diverter* diverter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ReentrancyGuard guard;
DCHECK(diverter);
// DCHECK(diverter not already in diverters_ under any key)
#ifndef NDEBUG
for (DiverterMap::const_iterator it = diverters_.begin();
it != diverters_.end(); ++it) {
DCHECK_NE(diverter, it->second);
}
#endif
// Add the diverter to the set of active diverters.
const Target target(render_process_id, render_view_id);
diverters_.insert(std::make_pair(target, diverter));
// If a mirroring session is active, start diverting the audio stream
// immediately.
SessionMap::iterator session_it = sessions_.find(target);
if (session_it != sessions_.end()) {
diverter->StartDiverting(
session_it->second->AddInput(diverter->GetAudioParameters()));
}
}
void AudioMirroringManager::RemoveDiverter(
int render_process_id, int render_view_id, Diverter* diverter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ReentrancyGuard guard;
// Stop diverting the audio stream if a mirroring session is active.
const Target target(render_process_id, render_view_id);
SessionMap::iterator session_it = sessions_.find(target);
if (session_it != sessions_.end())
diverter->StopDiverting();
// Remove the diverter from the set of active diverters.
for (DiverterMap::iterator it = diverters_.lower_bound(target);
it != diverters_.end() && it->first == target; ++it) {
if (it->second == diverter) {
diverters_.erase(it);
break;
}
}
}
void AudioMirroringManager::StartMirroring(
int render_process_id, int render_view_id,
MirroringDestination* destination) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ReentrancyGuard guard;
DCHECK(destination);
// Insert an entry into the set of active mirroring sessions. If a mirroring
// session is already active for |render_process_id| + |render_view_id|,
// replace the entry.
const Target target(render_process_id, render_view_id);
SessionMap::iterator session_it = sessions_.find(target);
MirroringDestination* old_destination;
if (session_it == sessions_.end()) {
old_destination = NULL;
sessions_.insert(std::make_pair(target, destination));
DVLOG(1) << "Start mirroring render_process_id:render_view_id="
<< render_process_id << ':' << render_view_id
<< " --> MirroringDestination@" << destination;
} else {
old_destination = session_it->second;
session_it->second = destination;
DVLOG(1) << "Switch mirroring of render_process_id:render_view_id="
<< render_process_id << ':' << render_view_id
<< " MirroringDestination@" << old_destination
<< " --> MirroringDestination@" << destination;
}
// Divert audio streams coming from |target| to |destination|. If streams
// were already diverted to the |old_destination|, remove them.
for (DiverterMap::iterator it = diverters_.lower_bound(target);
it != diverters_.end() && it->first == target; ++it) {
Diverter* const diverter = it->second;
if (old_destination)
diverter->StopDiverting();
diverter->StartDiverting(
destination->AddInput(diverter->GetAudioParameters()));
}
}
void AudioMirroringManager::StopMirroring(
int render_process_id, int render_view_id,
MirroringDestination* destination) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ReentrancyGuard guard;
// Stop mirroring if there is an active session *and* the destination
// matches.
const Target target(render_process_id, render_view_id);
SessionMap::iterator session_it = sessions_.find(target);
if (session_it == sessions_.end() || destination != session_it->second)
return;
DVLOG(1) << "Stop mirroring render_process_id:render_view_id="
<< render_process_id << ':' << render_view_id
<< " --> MirroringDestination@" << destination;
// Stop diverting each audio stream in the mirroring session being stopped.
for (DiverterMap::iterator it = diverters_.lower_bound(target);
it != diverters_.end() && it->first == target; ++it) {
it->second->StopDiverting();
}
// Remove the entry from the set of active mirroring sessions.
sessions_.erase(session_it);
}
} // namespace content