Commit 87b17ae6 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Audio Focus] Add no enforce flag

Adds an audio focus flag that enables audio focus manager but
disables enforcement of a single media session. This gives us
the option of enabling audio focus tracking without the user
facing change of enforcing a single media session.

BUG=875004

Change-Id: I5415ec76db7b22d4d91ea98c92e3f1515d9df97b
Reviewed-on: https://chromium-review.googlesource.com/c/1231915
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596446}
parent f6978cc5
......@@ -801,6 +801,9 @@ const FeatureEntry::Choice kEnableAudioFocusChoices[] = {
media_session::switches::kEnableAudioFocus,
media_session::switches::kEnableAudioFocusDuckFlash},
#endif // BUILDFLAG(ENABLE_PLUGINS)
{flag_descriptions::kEnableAudioFocusEnabledNoEnforce,
media_session::switches::kEnableAudioFocus,
media_session::switches::kEnableAudioFocusNoEnforce},
};
#endif // !defined(OS_ANDROID)
......
......@@ -2733,6 +2733,8 @@ const char kEnableAudioFocusEnabled[] = "Enabled";
const char kEnableAudioFocusEnabledDuckFlash[] =
"Enabled (Flash lowers volume when interrupted by other sound, "
"experimental)";
const char kEnableAudioFocusEnabledNoEnforce[] =
"Enabled (without enforcement of a single active media session)";
const char kInfiniteSessionRestoreName[] = "Infinite Session Restore";
const char kInfiniteSessionRestoreDescription[] =
......
......@@ -1655,6 +1655,7 @@ extern const char kEnableAudioFocusDescription[];
extern const char kEnableAudioFocusDisabled[];
extern const char kEnableAudioFocusEnabled[];
extern const char kEnableAudioFocusEnabledDuckFlash[];
extern const char kEnableAudioFocusEnabledNoEnforce[];
extern const char kEnableNewAppMenuIconName[];
extern const char kEnableNewAppMenuIconDescription[];
......
......@@ -20,6 +20,7 @@ source_set("lib") {
deps = [
"//mojo/public/cpp/bindings",
"//services/media_session/public/cpp",
"//services/media_session/public/mojom",
]
......@@ -45,6 +46,7 @@ source_set("tests") {
":lib",
"//base",
"//base/test:test_support",
"//services/media_session/public/cpp",
"//services/media_session/public/cpp/test:test_support",
"//services/media_session/public/mojom",
"//services/service_manager/public/cpp/test:test_support",
......
......@@ -10,6 +10,7 @@
#include "base/atomic_sequence_num.h"
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/media_session/public/cpp/switches.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
namespace media_session {
......@@ -187,24 +188,8 @@ void AudioFocusManager::AbandonAudioFocusInternal(RequestId id) {
return;
}
// Allow the top-most MediaSession having force duck to unduck even if
// it is not active.
for (auto iter = audio_focus_stack_.rbegin();
iter != audio_focus_stack_.rend(); ++iter) {
if (!(*iter)->info()->force_duck)
continue;
auto duck_row = std::move(*iter);
duck_row->session()->StopDucking();
audio_focus_stack_.erase(std::next(iter).base());
audio_focus_stack_.push_back(std::move(duck_row));
return;
}
// Only try to unduck the new MediaSession on top. The session might be
// still inactive but it will not be resumed (so it doesn't surprise the
// user).
audio_focus_stack_.back()->session()->StopDucking();
if (IsAudioFocusEnforcementEnabled())
EnforceAudioFocusAbandon();
// Notify observers that we lost audio focus.
observers_.ForAllPtrs([&row](mojom::AudioFocusObserver* observer) {
......@@ -240,6 +225,32 @@ void AudioFocusManager::RequestAudioFocusInternal(
std::unique_ptr<StackRow> row,
mojom::AudioFocusType type,
base::OnceCallback<void()> callback) {
// If audio focus is enabled then we should enforce this request and make sure
// the new active session is not ducking.
if (IsAudioFocusEnforcementEnabled()) {
EnforceAudioFocusRequest(type);
row->session()->StopDucking();
}
row->SetAudioFocusType(type);
audio_focus_stack_.push_back(std::move(row));
// Notify observers that we were gained audio focus.
mojom::MediaSessionInfoPtr session_info =
audio_focus_stack_.back()->info().Clone();
observers_.ForAllPtrs(
[&session_info, type](mojom::AudioFocusObserver* observer) {
observer->OnFocusGained(session_info.Clone(), type);
});
// We always grant the audio focus request but this may not always be the case
// in the future.
std::move(callback).Run();
}
void AudioFocusManager::EnforceAudioFocusRequest(mojom::AudioFocusType type) {
DCHECK(IsAudioFocusEnforcementEnabled());
if (type == mojom::AudioFocusType::kGainTransientMayDuck) {
for (auto& old_session : audio_focus_stack_)
old_session->session()->StartDucking();
......@@ -255,22 +266,29 @@ void AudioFocusManager::RequestAudioFocusInternal(
}
}
}
}
row->SetAudioFocusType(type);
row->session()->StopDucking();
audio_focus_stack_.push_back(std::move(row));
void AudioFocusManager::EnforceAudioFocusAbandon() {
DCHECK(IsAudioFocusEnforcementEnabled());
// Notify observers that we were gained audio focus.
mojom::MediaSessionInfoPtr session_info =
audio_focus_stack_.back()->info().Clone();
observers_.ForAllPtrs(
[&session_info, type](mojom::AudioFocusObserver* observer) {
observer->OnFocusGained(session_info.Clone(), type);
});
// Allow the top-most MediaSession having force duck to unduck even if
// it is not active.
for (auto iter = audio_focus_stack_.rbegin();
iter != audio_focus_stack_.rend(); ++iter) {
if (!(*iter)->info()->force_duck)
continue;
// We always grant the audio focus request but this may not always be the case
// in the future.
std::move(callback).Run();
auto duck_row = std::move(*iter);
duck_row->session()->StopDucking();
audio_focus_stack_.erase(std::next(iter).base());
audio_focus_stack_.push_back(std::move(duck_row));
return;
}
// Only try to unduck the new MediaSession on top. The session might be
// still inactive but it will not be resumed (so it doesn't surprise the
// user).
audio_focus_stack_.back()->session()->StopDucking();
}
void AudioFocusManager::ResetForTesting() {
......
......@@ -57,10 +57,13 @@ class AudioFocusManager : public mojom::AudioFocusManager,
// control its audio focus.
class StackRow;
void RequestAudioFocusInternal(std::unique_ptr<StackRow> row,
mojom::AudioFocusType type,
base::OnceCallback<void()> callback);
void AbandonAudioFocusInternal(RequestId id);
void RequestAudioFocusInternal(std::unique_ptr<StackRow>,
mojom::AudioFocusType,
base::OnceCallback<void()>);
void EnforceAudioFocusRequest(mojom::AudioFocusType);
void AbandonAudioFocusInternal(RequestId);
void EnforceAudioFocusAbandon();
// Flush for testing will flush any pending messages to the observers.
void FlushForTesting();
......
......@@ -12,13 +12,19 @@ namespace switches {
// Enable a internal audio focus management between tabs in such a way that two
// tabs can't play on top of each other.
// The allowed values are: "" (empty) or |switches::kEnableAudioFocusDuckFlash|.
// The allowed values are: "" (empty) or |kEnableAudioFocusDuckFlash|
// or |kEnableAudioFocusNoEnforce|.
const char kEnableAudioFocus[] = "enable-audio-focus";
// This value is used as an option for |kEnableAudioFocus|. Flash will
// be ducked when losing audio focus.
const char kEnableAudioFocusDuckFlash[] = "duck-flash";
// This value is used as an option for |kEnableAudioFocus|. If enabled then
// single media session audio focus will not be enforced. This should be used by
// embedders that wish to track audio focus but without the enforcement.
const char kEnableAudioFocusNoEnforce[] = "no-enforce";
#if !defined(OS_ANDROID)
// Turns on the internal media session backend. This should be used by embedders
// that want to control the media playback with the media session interfaces.
......@@ -38,6 +44,12 @@ bool IsAudioFocusDuckFlashEnabled() {
switches::kEnableAudioFocusDuckFlash;
}
bool IsAudioFocusEnforcementEnabled() {
return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kEnableAudioFocus) !=
switches::kEnableAudioFocusNoEnforce;
}
bool IsMediaSessionEnabled() {
// Media session is enabled on Android and Chrome OS to allow control of media
// players as needed.
......
......@@ -14,6 +14,8 @@ namespace switches {
COMPONENT_EXPORT(MEDIA_SESSION_CPP) extern const char kEnableAudioFocus[];
COMPONENT_EXPORT(MEDIA_SESSION_CPP)
extern const char kEnableAudioFocusDuckFlash[];
COMPONENT_EXPORT(MEDIA_SESSION_CPP)
extern const char kEnableAudioFocusNoEnforce[];
#if !defined(OS_ANDROID)
COMPONENT_EXPORT(MEDIA_SESSION_CPP)
......@@ -28,6 +30,10 @@ COMPONENT_EXPORT(MEDIA_SESSION_CPP) bool IsAudioFocusEnabled();
// audio focus duck flash should be enabled.
COMPONENT_EXPORT(MEDIA_SESSION_CPP) bool IsAudioFocusDuckFlashEnabled();
// Based on the command line of the current process, determine if
// audio focus enforcement should be enabled.
COMPONENT_EXPORT(MEDIA_SESSION_CPP) bool IsAudioFocusEnforcementEnabled();
COMPONENT_EXPORT(MEDIA_SESSION_CPP) bool IsMediaSessionEnabled();
} // namespace media_session
......
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