Commit 17349ec7 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Media Session] Add AudioFocus mojo API

Adds a mojo AudioFocus API that can be used to access
AudioFocusManager inside the media session service.

This also removes the RequestId when requesting or
abandoning audio focus and replaces it with a
AudioFocusRequestClient interface. We still keep the
ID around though as it is useful for testing and
identifying requests.

BUG=875004

Change-Id: I88374a08a14171f747a120beaa380c3b126bfb4f
Reviewed-on: https://chromium-review.googlesource.com/c/1199923
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarapacible <apacible@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596404}
parent 7c2a2894
...@@ -190,6 +190,7 @@ service_manifest("packaged_services_manifest") { ...@@ -190,6 +190,7 @@ service_manifest("packaged_services_manifest") {
"//services/audio:manifest", "//services/audio:manifest",
"//services/data_decoder:manifest", "//services/data_decoder:manifest",
"//services/device:manifest", "//services/device:manifest",
"//services/media_session:manifest",
"//services/metrics:manifest", "//services/metrics:manifest",
"//services/network:manifest", "//services/network:manifest",
"//services/resource_coordinator:manifest", "//services/resource_coordinator:manifest",
......
...@@ -117,6 +117,7 @@ ...@@ -117,6 +117,7 @@
], ],
"file": [ "file:filesystem", "file:leveldb" ], "file": [ "file:filesystem", "file:leveldb" ],
"media": [ "media:media" ], "media": [ "media:media" ],
"media_session": [ "media_session:app" ],
"metrics": [ "url_keyed_metrics" ], "metrics": [ "url_keyed_metrics" ],
"network": [ "network": [
"network_service", "network_service",
......
...@@ -11,48 +11,50 @@ ...@@ -11,48 +11,50 @@
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr.h" #include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h" #include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h" #include "services/media_session/public/mojom/audio_focus.mojom.h"
namespace media_session { namespace media_session {
class AudioFocusManager { class AudioFocusManager : public mojom::AudioFocusManager {
public: public:
using RequestId = int; using RequestId = uint64_t;
using RequestResponse = std::pair<RequestId, bool>;
// Returns Chromium's internal AudioFocusManager. // Returns Chromium's internal AudioFocusManager.
static AudioFocusManager* GetInstance(); static AudioFocusManager* GetInstance();
// Requests audio focus with |type| for the |media_session| with // mojom::AudioFocusManager.
// |session_info|. The function will return a |RequestResponse| which contains void RequestAudioFocus(mojom::AudioFocusRequestClientRequest request,
// a |RequestId| and a boolean as to whether the request was successful. If a mojom::MediaSessionPtr media_session,
// session wishes to request a different focus type it should provide its mojom::MediaSessionInfoPtr session_info,
// previous request id as |previous_id|. mojom::AudioFocusType type,
RequestResponse RequestAudioFocus(mojom::MediaSessionPtr media_session, RequestAudioFocusCallback callback) override;
mojom::MediaSessionInfoPtr session_info, void GetFocusRequests(GetFocusRequestsCallback callback) override;
mojom::AudioFocusType type, void GetDebugInfoForRequest(uint64_t request_id,
base::Optional<RequestId> previous_id); GetDebugInfoForRequestCallback callback) override;
void AddObserver(mojom::AudioFocusObserverPtr observer) override;
// Abandons audio focus for a request with |id|. The next request on top of // Bind to a mojom::AudioFocusManagerRequest.
// the stack will be granted audio focus. void BindToInterface(mojom::AudioFocusManagerRequest request);
void AbandonAudioFocus(RequestId id);
// Returns the current audio focus type for a request with |id|. // This will close all Mojo bindings and interface pointers. This should be
mojom::AudioFocusType GetFocusTypeForSession(RequestId id); // called by the MediaSession service before it is destroyed.
void CloseAllMojoObjects();
// Adds/removes audio focus observers.
mojo::InterfacePtrSetElementId AddObserver(mojom::AudioFocusObserverPtr);
void RemoveObserver(mojo::InterfacePtrSetElementId);
private: private:
friend struct base::DefaultSingletonTraits<AudioFocusManager>; friend struct base::DefaultSingletonTraits<AudioFocusManager>;
friend class AudioFocusManagerTest; friend class AudioFocusManagerTest;
// Media internals UI needs access to internal state. // StackRow is an AudioFocusRequestClient and allows a media session to
friend class MediaInternalsAudioFocusTest; // control its audio focus.
friend class MediaInternals; class StackRow;
void RequestAudioFocusInternal(std::unique_ptr<StackRow> row,
mojom::AudioFocusType type,
base::OnceCallback<void()> callback);
void AbandonAudioFocusInternal(RequestId id);
// Flush for testing will flush any pending messages to the observers. // Flush for testing will flush any pending messages to the observers.
void FlushForTesting(); void FlushForTesting();
...@@ -61,51 +63,20 @@ class AudioFocusManager { ...@@ -61,51 +63,20 @@ class AudioFocusManager {
void ResetForTesting(); void ResetForTesting();
AudioFocusManager(); AudioFocusManager();
~AudioFocusManager(); ~AudioFocusManager() override;
std::unique_ptr<StackRow> RemoveFocusEntryIfPresent(RequestId id);
void RemoveFocusEntryIfPresent(RequestId id); bool IsSessionOnTopOfAudioFocusStack(RequestId id,
mojom::AudioFocusType type) const;
// Holds mojo bindings for the Audio Focus Manager API.
mojo::BindingSet<mojom::AudioFocusManager> bindings_;
// Weak reference of managed observers. Observers are expected to remove // Weak reference of managed observers. Observers are expected to remove
// themselves before being destroyed. // themselves before being destroyed.
mojo::InterfacePtrSet<mojom::AudioFocusObserver> observers_; mojo::InterfacePtrSet<mojom::AudioFocusObserver> observers_;
// StackRow is a MediaSessionObserver and holds a cached copy of the latest
// MediaSessionInfo associated with the MediaSession. By keeping the info
// cached and readily available we can make audio focus decisions quickly
// without waiting on MediaSessions.
class StackRow : public mojom::MediaSessionObserver {
public:
StackRow(mojom::MediaSessionPtr session,
mojom::MediaSessionInfoPtr current_info,
mojom::AudioFocusType audio_focus_type,
RequestId id);
~StackRow() override;
// mojom::MediaSessionObserver.
void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) override;
mojom::MediaSession* session();
const mojom::MediaSessionInfoPtr& info() const;
mojom::AudioFocusType audio_focus_type() const { return audio_focus_type_; }
bool IsActive() const;
int id() { return id_; }
// Flush any pending mojo messages for testing.
void FlushForTesting();
private:
void OnConnectionError();
int id_;
mojom::MediaSessionPtr session_;
mojom::MediaSessionInfoPtr current_info_;
mojom::AudioFocusType audio_focus_type_;
mojo::Binding<mojom::MediaSessionObserver> binding_;
DISALLOW_COPY_AND_ASSIGN(StackRow);
};
// 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<StackRow>> audio_focus_stack_;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
"service_manager:connector": { "service_manager:connector": {
"provides": { "provides": {
"app": [ "app": [
"media_session.mojom.AudioFocus" "media_session.mojom.AudioFocusManager"
], ],
"tests": [ "*" ] "tests": [ "*" ]
} }
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "services/media_session/media_session_service.h" #include "services/media_session/media_session_service.h"
#include "base/bind.h"
#include "services/media_session/audio_focus_manager.h"
#include "services/service_manager/public/cpp/service_context.h" #include "services/service_manager/public/cpp/service_context.h"
namespace media_session { namespace media_session {
...@@ -14,12 +16,17 @@ std::unique_ptr<service_manager::Service> MediaSessionService::Create() { ...@@ -14,12 +16,17 @@ std::unique_ptr<service_manager::Service> MediaSessionService::Create() {
MediaSessionService::MediaSessionService() = default; MediaSessionService::MediaSessionService() = default;
MediaSessionService::~MediaSessionService() = default; MediaSessionService::~MediaSessionService() {
AudioFocusManager::GetInstance()->CloseAllMojoObjects();
}
void MediaSessionService::OnStart() { void MediaSessionService::OnStart() {
DLOG(ERROR) << "start";
ref_factory_.reset(new service_manager::ServiceContextRefFactory( ref_factory_.reset(new service_manager::ServiceContextRefFactory(
context()->CreateQuitClosure())); context()->CreateQuitClosure()));
registry_.AddInterface(
base::BindRepeating(&AudioFocusManager::BindToInterface,
base::Unretained(AudioFocusManager::GetInstance())));
} }
void MediaSessionService::OnBindInterface( void MediaSessionService::OnBindInterface(
......
...@@ -9,15 +9,23 @@ import "services/media_session/public/mojom/media_session.mojom"; ...@@ -9,15 +9,23 @@ import "services/media_session/public/mojom/media_session.mojom";
// These are the different types of audio focus that can be requested. // These are the different types of audio focus that can be requested.
enum AudioFocusType { enum AudioFocusType {
// Request permanent audio focus when you plan to play audio for the // Request permanent audio focus when you plan to play audio for the
// foreseeable future (for example, when playing music) and you expect // foreseeable future (for example, when playing music) and you expect the
// the previous holder of audio focus to stop playing. // previous holder of audio focus to stop playing.
kGain, kGain,
// Request transient focus when you expect to play audio for only a // Request transient focus when you expect to play audio for only a short
// short time and you expect the previous holder to pause playing. // time and you expect the previous holder to pause playing.
kGainTransientMayDuck, kGainTransientMayDuck,
}; };
// Contains information about |MediaSessions| that have requested audio focus
// and their current requested type.
struct AudioFocusRequestState {
MediaSessionInfo session_info;
AudioFocusType audio_focus_type;
uint64 request_id;
};
// The observer for audio focus events. // The observer for audio focus events.
interface AudioFocusObserver { interface AudioFocusObserver {
// The given |session| gained audio focus with the specified |type|. // The given |session| gained audio focus with the specified |type|.
...@@ -26,3 +34,42 @@ interface AudioFocusObserver { ...@@ -26,3 +34,42 @@ interface AudioFocusObserver {
// The given |session| lost audio focus. // The given |session| lost audio focus.
OnFocusLost(MediaSessionInfo session); OnFocusLost(MediaSessionInfo session);
}; };
// Controls audio focus for an associated request.
interface AudioFocusRequestClient {
// Requests updated audio focus for this request. If the request was granted
// then the callback will resolve.
RequestAudioFocus(MediaSessionInfo session_info, AudioFocusType type) => ();
// Abandons audio focus for this request.
AbandonAudioFocus();
// Notifies the audio focus backend when the associated session info changes.
MediaSessionInfoChanged(MediaSessionInfo session_info);
// Retrieve a unique ID for this request.
GetRequestId() => (uint64 request_id);
};
// Controls audio focus across the entire system.
interface AudioFocusManager {
// Requests audio focus with |type| for the |media_session| with
// |session_info|. Media sessions should provide a |request| that will
// provide an AudioFocusRequestClient that can be used to control this
// request. The callback will resolve when audio focus has been granted.
RequestAudioFocus(AudioFocusRequestClient& client,
MediaSession media_session,
MediaSessionInfo session_info,
AudioFocusType type) => ();
// Gets all the information about all |MediaSessions| that have requested
// audio focus and their current requested type.
GetFocusRequests() => (array<AudioFocusRequestState> requests);
// Gets debugging information for a |MediaSession| with |request_id|.
GetDebugInfoForRequest(uint64 request_id)
=> (MediaSessionDebugInfo debug_info);
// Adds observers that receive audio focus events.
AddObserver(AudioFocusObserver observer);
};
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