blob: 516c47547bd08eb53185b47f671c2d6f1f2f4a0a [file] [log] [blame]
// Copyright 2016 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/session/audio_focus_delegate.h"
#include "base/bind.h"
#include "base/no_destructor.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "content/browser/media/session/media_session_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/system_connector.h"
#include "media/base/media_switches.h"
#include "services/media_session/public/cpp/features.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace content {
using media_session::mojom::AudioFocusType;
namespace {
const char kAudioFocusSourceName[] = "web";
base::UnguessableToken GetAudioFocusGroupId(MediaSessionImpl* session) {
// Allow the media session to override the group id.
if (session->audio_focus_group_id() != base::UnguessableToken::Null())
return session->audio_focus_group_id();
// Use a shared audio focus group id for the whole browser. This will means
// that tabs will share audio focus if the enforcement mode is set to
// kSingleGroup.
static const base::NoDestructor<base::UnguessableToken> token(
base::UnguessableToken::Create());
return *token;
}
// AudioFocusDelegateDefault is the default implementation of
// AudioFocusDelegate which only handles audio focus between WebContents.
class AudioFocusDelegateDefault : public AudioFocusDelegate {
public:
explicit AudioFocusDelegateDefault(MediaSessionImpl* media_session);
~AudioFocusDelegateDefault() override;
// AudioFocusDelegate implementation.
AudioFocusResult RequestAudioFocus(AudioFocusType audio_focus_type) override;
void AbandonAudioFocus() override;
base::Optional<media_session::mojom::AudioFocusType> GetCurrentFocusType()
const override;
void MediaSessionInfoChanged(
media_session::mojom::MediaSessionInfoPtr) override;
const base::UnguessableToken& request_id() const override {
return request_id_;
}
private:
// Finishes an async audio focus request.
void FinishAudioFocusRequest(AudioFocusType type, bool success);
// Ensures that |audio_focus_ptr_| is connected.
void EnsureServiceConnection();
// Holds the latest MediaSessionInfo for |media_session_|.
media_session::mojom::MediaSessionInfoPtr session_info_;
// Holds a pointer to the Media Session service.
media_session::mojom::AudioFocusManagerPtr audio_focus_ptr_;
// If the media session has acquired audio focus then this will contain a
// pointer to that requests AudioFocusRequestClient.
mojo::Remote<media_session::mojom::AudioFocusRequestClient>
request_client_remote_;
// Weak pointer because |this| is owned by |media_session_|.
MediaSessionImpl* media_session_;
// The last requested AudioFocusType by the associated |media_session_|.
base::Optional<AudioFocusType> audio_focus_type_;
// ID to uniquely identify the audio focus delegate.
base::UnguessableToken const request_id_ = base::UnguessableToken::Create();
};
} // anonymous namespace
AudioFocusDelegateDefault::AudioFocusDelegateDefault(
MediaSessionImpl* media_session)
: media_session_(media_session) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
AudioFocusDelegateDefault::~AudioFocusDelegateDefault() = default;
AudioFocusDelegate::AudioFocusResult
AudioFocusDelegateDefault::RequestAudioFocus(AudioFocusType audio_focus_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!base::FeatureList::IsEnabled(
media_session::features::kMediaSessionService)) {
audio_focus_type_ = audio_focus_type;
return AudioFocusDelegate::AudioFocusResult::kSuccess;
}
if (request_client_remote_.is_bound()) {
// We have an existing request so we should request an updated focus type.
request_client_remote_->RequestAudioFocus(
session_info_.Clone(), audio_focus_type,
base::BindOnce(&AudioFocusDelegateDefault::FinishAudioFocusRequest,
base::Unretained(this), audio_focus_type,
true /* success */));
} else {
EnsureServiceConnection();
// Create a mojo interface pointer to our media session.
mojo::PendingRemote<media_session::mojom::MediaSession> media_session =
media_session_->AddRemote();
audio_focus_ptr_->RequestGroupedAudioFocus(
request_id_, request_client_remote_.BindNewPipeAndPassReceiver(),
std::move(media_session), session_info_.Clone(), audio_focus_type,
GetAudioFocusGroupId(media_session_),
base::BindOnce(&AudioFocusDelegateDefault::FinishAudioFocusRequest,
base::Unretained(this), audio_focus_type));
}
// Return delayed as we make the async call to request audio focus.
return AudioFocusDelegate::AudioFocusResult::kDelayed;
}
void AudioFocusDelegateDefault::AbandonAudioFocus() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
audio_focus_type_.reset();
if (!request_client_remote_.is_bound())
return;
request_client_remote_->AbandonAudioFocus();
request_client_remote_.reset();
audio_focus_ptr_.reset();
}
base::Optional<media_session::mojom::AudioFocusType>
AudioFocusDelegateDefault::GetCurrentFocusType() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return audio_focus_type_;
}
void AudioFocusDelegateDefault::MediaSessionInfoChanged(
media_session::mojom::MediaSessionInfoPtr session_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (request_client_remote_.is_bound())
request_client_remote_->MediaSessionInfoChanged(session_info.Clone());
session_info_ = std::move(session_info);
}
void AudioFocusDelegateDefault::FinishAudioFocusRequest(AudioFocusType type,
bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(request_client_remote_.is_bound());
audio_focus_type_ = type;
media_session_->FinishSystemAudioFocusRequest(type, success);
}
void AudioFocusDelegateDefault::EnsureServiceConnection() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!base::FeatureList::IsEnabled(
media_session::features::kMediaSessionService)) {
return;
}
if (audio_focus_ptr_.is_bound() && !audio_focus_ptr_.encountered_error())
return;
audio_focus_ptr_.reset();
// Connect to the Media Session service and bind |audio_focus_ptr_| to it.
GetSystemConnector()->BindInterface(media_session::mojom::kServiceName,
mojo::MakeRequest(&audio_focus_ptr_));
// We associate all media sessions with the browser context so we can filter
// by browser context in the UI.
audio_focus_ptr_->SetSource(media_session_->GetSourceId(),
kAudioFocusSourceName);
}
// static
std::unique_ptr<AudioFocusDelegate> AudioFocusDelegate::Create(
MediaSessionImpl* media_session) {
return std::unique_ptr<AudioFocusDelegate>(
new AudioFocusDelegateDefault(media_session));
}
} // namespace content