blob: e96c604614dfe19c1b2911ad5d732d635b9e5c15 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/video_capture/shared_memory_virtual_device_mojo_adapter.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/ranges/algorithm.h"
#include "media/base/bind_to_current_loop.h"
#include "media/capture/video/scoped_buffer_pool_reservation.h"
#include "media/capture/video/video_capture_buffer_pool_impl.h"
#include "media/capture/video/video_capture_buffer_pool_util.h"
#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/video_capture/public/mojom/constants.mojom.h"
namespace {
void OnNewBufferAcknowleged(
video_capture::mojom::SharedMemoryVirtualDevice::RequestFrameBufferCallback
callback,
int32_t buffer_id) {
std::move(callback).Run(buffer_id);
}
} // anonymous namespace
namespace video_capture {
SharedMemoryVirtualDeviceMojoAdapter::SharedMemoryVirtualDeviceMojoAdapter(
mojo::Remote<mojom::Producer> producer)
: producer_(std::move(producer)),
buffer_pool_(new media::VideoCaptureBufferPoolImpl(
media::VideoCaptureBufferType::kSharedMemory,
max_buffer_pool_buffer_count())) {}
SharedMemoryVirtualDeviceMojoAdapter::~SharedMemoryVirtualDeviceMojoAdapter() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
int SharedMemoryVirtualDeviceMojoAdapter::max_buffer_pool_buffer_count() {
return media::kVideoCaptureDefaultMaxBufferPoolSize;
}
void SharedMemoryVirtualDeviceMojoAdapter::RequestFrameBuffer(
const gfx::Size& dimension,
media::VideoPixelFormat pixel_format,
media::mojom::PlaneStridesPtr strides,
RequestFrameBufferCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
int buffer_id_to_drop = media::VideoCaptureBufferPool::kInvalidId;
int buffer_id = media::VideoCaptureBufferPool::kInvalidId;
const auto reserve_result = buffer_pool_->ReserveForProducer(
dimension, pixel_format, strides, 0 /* frame_feedback_id */, &buffer_id,
&buffer_id_to_drop);
// Remove dropped buffer if there is one.
if (buffer_id_to_drop != media::VideoCaptureBufferPool::kInvalidId) {
auto entry_iter = base::ranges::find(known_buffer_ids_, buffer_id_to_drop);
if (entry_iter != known_buffer_ids_.end()) {
known_buffer_ids_.erase(entry_iter);
if (producer_.is_bound())
producer_->OnBufferRetired(buffer_id_to_drop);
if (video_frame_handler_.is_bound()) {
video_frame_handler_->OnBufferRetired(buffer_id_to_drop);
} else if (video_frame_handler_in_process_) {
video_frame_handler_in_process_->OnBufferRetired(buffer_id_to_drop);
}
}
}
if (reserve_result !=
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded) {
std::move(callback).Run(mojom::kInvalidBufferId);
return;
}
if (!base::Contains(known_buffer_ids_, buffer_id)) {
if (video_frame_handler_.is_bound()) {
media::mojom::VideoBufferHandlePtr buffer_handle =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
buffer_pool_->DuplicateAsUnsafeRegion(buffer_id));
video_frame_handler_->OnNewBuffer(buffer_id, std::move(buffer_handle));
} else if (video_frame_handler_in_process_) {
media::mojom::VideoBufferHandlePtr buffer_handle =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
buffer_pool_->DuplicateAsUnsafeRegion(buffer_id));
video_frame_handler_in_process_->OnNewBuffer(buffer_id,
std::move(buffer_handle));
}
known_buffer_ids_.push_back(buffer_id);
// Share buffer handle with producer.
media::mojom::VideoBufferHandlePtr buffer_handle;
buffer_handle = media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
buffer_pool_->DuplicateAsUnsafeRegion(buffer_id));
// Invoke the response back only after the producer have acked
// that it has received the newly created buffer. This is need
// because the |producer_| and the |callback| are bound to different
// message pipes, so the order for calls to |producer_| and |callback|
// is not guaranteed.
if (producer_.is_bound())
producer_->OnNewBuffer(buffer_id, std::move(buffer_handle),
base::BindOnce(&OnNewBufferAcknowleged,
std::move(callback), buffer_id));
return;
}
std::move(callback).Run(buffer_id);
}
void SharedMemoryVirtualDeviceMojoAdapter::OnFrameReadyInBuffer(
int32_t buffer_id,
::media::mojom::VideoFrameInfoPtr frame_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Unknown buffer ID.
if (!base::Contains(known_buffer_ids_, buffer_id)) {
return;
}
// Notify receiver if there is one.
if (video_frame_handler_.is_bound()) {
if (!scoped_access_permission_map_) {
scoped_access_permission_map_ = ScopedAccessPermissionMap::
CreateMapAndSendVideoFrameAccessHandlerReady(video_frame_handler_);
}
buffer_pool_->HoldForConsumers(buffer_id, 1 /* num_clients */);
auto access_permission = std::make_unique<
media::ScopedBufferPoolReservation<media::ConsumerReleaseTraits>>(
buffer_pool_, buffer_id);
scoped_access_permission_map_->InsertAccessPermission(
buffer_id, std::move(access_permission));
video_frame_handler_->OnFrameReadyInBuffer(
mojom::ReadyFrameInBuffer::New(buffer_id, 0 /* frame_feedback_id */,
std::move(frame_info)),
{});
} else if (video_frame_handler_in_process_) {
buffer_pool_->HoldForConsumers(buffer_id, 1 /* num_clients */);
video_frame_handler_in_process_->OnFrameReadyInBuffer(
media::ReadyFrameInBuffer(
buffer_id, 0 /* frame_feedback_id */,
std::make_unique<media::ScopedBufferPoolReservation<
media::ConsumerReleaseTraits>>(buffer_pool_, buffer_id),
std::move(frame_info)),
{});
}
buffer_pool_->RelinquishProducerReservation(buffer_id);
}
void SharedMemoryVirtualDeviceMojoAdapter::Start(
const media::VideoCaptureParams& requested_settings,
mojo::PendingRemote<mojom::VideoFrameHandler> handler) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
video_frame_handler_.Bind(std::move(handler));
video_frame_handler_.set_disconnect_handler(base::BindOnce(
&SharedMemoryVirtualDeviceMojoAdapter::OnReceiverConnectionErrorOrClose,
base::Unretained(this)));
video_frame_handler_->OnStarted();
// Notify receiver of known buffers */
for (auto buffer_id : known_buffer_ids_) {
media::mojom::VideoBufferHandlePtr buffer_handle =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
buffer_pool_->DuplicateAsUnsafeRegion(buffer_id));
video_frame_handler_->OnNewBuffer(buffer_id, std::move(buffer_handle));
}
}
void SharedMemoryVirtualDeviceMojoAdapter::StartInProcess(
const media::VideoCaptureParams& requested_settings,
const base::WeakPtr<media::VideoFrameReceiver>& frame_handler) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
video_frame_handler_in_process_ = std::move(frame_handler);
video_frame_handler_in_process_->OnStarted();
// Notify receiver of known buffers */
for (auto buffer_id : known_buffer_ids_) {
media::mojom::VideoBufferHandlePtr buffer_handle =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
buffer_pool_->DuplicateAsUnsafeRegion(buffer_id));
video_frame_handler_in_process_->OnNewBuffer(buffer_id,
std::move(buffer_handle));
}
}
void SharedMemoryVirtualDeviceMojoAdapter::MaybeSuspend() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SharedMemoryVirtualDeviceMojoAdapter::Resume() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SharedMemoryVirtualDeviceMojoAdapter::GetPhotoState(
GetPhotoStateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run(nullptr);
}
void SharedMemoryVirtualDeviceMojoAdapter::SetPhotoOptions(
media::mojom::PhotoSettingsPtr settings,
SetPhotoOptionsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SharedMemoryVirtualDeviceMojoAdapter::TakePhoto(
TakePhotoCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SharedMemoryVirtualDeviceMojoAdapter::ProcessFeedback(
const media::VideoCaptureFeedback& feedback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SharedMemoryVirtualDeviceMojoAdapter::RequestRefreshFrame() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SharedMemoryVirtualDeviceMojoAdapter::Stop() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (video_frame_handler_.is_bound()) {
// Unsubscribe from connection error callbacks.
video_frame_handler_.set_disconnect_handler(base::OnceClosure());
// Send out OnBufferRetired events and OnStopped.
for (auto buffer_id : known_buffer_ids_)
video_frame_handler_->OnBufferRetired(buffer_id);
video_frame_handler_->OnStopped();
video_frame_handler_.reset();
} else if (video_frame_handler_in_process_) {
// Send out OnBufferRetired events and OnStopped.
for (auto buffer_id : known_buffer_ids_)
video_frame_handler_in_process_->OnBufferRetired(buffer_id);
video_frame_handler_in_process_->OnStopped();
video_frame_handler_in_process_ = nullptr;
}
}
void SharedMemoryVirtualDeviceMojoAdapter::OnReceiverConnectionErrorOrClose() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Stop();
}
} // namespace video_capture