Commit ee4c468a authored by Jennifer Apacible's avatar Jennifer Apacible Committed by Commit Bot

[Picture in Picture] Track current PiP'd video.

Keep track of the id of the video currently in Picture-in-Picture
mode. This is used for controlling video playback, such as play
and pause.

First of several patches relevant to controlling the video from
the PiP'd window.

BUG: 726619
Change-Id: Ie07dcc78d59c7b95e549e350479ee4d154400762
Reviewed-on: https://chromium-review.googlesource.com/960565
Commit-Queue: apacible <apacible@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545679}
parent bdac817c
......@@ -105,6 +105,11 @@ MediaWebContentsObserver::GetFullscreenVideoMediaPlayerId() const {
return fullscreen_player_;
}
const base::Optional<WebContentsObserver::MediaPlayerId>&
MediaWebContentsObserver::GetPictureInPictureVideoMediaPlayerId() const {
return pip_player_;
}
bool MediaWebContentsObserver::OnMessageReceived(
const IPC::Message& msg,
RenderFrameHost* render_frame_host) {
......@@ -123,6 +128,9 @@ bool MediaWebContentsObserver::OnMessageReceived(
OnMediaEffectivelyFullscreenChanged)
IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnMediaSizeChanged,
OnMediaSizeChanged)
IPC_MESSAGE_HANDLER(
MediaPlayerDelegateHostMsg_OnPictureInPictureSourceChanged,
OnPictureInPictureSourceChanged)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
......@@ -165,6 +173,11 @@ void MediaWebContentsObserver::OnMediaDestroyed(
RenderFrameHost* render_frame_host,
int delegate_id) {
OnMediaPaused(render_frame_host, delegate_id, true);
if (pip_player_ &&
pip_player_ == MediaPlayerId(render_frame_host, delegate_id)) {
pip_player_.reset();
}
}
void MediaWebContentsObserver::OnMediaPaused(RenderFrameHost* render_frame_host,
......@@ -268,6 +281,12 @@ void MediaWebContentsObserver::OnMediaSizeChanged(
web_contents_impl()->MediaResized(size, id);
}
void MediaWebContentsObserver::OnPictureInPictureSourceChanged(
RenderFrameHost* render_frame_host,
int delegate_id) {
pip_player_ = MediaPlayerId(render_frame_host, delegate_id);
}
void MediaWebContentsObserver::ClearWakeLocks(
RenderFrameHost* render_frame_host) {
std::set<MediaPlayerId> video_players;
......
......@@ -62,6 +62,10 @@ class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver {
// Gets the MediaPlayerId of the fullscreen video if it exists.
const base::Optional<MediaPlayerId>& GetFullscreenVideoMediaPlayerId() const;
// Gets the MediaPlayerId of the picture in picture video if it exists.
const base::Optional<MediaPlayerId>& GetPictureInPictureVideoMediaPlayerId()
const;
// WebContentsObserver implementation.
void WebContentsDestroyed() override;
void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
......@@ -109,6 +113,8 @@ class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver {
void OnMediaMutedStatusChanged(RenderFrameHost* render_frame_host,
int delegate_id,
bool muted);
void OnPictureInPictureSourceChanged(RenderFrameHost* render_frame_host,
int delegate_id);
// Clear |render_frame_host|'s tracking entry for its WakeLocks.
void ClearWakeLocks(RenderFrameHost* render_frame_host);
......@@ -146,6 +152,7 @@ class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver {
device::mojom::WakeLockPtr audio_wake_lock_;
device::mojom::WakeLockPtr video_wake_lock_;
base::Optional<MediaPlayerId> fullscreen_player_;
base::Optional<MediaPlayerId> pip_player_;
base::Optional<bool> picture_in_picture_allowed_in_fullscreen_;
bool has_audio_wake_lock_for_testing_ = false;
bool has_video_wake_lock_for_testing_ = false;
......
......@@ -3463,6 +3463,32 @@ TEST_F(WebContentsImplTest, ThemeColorChangeDependingOnFirstVisiblePaint) {
EXPECT_EQ(SK_ColorGREEN, observer.last_theme_color());
}
TEST_F(WebContentsImplTest, PictureInPictureMediaPlayerIdWasChanged) {
const int kPlayerVideoOnlyId = 30; /* arbitrary and used for tests */
MediaWebContentsObserver* observer =
contents()->media_web_contents_observer();
TestRenderFrameHost* rfh = main_test_rfh();
rfh->InitializeRenderFrameIfNeeded();
// If Picture-in-Picture was never triggered, the media player id would not be
// set.
EXPECT_FALSE(observer->GetPictureInPictureVideoMediaPlayerId().has_value());
rfh->OnMessageReceived(
MediaPlayerDelegateHostMsg_OnPictureInPictureSourceChanged(
rfh->GetRoutingID(), kPlayerVideoOnlyId));
EXPECT_TRUE(observer->GetPictureInPictureVideoMediaPlayerId().has_value());
EXPECT_EQ(kPlayerVideoOnlyId,
observer->GetPictureInPictureVideoMediaPlayerId()->second);
// Picture-in-Picture media player id should be reset when the media is
// destroyed.
rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnMediaDestroyed(
rfh->GetRoutingID(), kPlayerVideoOnlyId));
EXPECT_FALSE(observer->GetPictureInPictureVideoMediaPlayerId().has_value());
}
TEST_F(WebContentsImplTest, ParseDownloadHeaders) {
download::DownloadUrlParameters::RequestHeadersType request_headers =
WebContentsImpl::ParseDownloadHeaders("A: 1\r\nB: 2\r\nC: 3\r\n\r\n");
......
......@@ -83,4 +83,7 @@ IPC_MESSAGE_ROUTED2(MediaPlayerDelegateHostMsg_OnMediaSizeChanged,
int /* delegate_id, distinguishes instances */,
gfx::Size /* new size of video */)
IPC_MESSAGE_ROUTED1(MediaPlayerDelegateHostMsg_OnPictureInPictureSourceChanged,
int /* delegate id */)
#endif // CONTENT_COMMON_MEDIA_MEDIA_PLAYER_DELEGATE_MESSAGES_H_
......@@ -116,6 +116,12 @@ void RendererWebMediaPlayerDelegate::DidPlayerMutedStatusChange(int delegate_id,
delegate_id, muted));
}
void RendererWebMediaPlayerDelegate::DidPictureInPictureSourceChange(
int delegate_id) {
Send(new MediaPlayerDelegateHostMsg_OnPictureInPictureSourceChanged(
routing_id(), delegate_id));
}
void RendererWebMediaPlayerDelegate::DidPause(int player_id) {
DVLOG(2) << __func__ << "(" << player_id << ")";
DCHECK(id_map_.Lookup(player_id));
......
......@@ -65,6 +65,7 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate
blink::WebFullscreenVideoStatus fullscreen_video_status) override;
void DidPlayerSizeChange(int delegate_id, const gfx::Size& size) override;
void DidPlayerMutedStatusChange(int delegate_id, bool muted) override;
void DidPictureInPictureSourceChange(int delegate_id) override;
// content::RenderFrameObserver overrides.
void WasHidden() override;
......
......@@ -73,6 +73,10 @@ class FakeWebMediaPlayerDelegate
EXPECT_EQ(delegate_id_, delegate_id);
}
void DidPictureInPictureSourceChange(int delegate_id) override {
EXPECT_EQ(delegate_id_, delegate_id);
}
void DidPause(int delegate_id) override {
EXPECT_EQ(delegate_id_, delegate_id);
EXPECT_TRUE(playing_);
......
......@@ -100,6 +100,9 @@ class WebMediaPlayerDelegate {
// Notify that the muted status of the media player has changed.
virtual void DidPlayerMutedStatusChange(int delegate_id, bool muted) = 0;
// Notify that the source media player of Picture-in-Picture has changed.
virtual void DidPictureInPictureSourceChange(int delegate_id) = 0;
// Notify that playback is stopped. This will drop wake locks and remove any
// external controls.
//
......
......@@ -781,6 +781,10 @@ void WebMediaPlayerImpl::EnterPictureInPicture() {
pip_surface_info_cb_.Run(pip_surface_id_);
// Updates the MediaWebContentsObserver with |delegate_id_| to track which
// media player is in Picture-in-Picture mode.
delegate_->DidPictureInPictureSourceChange(delegate_id_);
if (client_)
client_->PictureInPictureStarted();
}
......
......@@ -178,6 +178,8 @@ class MockWebMediaPlayerDelegate : public WebMediaPlayerDelegate {
DCHECK_EQ(player_id_, delegate_id);
}
MOCK_METHOD1(DidPictureInPictureSourceChange, void(int));
void ClearStaleFlag(int player_id) override {
DCHECK_EQ(player_id_, player_id);
is_stale_ = false;
......@@ -223,6 +225,8 @@ class MockWebMediaPlayerDelegate : public WebMediaPlayerDelegate {
void SetFrameClosedForTesting(bool is_closed) { is_closed_ = is_closed; }
int player_id() { return player_id_; }
private:
Observer* observer_ = nullptr;
int player_id_ = 1234;
......@@ -402,7 +406,7 @@ class WebMediaPlayerImplTest : public testing::Test {
void OnMetadata(PipelineMetadata metadata) {
if (base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo)) {
EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
.WillOnce(ReturnRef(id_));
.WillOnce(ReturnRef(frame_sink_id_));
EXPECT_CALL(*compositor_, EnableSubmission(_, _));
}
wmpi_->OnMetadata(metadata);
......@@ -529,7 +533,12 @@ class WebMediaPlayerImplTest : public testing::Test {
// The client interface used by |wmpi_|.
NiceMock<MockWebMediaPlayerClient> client_;
viz::FrameSinkId id_ = viz::FrameSinkId(1, 1);
viz::FrameSinkId frame_sink_id_ = viz::FrameSinkId(1, 1);
viz::LocalSurfaceId local_surface_id_ =
viz::LocalSurfaceId(11, base::UnguessableToken::Deserialize(0x111111, 0));
viz::SurfaceId surface_id_ =
viz::SurfaceId(frame_sink_id_, local_surface_id_);
NiceMock<MockWebMediaPlayerDelegate> delegate_;
......@@ -1082,20 +1091,15 @@ TEST_F(WebMediaPlayerImplTest, PlaybackRateChangeMediaLogs) {
TEST_F(WebMediaPlayerImplTest, PictureInPictureTriggerCallback) {
InitializeWebMediaPlayerImpl();
// Set up valid viz::SurfaceId. Values are arbitrary for test purposes.
viz::FrameSinkId frame_sink_id = viz::FrameSinkId(1, 1);
viz::LocalSurfaceId local_surface_id =
viz::LocalSurfaceId(11, base::UnguessableToken::Deserialize(0x111111, 0));
const viz::SurfaceId& surface_id =
viz::SurfaceId(frame_sink_id, local_surface_id);
// This call should do nothing because there is no SurfaceId set.
wmpi_->EnterPictureInPicture();
EXPECT_CALL(client_, IsInPictureInPictureMode());
wmpi_->OnSurfaceIdUpdated(surface_id);
wmpi_->OnSurfaceIdUpdated(surface_id_);
testing::Mock::VerifyAndClearExpectations(&client_);
EXPECT_CALL(pip_surface_info_cb_, Run(surface_id));
EXPECT_CALL(delegate_,
DidPictureInPictureSourceChange(delegate_.player_id()));
EXPECT_CALL(pip_surface_info_cb_, Run(surface_id_));
// This call should trigger the callback since the SurfaceId is set.
wmpi_->EnterPictureInPicture();
}
......
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