blob: 20ff5db5ac9cb29c20e511942bbf5d9f84e36e48 [file] [log] [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 OnNewCaptureVersion(const media::CaptureVersion& capture_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.
// `capture_version_source` indicates how many times the source was changed
// in the current capture-session. Changing the source resets sub-capture.
static void CreateCapturerViaGlobalManager(
mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer> receiver,
uint32_t capture_version_source);
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.)
// TODO(crbug.com/394794490): Change to a media::CaptureVersion.
uint32_t sub_capture_version_ = 0;
// Prevent display sleeping while content capture is in progress.
mojo::Remote<device::mojom::WakeLock> wake_lock_;
bool has_sent_on_started_to_client_ = false;
// 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_