blob: 151ffd6dadd2143e29c4bc08eea68e024e0698e8 [file] [log] [blame]
// Copyright (c) 2013 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.
#include "media/capture/video/video_capture_buffer_pool_impl.h"
#include <memory>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "media/capture/video/video_capture_buffer_handle.h"
#include "media/capture/video/video_capture_buffer_tracker.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/buffer_format_util.h"
namespace media {
VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl(
std::unique_ptr<VideoCaptureBufferTrackerFactory> buffer_tracker_factory,
int count)
: count_(count),
next_buffer_id_(0),
buffer_tracker_factory_(std::move(buffer_tracker_factory)) {
DCHECK_GT(count, 0);
}
VideoCaptureBufferPoolImpl::~VideoCaptureBufferPoolImpl() = default;
mojo::ScopedSharedBufferHandle
VideoCaptureBufferPoolImpl::GetHandleForInterProcessTransit(int buffer_id,
bool read_only) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return mojo::ScopedSharedBufferHandle();
}
return tracker->GetHandleForTransit(read_only);
}
base::SharedMemoryHandle
VideoCaptureBufferPoolImpl::GetNonOwnedSharedMemoryHandleForLegacyIPC(
int buffer_id) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return base::SharedMemoryHandle();
}
return tracker->GetNonOwnedSharedMemoryHandleForLegacyIPC();
}
mojom::SharedMemoryViaRawFileDescriptorPtr
VideoCaptureBufferPoolImpl::CreateSharedMemoryViaRawFileDescriptorStruct(
int buffer_id) {
// This requires platforms where base::SharedMemoryHandle is backed by a
// file descriptor.
#if defined(OS_LINUX)
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return 0u;
}
auto result = mojom::SharedMemoryViaRawFileDescriptor::New();
result->file_descriptor_handle = mojo::WrapPlatformFile(
base::SharedMemory::DuplicateHandle(
tracker->GetNonOwnedSharedMemoryHandleForLegacyIPC())
.GetHandle());
result->shared_memory_size_in_bytes = tracker->GetMemorySizeInBytes();
return result;
#else
NOTREACHED();
return mojom::SharedMemoryViaRawFileDescriptorPtr();
#endif
}
std::unique_ptr<VideoCaptureBufferHandle>
VideoCaptureBufferPoolImpl::GetHandleForInProcessAccess(int buffer_id) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return nullptr;
}
return tracker->GetMemoryMappedAccess();
}
VideoCaptureDevice::Client::ReserveResult
VideoCaptureBufferPoolImpl::ReserveForProducer(
const gfx::Size& dimensions,
VideoPixelFormat format,
const mojom::PlaneStridesPtr& strides,
int frame_feedback_id,
int* buffer_id,
int* buffer_id_to_drop) {
base::AutoLock lock(lock_);
return ReserveForProducerInternal(dimensions, format, strides,
frame_feedback_id, buffer_id,
buffer_id_to_drop);
}
void VideoCaptureBufferPoolImpl::RelinquishProducerReservation(int buffer_id) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return;
}
DCHECK(tracker->held_by_producer());
tracker->set_held_by_producer(false);
}
void VideoCaptureBufferPoolImpl::HoldForConsumers(int buffer_id,
int num_clients) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return;
}
DCHECK(tracker->held_by_producer());
DCHECK(!tracker->consumer_hold_count());
tracker->set_consumer_hold_count(num_clients);
// Note: |held_by_producer()| will stay true until
// RelinquishProducerReservation() (usually called by destructor of the object
// wrapping this tracker, e.g. a VideoFrame).
}
void VideoCaptureBufferPoolImpl::RelinquishConsumerHold(int buffer_id,
int num_clients) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return;
}
DCHECK_GE(tracker->consumer_hold_count(), num_clients);
tracker->set_consumer_hold_count(tracker->consumer_hold_count() -
num_clients);
}
double VideoCaptureBufferPoolImpl::GetBufferPoolUtilization() const {
base::AutoLock lock(lock_);
int num_buffers_held = 0;
for (const auto& entry : trackers_) {
VideoCaptureBufferTracker* const tracker = entry.second.get();
if (tracker->held_by_producer() || tracker->consumer_hold_count() > 0)
++num_buffers_held;
}
return static_cast<double>(num_buffers_held) / count_;
}
VideoCaptureDevice::Client::ReserveResult
VideoCaptureBufferPoolImpl::ReserveForProducerInternal(
const gfx::Size& dimensions,
VideoPixelFormat pixel_format,
const mojom::PlaneStridesPtr& strides,
int frame_feedback_id,
int* buffer_id,
int* buffer_id_to_drop) {
lock_.AssertAcquired();
// Look for a tracker that's allocated, big enough, and not in use. Track the
// largest one that's not big enough, in case we have to reallocate a tracker.
*buffer_id_to_drop = kInvalidId;
uint32_t largest_memory_size_in_bytes = 0;
auto tracker_to_drop = trackers_.end();
for (auto it = trackers_.begin(); it != trackers_.end(); ++it) {
VideoCaptureBufferTracker* const tracker = it->second.get();
if (!tracker->consumer_hold_count() && !tracker->held_by_producer()) {
if (tracker->IsReusableForFormat(dimensions, pixel_format, strides)) {
// Reuse this buffer
tracker->set_held_by_producer(true);
tracker->set_frame_feedback_id(frame_feedback_id);
*buffer_id = it->first;
return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
}
if (tracker->GetMemorySizeInBytes() > largest_memory_size_in_bytes) {
largest_memory_size_in_bytes = tracker->GetMemorySizeInBytes();
tracker_to_drop = it;
}
}
}
// Preferably grow the pool by creating a new tracker. If we're at maximum
// size, reallocate by deleting an existing one.
if (trackers_.size() == static_cast<size_t>(count_)) {
if (tracker_to_drop == trackers_.end()) {
// We're out of space, and can't find an unused tracker to reallocate.
*buffer_id = kInvalidId;
return VideoCaptureDevice::Client::ReserveResult::kMaxBufferCountExceeded;
}
*buffer_id_to_drop = tracker_to_drop->first;
trackers_.erase(tracker_to_drop);
}
// Create the new tracker.
const int new_buffer_id = next_buffer_id_++;
std::unique_ptr<VideoCaptureBufferTracker> tracker =
buffer_tracker_factory_->CreateTracker();
if (!tracker->Init(dimensions, pixel_format, strides)) {
DLOG(ERROR) << "Error initializing VideoCaptureBufferTracker";
*buffer_id = kInvalidId;
return VideoCaptureDevice::Client::ReserveResult::kAllocationFailed;
}
tracker->set_held_by_producer(true);
tracker->set_frame_feedback_id(frame_feedback_id);
trackers_[new_buffer_id] = std::move(tracker);
*buffer_id = new_buffer_id;
return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
}
VideoCaptureBufferTracker* VideoCaptureBufferPoolImpl::GetTracker(
int buffer_id) {
auto it = trackers_.find(buffer_id);
return (it == trackers_.end()) ? nullptr : it->second.get();
}
} // namespace media