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:

committed by
Chromium LUCI CQ

parent
9219c23865
commit
2f389a545e
content/browser/media/session
media/base
@ -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);
|
||||
|
Reference in New Issue
Block a user