[video pip] Add progress bar to 2024 UI
This CL adds a progress bar to the video picture-in-picture controls. It reuses the existing MediaProgressView element for this, and routes the commands to the VideoPictureInPictureWindowController. Bug: 360357715 Change-Id: I209d3d7a0c045fe00ec972861763789ce8c6aa2e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5912245 Reviewed-by: Peter Kvitek <kvitekp@chromium.org> Reviewed-by: Tommy Steimel <steimel@chromium.org> Reviewed-by: Avi Drissman <avi@chromium.org> Commit-Queue: Tommy Steimel <steimel@chromium.org> Reviewed-by: Fr <beaufort.francois@gmail.com> Cr-Commit-Position: refs/heads/main@{#1378645}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
a97a6a9628
commit
b1c91d9b6a
chrome/browser
picture_in_picture
ui
content
browser
compute_pressure
picture_in_picture
public
web_test
headless/lib/browser
@ -103,6 +103,8 @@ class MockVideoPictureInPictureWindowController
|
||||
MOCK_METHOD0(GetWebContents, content::WebContents*());
|
||||
MOCK_METHOD0(GetChildWebContents, content::WebContents*());
|
||||
MOCK_METHOD0(TogglePlayPause, bool());
|
||||
MOCK_METHOD0(Play, void());
|
||||
MOCK_METHOD0(Pause, void());
|
||||
MOCK_METHOD0(SkipAd, void());
|
||||
MOCK_METHOD0(NextTrack, void());
|
||||
MOCK_METHOD0(PreviousTrack, void());
|
||||
@ -111,6 +113,7 @@ class MockVideoPictureInPictureWindowController
|
||||
MOCK_METHOD0(HangUp, void());
|
||||
MOCK_METHOD0(PreviousSlide, void());
|
||||
MOCK_METHOD0(NextSlide, void());
|
||||
MOCK_METHOD1(SeekTo, void(base::TimeDelta time));
|
||||
MOCK_CONST_METHOD0(GetSourceBounds, const gfx::Rect&());
|
||||
MOCK_METHOD0(GetWindowBounds, std::optional<gfx::Rect>());
|
||||
MOCK_METHOD0(GetOrigin, std::optional<url::Origin>());
|
||||
|
@ -79,6 +79,7 @@ class OverlayWindowAndroid : public content::VideoOverlayWindow,
|
||||
void SetHangUpButtonVisibility(bool is_visible) override;
|
||||
void SetNextSlideButtonVisibility(bool is_visible) override;
|
||||
void SetPreviousSlideButtonVisibility(bool is_visible) override;
|
||||
void SetMediaPosition(const media_session::MediaPosition&) override {}
|
||||
void SetSurfaceId(const viz::SurfaceId& surface_id) override;
|
||||
|
||||
private:
|
||||
|
@ -597,8 +597,13 @@ void VideoOverlayWindowViews::OnMouseEvent(ui::MouseEvent* event) {
|
||||
// On Windows, ui::EventType::kMouseExited is triggered when hovering over
|
||||
// the media controls because of the HitTest. This check ensures the
|
||||
// controls are visible if the mouse is still over the window.
|
||||
// We also check that the user isn't currently dragging the progress bar,
|
||||
// since setting visibility to false during the drag will prevent the drag
|
||||
// from functioning properly (and we'll lose the drag end).
|
||||
const bool should_update_control_visibility =
|
||||
!GetWindowBackgroundView()->bounds().Contains(event->location());
|
||||
!GetWindowBackgroundView()->bounds().Contains(event->location()) &&
|
||||
progress_view_drag_state_ ==
|
||||
global_media_controls::DragState::kDragEnded;
|
||||
if (should_update_control_visibility)
|
||||
UpdateControlsVisibility(false);
|
||||
break;
|
||||
@ -788,7 +793,8 @@ bool VideoOverlayWindowViews::ControlsHitTestContainsPoint(
|
||||
GetToggleCameraButtonBounds().Contains(point) ||
|
||||
GetHangUpButtonBounds().Contains(point) ||
|
||||
GetPreviousSlideControlsBounds().Contains(point) ||
|
||||
GetNextSlideControlsBounds().Contains(point)) {
|
||||
GetNextSlideControlsBounds().Contains(point) ||
|
||||
GetProgressViewBounds().Contains(point)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -837,6 +843,7 @@ void VideoOverlayWindowViews::SetUpViews() {
|
||||
std::unique_ptr<ToggleMicrophoneButton> toggle_microphone_button;
|
||||
std::unique_ptr<ToggleCameraButton> toggle_camera_button;
|
||||
std::unique_ptr<HangUpButton> hang_up_button;
|
||||
std::unique_ptr<global_media_controls::MediaProgressView> progress_view;
|
||||
|
||||
if (Use2024UI()) {
|
||||
play_pause_controls_view->SetSize(
|
||||
@ -887,6 +894,30 @@ void VideoOverlayWindowViews::SetUpViews() {
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_PICTURE_IN_PICTURE_NEXT_TRACK_CONTROL_ACCESSIBLE_TEXT));
|
||||
next_track_controls_view->SetSize(kPreviousNextButtonSize);
|
||||
// `base::Unretained()` is okay here since we own the progress view.
|
||||
progress_view = std::make_unique<global_media_controls::MediaProgressView>(
|
||||
/*use_squiggly_line=*/false,
|
||||
/*playing_foreground_color_id=*/ui::kColorSysPrimary,
|
||||
/*playing_background_color_id=*/ui::kColorSysStateDisabledContainer,
|
||||
/*paused_foreground_color_id=*/ui::kColorSysStateDisabledContainer,
|
||||
/*paused_background_color_id=*/ui::kColorSysStateDisabledContainer,
|
||||
/*focus_ring_color_id=*/ui::kColorSysStateFocusRing,
|
||||
/*drag_state_change_callback=*/
|
||||
base::BindRepeating(
|
||||
&VideoOverlayWindowViews::OnProgressDragStateChanged,
|
||||
base::Unretained(this)),
|
||||
/*playback_state_change_for_dragging_callback=*/
|
||||
base::BindRepeating(
|
||||
&VideoOverlayWindowViews::ChangePlaybackStateForProgressDrag,
|
||||
base::Unretained(this)),
|
||||
/*seek_callback=*/
|
||||
base::BindRepeating(
|
||||
&VideoOverlayWindowViews::SeekForProgressBarInteraction,
|
||||
base::Unretained(this)),
|
||||
/*on_update_progress_callback=*/
|
||||
base::BindRepeating(
|
||||
&VideoOverlayWindowViews::OnProgressViewUpdateCurrentTime,
|
||||
base::Unretained(this)));
|
||||
} else {
|
||||
back_to_tab_label_button =
|
||||
std::make_unique<BackToTabLabelButton>(base::BindRepeating(
|
||||
@ -1025,7 +1056,11 @@ void VideoOverlayWindowViews::SetUpViews() {
|
||||
next_track_controls_view->layer()->SetFillsBoundsOpaquely(false);
|
||||
next_track_controls_view->layer()->SetName("NextTrackControlsView");
|
||||
|
||||
if (!Use2024UI()) {
|
||||
if (Use2024UI()) {
|
||||
progress_view->SetPaintToLayer(ui::LAYER_TEXTURED);
|
||||
progress_view->layer()->SetFillsBoundsOpaquely(false);
|
||||
progress_view->layer()->SetName("ProgressView");
|
||||
} else {
|
||||
// views::View that holds the skip-ad label button.
|
||||
// -------------------------
|
||||
skip_ad_controls_view->SetPaintToLayer(ui::LAYER_TEXTURED);
|
||||
@ -1089,6 +1124,11 @@ void VideoOverlayWindowViews::SetUpViews() {
|
||||
play_pause_controls_view_ = controls_container_view->AddChildView(
|
||||
std::move(play_pause_controls_view));
|
||||
|
||||
if (Use2024UI()) {
|
||||
progress_view_ =
|
||||
controls_container_view->AddChildView(std::move(progress_view));
|
||||
}
|
||||
|
||||
next_track_controls_view_ = controls_container_view->AddChildView(
|
||||
std::move(next_track_controls_view));
|
||||
if (!Use2024UI()) {
|
||||
@ -1203,6 +1243,7 @@ void VideoOverlayWindowViews::OnUpdateControlsBounds() {
|
||||
if (Use2024UI()) {
|
||||
constexpr int kTopControlsHeight = 34;
|
||||
constexpr int kBottomControlsHeight = 64;
|
||||
constexpr int kProgressBarHeight = 26;
|
||||
constexpr int kControlHorizontalMargin = 8;
|
||||
constexpr int kBottomControlsHorizontalMargin = 8;
|
||||
constexpr int kBottomControlsVerticalMargin = 4;
|
||||
@ -1245,6 +1286,28 @@ void VideoOverlayWindowViews::OnUpdateControlsBounds() {
|
||||
kPreviousNextButtonSize.width()),
|
||||
bottom_controls_bounds.y() + kBottomControlsVerticalMargin});
|
||||
|
||||
// The progress bars should take up all the space that is left after the
|
||||
// previous and next buttons. Here we calculate how much horizontal space
|
||||
// one of those buttons takes up and use that to calculate the width and x
|
||||
// position of the progress view.
|
||||
constexpr int kPreviousNextTrackWidthPlusHorizontalMargins =
|
||||
kBottomControlsHorizontalMargin + (2 * kControlHorizontalMargin) +
|
||||
kPreviousNextButtonSize.width();
|
||||
progress_view_->SetPosition(
|
||||
{bottom_controls_bounds.x() +
|
||||
kPreviousNextTrackWidthPlusHorizontalMargins,
|
||||
bottom_controls_bounds.y() + kBottomControlsVerticalMargin});
|
||||
progress_view_->SetSize(
|
||||
{bounds.width() - (2 * kPreviousNextTrackWidthPlusHorizontalMargins),
|
||||
kProgressBarHeight});
|
||||
|
||||
// The play/pause button should not be visible while dragging the progress
|
||||
// bar.
|
||||
play_pause_controls_view_->SetVisible(
|
||||
show_play_pause_button_ &&
|
||||
progress_view_drag_state_ !=
|
||||
global_media_controls::DragState::kDragStarted);
|
||||
|
||||
// The previous and next track buttons are always visible, but disabled if
|
||||
// there is no action handler for them.
|
||||
previous_track_controls_view_->SetEnabled(show_previous_track_button_);
|
||||
@ -1573,6 +1636,15 @@ void VideoOverlayWindowViews::SetHangUpButtonVisibility(bool is_visible) {
|
||||
UpdateControlsBounds();
|
||||
}
|
||||
|
||||
void VideoOverlayWindowViews::SetMediaPosition(
|
||||
const media_session::MediaPosition& position) {
|
||||
if (!Use2024UI()) {
|
||||
return;
|
||||
}
|
||||
position_ = position;
|
||||
progress_view_->UpdateProgress(position);
|
||||
}
|
||||
|
||||
void VideoOverlayWindowViews::SetSurfaceId(const viz::SurfaceId& surface_id) {
|
||||
// The PiP window may have a previous surface set. If the window stays open
|
||||
// since then, we need to unregister the previous frame sink; otherwise the
|
||||
@ -1741,6 +1813,13 @@ gfx::Rect VideoOverlayWindowViews::GetNextSlideControlsBounds() {
|
||||
return next_slide_controls_view_->GetMirroredBounds();
|
||||
}
|
||||
|
||||
gfx::Rect VideoOverlayWindowViews::GetProgressViewBounds() {
|
||||
if (!Use2024UI()) {
|
||||
return gfx::Rect();
|
||||
}
|
||||
return progress_view_->GetMirroredBounds();
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
int VideoOverlayWindowViews::GetResizeHTComponent() const {
|
||||
return resize_handle_view_->GetHTComponent();
|
||||
@ -1808,6 +1887,11 @@ VideoOverlayWindowViews::previous_slide_controls_view_for_testing() const {
|
||||
return previous_slide_controls_view_;
|
||||
}
|
||||
|
||||
global_media_controls::MediaProgressView*
|
||||
VideoOverlayWindowViews::progress_view_for_testing() const {
|
||||
return progress_view_;
|
||||
}
|
||||
|
||||
CloseImageButton* VideoOverlayWindowViews::close_button_for_testing() const {
|
||||
return close_controls_view_;
|
||||
}
|
||||
@ -1870,3 +1954,31 @@ void VideoOverlayWindowViews::RemoveOverlayViewIfExists() {
|
||||
OnSizeConstraintsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoOverlayWindowViews::OnProgressDragStateChanged(
|
||||
global_media_controls::DragState drag_state) {
|
||||
progress_view_drag_state_ = drag_state;
|
||||
OnUpdateControlsBounds();
|
||||
}
|
||||
|
||||
void VideoOverlayWindowViews::ChangePlaybackStateForProgressDrag(
|
||||
global_media_controls::PlaybackStateChangeForDragging
|
||||
playback_state_change) {
|
||||
if (playback_state_change ==
|
||||
global_media_controls::PlaybackStateChangeForDragging::
|
||||
kPauseForDraggingStarted) {
|
||||
controller_->Pause();
|
||||
} else {
|
||||
controller_->Play();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoOverlayWindowViews::SeekForProgressBarInteraction(
|
||||
double seek_progress) {
|
||||
controller_->SeekTo(seek_progress * position_.duration());
|
||||
}
|
||||
|
||||
void VideoOverlayWindowViews::OnProgressViewUpdateCurrentTime(
|
||||
base::TimeDelta current_time) {
|
||||
// TODO(crbug.com/360357715): Update current time view once it exists.
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "base/timer/timer.h"
|
||||
#include "chrome/browser/picture_in_picture/auto_pip_setting_overlay_view.h"
|
||||
#include "chromeos/ui/frame/highlight_border_overlay.h"
|
||||
#include "components/global_media_controls/public/views/media_progress_view.h"
|
||||
#include "content/public/browser/overlay_window.h"
|
||||
#include "content/public/browser/video_picture_in_picture_window_controller.h"
|
||||
#include "ui/display/display.h"
|
||||
@ -78,6 +79,7 @@ class VideoOverlayWindowViews : public content::VideoOverlayWindow,
|
||||
void SetHangUpButtonVisibility(bool is_visible) override;
|
||||
void SetPreviousSlideButtonVisibility(bool is_visible) override;
|
||||
void SetNextSlideButtonVisibility(bool is_visible) override;
|
||||
void SetMediaPosition(const media_session::MediaPosition& position) override;
|
||||
void SetSurfaceId(const viz::SurfaceId& surface_id) override;
|
||||
|
||||
// views::Widget:
|
||||
@ -154,6 +156,7 @@ class VideoOverlayWindowViews : public content::VideoOverlayWindow,
|
||||
gfx::Rect GetHangUpButtonBounds();
|
||||
gfx::Rect GetPreviousSlideControlsBounds();
|
||||
gfx::Rect GetNextSlideControlsBounds();
|
||||
gfx::Rect GetProgressViewBounds();
|
||||
|
||||
PlaybackImageButton* play_pause_controls_view_for_testing() const;
|
||||
SimpleOverlayWindowImageButton* next_track_controls_view_for_testing() const;
|
||||
@ -166,6 +169,7 @@ class VideoOverlayWindowViews : public content::VideoOverlayWindow,
|
||||
SimpleOverlayWindowImageButton* next_slide_controls_view_for_testing() const;
|
||||
SimpleOverlayWindowImageButton* previous_slide_controls_view_for_testing()
|
||||
const;
|
||||
global_media_controls::MediaProgressView* progress_view_for_testing() const;
|
||||
CloseImageButton* close_button_for_testing() const;
|
||||
OverlayWindowMinimizeButton* minimize_button_for_testing() const;
|
||||
OverlayWindowBackToTabButton* back_to_tab_button_for_testing() const;
|
||||
@ -276,6 +280,13 @@ class VideoOverlayWindowViews : public content::VideoOverlayWindow,
|
||||
// Removes the `overlay_view_` if it exists.
|
||||
void RemoveOverlayViewIfExists();
|
||||
|
||||
void OnProgressDragStateChanged(global_media_controls::DragState drag_state);
|
||||
void ChangePlaybackStateForProgressDrag(
|
||||
global_media_controls::PlaybackStateChangeForDragging
|
||||
playback_state_change);
|
||||
void SeekForProgressBarInteraction(double seek_progress);
|
||||
void OnProgressViewUpdateCurrentTime(base::TimeDelta current_time);
|
||||
|
||||
// Not owned; |controller_| owns |this|.
|
||||
raw_ptr<content::VideoPictureInPictureWindowController> controller_;
|
||||
|
||||
@ -344,6 +355,7 @@ class VideoOverlayWindowViews : public content::VideoOverlayWindow,
|
||||
raw_ptr<SimpleOverlayWindowImageButton> previous_slide_controls_view_ =
|
||||
nullptr;
|
||||
raw_ptr<SimpleOverlayWindowImageButton> next_slide_controls_view_ = nullptr;
|
||||
raw_ptr<global_media_controls::MediaProgressView> progress_view_ = nullptr;
|
||||
raw_ptr<AutoPipSettingOverlayView> overlay_view_ = nullptr;
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
@ -388,6 +400,15 @@ class VideoOverlayWindowViews : public content::VideoOverlayWindow,
|
||||
// case when Media Session "nextslide" action is handled by the website.
|
||||
bool show_next_slide_button_ = false;
|
||||
|
||||
// Tracks whether or not the progress bar is currently being dragged by the
|
||||
// user. Used to ensure that controls don't hide while dragging.
|
||||
global_media_controls::DragState progress_view_drag_state_ =
|
||||
global_media_controls::DragState::kDragEnded;
|
||||
|
||||
// Tracks the current position of media playback. Used for seeking to the
|
||||
// proper time when the user interacts with the progress bar.
|
||||
media_session::MediaPosition position_;
|
||||
|
||||
// Whether or not the current frame sink for the surface displayed in the
|
||||
// |video_view_| is registered as the child of the overlay window frame sink.
|
||||
bool has_registered_frame_sink_hierarchy_ = false;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "chrome/browser/ui/views/overlay/simple_overlay_window_image_button.h"
|
||||
#include "chrome/test/base/testing_profile.h"
|
||||
#include "chrome/test/views/chrome_views_test_base.h"
|
||||
#include "components/global_media_controls/public/views/media_progress_view.h"
|
||||
#include "content/public/browser/overlay_window.h"
|
||||
#include "content/public/browser/video_picture_in_picture_window_controller.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
@ -105,6 +106,8 @@ class TestVideoPictureInPictureWindowController
|
||||
content::WebContents* GetWebContents() override { return web_contents_; }
|
||||
content::WebContents* GetChildWebContents() override { return nullptr; }
|
||||
bool TogglePlayPause() override { return false; }
|
||||
void Play() override {}
|
||||
void Pause() override {}
|
||||
void SkipAd() override {}
|
||||
void NextTrack() override {}
|
||||
void PreviousTrack() override {}
|
||||
@ -113,6 +116,7 @@ class TestVideoPictureInPictureWindowController
|
||||
void ToggleMicrophone() override {}
|
||||
void ToggleCamera() override {}
|
||||
void HangUp() override {}
|
||||
MOCK_METHOD(void, SeekTo, (base::TimeDelta time));
|
||||
const gfx::Rect& GetSourceBounds() const override { return source_bounds_; }
|
||||
std::optional<gfx::Rect> GetWindowBounds() override { return std::nullopt; }
|
||||
std::optional<url::Origin> GetOrigin() override { return std::nullopt; }
|
||||
@ -728,6 +732,13 @@ TEST_F(VideoOverlayWindowViewsTest, IsTrackedByTheOcclusionObserver) {
|
||||
EXPECT_EQ(0u, tracker->GetPictureInPictureWidgetsForTesting().size());
|
||||
}
|
||||
|
||||
TEST_F(VideoOverlayWindowViewsTest, ProgressBarNotDrawnWhen2024UIIsDisabled) {
|
||||
overlay_window().ForceControlsVisibleForTesting(true);
|
||||
global_media_controls::MediaProgressView* progress_view =
|
||||
overlay_window().progress_view_for_testing();
|
||||
ASSERT_EQ(nullptr, progress_view);
|
||||
}
|
||||
|
||||
class VideoOverlayWindowViewsWith2024UITest
|
||||
: public VideoOverlayWindowViewsTest {
|
||||
public:
|
||||
@ -770,3 +781,23 @@ TEST_F(VideoOverlayWindowViewsWith2024UITest, ShowsBackToTabImageButton) {
|
||||
button_clicker.NotifyClick(dummy_event);
|
||||
testing::Mock::VerifyAndClearExpectations(&pip_window_controller());
|
||||
}
|
||||
|
||||
TEST_F(VideoOverlayWindowViewsWith2024UITest, ProgressBarSeeksVideo) {
|
||||
overlay_window().ForceControlsVisibleForTesting(true);
|
||||
global_media_controls::MediaProgressView* progress_view =
|
||||
overlay_window().progress_view_for_testing();
|
||||
ASSERT_NE(nullptr, progress_view);
|
||||
EXPECT_TRUE(progress_view->IsDrawn());
|
||||
|
||||
gfx::Point point(progress_view->width() / 2, progress_view->height() / 2);
|
||||
ui::MouseEvent pressed_event(ui::EventType::kMousePressed, point, point,
|
||||
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
|
||||
ui::EF_LEFT_MOUSE_BUTTON);
|
||||
EXPECT_CALL(pip_window_controller(), SeekTo(_));
|
||||
progress_view->OnMousePressed(pressed_event);
|
||||
|
||||
ui::MouseEvent released_event = ui::MouseEvent(
|
||||
ui::EventType::kMouseReleased, point, point, ui::EventTimeForNow(),
|
||||
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
|
||||
progress_view->OnMouseReleased(released_event);
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ class TestVideoOverlayWindow : public VideoOverlayWindow {
|
||||
void SetHangUpButtonVisibility(bool is_visible) override {}
|
||||
void SetNextSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetPreviousSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetMediaPosition(const media_session::MediaPosition&) override {}
|
||||
void SetSurfaceId(const viz::SurfaceId& surface_id) override {}
|
||||
|
||||
private:
|
||||
|
@ -92,6 +92,7 @@ class TestOverlayWindow : public VideoOverlayWindow {
|
||||
void SetHangUpButtonVisibility(bool is_visible) override {}
|
||||
void SetNextSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetPreviousSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetMediaPosition(const media_session::MediaPosition&) override {}
|
||||
void SetSurfaceId(const viz::SurfaceId& surface_id) override {}
|
||||
|
||||
private:
|
||||
|
@ -72,6 +72,7 @@ class TestVideoOverlayWindow : public VideoOverlayWindow {
|
||||
void SetHangUpButtonVisibility(bool is_visible) override {}
|
||||
void SetNextSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetPreviousSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetMediaPosition(const media_session::MediaPosition&) override {}
|
||||
void SetSurfaceId(const viz::SurfaceId& surface_id) override {}
|
||||
|
||||
const std::optional<PlaybackState>& playback_state() const {
|
||||
|
@ -208,17 +208,30 @@ bool VideoPictureInPictureWindowControllerImpl::TogglePlayPause() {
|
||||
DCHECK(active_session_);
|
||||
|
||||
if (IsPlayerActive()) {
|
||||
if (media_session_action_pause_handled_) {
|
||||
MediaSessionImpl::Get(web_contents())
|
||||
->Suspend(MediaSession::SuspendType::kUI);
|
||||
return true /* still playing */;
|
||||
}
|
||||
|
||||
active_session_->GetMediaPlayerRemote()->RequestPause(
|
||||
/*triggered_by_user=*/false);
|
||||
return false /* paused */;
|
||||
return PauseInternal();
|
||||
}
|
||||
return PlayInternal();
|
||||
}
|
||||
|
||||
void VideoPictureInPictureWindowControllerImpl::Play() {
|
||||
// This comes from the window, rather than the renderer, so we must actually
|
||||
// have a window at this point.
|
||||
DCHECK(window_);
|
||||
DCHECK(active_session_);
|
||||
|
||||
PlayInternal();
|
||||
}
|
||||
|
||||
void VideoPictureInPictureWindowControllerImpl::Pause() {
|
||||
// This comes from the window, rather than the renderer, so we must actually
|
||||
// have a window at this point.
|
||||
DCHECK(window_);
|
||||
DCHECK(active_session_);
|
||||
|
||||
PauseInternal();
|
||||
}
|
||||
|
||||
bool VideoPictureInPictureWindowControllerImpl::PlayInternal() {
|
||||
if (media_session_action_play_handled_) {
|
||||
MediaSessionImpl::Get(web_contents())
|
||||
->Resume(MediaSession::SuspendType::kUI);
|
||||
@ -229,6 +242,18 @@ bool VideoPictureInPictureWindowControllerImpl::TogglePlayPause() {
|
||||
return true /* playing */;
|
||||
}
|
||||
|
||||
bool VideoPictureInPictureWindowControllerImpl::PauseInternal() {
|
||||
if (media_session_action_pause_handled_) {
|
||||
MediaSessionImpl::Get(web_contents())
|
||||
->Suspend(MediaSession::SuspendType::kUI);
|
||||
return true /* still playing */;
|
||||
}
|
||||
|
||||
active_session_->GetMediaPlayerRemote()->RequestPause(
|
||||
/*triggered_by_user=*/false);
|
||||
return false /* paused */;
|
||||
}
|
||||
|
||||
PictureInPictureResult VideoPictureInPictureWindowControllerImpl::StartSession(
|
||||
PictureInPictureServiceImpl* service,
|
||||
const MediaPlayerId& player_id,
|
||||
@ -340,6 +365,12 @@ void VideoPictureInPictureWindowControllerImpl::HangUp() {
|
||||
MediaSession::Get(web_contents())->HangUp();
|
||||
}
|
||||
|
||||
void VideoPictureInPictureWindowControllerImpl::SeekTo(base::TimeDelta time) {
|
||||
if (media_session_action_seek_to_handled_) {
|
||||
MediaSession::Get(web_contents())->SeekTo(time);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoPictureInPictureWindowControllerImpl::MediaSessionInfoChanged(
|
||||
const media_session::mojom::MediaSessionInfoPtr& info) {
|
||||
if (!info)
|
||||
@ -395,6 +426,9 @@ void VideoPictureInPictureWindowControllerImpl::MediaSessionActionsChanged(
|
||||
media_session_action_next_slide_handled_ =
|
||||
actions.find(media_session::mojom::MediaSessionAction::kNextSlide) !=
|
||||
actions.end();
|
||||
media_session_action_seek_to_handled_ =
|
||||
actions.find(media_session::mojom::MediaSessionAction::kSeekTo) !=
|
||||
actions.end();
|
||||
|
||||
if (!window_)
|
||||
return;
|
||||
@ -420,6 +454,10 @@ void VideoPictureInPictureWindowControllerImpl::MediaSessionPositionChanged(
|
||||
const std::optional<media_session::MediaPosition>& media_position) {
|
||||
media_position_ = media_position;
|
||||
UpdatePlaybackState();
|
||||
|
||||
if (window_ && media_position.has_value()) {
|
||||
window_->SetMediaPosition(*media_position);
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Size VideoPictureInPictureWindowControllerImpl::GetSize() {
|
||||
|
@ -72,6 +72,8 @@ class CONTENT_EXPORT VideoPictureInPictureWindowControllerImpl
|
||||
WebContents* GetWebContents() override;
|
||||
WebContents* GetChildWebContents() override;
|
||||
bool TogglePlayPause() override;
|
||||
void Play() override;
|
||||
void Pause() override;
|
||||
void SkipAd() override;
|
||||
void NextTrack() override;
|
||||
void PreviousTrack() override;
|
||||
@ -80,6 +82,7 @@ class CONTENT_EXPORT VideoPictureInPictureWindowControllerImpl
|
||||
void HangUp() override;
|
||||
void PreviousSlide() override;
|
||||
void NextSlide() override;
|
||||
void SeekTo(base::TimeDelta time) override;
|
||||
void SetOnWindowCreatedNotifyObserversCallback(
|
||||
base::OnceClosure on_window_created_notify_observers_callback) override;
|
||||
|
||||
@ -168,6 +171,12 @@ class CONTENT_EXPORT VideoPictureInPictureWindowControllerImpl
|
||||
// Returns the web_contents() as a WebContentsImpl*.
|
||||
WebContentsImpl* GetWebContentsImpl();
|
||||
|
||||
// Returns true if the player is active after this call.
|
||||
bool PlayInternal();
|
||||
|
||||
// Returns true if the player is active after this call.
|
||||
bool PauseInternal();
|
||||
|
||||
std::unique_ptr<VideoOverlayWindow> window_;
|
||||
|
||||
viz::SurfaceId surface_id_;
|
||||
@ -184,6 +193,7 @@ class CONTENT_EXPORT VideoPictureInPictureWindowControllerImpl
|
||||
bool media_session_action_hang_up_handled_ = false;
|
||||
bool media_session_action_previous_slide_handled_ = false;
|
||||
bool media_session_action_next_slide_handled_ = false;
|
||||
bool media_session_action_seek_to_handled_ = false;
|
||||
|
||||
// Tracks the current microphone state.
|
||||
bool microphone_muted_ = false;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "services/media_session/public/cpp/media_position.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
namespace gfx {
|
||||
@ -67,6 +68,8 @@ class VideoOverlayWindow {
|
||||
virtual void SetHangUpButtonVisibility(bool is_visible) = 0;
|
||||
virtual void SetNextSlideButtonVisibility(bool is_visible) = 0;
|
||||
virtual void SetPreviousSlideButtonVisibility(bool is_visible) = 0;
|
||||
virtual void SetMediaPosition(
|
||||
const media_session::MediaPosition& position) = 0;
|
||||
|
||||
virtual void SetSurfaceId(const viz::SurfaceId& surface_id) = 0;
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define CONTENT_PUBLIC_BROWSER_VIDEO_PICTURE_IN_PICTURE_WINDOW_CONTROLLER_H_
|
||||
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "base/time/time.h"
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/public/browser/picture_in_picture_window_controller.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
@ -34,6 +35,12 @@ class VideoPictureInPictureWindowController
|
||||
// call.
|
||||
virtual bool TogglePlayPause() = 0;
|
||||
|
||||
// Requests the player to begin playback.
|
||||
virtual void Play() = 0;
|
||||
|
||||
// Requests the player to suspend playback.
|
||||
virtual void Pause() = 0;
|
||||
|
||||
// Called when the user interacts with the "Toggle Microphone" control.
|
||||
virtual void ToggleMicrophone() = 0;
|
||||
|
||||
@ -49,6 +56,9 @@ class VideoPictureInPictureWindowController
|
||||
// Called when the user interacts with the "Next Slide" control.
|
||||
virtual void NextSlide() = 0;
|
||||
|
||||
// Called when the user interacts with the progress bar control.
|
||||
virtual void SeekTo(base::TimeDelta time) = 0;
|
||||
|
||||
// Returns the source bounds of the video, in the WebContents top-level
|
||||
// coordinate space, of the video before it enters picture in picture.
|
||||
virtual const gfx::Rect& GetSourceBounds() const = 0;
|
||||
|
@ -149,6 +149,7 @@ class BoundsMatchVideoSizeOverlayWindow : public VideoOverlayWindow {
|
||||
void SetHangUpButtonVisibility(bool is_visible) override {}
|
||||
void SetNextSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetPreviousSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetMediaPosition(const media_session::MediaPosition&) override {}
|
||||
void SetSurfaceId(const viz::SurfaceId& surface_id) override {}
|
||||
|
||||
private:
|
||||
|
@ -107,6 +107,7 @@ class HeadlessVideoOverlayWindow : public content::VideoOverlayWindow {
|
||||
void SetHangUpButtonVisibility(bool is_visible) override {}
|
||||
void SetNextSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetPreviousSlideButtonVisibility(bool is_visible) override {}
|
||||
void SetMediaPosition(const media_session::MediaPosition&) override {}
|
||||
|
||||
void SetSurfaceId(const viz::SurfaceId& surface_id) override {}
|
||||
|
||||
|
Reference in New Issue
Block a user