blob: 8b5b93e3a91f92b0ac029dc353315699d0dd9977 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// 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/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process.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.h"
#include "media/capture/video/video_frame_receiver.h"
#include "media/capture/video_capture_types.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);
// Warning: This value should not be changed, because doing so would change
// the meaning of logged UMA events for histograms Media.VideoCapture.Error
// and Media.VideoCapture.MaxFrameDropExceeded.
static constexpr int kMaxConsecutiveFrameDropForSameReasonCount = 10;
// Number of logs for dropped frames to be emitted before suppressing.
static constexpr int kMaxEmittedLogsForDroppedFramesBeforeSuppressing = 3;
// Suppressed logs for dropped frames will still be emitted this often.
static constexpr int kFrequencyForSuppressedLogs = 100;
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);
// 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 base::Optional<media::VideoCaptureFormat> GetVideoCaptureFormat() const;
bool has_received_frames() const { return has_received_frames_; }
// Implementation of media::VideoFrameReceiver interface:
void OnNewBuffer(int32_t buffer_id,
media::mojom::VideoBufferHandlePtr buffer_handle) override;
void OnFrameReadyInBuffer(
media::ReadyFrameInBuffer frame,
std::vector<media::ReadyFrameInBuffer> scaled_frames) override;
void OnBufferRetired(int buffer_id) override;
void OnError(media::VideoCaptureError error) override;
void OnFrameDropped(media::VideoCaptureFrameDropReason reason) 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);
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 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_; }
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_;
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_;
};
struct FrameDropLogState {
FrameDropLogState(media::VideoCaptureFrameDropReason reason =
media::VideoCaptureFrameDropReason::kNone);
int drop_count = 0;
media::VideoCaptureFrameDropReason drop_reason =
media::VideoCaptureFrameDropReason::kNone;
bool max_log_count_exceeded = false;
};
~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_;
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_;
FrameDropLogState frame_drop_log_state_;
// Tracks how often each frame-drop reason was encountered.
std::map<media::VideoCaptureFrameDropReason, int> frame_drop_log_counters_;
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_;
base::Optional<media::VideoCaptureFormat> video_capture_format_;
base::WeakPtrFactory<VideoCaptureController> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(VideoCaptureController);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_