Commit 1112acc3 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

Media Controller: Add support for previous and next track

Add support for Previous/NextTrack to MediaController
and the underlying MediaSession. This will be mapped
to the previous and next track media keys on CrOS.

BUG=893296

Change-Id: I582cd70a04c24af69c80ca891a281311b4d05434
Reviewed-on: https://chromium-review.googlesource.com/c/1287268Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603528}
parent 754c97a7
...@@ -44,6 +44,8 @@ class MockMediaSession : public content::MediaSession { ...@@ -44,6 +44,8 @@ class MockMediaSession : public content::MediaSession {
MOCK_METHOD1(RemoveObserver, void(content::MediaSessionObserver*)); MOCK_METHOD1(RemoveObserver, void(content::MediaSessionObserver*));
MOCK_METHOD1(GetMediaSessionInfo, void(GetMediaSessionInfoCallback)); MOCK_METHOD1(GetMediaSessionInfo, void(GetMediaSessionInfoCallback));
MOCK_METHOD1(GetDebugInfo, void(GetDebugInfoCallback)); MOCK_METHOD1(GetDebugInfo, void(GetDebugInfoCallback));
MOCK_METHOD0(PreviousTrack, void());
MOCK_METHOD0(NextTrack, void());
private: private:
DISALLOW_COPY_AND_ASSIGN(MockMediaSession); DISALLOW_COPY_AND_ASSIGN(MockMediaSession);
......
...@@ -755,6 +755,14 @@ void MediaSessionImpl::FinishSystemAudioFocusRequest( ...@@ -755,6 +755,14 @@ void MediaSessionImpl::FinishSystemAudioFocusRequest(
} }
} }
void MediaSessionImpl::PreviousTrack() {
DidReceiveAction(blink::mojom::MediaSessionAction::PREVIOUS_TRACK);
}
void MediaSessionImpl::NextTrack() {
DidReceiveAction(blink::mojom::MediaSessionAction::NEXT_TRACK);
}
void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() { void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() {
if (audio_focus_state_ == State::INACTIVE || !normal_players_.empty() || if (audio_focus_state_ == State::INACTIVE || !normal_players_.empty() ||
!pepper_players_.empty() || !one_shot_players_.empty()) { !pepper_players_.empty() || !one_shot_players_.empty()) {
......
...@@ -227,6 +227,12 @@ class MediaSessionImpl : public MediaSession, ...@@ -227,6 +227,12 @@ class MediaSessionImpl : public MediaSession,
media_session::mojom::AudioFocusType type, media_session::mojom::AudioFocusType type,
bool result); bool result);
// Skip to the previous track.
CONTENT_EXPORT void PreviousTrack() override;
// Skip to the next track.
CONTENT_EXPORT void NextTrack() override;
private: private:
friend class content::WebContentsUserData<MediaSessionImpl>; friend class content::WebContentsUserData<MediaSessionImpl>;
friend class ::MediaSessionImplBrowserTest; friend class ::MediaSessionImplBrowserTest;
......
...@@ -413,4 +413,45 @@ TEST_F(MediaSessionImplServiceRoutingTest, ...@@ -413,4 +413,45 @@ TEST_F(MediaSessionImplServiceRoutingTest,
->DidReceiveAction(blink::mojom::MediaSessionAction::PAUSE); ->DidReceiveAction(blink::mojom::MediaSessionAction::PAUSE);
} }
TEST_F(MediaSessionImplServiceRoutingTest,
TestPreviousTrackBehaviorWhenMainFrameIsRouted) {
base::RunLoop run_loop;
StartPlayerForFrame(main_frame_);
StartPlayerForFrame(sub_frame_);
CreateServiceForFrame(main_frame_);
EXPECT_CALL(
*GetClientForFrame(main_frame_),
DidReceiveAction(blink::mojom::MediaSessionAction::PREVIOUS_TRACK))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
services_[main_frame_]->EnableAction(
blink::mojom::MediaSessionAction::PREVIOUS_TRACK);
MediaSessionImpl::Get(contents())->PreviousTrack();
run_loop.Run();
}
TEST_F(MediaSessionImplServiceRoutingTest,
TestNextTrackBehaviorWhenMainFrameIsRouted) {
base::RunLoop run_loop;
StartPlayerForFrame(main_frame_);
StartPlayerForFrame(sub_frame_);
CreateServiceForFrame(main_frame_);
EXPECT_CALL(*GetClientForFrame(main_frame_),
DidReceiveAction(blink::mojom::MediaSessionAction::NEXT_TRACK))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
services_[main_frame_]->EnableAction(
blink::mojom::MediaSessionAction::NEXT_TRACK);
MediaSessionImpl::Get(contents())->NextTrack();
run_loop.Run();
}
} // namespace content } // namespace content
...@@ -85,6 +85,14 @@ class MediaSession : public media_session::mojom::MediaSession { ...@@ -85,6 +85,14 @@ class MediaSession : public media_session::mojom::MediaSession {
void AddObserver( void AddObserver(
media_session::mojom::MediaSessionObserverPtr observer) override = 0; media_session::mojom::MediaSessionObserverPtr observer) override = 0;
// Skip to the previous track. If there is no previous track then this will be
// a no-op.
void PreviousTrack() override = 0;
// Skip to the next track. If there is no next track then this will be a
// no-op.
void NextTrack() override = 0;
protected: protected:
MediaSession() = default; MediaSession() = default;
......
...@@ -63,6 +63,20 @@ void MediaController::MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) { ...@@ -63,6 +63,20 @@ void MediaController::MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) {
session_info_ = std::move(info); session_info_ = std::move(info);
} }
void MediaController::PreviousTrack() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_)
session_->PreviousTrack();
}
void MediaController::NextTrack() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_)
session_->NextTrack();
}
void MediaController::SetMediaSession(mojom::MediaSession* session) { void MediaController::SetMediaSession(mojom::MediaSession* session) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
session_ = session; session_ = session;
......
...@@ -31,6 +31,8 @@ class MediaController : public mojom::MediaController, ...@@ -31,6 +31,8 @@ class MediaController : public mojom::MediaController,
void Resume() override; void Resume() override;
void ToggleSuspendResume() override; void ToggleSuspendResume() override;
void AddObserver(mojom::MediaSessionObserverPtr) override; void AddObserver(mojom::MediaSessionObserverPtr) override;
void PreviousTrack() override;
void NextTrack() override;
// mojom::MediaSessionObserver overrides. // mojom::MediaSessionObserver overrides.
void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr) override; void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr) override;
......
...@@ -276,4 +276,38 @@ TEST_F(MediaControllerTest, ActiveController_Observer_StateTransition) { ...@@ -276,4 +276,38 @@ TEST_F(MediaControllerTest, ActiveController_Observer_StateTransition) {
} }
} }
TEST_F(MediaControllerTest, ActiveController_PreviousTrack) {
test::MockMediaSession media_session;
EXPECT_EQ(0, media_session.prev_track_count());
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
EXPECT_EQ(0, media_session.prev_track_count());
}
controller()->PreviousTrack();
controller().FlushForTesting();
EXPECT_EQ(1, media_session.prev_track_count());
}
TEST_F(MediaControllerTest, ActiveController_NextTrack) {
test::MockMediaSession media_session;
EXPECT_EQ(0, media_session.next_track_count());
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
EXPECT_EQ(0, media_session.next_track_count());
}
controller()->NextTrack();
controller().FlushForTesting();
EXPECT_EQ(1, media_session.next_track_count());
}
} // namespace media_session } // namespace media_session
...@@ -102,6 +102,14 @@ void MockMediaSession::GetDebugInfo(GetDebugInfoCallback callback) { ...@@ -102,6 +102,14 @@ void MockMediaSession::GetDebugInfo(GetDebugInfoCallback callback) {
std::move(callback).Run(std::move(debug_info)); std::move(callback).Run(std::move(debug_info));
} }
void MockMediaSession::PreviousTrack() {
prev_track_count_++;
}
void MockMediaSession::NextTrack() {
next_track_count_++;
}
void MockMediaSession::Stop() { void MockMediaSession::Stop() {
SetState(mojom::MediaSessionInfo::SessionState::kInactive); SetState(mojom::MediaSessionInfo::SessionState::kInactive);
} }
......
...@@ -63,6 +63,8 @@ class MockMediaSession : public mojom::MediaSession { ...@@ -63,6 +63,8 @@ class MockMediaSession : public mojom::MediaSession {
void GetMediaSessionInfo(GetMediaSessionInfoCallback) override; void GetMediaSessionInfo(GetMediaSessionInfoCallback) override;
void AddObserver(mojom::MediaSessionObserverPtr) override; void AddObserver(mojom::MediaSessionObserverPtr) override;
void GetDebugInfo(GetDebugInfoCallback) override; void GetDebugInfo(GetDebugInfoCallback) override;
void PreviousTrack() override;
void NextTrack() override;
void Stop(); void Stop();
...@@ -80,6 +82,9 @@ class MockMediaSession : public mojom::MediaSession { ...@@ -80,6 +82,9 @@ class MockMediaSession : public mojom::MediaSession {
} }
void FlushForTesting(); void FlushForTesting();
int prev_track_count() const { return prev_track_count_; }
int next_track_count() const { return next_track_count_; }
private: private:
void SetState(mojom::MediaSessionInfo::SessionState); void SetState(mojom::MediaSessionInfo::SessionState);
void NotifyObservers(); void NotifyObservers();
...@@ -90,6 +95,9 @@ class MockMediaSession : public mojom::MediaSession { ...@@ -90,6 +95,9 @@ class MockMediaSession : public mojom::MediaSession {
const bool force_duck_ = false; const bool force_duck_ = false;
bool is_ducking_ = false; bool is_ducking_ = false;
int prev_track_count_ = 0;
int next_track_count_ = 0;
mojom::MediaSessionInfo::SessionState state_ = mojom::MediaSessionInfo::SessionState state_ =
mojom::MediaSessionInfo::SessionState::kInactive; mojom::MediaSessionInfo::SessionState::kInactive;
......
...@@ -26,6 +26,8 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) TestMediaController ...@@ -26,6 +26,8 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) TestMediaController
void Resume() override {} void Resume() override {}
void ToggleSuspendResume() override; void ToggleSuspendResume() override;
void AddObserver(mojom::MediaSessionObserverPtr) override {} void AddObserver(mojom::MediaSessionObserverPtr) override {}
void PreviousTrack() override {}
void NextTrack() override {}
int toggle_suspend_resume_count() const { int toggle_suspend_resume_count() const {
return toggle_suspend_resume_count_; return toggle_suspend_resume_count_;
......
...@@ -24,4 +24,10 @@ interface MediaController { ...@@ -24,4 +24,10 @@ interface MediaController {
// If the active session changes then observers do not need to be readded. // If the active session changes then observers do not need to be readded.
// Adding the observer will update the observer with the latest state. // Adding the observer will update the observer with the latest state.
AddObserver(MediaSessionObserver observer); AddObserver(MediaSessionObserver observer);
// Skip to the previous track.
PreviousTrack();
// Skip to the next track.
NextTrack();
}; };
...@@ -65,7 +65,7 @@ interface MediaSessionObserver { ...@@ -65,7 +65,7 @@ interface MediaSessionObserver {
// WebContents or ARC app. // WebContents or ARC app.
// TODO(https://crbug.com/875004): migrate media session from content/public // TODO(https://crbug.com/875004): migrate media session from content/public
// to mojo. // to mojo.
// Next Method ID: 6 // Next Method ID: 9
interface MediaSession { interface MediaSession {
[Extensible] [Extensible]
enum SuspendType { enum SuspendType {
...@@ -100,4 +100,10 @@ interface MediaSession { ...@@ -100,4 +100,10 @@ interface MediaSession {
Resume@5(SuspendType suspend_type); Resume@5(SuspendType suspend_type);
AddObserver@6(MediaSessionObserver observer); AddObserver@6(MediaSessionObserver observer);
// Skip to the previous track.
PreviousTrack@7();
// Skip to the next track.
NextTrack@8();
}; };
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