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
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306

content / browser / renderer_host / media / video_capture_controller.h [blame]

// Copyright 2012 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_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_
#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_

#include <list>
#include <memory>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process.h"
#include "base/time/time.h"
#include "base/token.h"
#include "base/unguessable_token.h"
#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
#include "content/browser/renderer_host/media/video_capture_provider.h"
#include "content/common/content_export.h"
#include "content/public/browser/video_capture_device_launcher.h"
#include "media/capture/mojom/video_capture.mojom.h"
#include "media/capture/mojom/video_capture_types.mojom.h"
#include "media/capture/video/video_frame_receiver.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/video_effects/public/mojom/video_effects_processor.mojom-forward.h"
#include "third_party/blink/public/common/media/video_capture.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"

namespace content {

class VideoCaptureDeviceLaunchObserver;

// Implementation of media::VideoFrameReceiver that distributes received frames
// to potentially multiple connected clients.
// A call to CreateAndStartDeviceAsync() asynchronously brings up the device. If
// CreateAndStartDeviceAsync() has been called, ReleaseDeviceAsync() must be
// called before releasing the instance.
// Instances must be RefCountedThreadSafe, because an owner
// (VideoCaptureManager) wants to be able to release its reference during an
// (asynchronously executing) run of CreateAndStartDeviceAsync(). To this end,
// the owner passes in the shared ownership as part of |done_cb| into
// CreateAndStartDeviceAsync().
class CONTENT_EXPORT VideoCaptureController
    : public media::VideoFrameReceiver,
      public VideoCaptureDeviceLauncher::Callbacks,
      public base::RefCountedThreadSafe<VideoCaptureController> {
 public:
  VideoCaptureController(
      const std::string& device_id,
      blink::mojom::MediaStreamType stream_type,
      const media::VideoCaptureParams& params,
      std::unique_ptr<VideoCaptureDeviceLauncher> device_launcher,
      base::RepeatingCallback<void(const std::string&)> emit_log_message_cb);

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

  base::WeakPtr<VideoCaptureController> GetWeakPtrForIOThread();

  // Start video capturing and try to use the resolution specified in |params|.
  // Buffers will be shared to the client as necessary. The client will continue
  // to receive frames from the device until RemoveClient() is called.
  void AddClient(const VideoCaptureControllerID& id,
                 VideoCaptureControllerEventHandler* event_handler,
                 const media::VideoCaptureSessionId& session_id,
                 const media::VideoCaptureParams& params,
                 std::optional<url::Origin> origin);

  // Stop video capture. This will take back all buffers held by
  // |event_handler|, and |event_handler| shouldn't use those buffers any more.
  // Returns the session_id of the stopped client, or an empty
  // base::UnguessableToken if the indicated client was not registered.
  base::UnguessableToken RemoveClient(
      const VideoCaptureControllerID& id,
      VideoCaptureControllerEventHandler* event_handler);

  // Pause the video capture for specified client.
  void PauseClient(const VideoCaptureControllerID& id,
                   VideoCaptureControllerEventHandler* event_handler);
  // Resume the video capture for specified client.
  // Returns true if the client will be resumed.
  bool ResumeClient(const VideoCaptureControllerID& id,
                    VideoCaptureControllerEventHandler* event_handler);

  size_t GetClientCount() const;

  // Return true if there is client that isn't paused.
  bool HasActiveClient() const;

  // Return true if there is client paused.
  bool HasPausedClient() const;

  // API called directly by VideoCaptureManager in case the device is
  // prematurely closed.
  void StopSession(const base::UnguessableToken& session_id);

  // Return a buffer with id |buffer_id| previously given in
  // VideoCaptureControllerEventHandler::OnBufferReady.
  // If the consumer provided resource utilization
  // feedback, this will be passed here (-1.0 indicates no feedback).
  void ReturnBuffer(const VideoCaptureControllerID& id,
                    VideoCaptureControllerEventHandler* event_handler,
                    int buffer_id,
                    const media::VideoCaptureFeedback& feedback);

  const std::optional<media::VideoCaptureFormat> GetVideoCaptureFormat() const;

  bool has_received_frames() const { return has_received_frames_; }

  const std::optional<url::Origin> GetFirstClientOrigin() const;

  // Implementation of media::VideoFrameReceiver interface:
  void OnCaptureConfigurationChanged() override;
  void OnNewBuffer(int32_t buffer_id,
                   media::mojom::VideoBufferHandlePtr buffer_handle) override;
  void OnFrameReadyInBuffer(media::ReadyFrameInBuffer frame) override;
  void OnBufferRetired(int buffer_id) override;
  void OnError(media::VideoCaptureError error) override;
  void OnFrameDropped(media::VideoCaptureFrameDropReason reason) override;
  void OnNewSubCaptureTargetVersion(
      uint32_t sub_capture_target_version) override;
  void OnFrameWithEmptyRegionCapture() override;
  void OnLog(const std::string& message) override;
  void OnStarted() override;
  void OnStartedUsingGpuDecode() override;
  void OnStopped() override;

  // Implementation of VideoCaptureDeviceLauncher::Callbacks interface:
  void OnDeviceLaunched(
      std::unique_ptr<LaunchedVideoCaptureDevice> device) override;
  void OnDeviceLaunchFailed(media::VideoCaptureError error) override;
  void OnDeviceLaunchAborted() override;

  void OnDeviceConnectionLost();

  void CreateAndStartDeviceAsync(
      const media::VideoCaptureParams& params,
      VideoCaptureDeviceLaunchObserver* callbacks,
      base::OnceClosure done_cb,
      mojo::PendingRemote<video_effects::mojom::VideoEffectsProcessor>
          video_effects_processor);
  void ReleaseDeviceAsync(base::OnceClosure done_cb);
  bool IsDeviceAlive() const;
  void GetPhotoState(
      media::VideoCaptureDevice::GetPhotoStateCallback callback) const;
  void SetPhotoOptions(
      media::mojom::PhotoSettingsPtr settings,
      media::VideoCaptureDevice::SetPhotoOptionsCallback callback);
  void TakePhoto(media::VideoCaptureDevice::TakePhotoCallback callback);
  void MaybeSuspend();
  void Resume();
  void ApplySubCaptureTarget(
      media::mojom::SubCaptureTargetType type,
      const base::Token& target,
      uint32_t sub_capture_target_version,
      base::OnceCallback<void(media::mojom::ApplySubCaptureTargetResult)>
          callback);
  void RequestRefreshFrame();
  void SetDesktopCaptureWindowIdAsync(gfx::NativeViewId window_id,
                                      base::OnceClosure done_cb);
  int serial_id() const { return serial_id_; }
  const std::string& device_id() const { return device_id_; }
  blink::mojom::MediaStreamType stream_type() const { return stream_type_; }
  const media::VideoCaptureParams& parameters() const { return parameters_; }
  bool was_crop_ever_called() const { return was_crop_ever_called_; }

 private:
  friend class base::RefCountedThreadSafe<VideoCaptureController>;
  struct ControllerClient;
  typedef std::list<std::unique_ptr<ControllerClient>> ControllerClients;

  class BufferContext {
   public:
    BufferContext(
        int buffer_context_id,
        int buffer_id,
        media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer,
        media::mojom::VideoBufferHandlePtr buffer_handle);
    ~BufferContext();
    BufferContext(BufferContext&& other);
    BufferContext& operator=(BufferContext&& other);
    int buffer_context_id() const { return buffer_context_id_; }
    int buffer_id() const { return buffer_id_; }
    bool is_retired() const { return is_retired_; }
    const media::mojom::VideoBufferHandlePtr& buffer_handle() const {
      return buffer_handle_;
    }
    void set_is_retired() { is_retired_ = true; }
    void set_frame_feedback_id(int id) { frame_feedback_id_ = id; }
    void set_consumer_feedback_observer(
        media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer) {
      consumer_feedback_observer_ = consumer_feedback_observer;
    }
    void set_read_permission(
        std::unique_ptr<
            media::VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>
            buffer_read_permission) {
      buffer_read_permission_ = std::move(buffer_read_permission);
    }
    void RecordConsumerUtilization(const media::VideoCaptureFeedback& feedback);
    void IncreaseConsumerCount();
    void DecreaseConsumerCount();
    bool HasConsumers() const { return consumer_hold_count_ > 0; }
    media::mojom::VideoBufferHandlePtr CloneBufferHandle();

   private:
    int buffer_context_id_;
    int buffer_id_;
    bool is_retired_;
    int frame_feedback_id_;
    raw_ptr<media::VideoFrameConsumerFeedbackObserver>
        consumer_feedback_observer_;
    media::mojom::VideoBufferHandlePtr buffer_handle_;
    media::VideoCaptureFeedback combined_consumer_feedback_;

    int consumer_hold_count_;
    std::unique_ptr<
        media::VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>
        buffer_read_permission_;
  };

  ~VideoCaptureController() override;

  // Find a client of |id| and |handler| in |clients|.
  ControllerClient* FindClient(const VideoCaptureControllerID& id,
                               VideoCaptureControllerEventHandler* handler,
                               const ControllerClients& clients);

  // Find a client of |session_id| in |clients|.
  ControllerClient* FindClient(const base::UnguessableToken& session_id,
                               const ControllerClients& clients);

  std::vector<BufferContext>::iterator FindBufferContextFromBufferContextId(
      int buffer_context_id);
  std::vector<BufferContext>::iterator FindUnretiredBufferContextFromBufferId(
      int buffer_id);

  ReadyBuffer MakeReadyBufferAndSetContextFeedbackId(
      int buffer_id,
      int frame_feedback_id,
      media::mojom::VideoFrameInfoPtr frame_info,
      BufferContext** out_buffer_context);
  void MakeClientUseBufferContext(BufferContext* frame_context,
                                  ControllerClient* client);

  void OnClientFinishedConsumingBuffer(
      ControllerClient* client,
      int buffer_id,
      const media::VideoCaptureFeedback& feedback);
  void ReleaseBufferContext(
      const std::vector<BufferContext>::iterator& buffer_state_iter);

  using EventHandlerAction =
      base::RepeatingCallback<void(VideoCaptureControllerEventHandler* client,
                                   const VideoCaptureControllerID& id)>;
  void PerformForClientsWithOpenSession(EventHandlerAction action);

  void EmitLogMessage(const std::string& message, int verbose_log_level);

  void MaybeEmitFrameDropLogMessage(media::VideoCaptureFrameDropReason reason);

  const int serial_id_;
  const std::string device_id_;
  const blink::mojom::MediaStreamType stream_type_;
  const media::VideoCaptureParams parameters_;
  std::unique_ptr<VideoCaptureDeviceLauncher> device_launcher_;
  base::RepeatingCallback<void(const std::string&)> emit_log_message_cb_;
  std::unique_ptr<LaunchedVideoCaptureDevice> launched_device_;
  raw_ptr<VideoCaptureDeviceLaunchObserver> device_launch_observer_;

  std::vector<BufferContext> buffer_contexts_;

  // All clients served by this controller.
  ControllerClients controller_clients_;

  // Takes on only the states 'STARTING', 'STARTED' and 'ERROR'. 'ERROR' is an
  // absorbing state which stops the flow of data to clients.
  blink::VideoCaptureState state_;

  int next_buffer_context_id_ = 0;

  // True if the controller has received a video frame from the device.
  bool has_received_frames_;
  base::TimeTicks time_of_start_request_;

  std::optional<media::VideoCaptureFormat> video_capture_format_;

  std::optional<url::Origin> first_client_origin_;

  // As a work-around to technical limitations, we don't allow multiple
  // captures of the same tab, by the same capturer, if the first capturer
  // invoked cropping. (Any capturer but the first one would have been
  // blocked earlier in the pipeline.) That is because the
  // `sub_capture_target_version` would otherwise not line up between the
  // various ControllerClients.
  bool was_crop_ever_called_ = false;

  base::WeakPtrFactory<VideoCaptureController> weak_ptr_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_