Commit 50f9789a authored by miu's avatar miu Committed by Commit bot

WebContentsAudioMuter: Mute all audio output from a WebContentsImpl.

Adds support in libcontent for muting all audio output from a
WebContentsImpl.  This is meant to be activated via user actions in
the browser tab strip UI (follow-up change pending).  To be clear, this
change introduces the functionality required to mute audio, but there is
nothing in this change to activate it.  The plan is to provide this
feature experimentally behind about:flags, and gather data to drive
later launch decisions.

Main approach: Introduce the WebContentsAudioMuter, which leverages the
existing WebContents audio capture mechanisms (AudioMirroringManager) to
divert all audio output to a "null" destination.  In other words, this
is essentially "audio capture to nowhere."

BUG=360372

Review URL: https://codereview.chromium.org/586303004

Cr-Commit-Position: refs/heads/master@{#296078}
parent a35a53a6
......@@ -74,6 +74,10 @@ class WebContentsAudioInputStream::Impl
void StartMirroring();
void StopMirroring();
// Invoked on the UI thread to make sure WebContents muting is turned off for
// successful audio capture.
void UnmuteWebContentsAudio();
// AudioMirroringManager::MirroringDestination implementation
virtual void QueryForMatches(
const std::set<SourceFrameRef>& candidates,
......@@ -174,6 +178,14 @@ void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) {
mixer_stream_->Start(callback);
StartMirroring();
// WebContents audio muting is implemented as audio capture to nowhere.
// Unmuting will stop that audio capture, allowing AudioMirroringManager to
// divert audio capture to here.
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&Impl::UnmuteWebContentsAudio, this));
}
void WebContentsAudioInputStream::Impl::Stop() {
......@@ -235,6 +247,14 @@ void WebContentsAudioInputStream::Impl::StopMirroring() {
make_scoped_refptr(this)));
}
void WebContentsAudioInputStream::Impl::UnmuteWebContentsAudio() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
WebContents* const contents = tracker_->web_contents();
if (contents)
contents->SetAudioMuted(false);
}
void WebContentsAudioInputStream::Impl::QueryForMatches(
const std::set<SourceFrameRef>& candidates,
const MatchesCallback& results_callback) {
......
// 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 "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_consumer.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)
: consumer_(media::AudioManager::Get()->GetWorkerTaskRunner(), params) {}
// AudioOutputStream implementation.
virtual bool Open() OVERRIDE { return true; }
virtual void Start(AudioSourceCallback* callback) OVERRIDE {
consumer_.Start(base::Bind(&AudioDiscarder::FetchAudioData, callback));
}
virtual void Stop() OVERRIDE { consumer_.Stop(); }
virtual void SetVolume(double volume) OVERRIDE {}
virtual void GetVolume(double* volume) OVERRIDE { *volume = 0; }
virtual void Close() OVERRIDE { delete this; }
private:
virtual ~AudioDiscarder() {}
static void FetchAudioData(AudioSourceCallback* callback,
media::AudioBus* audio_bus) {
callback->OnMoreData(audio_bus, media::AudioBuffersState());
}
// Calls FetchAudioData() at regular intervals and discards the data.
media::FakeAudioConsumer consumer_;
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;
virtual ~MuteDestination() {}
virtual 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);
}
virtual 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
// 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.
#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_MUTER_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_MUTER_H_
#include "base/memory/ref_counted.h"
namespace content {
class WebContents;
// Mutes all audio output from a WebContents. Internally, this is accomplished
// by providing a MirroringDestination implementation, similar to that found in
// WebContentsAudioInputStream for audio capture/mirroring. However, the
// WebContentsAudioMuter::MuteDestination only pumps the audio data and discards
// it.
class WebContentsAudioMuter {
public:
explicit WebContentsAudioMuter(WebContents* web_contents);
~WebContentsAudioMuter();
bool is_muting() const { return is_muting_; }
void StartMuting();
void StopMuting();
private:
// AudioMirroringManager::MirroringDestination implementation which is
// ref-counted so it remains alive as tasks referencing it are posted on both
// the UI and IO threads.
class MuteDestination;
const scoped_refptr<MuteDestination> destination_;
bool is_muting_;
DISALLOW_COPY_AND_ASSIGN(WebContentsAudioMuter);
};
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_MUTER_H_
......@@ -40,6 +40,7 @@
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/manifest/manifest_manager_host.h"
#include "content/browser/media/audio_stream_monitor.h"
#include "content/browser/media/capture/web_contents_audio_muter.h"
#include "content/browser/media/midi_dispatcher_host.h"
#include "content/browser/message_port_message_filter.h"
#include "content/browser/message_port_service.h"
......@@ -987,6 +988,30 @@ int WebContentsImpl::GetCapturerCount() const {
return capturer_count_;
}
bool WebContentsImpl::IsAudioMuted() const {
return audio_muter_.get() && audio_muter_->is_muting();
}
void WebContentsImpl::SetAudioMuted(bool mute) {
DVLOG(1) << "SetAudioMuted(mute=" << mute << "), was " << IsAudioMuted()
<< " for WebContentsImpl@" << this;
if (mute == IsAudioMuted())
return;
if (mute) {
if (!audio_muter_)
audio_muter_.reset(new WebContentsAudioMuter(this));
audio_muter_->StartMuting();
} else {
DCHECK(audio_muter_);
audio_muter_->StopMuting();
}
// Notification for UI updates in response to the changed muting state.
NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
}
bool WebContentsImpl::IsCrashed() const {
return (crashed_status_ == base::TERMINATION_STATUS_PROCESS_CRASHED ||
crashed_status_ == base::TERMINATION_STATUS_ABNORMAL_TERMINATION ||
......
......@@ -65,6 +65,7 @@ class SavePackage;
class ScreenOrientationDispatcherHost;
class SiteInstance;
class TestWebContents;
class WebContentsAudioMuter;
class WebContentsDelegate;
class WebContentsImpl;
class WebContentsObserver;
......@@ -246,6 +247,8 @@ class CONTENT_EXPORT WebContentsImpl
virtual void IncrementCapturerCount(const gfx::Size& capture_size) OVERRIDE;
virtual void DecrementCapturerCount() OVERRIDE;
virtual int GetCapturerCount() const OVERRIDE;
virtual bool IsAudioMuted() const OVERRIDE;
virtual void SetAudioMuted(bool mute) OVERRIDE;
virtual bool IsCrashed() const OVERRIDE;
virtual void SetIsCrashed(base::TerminationStatus status,
int error_code) OVERRIDE;
......@@ -1241,6 +1244,9 @@ class CONTENT_EXPORT WebContentsImpl
// Monitors power levels for audio streams associated with this WebContents.
AudioStreamMonitor audio_stream_monitor_;
// Created on-demand to mute all audio output from this WebContents.
scoped_ptr<WebContentsAudioMuter> audio_muter_;
base::WeakPtrFactory<WebContentsImpl> loading_weak_factory_;
DISALLOW_COPY_AND_ASSIGN(WebContentsImpl);
......
......@@ -849,6 +849,8 @@
'browser/media/capture/video_capture_oracle.h',
'browser/media/capture/web_contents_audio_input_stream.cc',
'browser/media/capture/web_contents_audio_input_stream.h',
'browser/media/capture/web_contents_audio_muter.cc',
'browser/media/capture/web_contents_audio_muter.h',
'browser/media/capture/web_contents_capture_util.cc',
'browser/media/capture/web_contents_capture_util.h',
'browser/media/capture/web_contents_tracker.cc',
......
......@@ -306,6 +306,10 @@ class WebContents : public PageNavigator,
virtual void DecrementCapturerCount() = 0;
virtual int GetCapturerCount() const = 0;
// Indicates/Sets whether all audio output from this WebContents is muted.
virtual bool IsAudioMuted() const = 0;
virtual void SetAudioMuted(bool mute) = 0;
// Indicates whether this tab should be considered crashed. The setter will
// also notify the delegate when the flag is changed.
virtual bool IsCrashed() const = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment