Commit dd132db3 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Media Session] Move StackRow out of AFM

Move StackRow out of AFM into a new AudioFocusRequest
interface.

BUG=914427

Change-Id: I52e36de5a7e5fd5a42649a55baa040ce0b46ba95
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1629430Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#663831}
parent b40f5ce9
...@@ -13,6 +13,8 @@ source_set("lib") { ...@@ -13,6 +13,8 @@ source_set("lib") {
"audio_focus_manager.h", "audio_focus_manager.h",
"audio_focus_manager_metrics_helper.cc", "audio_focus_manager_metrics_helper.cc",
"audio_focus_manager_metrics_helper.h", "audio_focus_manager_metrics_helper.h",
"audio_focus_request.cc",
"audio_focus_request.h",
"media_controller.cc", "media_controller.cc",
"media_controller.h", "media_controller.h",
"media_session_service.cc", "media_session_service.cc",
......
...@@ -28,8 +28,15 @@ namespace test { ...@@ -28,8 +28,15 @@ namespace test {
class MockMediaSession; class MockMediaSession;
} // namespace test } // namespace test
class AudioFocusRequest;
class MediaController; class MediaController;
struct EnforcementState {
bool should_duck = false;
bool should_stop = false;
bool should_suspend = false;
};
class AudioFocusManager : public mojom::AudioFocusManager, class AudioFocusManager : public mojom::AudioFocusManager,
public mojom::AudioFocusManagerDebug, public mojom::AudioFocusManagerDebug,
public mojom::MediaControllerManager { public mojom::MediaControllerManager {
...@@ -81,13 +88,10 @@ class AudioFocusManager : public mojom::AudioFocusManager, ...@@ -81,13 +88,10 @@ class AudioFocusManager : public mojom::AudioFocusManager,
private: private:
friend class AudioFocusManagerTest; friend class AudioFocusManagerTest;
friend class AudioFocusRequest;
friend class MediaControllerTest; friend class MediaControllerTest;
friend class test::MockMediaSession; friend class test::MockMediaSession;
// StackRow is an AudioFocusRequestClient and allows a media session to
// control its audio focus.
class StackRow;
// BindingContext stores associated metadata for mojo binding. // BindingContext stores associated metadata for mojo binding.
struct BindingContext { struct BindingContext {
// The source name is associated with a binding when a client calls // The source name is associated with a binding when a client calls
...@@ -97,13 +101,7 @@ class AudioFocusManager : public mojom::AudioFocusManager, ...@@ -97,13 +101,7 @@ class AudioFocusManager : public mojom::AudioFocusManager,
std::string source_name; std::string source_name;
}; };
struct EnforcementState { void RequestAudioFocusInternal(std::unique_ptr<AudioFocusRequest>,
bool should_duck = false;
bool should_stop = false;
bool should_suspend = false;
};
void RequestAudioFocusInternal(std::unique_ptr<StackRow>,
mojom::AudioFocusType); mojom::AudioFocusType);
void AbandonAudioFocusInternal(RequestId); void AbandonAudioFocusInternal(RequestId);
...@@ -111,7 +109,7 @@ class AudioFocusManager : public mojom::AudioFocusManager, ...@@ -111,7 +109,7 @@ class AudioFocusManager : public mojom::AudioFocusManager,
void MaybeUpdateActiveSession(); void MaybeUpdateActiveSession();
std::unique_ptr<StackRow> RemoveFocusEntryIfPresent(RequestId id); std::unique_ptr<AudioFocusRequest> RemoveFocusEntryIfPresent(RequestId id);
// Returns the source name of the binding currently accessing the Audio // Returns the source name of the binding currently accessing the Audio
// Focus Manager API over mojo. // Focus Manager API over mojo.
...@@ -120,12 +118,13 @@ class AudioFocusManager : public mojom::AudioFocusManager, ...@@ -120,12 +118,13 @@ class AudioFocusManager : public mojom::AudioFocusManager,
bool IsSessionOnTopOfAudioFocusStack(RequestId id, bool IsSessionOnTopOfAudioFocusStack(RequestId id,
mojom::AudioFocusType type) const; mojom::AudioFocusType type) const;
bool ShouldSessionBeSuspended(const StackRow* session, bool ShouldSessionBeSuspended(const AudioFocusRequest* session,
const EnforcementState& state) const; const EnforcementState& state) const;
bool ShouldSessionBeDucked(const StackRow* session, bool ShouldSessionBeDucked(const AudioFocusRequest* session,
const EnforcementState& state) const; const EnforcementState& state) const;
void EnforceSingleSession(StackRow* session, const EnforcementState& state); void EnforceSingleSession(AudioFocusRequest* session,
const EnforcementState& state);
// This |MediaController| acts as a proxy for controlling the active // This |MediaController| acts as a proxy for controlling the active
// |MediaSession| over mojo. // |MediaSession| over mojo.
...@@ -147,7 +146,7 @@ class AudioFocusManager : public mojom::AudioFocusManager, ...@@ -147,7 +146,7 @@ class AudioFocusManager : public mojom::AudioFocusManager,
// A stack of Mojo interface pointers and their requested audio focus type. // A stack of Mojo interface pointers and their requested audio focus type.
// A MediaSession must abandon audio focus before its destruction. // A MediaSession must abandon audio focus before its destruction.
std::list<std::unique_ptr<StackRow>> audio_focus_stack_; std::list<std::unique_ptr<AudioFocusRequest>> audio_focus_stack_;
mojom::EnforcementMode enforcement_mode_; mojom::EnforcementMode enforcement_mode_;
......
// Copyright 2018 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 "services/media_session/audio_focus_request.h"
#include "services/media_session/audio_focus_manager.h"
namespace media_session {
AudioFocusRequest::AudioFocusRequest(
AudioFocusManager* owner,
mojom::AudioFocusRequestClientRequest request,
mojom::MediaSessionPtr session,
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType audio_focus_type,
const base::UnguessableToken& id,
const std::string& source_name,
const base::UnguessableToken& group_id)
: metrics_helper_(source_name),
session_(std::move(session)),
session_info_(std::move(session_info)),
audio_focus_type_(audio_focus_type),
binding_(this, std::move(request)),
id_(id),
source_name_(source_name),
group_id_(group_id),
owner_(owner) {
// Listen for mojo errors.
binding_.set_connection_error_handler(base::BindOnce(
&AudioFocusRequest::OnConnectionError, base::Unretained(this)));
session_.set_connection_error_handler(base::BindOnce(
&AudioFocusRequest::OnConnectionError, base::Unretained(this)));
metrics_helper_.OnRequestAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::kInitial,
audio_focus_type);
}
AudioFocusRequest::~AudioFocusRequest() = default;
void AudioFocusRequest::RequestAudioFocus(
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type,
RequestAudioFocusCallback callback) {
SetSessionInfo(std::move(session_info));
if (session_info_->state == mojom::MediaSessionInfo::SessionState::kActive &&
owner_->IsSessionOnTopOfAudioFocusStack(id(), type)) {
// Return early is this session is already on top of the stack.
std::move(callback).Run();
return;
}
// Remove this AudioFocusRequest for the audio focus stack.
std::unique_ptr<AudioFocusRequest> row =
owner_->RemoveFocusEntryIfPresent(id());
DCHECK(row);
owner_->RequestAudioFocusInternal(std::move(row), type);
std::move(callback).Run();
metrics_helper_.OnRequestAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::kUpdate,
audio_focus_type_);
}
void AudioFocusRequest::AbandonAudioFocus() {
metrics_helper_.OnAbandonAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::kAPI);
owner_->AbandonAudioFocusInternal(id_);
}
void AudioFocusRequest::MediaSessionInfoChanged(
mojom::MediaSessionInfoPtr info) {
bool suspended_change =
(info->state == mojom::MediaSessionInfo::SessionState::kSuspended ||
IsSuspended()) &&
info->state != session_info_->state;
SetSessionInfo(std::move(info));
// If we have transitioned to/from a suspended state then we should
// re-enforce audio focus.
if (suspended_change)
owner_->EnforceAudioFocus();
}
bool AudioFocusRequest::IsSuspended() const {
return session_info_->state ==
mojom::MediaSessionInfo::SessionState::kSuspended;
}
mojom::AudioFocusRequestStatePtr AudioFocusRequest::ToAudioFocusRequestState()
const {
auto request = mojom::AudioFocusRequestState::New();
request->session_info = session_info_.Clone();
request->audio_focus_type = audio_focus_type_;
request->request_id = id_;
request->source_name = source_name_;
return request;
}
void AudioFocusRequest::BindToMediaController(
mojom::MediaControllerRequest request) {
if (!controller_) {
controller_ = std::make_unique<MediaController>();
controller_->SetMediaSession(this);
}
controller_->BindToInterface(std::move(request));
}
void AudioFocusRequest::Suspend(const EnforcementState& state) {
DCHECK(!session_info_->force_duck);
// In most cases if we stop or suspend we should call the ::Suspend method
// on the media session. The only exception is if the session has the
// |prefer_stop_for_gain_focus_loss| bit set in which case we should use
// ::Stop and ::Suspend respectively.
if (state.should_stop && session_info_->prefer_stop_for_gain_focus_loss) {
session_->Stop(mojom::MediaSession::SuspendType::kSystem);
} else {
was_suspended_ = was_suspended_ || state.should_suspend;
session_->Suspend(mojom::MediaSession::SuspendType::kSystem);
}
}
void AudioFocusRequest::MaybeResume() {
DCHECK(!session_info_->force_duck);
if (!was_suspended_)
return;
was_suspended_ = false;
session_->Resume(mojom::MediaSession::SuspendType::kSystem);
}
void AudioFocusRequest::SetSessionInfo(
mojom::MediaSessionInfoPtr session_info) {
bool is_controllable_changed =
session_info_->is_controllable != session_info->is_controllable;
session_info_ = std::move(session_info);
if (is_controllable_changed)
owner_->MaybeUpdateActiveSession();
}
void AudioFocusRequest::OnConnectionError() {
// Since we have multiple pathways that can call |OnConnectionError| we
// should use the |encountered_error_| bit to make sure we abandon focus
// just the first time.
if (encountered_error_)
return;
encountered_error_ = true;
metrics_helper_.OnAbandonAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::
kConnectionError);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&AudioFocusManager::AbandonAudioFocusInternal,
base::Unretained(owner_), id_));
}
} // namespace media_session
// Copyright 2019 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 SERVICES_MEDIA_SESSION_AUDIO_FOCUS_REQUEST_H_
#define SERVICES_MEDIA_SESSION_AUDIO_FOCUS_REQUEST_H_
#include "base/macros.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/media_session/audio_focus_manager_metrics_helper.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
namespace media_session {
class AudioFocusManager;
struct EnforcementState;
class MediaController;
class AudioFocusRequest : public mojom::AudioFocusRequestClient {
public:
AudioFocusRequest(AudioFocusManager* owner,
mojom::AudioFocusRequestClientRequest request,
mojom::MediaSessionPtr session,
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType audio_focus_type,
const base::UnguessableToken& id,
const std::string& source_name,
const base::UnguessableToken& group_id);
~AudioFocusRequest() override;
// mojom::AudioFocusRequestClient.
void RequestAudioFocus(mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type,
RequestAudioFocusCallback callback) override;
void AbandonAudioFocus() override;
void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) override;
// The current audio focus type that this request has.
mojom::AudioFocusType audio_focus_type() const { return audio_focus_type_; }
void set_audio_focus_type(mojom::AudioFocusType type) {
audio_focus_type_ = type;
}
// Returns whether the underyling media session is currently suspended.
bool IsSuspended() const;
// Returns the state of this audio focus request.
mojom::AudioFocusRequestStatePtr ToAudioFocusRequestState() const;
// Bind a mojo media controller to control the underlying media session.
void BindToMediaController(mojom::MediaControllerRequest request);
// Suspends the underlying media session.
void Suspend(const EnforcementState& state);
// If the underlying media session previously suspended this session then this
// will resume it.
void MaybeResume();
mojom::MediaSession* ipc() { return session_.get(); }
const mojom::MediaSessionInfoPtr& info() const { return session_info_; }
const base::UnguessableToken& id() const { return id_; }
const std::string& source_name() const { return source_name_; }
const base::UnguessableToken& group_id() const { return group_id_; }
private:
void SetSessionInfo(mojom::MediaSessionInfoPtr session_info);
void OnConnectionError();
AudioFocusManagerMetricsHelper metrics_helper_;
bool encountered_error_ = false;
bool was_suspended_ = false;
std::unique_ptr<MediaController> controller_;
mojom::MediaSessionPtr session_;
mojom::MediaSessionInfoPtr session_info_;
mojom::AudioFocusType audio_focus_type_;
mojo::Binding<mojom::AudioFocusRequestClient> binding_;
// The ID of the audio focus request.
base::UnguessableToken const id_;
// The name of the source that created this audio focus request (used for
// metrics).
std::string const source_name_;
// The group ID of the audio focus request.
base::UnguessableToken const group_id_;
// Weak pointer to the owning |AudioFocusManager| instance.
AudioFocusManager* const owner_;
DISALLOW_COPY_AND_ASSIGN(AudioFocusRequest);
};
} // namespace media_session
#endif // SERVICES_MEDIA_SESSION_AUDIO_FOCUS_REQUEST_H_
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <set> #include <set>
#include "services/media_session/audio_focus_request.h"
#include "services/media_session/public/cpp/media_image_manager.h" #include "services/media_session/public/cpp/media_image_manager.h"
namespace media_session { namespace media_session {
...@@ -51,8 +52,8 @@ class MediaController::ImageObserverHolder { ...@@ -51,8 +52,8 @@ class MediaController::ImageObserverHolder {
return; return;
} }
DCHECK(owner_->session_); DCHECK(owner_->session_->ipc());
owner_->session_->GetMediaImageBitmap( owner_->session_->ipc()->GetMediaImageBitmap(
*image, minimum_size_px_, desired_size_px_, *image, minimum_size_px_, desired_size_px_,
base::BindOnce(&MediaController::ImageObserverHolder::OnImage, base::BindOnce(&MediaController::ImageObserverHolder::OnImage,
base::Unretained(this))); base::Unretained(this)));
...@@ -92,21 +93,21 @@ void MediaController::Suspend() { ...@@ -92,21 +93,21 @@ void MediaController::Suspend() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_) if (session_)
session_->Suspend(mojom::MediaSession::SuspendType::kUI); session_->ipc()->Suspend(mojom::MediaSession::SuspendType::kUI);
} }
void MediaController::Resume() { void MediaController::Resume() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_) if (session_)
session_->Resume(mojom::MediaSession::SuspendType::kUI); session_->ipc()->Resume(mojom::MediaSession::SuspendType::kUI);
} }
void MediaController::Stop() { void MediaController::Stop() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_) if (session_)
session_->Stop(mojom::MediaSession::SuspendType::kUI); session_->ipc()->Stop(mojom::MediaSession::SuspendType::kUI);
} }
void MediaController::ToggleSuspendResume() { void MediaController::ToggleSuspendResume() {
...@@ -128,8 +129,13 @@ void MediaController::ToggleSuspendResume() { ...@@ -128,8 +129,13 @@ void MediaController::ToggleSuspendResume() {
void MediaController::AddObserver(mojom::MediaControllerObserverPtr observer) { void MediaController::AddObserver(mojom::MediaControllerObserverPtr observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_) {
observer->MediaSessionChanged(session_->id());
} else {
observer->MediaSessionChanged(base::nullopt);
}
// Flush the new observer with the current state. // Flush the new observer with the current state.
observer->MediaSessionChanged(request_id_);
observer->MediaSessionInfoChanged(session_info_.Clone()); observer->MediaSessionInfoChanged(session_info_.Clone());
observer->MediaSessionMetadataChanged(session_metadata_); observer->MediaSessionMetadataChanged(session_metadata_);
observer->MediaSessionActionsChanged(session_actions_); observer->MediaSessionActionsChanged(session_actions_);
...@@ -203,21 +209,21 @@ void MediaController::PreviousTrack() { ...@@ -203,21 +209,21 @@ void MediaController::PreviousTrack() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_) if (session_)
session_->PreviousTrack(); session_->ipc()->PreviousTrack();
} }
void MediaController::NextTrack() { void MediaController::NextTrack() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_) if (session_)
session_->NextTrack(); session_->ipc()->NextTrack();
} }
void MediaController::Seek(base::TimeDelta seek_time) { void MediaController::Seek(base::TimeDelta seek_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_) if (session_)
session_->Seek(seek_time); session_->ipc()->Seek(seek_time);
} }
void MediaController::ObserveImages( void MediaController::ObserveImages(
...@@ -234,13 +240,10 @@ void MediaController::ObserveImages( ...@@ -234,13 +240,10 @@ void MediaController::ObserveImages(
it == session_images_.end() ? std::vector<MediaImage>() : it->second)); it == session_images_.end() ? std::vector<MediaImage>() : it->second));
} }
void MediaController::SetMediaSession( void MediaController::SetMediaSession(AudioFocusRequest* session) {
mojom::MediaSession* session,
const base::UnguessableToken& request_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(session); DCHECK(session);
DCHECK(request_id);
if (session_ == session) if (session_ == session)
return; return;
...@@ -248,18 +251,16 @@ void MediaController::SetMediaSession( ...@@ -248,18 +251,16 @@ void MediaController::SetMediaSession(
Reset(); Reset();
session_ = session; session_ = session;
request_id_ = request_id;
// We should always notify the observers that the media session has changed. // We should always notify the observers that the media session has changed.
observers_.ForAllPtrs( observers_.ForAllPtrs([session](mojom::MediaControllerObserver* observer) {
[&request_id](mojom::MediaControllerObserver* observer) { observer->MediaSessionChanged(session->id());
observer->MediaSessionChanged(request_id);
}); });
// Add |this| as an observer for |session|. // Add |this| as an observer for |session|.
mojom::MediaSessionObserverPtr observer; mojom::MediaSessionObserverPtr observer;
session_binding_.Bind(mojo::MakeRequest(&observer)); session_binding_.Bind(mojo::MakeRequest(&observer));
session->AddObserver(std::move(observer)); session->ipc()->AddObserver(std::move(observer));
} }
void MediaController::ClearMediaSession() { void MediaController::ClearMediaSession() {
...@@ -300,7 +301,6 @@ void MediaController::CleanupImageObservers() { ...@@ -300,7 +301,6 @@ void MediaController::CleanupImageObservers() {
void MediaController::Reset() { void MediaController::Reset() {
session_ = nullptr; session_ = nullptr;
request_id_.reset();
session_binding_.Close(); session_binding_.Close();
session_info_.reset(); session_info_.reset();
session_metadata_.reset(); session_metadata_.reset();
......
...@@ -19,12 +19,10 @@ ...@@ -19,12 +19,10 @@
#include "services/media_session/public/mojom/media_controller.mojom.h" #include "services/media_session/public/mojom/media_controller.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h" #include "services/media_session/public/mojom/media_session.mojom.h"
namespace base {
class UnguessableToken;
} // namespace base
namespace media_session { namespace media_session {
class AudioFocusRequest;
// MediaController provides a control surface over Mojo for controlling a // MediaController provides a control surface over Mojo for controlling a
// specific MediaSession. If |session_| is nullptr then all commands will be // specific MediaSession. If |session_| is nullptr then all commands will be
// dropped. MediaController is also a MediaSessionObserver and will forward // dropped. MediaController is also a MediaSessionObserver and will forward
...@@ -60,8 +58,7 @@ class MediaController : public mojom::MediaController, ...@@ -60,8 +58,7 @@ class MediaController : public mojom::MediaController,
const base::flat_map<mojom::MediaSessionImageType, const base::flat_map<mojom::MediaSessionImageType,
std::vector<MediaImage>>& images) override; std::vector<MediaImage>>& images) override;
void SetMediaSession(mojom::MediaSession* session, void SetMediaSession(AudioFocusRequest* session);
const base::UnguessableToken& request_id);
void ClearMediaSession(); void ClearMediaSession();
void BindToInterface(mojom::MediaControllerRequest request); void BindToInterface(mojom::MediaControllerRequest request);
...@@ -93,9 +90,8 @@ class MediaController : public mojom::MediaController, ...@@ -93,9 +90,8 @@ class MediaController : public mojom::MediaController,
base::flat_map<mojom::MediaSessionImageType, std::vector<MediaImage>> base::flat_map<mojom::MediaSessionImageType, std::vector<MediaImage>>
session_images_; session_images_;
// Raw pointer to the local proxy. This is used for sending control events to // Raw pointer to the media session we are controlling.
// the underlying MediaSession. AudioFocusRequest* session_ = nullptr;
mojom::MediaSession* session_ = nullptr;
// Observers that are observing |this|. // Observers that are observing |this|.
mojo::InterfacePtrSet<mojom::MediaControllerObserver> observers_; mojo::InterfacePtrSet<mojom::MediaControllerObserver> observers_;
...@@ -106,9 +102,6 @@ class MediaController : public mojom::MediaController, ...@@ -106,9 +102,6 @@ class MediaController : public mojom::MediaController,
// Manages individual image observers. // Manages individual image observers.
std::vector<std::unique_ptr<ImageObserverHolder>> image_observers_; std::vector<std::unique_ptr<ImageObserverHolder>> image_observers_;
// The request id for the bound media session.
base::Optional<base::UnguessableToken> request_id_;
// Protects |session_| as it is not thread safe. // Protects |session_| as it is not thread safe.
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
......
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