1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222

content / browser / picture_in_picture / video_picture_in_picture_window_controller_impl.h [blame]

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_PICTURE_IN_PICTURE_VIDEO_PICTURE_IN_PICTURE_WINDOW_CONTROLLER_IMPL_H_
#define CONTENT_BROWSER_PICTURE_IN_PICTURE_VIDEO_PICTURE_IN_PICTURE_WINDOW_CONTROLLER_IMPL_H_

#include <map>
#include <set>

#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "content/common/content_export.h"
#include "content/public/browser/media_player_id.h"
#include "content/public/browser/video_picture_in_picture_window_controller.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "media/mojo/mojom/media_player.mojom.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom.h"

namespace content {

class PictureInPictureServiceImpl;
class PictureInPictureSession;
class WebContents;
class WebContentsImpl;
enum class PictureInPictureResult;

// VideoPictureInPictureWindowControllerImpl is the corner stone of the video
// Picture-in-Picture feature in the //content layer. It handles the session
// creation requests (sent by the PictureInPictureServiceImpl), owns the session
// object and therefore handles its lifetime, and communicate with the rest of
// the browser. Requests to the WebContents are sent by the controller and it
// gets notified when the browser needs it to update the Picture-in-Picture
// session.
// The VideoPictureInPictureWindowControllerImpl is managing Picture-in-Picture
// at a WebContents level. If multiple calls request a Picture-in-Picture
// session either in the same frame or in different frames, the controller will
// handle creating the new session, stopping the current one and making sure the
// window is kept around when possible.
class CONTENT_EXPORT VideoPictureInPictureWindowControllerImpl
    : public VideoPictureInPictureWindowController,
      public WebContentsUserData<VideoPictureInPictureWindowControllerImpl>,
      public WebContentsObserver {
 public:
  // Gets a reference to the controller associated with |web_contents| and
  // creates one if it does not exist. The returned pointer is guaranteed to be
  // non-null.
  static VideoPictureInPictureWindowControllerImpl* GetOrCreateForWebContents(
      WebContents* web_contents);

  VideoPictureInPictureWindowControllerImpl(
      const VideoPictureInPictureWindowControllerImpl&) = delete;
  VideoPictureInPictureWindowControllerImpl& operator=(
      const VideoPictureInPictureWindowControllerImpl&) = delete;

  ~VideoPictureInPictureWindowControllerImpl() override;

  using PlayerSet = std::set<int>;

  // PictureInPictureWindowController:
  void Show() override;
  void FocusInitiator() override;
  void Close(bool should_pause_video) override;
  void CloseAndFocusInitiator() override;
  void OnWindowDestroyed(bool should_pause_video) override;
  VideoOverlayWindow* GetWindowForTesting() override;
  void UpdateLayerBounds() override;
  bool IsPlayerActive() override;
  WebContents* GetWebContents() override;
  WebContents* GetChildWebContents() override;
  bool TogglePlayPause() override;
  void SkipAd() override;
  void NextTrack() override;
  void PreviousTrack() override;
  void ToggleMicrophone() override;
  void ToggleCamera() override;
  void HangUp() override;
  void PreviousSlide() override;
  void NextSlide() override;
  void SetOnWindowCreatedNotifyObserversCallback(
      base::OnceClosure on_window_created_notify_observers_callback) override;

  const gfx::Rect& GetSourceBounds() const override;
  std::optional<gfx::Rect> GetWindowBounds() override;

  std::optional<url::Origin> GetOrigin() override;
  void SetOrigin(std::optional<url::Origin> origin);

  // Called by the MediaSessionImpl when the MediaSessionInfo changes.
  void MediaSessionInfoChanged(
      const media_session::mojom::MediaSessionInfoPtr& info);

  void MediaSessionActionsChanged(
      const std::set<media_session::mojom::MediaSessionAction>& actions);

  void MediaSessionPositionChanged(
      const std::optional<media_session::MediaPosition>& media_position);

  gfx::Size GetSize();

  // WebContentsObserver:
  void MediaStartedPlaying(const MediaPlayerInfo&,
                           const MediaPlayerId&) override;
  void MediaStoppedPlaying(const MediaPlayerInfo&,
                           const MediaPlayerId&,
                           WebContentsObserver::MediaStoppedReason) override;
  void WebContentsDestroyed() override;

  // Embeds a surface in the Picture-in-Picture window.
  void EmbedSurface(const viz::SurfaceId& surface_id,
                    const gfx::Size& natural_size);

  void SetShowPlayPauseButton(bool show_play_pause_button);

  // Called by PictureInPictureServiceImpl when a session request is received.
  // The call should return the |session_remote| and |window_size| as out
  // params. A failure to create the session should be expressed with an empty
  // |window_size| and uninitialized |session_remote|.
  // Returns whether the session creation was successful.
  PictureInPictureResult StartSession(
      PictureInPictureServiceImpl* service,
      const MediaPlayerId&,
      mojo::PendingAssociatedRemote<media::mojom::MediaPlayer> player_remote,
      const viz::SurfaceId& surface_id,
      const gfx::Size& natural_size,
      bool show_play_pause_button,
      mojo::PendingRemote<blink::mojom::PictureInPictureSessionObserver>,
      const gfx::Rect& source_bounds,
      mojo::PendingRemote<blink::mojom::PictureInPictureSession>*
          session_remote,
      gfx::Size* window_size);

  // Called by PictureInPictureServiceImpl when the service is about to be
  // destroyed. It allows |this| to close the |active_session_| if it is
  // associated with the service.
  void OnServiceDeleted(PictureInPictureServiceImpl* service);

  PictureInPictureSession* active_session_for_testing() const {
    return active_session_.get();
  }

 protected:
  // Use VideoPictureInPictureWindowControllerImpl::GetOrCreateForWebContents()
  // to create an instance.
  explicit VideoPictureInPictureWindowControllerImpl(WebContents* web_contents);

 private:
  friend class WebContentsUserData<VideoPictureInPictureWindowControllerImpl>;

  // Recompute the playback state and update the window accordingly.
  void UpdatePlaybackState();

  // Signal to the media player that |this| is leaving Picture-in-Picture mode.
  void OnLeavingPictureInPicture(bool should_pause_video);

  // Internal method to set the states after the window was closed, whether via
  // the system or by the browser.
  void CloseInternal(bool should_pause_video);

  // Allow play/pause button to be visible if Media Session actions "play" and
  // "pause" are both handled by the website or if
  // always_hide_play_pause_button_ is false.
  void UpdatePlayPauseButtonVisibility();

  // Returns the web_contents() as a WebContentsImpl*.
  WebContentsImpl* GetWebContentsImpl();

  std::unique_ptr<VideoOverlayWindow> window_;

  viz::SurfaceId surface_id_;

  // Used to show/hide some actions in Picture-in-Picture window. These are set
  // to true when website handles some Media Session actions.
  bool media_session_action_play_handled_ = false;
  bool media_session_action_pause_handled_ = false;
  bool media_session_action_skip_ad_handled_ = false;
  bool media_session_action_next_track_handled_ = false;
  bool media_session_action_previous_track_handled_ = false;
  bool media_session_action_toggle_microphone_handled_ = false;
  bool media_session_action_toggle_camera_handled_ = false;
  bool media_session_action_hang_up_handled_ = false;
  bool media_session_action_previous_slide_handled_ = false;
  bool media_session_action_next_slide_handled_ = false;

  // Tracks the current microphone state.
  bool microphone_muted_ = false;

  // Tracks the current camera state.
  bool camera_turned_on_ = false;

  // Used to hide play/pause button if video is a MediaStream or has infinite
  // duration. Play/pause button visibility can be overridden by the Media
  // Session API in UpdatePlayPauseButtonVisibility().
  bool always_show_play_pause_button_ = false;

  // Session currently associated with the Picture-in-Picture window. The
  // session object makes the bridge with the renderer process by handling
  // requests and holding states such as the active player id.
  // The session will be nullptr when there is no active session.
  std::unique_ptr<PictureInPictureSession> active_session_;

  // The media position info as last reported to us by MediaSessionImpl.
  std::optional<media_session::MediaPosition> media_position_;

  // Coordinates of the video element in WebContents coordinates.
  gfx::Rect source_bounds_;

  // The origin of the initiator.
  std::optional<url::Origin> origin_;

  // Callback to notify the observers about the video PiP window creation event.
  base::OnceClosure on_window_created_notify_observers_callback_;

  WEB_CONTENTS_USER_DATA_KEY_DECL();
};

}  // namespace content

#endif  // CONTENT_BROWSER_PICTURE_IN_PICTURE_VIDEO_PICTURE_IN_PICTURE_WINDOW_CONTROLLER_IMPL_H_