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
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259

content / browser / media / capture / frame_sink_video_capture_device.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_MEDIA_CAPTURE_FRAME_SINK_VIDEO_CAPTURE_DEVICE_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_FRAME_SINK_VIDEO_CAPTURE_DEVICE_H_

#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "build/build_config.h"
#include "components/viz/common/gpu/context_lost_observer.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/video_capture_target.h"
#include "components/viz/host/client_frame_sink_video_capturer.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "media/capture/mojom/video_capture_types.mojom.h"
#include "media/capture/video/video_capture_device.h"
#include "media/capture/video/video_frame_receiver.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/wake_lock.mojom.h"
#include "services/viz/public/cpp/compositing/video_capture_target_mojom_traits.h"
#include "ui/compositor/compositor.h"

namespace content {

class MouseCursorOverlayController;

class ContextProviderObserver;

// A virtualized VideoCaptureDevice that captures the displayed contents of a
// frame sink (see viz::CompositorFrameSink), such as the composited main view
// of a WebContents instance, producing a stream of video frames.
//
// From the point-of-view of the VIZ service, this is a consumer of video frames
// (viz::mojom::FrameSinkVideoConsumer). However, from the point-of-view of the
// video capture stack, this is a device (media::VideoCaptureDevice) that
// produces video frames. Therefore, a FrameSinkVideoCaptureDevice is really a
// proxy between the two subsystems.
//
// Usually, a subclass implementation is instantiated and used, such as
// WebContentsVideoCaptureDevice or AuraWindowCaptureDevice. These subclasses
// provide additional implementation, to update which frame sink is targeted for
// capture, and to notify other components that capture is taking place.
class CONTENT_EXPORT FrameSinkVideoCaptureDevice
    : public media::VideoCaptureDevice,
      public viz::mojom::FrameSinkVideoConsumer {
 public:
  FrameSinkVideoCaptureDevice();

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

  ~FrameSinkVideoCaptureDevice() override;

  // Deviation from the VideoCaptureDevice interface: Since the memory pooling
  // provided by a VideoCaptureDevice::Client is not needed, this
  // FrameSinkVideoCaptureDevice will provide frames to a VideoFrameReceiver
  // directly.
  void AllocateAndStartWithReceiver(
      const media::VideoCaptureParams& params,
      std::unique_ptr<media::VideoFrameReceiver> receiver);

  // Returns the VideoCaptureParams passed to AllocateAndStartWithReceiver().
  const media::VideoCaptureParams& capture_params() const {
    return capture_params_;
  }

  // VideoCaptureDevice implementation.
  void AllocateAndStart(const media::VideoCaptureParams& params,
                        std::unique_ptr<Client> client) final;
  void RequestRefreshFrame() final;
  void MaybeSuspend() final;
  void Resume() final;
  void ApplySubCaptureTarget(
      media::mojom::SubCaptureTargetType type,
      const base::Token& target,
      uint32_t sub_capture_target_version,
      base::OnceCallback<void(media::mojom::ApplySubCaptureTargetResult)>
          callback) override;
  void StopAndDeAllocate() final;
  void OnUtilizationReport(media::VideoCaptureFeedback feedback) override;

  // FrameSinkVideoConsumer implementation.
  void OnFrameCaptured(
      media::mojom::VideoBufferHandlePtr data,
      media::mojom::VideoFrameInfoPtr info,
      const gfx::Rect& content_rect,
      mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
          callbacks) override;
  void OnNewSubCaptureTargetVersion(uint32_t sub_capture_target_version) final;
  void OnFrameWithEmptyRegionCapture() final;
  void OnStopped() final;
  void OnLog(const std::string& message) final;

  // These are called to notify when the capture target has changed or was
  // permanently lost. NOTE: a target can be temporarily std::nullopt without
  // being permanently lost.
  virtual void OnTargetChanged(
      const std::optional<viz::VideoCaptureTarget>& target,
      uint32_t sub_capture_target_version);
  virtual void OnTargetPermanentlyLost();

 protected:
  MouseCursorOverlayController* cursor_controller() const {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
    return cursor_controller_.get();
#else
    return nullptr;
#endif
  }

  // Subclasses override these to perform additional start/stop tasks.
  virtual void WillStart();
  virtual void DidStop();

  // Establishes connection to FrameSinkVideoCapturer. The default
  // implementation calls CreateCapturerViaGlobalManager(), but subclasses
  // and/or tests may provide alternatives.
  virtual void CreateCapturer(
      mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer> receiver);

  // Establishes connection to FrameSinkVideoCapturer using the global
  // viz::HostFrameSinkManager.
  static void CreateCapturerViaGlobalManager(
      mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer> receiver);

 private:
  using BufferId = decltype(media::VideoCaptureDevice::Client::Buffer::id);

  void AllocateAndStartWithReceiverInternal();

  // Starts observing changes to context provider.
  void ObserveContextProvider();

  // Re-creates the |capturer_| if needed. The capturer will be recreated (and
  // re-started if the current one was running) if it is configured to use a
  // pixel format that is different than the pixel format that we are able to
  // use given current device capabilities (e.g. when a capturer was configured
  // to use NV12 format but conditions changed and now we can only capture
  // I420 format).
  void RestartCapturerIfNeeded();

  // Helper, checks if the FrameSinkVideoCapturer should be able to support
  // capture using NV12 pixel format - this depends on device capabilities.
  bool CanSupportNV12Format() const;

  // Helper, returns desired video pixel format based on the contents of
  // |capture_parameters_|. If the capture parameters specify
  // PIXEL_FORMAT_UNKNOWN, it means we need to decide between I420 and NV12.
  media::VideoPixelFormat GetDesiredVideoPixelFormat() const;

  void AllocateCapturer(media::VideoPixelFormat pixel_format);

  // If not consuming and all preconditions are met, set up and start consuming.
  void MaybeStartConsuming();

  // If consuming, shut it down.
  void MaybeStopConsuming();

  // Notifies the capturer that consumption of the frame is complete.
  void OnFramePropagationComplete(BufferId buffer_id);

  // Helper that logs the given error |message| to the |receiver_| and then
  // stops capture and this VideoCaptureDevice.
  void OnFatalError(std::string message);

  // Helper that requests wake lock to prevent the display from sleeping while
  // capturing is going on.
  void RequestWakeLock();

  // Helper to set `gpu_capabilities_` on the appropriate thread. Can be called
  // from any thread, will hop to the sequence on which the device was created.
  // This indirection is needed to support cancellation of handed out callbacks.
  void SetGpuCapabilitiesOnDevice(
      std::optional<gpu::Capabilities> capabilities);

  // Current capture target. This is cached to resolve a race where
  // `OnTargetChanged()` can be called before the |capturer_| is created in
  // `OnCapturerCreated()`.
  std::optional<viz::VideoCaptureTarget> target_;

  // The requested format, rate, and other capture constraints.
  media::VideoCaptureParams capture_params_;

  // Set to true when `MaybeSuspend()` is called, and false when Resume() is
  // called. This reflects the needs of the downstream client.
  bool suspend_requested_ = false;

  // Receives video frames from this capture device, for propagation into the
  // video capture stack. This is set by `AllocateAndStartWithReceiver()`, and
  // cleared by `StopAndDeAllocate()`.
  std::unique_ptr<media::VideoFrameReceiver> receiver_;

  std::unique_ptr<viz::ClientFrameSinkVideoCapturer> capturer_;

  // Capabilities obtained from `viz::ContextProvider`.
  std::optional<gpu::Capabilities> gpu_capabilities_
      GUARDED_BY_CONTEXT(sequence_checker_);

  // Instance that is responsible for monitoring for context loss events on the
  // `viz::ContextProvider`. May be null.
  std::unique_ptr<ContextProviderObserver, BrowserThread::DeleteOnUIThread>
      context_provider_observer_ GUARDED_BY_CONTEXT(sequence_checker_);

  // A vector that holds the "callbacks" mojo::Remote for each frame while the
  // frame is being processed by VideoFrameReceiver. The index corresponding to
  // a particular frame is used as the BufferId passed to VideoFrameReceiver.
  // Therefore, non-null pointers in this vector must never move to a different
  // position.
  std::vector<mojo::Remote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>>
      frame_callbacks_;

  // Set when `OnFatalError()` is called. This prevents any future
  // AllocateAndStartWithReceiver() calls from succeeding.
  std::optional<std::string> fatal_error_message_;

  SEQUENCE_CHECKER(sequence_checker_);

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
  // Controls the overlay that renders the mouse cursor onto each video frame.
  const std::unique_ptr<MouseCursorOverlayController,
                        BrowserThread::DeleteOnUIThread>
      cursor_controller_;
#endif

  // Whenever the sub-capture-target of a stream changes, the associated
  // sub-capture-target-version is incremented. This value is used in frames'
  // metadata so as to allow other modules (mostly Blink) to see which frames
  // are cropped/restricted to the old/new specified sub-capture-target.
  // The value 0 is used before any sub-capture-target is assigned.
  // (Note that by applying and then removing a sub-capture target,
  // values other than 0 can also be associated with an uncropped track.)
  uint32_t sub_capture_target_version_ = 0;

  // Prevent display sleeping while content capture is in progress.
  mojo::Remote<device::mojom::WakeLock> wake_lock_;

  // Creates WeakPtrs for use on the device thread.
  base::WeakPtrFactory<FrameSinkVideoCaptureDevice> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_FRAME_SINK_VIDEO_CAPTURE_DEVICE_H_