Commit 88c43d22 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Media Session] Add metadata changed to observer

Add the MediaSessionMetadataChanged event to the
observer that will be fired if the metadata
associated with the media session changes.

This will be used for the notification to display
metadata of the currently playing media.

BUG=875004

Change-Id: I17d4e2af8b1d3b5b6213ec2eaaf0c29eb58c5937
Reviewed-on: https://chromium-review.googlesource.com/c/1316659
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarDan Erat <derat@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612749}
parent 06d647ed
......@@ -122,6 +122,11 @@ void MediaNotificationController::MediaSessionInfoChanged(
view_->UpdateWithMediaSessionInfo(session_info_);
}
void MediaNotificationController::MediaSessionMetadataChanged(
const base::Optional<media_session::MediaMetadata>& metadata) {
NOTIMPLEMENTED();
}
void MediaNotificationController::FlushForTesting() {
media_controller_ptr_.FlushForTesting();
}
......
......@@ -43,6 +43,8 @@ class ASH_EXPORT MediaNotificationController
// media_session::mojom::MediaSessionObserver:
void MediaSessionInfoChanged(
media_session::mojom::MediaSessionInfoPtr session_info) override;
void MediaSessionMetadataChanged(
const base::Optional<media_session::MediaMetadata>& metadata) override;
void FlushForTesting();
void SetMediaControllerForTesting(
......
......@@ -12,6 +12,7 @@
#include "media/base/media_content_type.h"
#include "services/media_session/public/cpp/switches.h"
#include "services/media_session/public/cpp/test/audio_focus_test_util.h"
#include "services/media_session/public/cpp/test/mock_media_session.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"
......@@ -26,50 +27,6 @@ const char kExpectedSourceName[] = "web";
} // namespace
class MediaSessionStateObserver
: public media_session::mojom::MediaSessionObserver {
public:
explicit MediaSessionStateObserver(
media_session::mojom::MediaSession* media_session)
: binding_(this) {
media_session::mojom::MediaSessionObserverPtr observer;
binding_.Bind(mojo::MakeRequest(&observer));
media_session->AddObserver(std::move(observer));
}
~MediaSessionStateObserver() override = default;
// media_session::mojom::MediaSessionObserver
void MediaSessionInfoChanged(
media_session::mojom::MediaSessionInfoPtr session_info) override {
if (desired_state_.has_value() &&
desired_state_.value() == session_info->state) {
run_loop_.Quit();
return;
}
session_info_ = std::move(session_info);
}
void WaitForState(
media_session::mojom::MediaSessionInfo::SessionState state) {
if (session_info_ && state == session_info_->state)
return;
desired_state_ = state;
run_loop_.Run();
}
protected:
base::RunLoop run_loop_;
mojo::Binding<media_session::mojom::MediaSessionObserver> binding_;
base::Optional<media_session::mojom::MediaSessionInfo::SessionState>
desired_state_;
media_session::mojom::MediaSessionInfoPtr session_info_;
DISALLOW_COPY_AND_ASSIGN(MediaSessionStateObserver);
};
class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
protected:
void SetUpOnMainThread() override {
......@@ -121,14 +78,16 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
}
{
MediaSessionStateObserver state_observer(media_session);
state_observer.WaitForState(
media_session::test::MockMediaSessionMojoObserver observer(
*media_session);
observer.WaitForState(
media_session::mojom::MediaSessionInfo::SessionState::kActive);
}
{
MediaSessionStateObserver state_observer(other_media_session);
state_observer.WaitForState(
media_session::test::MockMediaSessionMojoObserver observer(
*other_media_session);
observer.WaitForState(
media_session::mojom::MediaSessionInfo::SessionState::kInactive);
}
......@@ -144,16 +103,18 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
}
{
MediaSessionStateObserver state_observer(media_session);
state_observer.WaitForState(
media_session::test::MockMediaSessionMojoObserver observer(
*media_session);
observer.WaitForState(
use_separate_group_id
? media_session::mojom::MediaSessionInfo::SessionState::kSuspended
: media_session::mojom::MediaSessionInfo::SessionState::kActive);
}
{
MediaSessionStateObserver state_observer(other_media_session);
state_observer.WaitForState(
media_session::test::MockMediaSessionMojoObserver observer(
*other_media_session);
observer.WaitForState(
media_session::mojom::MediaSessionInfo::SessionState::kActive);
}
......@@ -167,14 +128,16 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
}
{
MediaSessionStateObserver state_observer(media_session);
state_observer.WaitForState(
media_session::test::MockMediaSessionMojoObserver observer(
*media_session);
observer.WaitForState(
media_session::mojom::MediaSessionInfo::SessionState::kInactive);
}
{
MediaSessionStateObserver state_observer(other_media_session);
state_observer.WaitForState(
media_session::test::MockMediaSessionMojoObserver observer(
*other_media_session);
observer.WaitForState(
media_session::mojom::MediaSessionInfo::SessionState::kInactive);
}
}
......
......@@ -211,6 +211,11 @@ void MediaSessionImpl::NotifyMediaSessionMetadataChange(
const base::Optional<media_session::MediaMetadata>& metadata) {
for (auto& observer : observers_)
observer.MediaSessionMetadataChanged(metadata);
mojo_observers_.ForAllPtrs(
[&metadata](media_session::mojom::MediaSessionObserver* observer) {
observer->MediaSessionMetadataChanged(metadata);
});
}
void MediaSessionImpl::NotifyMediaSessionActionsChange(
......@@ -742,6 +747,9 @@ void MediaSessionImpl::GetMediaSessionInfo(
void MediaSessionImpl::AddObserver(
media_session::mojom::MediaSessionObserverPtr observer) {
observer->MediaSessionInfoChanged(GetMediaSessionInfoSync());
observer->MediaSessionMetadataChanged(
routed_service_ ? routed_service_->metadata() : base::nullopt);
mojo_observers_.AddPtr(std::move(observer));
}
......
......@@ -24,6 +24,7 @@
#include "content/public/test/content_browser_test.h"
#include "content/shell/browser/shell.h"
#include "media/base/media_content_type.h"
#include "services/media_session/public/cpp/test/mock_media_session.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -1742,6 +1743,35 @@ IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
media_session_->AddObserver(mock_media_session_observer());
}
IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
AddingMojoObserverNotifiesCurrentInformation_EmptyInfo) {
media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
EXPECT_FALSE(observer.WaitForMetadata());
}
IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
AddingMojoObserverNotifiesCurrentInformation_WithInfo) {
// Set up the service and information.
EnsureMediaSessionService();
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
metadata.album = base::ASCIIToUTF16("album");
mock_media_session_service_->SetMetadata(metadata);
// Make sure the service is routed,
auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
shell()->web_contents()->GetMainFrame());
{
media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent);
ResolveAudioFocusSuccess();
EXPECT_EQ(metadata, *observer.WaitForMetadata());
}
}
IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_RequestFailure_Gain) {
auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>();
......
......@@ -17,6 +17,7 @@
#include "content/test/test_web_contents.h"
#include "media/base/media_content_type.h"
#include "services/media_session/public/cpp/media_metadata.h"
#include "services/media_session/public/cpp/test/mock_media_session.h"
#include "services/media_session/public/mojom/constants.mojom.h"
#include "third_party/blink/public/platform/modules/mediasession/media_session.mojom.h"
......@@ -157,6 +158,10 @@ class MediaSessionImplServiceRoutingTest
return MediaSessionImpl::Get(contents())->ComputeServiceForRouting();
}
MediaSessionImpl* GetMediaSession() {
return MediaSessionImpl::Get(contents());
}
TestRenderFrameHost* main_frame_;
TestRenderFrameHost* sub_frame_;
......@@ -546,4 +551,35 @@ TEST_F(MediaSessionImplServiceRoutingTest,
run_loop.Run();
}
TEST_F(MediaSessionImplServiceRoutingTest,
NotifyMojoObserverMetadataWhenControllable) {
media_session::MediaMetadata expected_metadata;
expected_metadata.title = base::ASCIIToUTF16("title");
expected_metadata.artist = base::ASCIIToUTF16("artist");
expected_metadata.album = base::ASCIIToUTF16("album");
CreateServiceForFrame(main_frame_);
StartPlayerForFrame(main_frame_);
{
media_session::test::MockMediaSessionMojoObserver observer(
*GetMediaSession());
services_[main_frame_]->SetMetadata(expected_metadata);
EXPECT_EQ(expected_metadata, *observer.WaitForMetadata());
}
}
TEST_F(MediaSessionImplServiceRoutingTest,
NotifyMojoObserverMetadataEmptyWhenControllable) {
CreateServiceForFrame(main_frame_);
StartPlayerForFrame(main_frame_);
{
media_session::test::MockMediaSessionMojoObserver observer(
*GetMediaSession());
services_[main_frame_]->SetMetadata(base::nullopt);
EXPECT_FALSE(observer.WaitForMetadata());
}
}
} // namespace content
......@@ -45,10 +45,11 @@ void MediaController::ToggleSuspendResume() {
void MediaController::AddObserver(mojom::MediaSessionObserverPtr observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Flush the new observer with the latest session info. If there is no info
// then we will update |observer| when |MediaSessionInfoChanged| is called.
// Flush the new observer with the state. We always flush the metadata as that
// is optional so null is a valid value whereas the session info is required.
if (!session_info_.is_null())
observer->MediaSessionInfoChanged(session_info_.Clone());
observer->MediaSessionMetadataChanged(session_metadata_);
observers_.AddPtr(std::move(observer));
}
......@@ -63,6 +64,17 @@ void MediaController::MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) {
session_info_ = std::move(info);
}
void MediaController::MediaSessionMetadataChanged(
const base::Optional<MediaMetadata>& metadata) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.ForAllPtrs([&metadata](mojom::MediaSessionObserver* observer) {
observer->MediaSessionMetadataChanged(metadata);
});
session_metadata_ = metadata;
}
void MediaController::PreviousTrack() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -91,6 +103,8 @@ bool MediaController::SetMediaSession(mojom::MediaSession* session) {
if (changed) {
session_binding_.Close();
session_info_.reset();
session_metadata_.reset();
if (session) {
// Add |this| as an observer for |session|.
......
......@@ -7,10 +7,12 @@
#include <memory>
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "services/media_session/public/cpp/media_metadata.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
......@@ -38,6 +40,8 @@ class MediaController : public mojom::MediaController,
// mojom::MediaSessionObserver overrides.
void MediaSessionInfoChanged(
mojom::MediaSessionInfoPtr session_info) override;
void MediaSessionMetadataChanged(
const base::Optional<MediaMetadata>&) override;
// Sets the media session that the controller should be bound to. If the
// session is already bound to the same session then we will return false.
......@@ -53,6 +57,9 @@ class MediaController : public mojom::MediaController,
// The current info for the |session_|.
mojom::MediaSessionInfoPtr session_info_;
// The current metadata for |session_|.
base::Optional<MediaMetadata> session_metadata_;
// Raw pointer to the local proxy. This is used for sending control events to
// the underlying MediaSession.
mojom::MediaSession* session_ = nullptr;
......
......@@ -9,9 +9,11 @@
#include <vector>
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "services/media_session/media_session_service.h"
#include "services/media_session/public/cpp/media_metadata.h"
#include "services/media_session/public/cpp/test/mock_media_session.h"
#include "services/media_session/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
......@@ -459,4 +461,108 @@ TEST_F(MediaControllerTest, ActiveController_Seek) {
EXPECT_EQ(1, media_session.seek_count());
}
TEST_F(MediaControllerTest, ActiveController_Metadata_Observer_Abandoned) {
MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
metadata.album = base::ASCIIToUTF16("album");
test::MockMediaSession media_session;
base::Optional<MediaMetadata> test_metadata(metadata);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.SimulateMetadataChanged(test_metadata);
media_session.AbandonAudioFocusFromClient();
{
test::MockMediaSessionMojoObserver observer(controller());
EXPECT_FALSE(observer.WaitForMetadata());
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_Observer_Empty) {
test::MockMediaSession media_session;
base::Optional<MediaMetadata> test_metadata;
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::MockMediaSessionMojoObserver observer(controller());
media_session.SimulateMetadataChanged(test_metadata);
EXPECT_EQ(test_metadata, observer.WaitForMetadata());
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_Observer_WithInfo) {
MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
metadata.album = base::ASCIIToUTF16("album");
test::MockMediaSession media_session;
base::Optional<MediaMetadata> test_metadata(metadata);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::MockMediaSessionMojoObserver observer(controller());
media_session.SimulateMetadataChanged(test_metadata);
EXPECT_EQ(metadata, *observer.WaitForMetadata());
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_AddObserver_Empty) {
test::MockMediaSession media_session;
base::Optional<MediaMetadata> test_metadata;
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.SimulateMetadataChanged(test_metadata);
{
test::MockMediaSessionMojoObserver observer(controller());
EXPECT_EQ(test_metadata, observer.WaitForMetadata());
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_AddObserver_WithInfo) {
MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
metadata.album = base::ASCIIToUTF16("album");
test::MockMediaSession media_session;
base::Optional<MediaMetadata> test_metadata(metadata);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.SimulateMetadataChanged(test_metadata);
{
test::MockMediaSessionMojoObserver observer(controller());
EXPECT_EQ(metadata, *observer.WaitForMetadata());
}
}
} // namespace media_session
......@@ -39,6 +39,16 @@ void MockMediaSessionMojoObserver::MediaSessionInfoChanged(
}
}
void MockMediaSessionMojoObserver::MediaSessionMetadataChanged(
const base::Optional<MediaMetadata>& metadata) {
session_metadata_ = metadata;
if (waiting_for_metadata_) {
run_loop_.Quit();
waiting_for_metadata_ = false;
}
}
void MockMediaSessionMojoObserver::WaitForState(
mojom::MediaSessionInfo::SessionState wanted_state) {
if (session_info_ && session_info_->state == wanted_state)
......@@ -57,6 +67,16 @@ void MockMediaSessionMojoObserver::WaitForPlaybackState(
run_loop_.Run();
}
const base::Optional<MediaMetadata>&
MockMediaSessionMojoObserver::WaitForMetadata() {
if (!session_metadata_.has_value()) {
waiting_for_metadata_ = true;
run_loop_.Run();
}
return session_metadata_.value();
}
MockMediaSession::MockMediaSession() = default;
MockMediaSession::MockMediaSession(bool force_duck) : force_duck_(force_duck) {}
......@@ -217,6 +237,13 @@ void MockMediaSession::FlushForTesting() {
afr_client_.FlushForTesting();
}
void MockMediaSession::SimulateMetadataChanged(
const base::Optional<MediaMetadata>& metadata) {
observers_.ForAllPtrs([&metadata](mojom::MediaSessionObserver* observer) {
observer->MediaSessionMetadataChanged(metadata);
});
}
void MockMediaSession::SetState(mojom::MediaSessionInfo::SessionState state) {
state_ = state;
NotifyObservers();
......
......@@ -11,6 +11,7 @@
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "services/media_session/public/cpp/media_metadata.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
......@@ -35,9 +36,12 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP)
// mojom::MediaSessionObserver overrides.
void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr session) override;
void MediaSessionMetadataChanged(
const base::Optional<MediaMetadata>& metadata) override;
void WaitForState(mojom::MediaSessionInfo::SessionState wanted_state);
void WaitForPlaybackState(mojom::MediaPlaybackState wanted_state);
const base::Optional<MediaMetadata>& WaitForMetadata();
const mojom::MediaSessionInfoPtr& session_info() const {
return session_info_;
......@@ -45,6 +49,9 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP)
private:
mojom::MediaSessionInfoPtr session_info_;
base::Optional<base::Optional<MediaMetadata>> session_metadata_;
bool waiting_for_metadata_ = false;
base::Optional<mojom::MediaSessionInfo::SessionState> wanted_state_;
base::Optional<mojom::MediaPlaybackState> wanted_playback_state_;
base::RunLoop run_loop_;
......@@ -63,13 +70,13 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) MockMediaSession
~MockMediaSession() override;
// mojom::MediaSession overrides.
void Suspend(SuspendType) override;
void Resume(SuspendType) override;
void Suspend(SuspendType type) override;
void Resume(SuspendType type) override;
void StartDucking() override;
void StopDucking() override;
void GetMediaSessionInfo(GetMediaSessionInfoCallback) override;
void AddObserver(mojom::MediaSessionObserverPtr) override;
void GetDebugInfo(GetDebugInfoCallback) override;
void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override;
void AddObserver(mojom::MediaSessionObserverPtr observer) override;
void GetDebugInfo(GetDebugInfoCallback callback) override;
void PreviousTrack() override;
void NextTrack() override;
void Seek(base::TimeDelta seek_time) override;
......@@ -80,8 +87,8 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) MockMediaSession
base::UnguessableToken GetRequestIdFromClient();
base::UnguessableToken RequestAudioFocusFromService(
mojom::AudioFocusManagerPtr&,
mojom::AudioFocusType);
mojom::AudioFocusManagerPtr& service,
mojom::AudioFocusType audio_foucs_type);
base::UnguessableToken RequestGroupedAudioFocusFromService(
mojom::AudioFocusManagerPtr& service,
......@@ -95,6 +102,8 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) MockMediaSession
}
void FlushForTesting();
void SimulateMetadataChanged(const base::Optional<MediaMetadata>& metadata);
int prev_track_count() const { return prev_track_count_; }
int next_track_count() const { return next_track_count_; }
int add_observer_count() const { return add_observer_count_; }
......
......@@ -90,8 +90,13 @@ struct MediaSessionDebugInfo {
// The observer for observing media session events.
// Next Method ID: 1
interface MediaSessionObserver {
// The info associated with the session changed.
// Call when the info associated with the session changed.
MediaSessionInfoChanged@0(MediaSessionInfo info);
// Called when the observed MediaSession has changed metadata. The metadata
// can be null to be reset, e.g. the media that was being played has been
// stopped.
MediaSessionMetadataChanged@1(MediaMetadata? metadata);
};
// A MediaSession manages the media session and audio focus for a given
......
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