blob: d240f6b6418cf38da43d56ab3dc69f2eac74ed97 [file] [log] [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_types.mojom-forward.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/cpp/buildflags.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
#if BUILDFLAG(ENABLE_VIDEO_EFFECTS)
#include "services/video_effects/public/mojom/video_effects_processor.mojom-forward.h"
#endif
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,
#if BUILDFLAG(ENABLE_VIDEO_EFFECTS)
mojo::PendingRemote<video_effects::mojom::VideoEffectsProcessor>
video_effects_processor,
#endif
mojo::PendingRemote<media::mojom::ReadonlyVideoEffectsManager>
readonly_video_effects_manager);
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_;
};
// `kError` is an absorbing state which stops the flow of data to clients.
enum class State { kStarting, kStarted, kError };
~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_;
State state_ = State::kStarting;
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_