blob: 82a03fa5cf222f1af26d927de9f3ad42355d89ae [file] [log] [blame]
// Copyright 2014 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/media/capture/web_contents_audio_muter.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/audio/fake_audio_worker.h"
#include "media/base/bind_to_current_loop.h"
namespace content {
namespace {
// An AudioOutputStream that pumps audio data, but does nothing with it.
// Pumping the audio data is necessary because video playback is synchronized to
// the audio stream and will freeze otherwise.
//
// TODO(miu): media::FakeAudioOutputStream does pretty much the same thing as
// this class, but requires construction/destruction via media::AudioManagerBase
// on the audio thread. Once that's fixed, this class will no longer be needed.
// http://crbug.com/416278
class AudioDiscarder : public media::AudioOutputStream {
public:
explicit AudioDiscarder(const media::AudioParameters& params)
: worker_(media::AudioManager::Get()->GetWorkerTaskRunner(), params),
audio_bus_(media::AudioBus::Create(params)) {}
// AudioOutputStream implementation.
bool Open() override { return true; }
void Start(AudioSourceCallback* callback) override {
worker_.Start(base::Bind(&AudioDiscarder::FetchAudioData,
base::Unretained(this),
callback));
}
void Stop() override { worker_.Stop(); }
void SetVolume(double volume) override {}
void GetVolume(double* volume) override { *volume = 0; }
void Close() override { delete this; }
private:
~AudioDiscarder() override {}
void FetchAudioData(AudioSourceCallback* callback) {
callback->OnMoreData(audio_bus_.get(), 0, 0);
}
// Calls FetchAudioData() at regular intervals and discards the data.
media::FakeAudioWorker worker_;
scoped_ptr<media::AudioBus> audio_bus_;
DISALLOW_COPY_AND_ASSIGN(AudioDiscarder);
};
} // namespace
// A simple AudioMirroringManager::MirroringDestination implementation that
// identifies the audio streams rendered by a WebContents and provides
// AudioDiscarders to AudioMirroringManager.
class WebContentsAudioMuter::MuteDestination
: public base::RefCountedThreadSafe<MuteDestination>,
public AudioMirroringManager::MirroringDestination {
public:
explicit MuteDestination(WebContents* web_contents)
: web_contents_(web_contents) {}
private:
friend class base::RefCountedThreadSafe<MuteDestination>;
typedef AudioMirroringManager::SourceFrameRef SourceFrameRef;
~MuteDestination() override {}
void QueryForMatches(const std::set<SourceFrameRef>& candidates,
const MatchesCallback& results_callback) override {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&MuteDestination::QueryForMatchesOnUIThread,
this,
candidates,
media::BindToCurrentLoop(results_callback)));
}
void QueryForMatchesOnUIThread(const std::set<SourceFrameRef>& candidates,
const MatchesCallback& results_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::set<SourceFrameRef> matches;
// Add each ID to |matches| if it maps to a RenderFrameHost that maps to the
// WebContents being muted.
for (std::set<SourceFrameRef>::const_iterator i = candidates.begin();
i != candidates.end(); ++i) {
WebContents* const contents_containing_frame =
WebContents::FromRenderFrameHost(
RenderFrameHost::FromID(i->first, i->second));
if (contents_containing_frame == web_contents_)
matches.insert(*i);
}
results_callback.Run(matches);
}
media::AudioOutputStream* AddInput(
const media::AudioParameters& params) override {
return new AudioDiscarder(params);
}
WebContents* const web_contents_;
DISALLOW_COPY_AND_ASSIGN(MuteDestination);
};
WebContentsAudioMuter::WebContentsAudioMuter(WebContents* web_contents)
: destination_(new MuteDestination(web_contents)), is_muting_(false) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
WebContentsAudioMuter::~WebContentsAudioMuter() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
StopMuting();
}
void WebContentsAudioMuter::StartMuting() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (is_muting_)
return;
is_muting_ = true;
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&AudioMirroringManager::StartMirroring,
base::Unretained(AudioMirroringManager::GetInstance()),
destination_));
}
void WebContentsAudioMuter::StopMuting() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!is_muting_)
return;
is_muting_ = false;
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&AudioMirroringManager::StopMirroring,
base::Unretained(AudioMirroringManager::GetInstance()),
destination_));
}
} // namespace content