0

Defer audio focus requests until audible on android

To prevent chrome from stopping background playback due to
silent media, this change delays requesting audio focus
on android until the WebContents is audible.

Change-Id: I1de3617723092ccc7cfe0336866d5a5b171f6431
Bug: 1210462
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5258787
Reviewed-by: Tommy Steimel <steimel@chromium.org>
Reviewed-by: Thomas Guilbert <tguilbert@chromium.org>
Commit-Queue: Frank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1255854}
This commit is contained in:
Frank Liberato
2024-02-03 00:45:25 +00:00
committed by Chromium LUCI CQ
parent 9219c23865
commit 2f389a545e
4 changed files with 50 additions and 3 deletions

@ -17,7 +17,11 @@ namespace content {
AudioFocusDelegateAndroid::AudioFocusDelegateAndroid(
MediaSessionImpl* media_session)
: media_session_(media_session) {}
: media_session_(media_session) {
if (base::FeatureList::IsEnabled(media::kDeferAudioFocusUntilAudible)) {
Observe(media_session_->web_contents());
}
}
AudioFocusDelegateAndroid::~AudioFocusDelegateAndroid() {
JNIEnv* env = base::android::AttachCurrentThread();
@ -35,8 +39,19 @@ void AudioFocusDelegateAndroid::Initialize() {
AudioFocusDelegate::AudioFocusResult
AudioFocusDelegateAndroid::RequestAudioFocus(
media_session::mojom::AudioFocusType audio_focus_type) {
if (!base::FeatureList::IsEnabled(media::kRequestSystemAudioFocus))
if (!base::FeatureList::IsEnabled(media::kRequestSystemAudioFocus)) {
return AudioFocusDelegate::AudioFocusResult::kSuccess;
}
if (base::FeatureList::IsEnabled(media::kDeferAudioFocusUntilAudible) &&
audio_focus_type == media_session::mojom::AudioFocusType::kGain &&
!media_session_->web_contents()->IsCurrentlyAudible()) {
is_deferred_gain_pending_ = true;
return AudioFocusDelegate::AudioFocusResult::kDelayed;
}
// Any previously deferred gain request is no longer pending.
is_deferred_gain_pending_ = false;
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
@ -51,6 +66,7 @@ AudioFocusDelegateAndroid::RequestAudioFocus(
void AudioFocusDelegateAndroid::AbandonAudioFocus() {
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
is_deferred_gain_pending_ = false;
Java_AudioFocusDelegate_abandonAudioFocus(env, j_media_session_delegate_);
}
@ -99,6 +115,17 @@ void AudioFocusDelegateAndroid::RecordSessionDuck(
media_session_->RecordSessionDuck();
}
void AudioFocusDelegateAndroid::OnAudioStateChanged(bool is_audible) {
if (!is_deferred_gain_pending_ || !is_audible) {
return;
}
constexpr auto type = media_session::mojom::AudioFocusType::kGain;
auto result = RequestAudioFocus(type);
media_session_->FinishSystemAudioFocusRequest(
type, result != AudioFocusResult::kFailed);
}
// static
std::unique_ptr<AudioFocusDelegate> AudioFocusDelegate::Create(
MediaSessionImpl* media_session) {

@ -10,6 +10,7 @@
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
#include "content/browser/media/session/audio_focus_delegate.h"
#include "content/public/browser/web_contents_observer.h"
namespace media_session {
namespace mojom {
@ -21,7 +22,8 @@ namespace content {
// AudioFocusDelegateAndroid handles the audio focus at a system level on
// Android. It is also proxying the JNI calls.
class AudioFocusDelegateAndroid : public AudioFocusDelegate {
class AudioFocusDelegateAndroid : public AudioFocusDelegate,
public WebContentsObserver {
public:
explicit AudioFocusDelegateAndroid(MediaSessionImpl* media_session);
@ -66,10 +68,15 @@ class AudioFocusDelegateAndroid : public AudioFocusDelegate {
void MediaSessionInfoChanged(
const media_session::mojom::MediaSessionInfoPtr&) override {}
protected:
// WebContentsObserver
void OnAudioStateChanged(bool is_audible) override;
private:
// Weak pointer because |this| is owned by |media_session_|.
raw_ptr<MediaSessionImpl> media_session_;
base::android::ScopedJavaGlobalRef<jobject> j_media_session_delegate_;
bool is_deferred_gain_pending_ = false;
};
} // namespace content

@ -691,6 +691,18 @@ BASE_FEATURE(kDedicatedMediaServiceThread,
#endif
);
// Defer requesting persistent audio focus until the WebContents is audible.
// The goal is to prevent silent playback from taking audio focus from
// background apps on android, where focus is typically exclusive.
BASE_FEATURE(kDeferAudioFocusUntilAudible,
"DeferAudioFocusUntilAudible",
#if BUILDFLAG(IS_ANDROID)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
#endif
);
// Allows document picture-in-picture pages to request capture.
BASE_FEATURE(kDocumentPictureInPictureCapture,
"DocumentPictureInPictureCapture",

@ -220,6 +220,7 @@ MEDIA_EXPORT BASE_DECLARE_FEATURE(kAudioFlexibleLoopbackForSystemLoopback);
#endif
MEDIA_EXPORT BASE_DECLARE_FEATURE(kD3D11VideoDecoderUseSharedHandle);
MEDIA_EXPORT BASE_DECLARE_FEATURE(kDedicatedMediaServiceThread);
MEDIA_EXPORT BASE_DECLARE_FEATURE(kDeferAudioFocusUntilAudible);
MEDIA_EXPORT BASE_DECLARE_FEATURE(kDocumentPictureInPictureCapture);
MEDIA_EXPORT BASE_DECLARE_FEATURE(kEnableTabMuting);
MEDIA_EXPORT BASE_DECLARE_FEATURE(kExposeSwDecodersToWebRTC);