blob: dae889e5dc21c8305f4dd37964b83f94e91e5174 [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/platform_shared_memory_region.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_pool_util.h"
#include "media/capture/video/video_capture_buffer_tracker.h"
#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
#include "ui/gfx/buffer_format_util.h"
#if defined(OS_WIN)
#include "media/capture/video/win/video_capture_buffer_tracker_factory_win.h"
#endif // defined(OS_WIN)
namespace media {
VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl(
VideoCaptureBufferType buffer_type)
: VideoCaptureBufferPoolImpl(buffer_type,
kVideoCaptureDefaultMaxBufferPoolSize) {}
VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl(
VideoCaptureBufferType buffer_type,
int count)
: buffer_type_(buffer_type),
count_(count),
#if defined(OS_WIN)
buffer_tracker_factory_(
std::make_unique<media::VideoCaptureBufferTrackerFactoryWin>())
#else
buffer_tracker_factory_(
std::make_unique<media::VideoCaptureBufferTrackerFactoryImpl>())
#endif
{
DCHECK_GT(count, 0);
}
VideoCaptureBufferPoolImpl::~VideoCaptureBufferPoolImpl() = default;
base::UnsafeSharedMemoryRegion
VideoCaptureBufferPoolImpl::DuplicateAsUnsafeRegion(int buffer_id) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return {};
}
return tracker->DuplicateAsUnsafeRegion();
}
mojo::ScopedSharedBufferHandle
VideoCaptureBufferPoolImpl::DuplicateAsMojoBuffer(int buffer_id) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return mojo::ScopedSharedBufferHandle();
}
return tracker->DuplicateAsMojoBuffer();
}
mojom::SharedMemoryViaRawFileDescriptorPtr
VideoCaptureBufferPoolImpl::CreateSharedMemoryViaRawFileDescriptorStruct(
int buffer_id) {
// This requires platforms where base::SharedMemoryHandle is backed by a
// file descriptor.
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return 0u;
}
// Convert the mojo::ScopedSharedBufferHandle to a PlatformSharedMemoryRegion
// in order to extract the platform file descriptor.
base::subtle::PlatformSharedMemoryRegion platform_region =
mojo::UnwrapPlatformSharedMemoryRegion(tracker->DuplicateAsMojoBuffer());
if (!platform_region.IsValid()) {
NOTREACHED();
return 0u;
}
base::subtle::ScopedFDPair fds = platform_region.PassPlatformHandle();
auto result = mojom::SharedMemoryViaRawFileDescriptor::New();
result->file_descriptor_handle = mojo::PlatformHandle(std::move(fds.fd));
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();
}
gfx::GpuMemoryBufferHandle VideoCaptureBufferPoolImpl::GetGpuMemoryBufferHandle(
int buffer_id) {
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
if (!tracker) {
NOTREACHED() << "Invalid buffer_id.";
return gfx::GpuMemoryBufferHandle();
}
return tracker->GetGpuMemoryBufferHandle();
}
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;
}
tracker->SetHeldByProducer(false);
}
int VideoCaptureBufferPoolImpl::ReserveIdForExternalBuffer(
const gfx::GpuMemoryBufferHandle& handle,
int* buffer_id_to_drop) {
base::AutoLock lock(lock_);
// Look for a tracker that matches this buffer and is not in use. While
// iterating, find the least recently used tracker.
*buffer_id_to_drop = kInvalidId;
auto lru_tracker_it = trackers_.end();
for (auto it = trackers_.begin(); it != trackers_.end(); ++it) {
VideoCaptureBufferTracker* const tracker = it->second.get();
if (tracker->IsHeldByProducerOrConsumer())
continue;
if (tracker->IsSameGpuMemoryBuffer(handle)) {
tracker->SetHeldByProducer(true);
return it->first;
}
if (lru_tracker_it == trackers_.end() ||
lru_tracker_it->second->LastCustomerUseSequenceNumber() >
tracker->LastCustomerUseSequenceNumber()) {
lru_tracker_it = it;
}
}
// Free the least recently used tracker, if needed.
if (trackers_.size() >= static_cast<size_t>(count_) &&
lru_tracker_it != trackers_.end()) {
*buffer_id_to_drop = lru_tracker_it->first;
trackers_.erase(lru_tracker_it);
}
// Create the new tracker.
const int new_buffer_id = next_buffer_id_++;
auto tracker =
buffer_tracker_factory_->CreateTrackerForExternalGpuMemoryBuffer(handle);
tracker->SetHeldByProducer(true);
trackers_[new_buffer_id] = std::move(tracker);
return new_buffer_id;
}
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;
}
tracker->AddConsumerHolds(num_clients);
// Note: The buffer will stay held by the producer 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;
}
tracker->RemoveConsumerHolds(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->IsHeldByProducerOrConsumer())
++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->IsHeldByProducerOrConsumer()) {
if (tracker->IsReusableForFormat(dimensions, pixel_format, strides)) {
// Reuse this buffer
tracker->SetHeldByProducer(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;
DLOG(ERROR) << __func__
<< " max buffer count exceeded count_ = " << count_;
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(buffer_type_);
if (!tracker || !tracker->Init(dimensions, pixel_format, strides)) {
DLOG(ERROR) << "Error initializing VideoCaptureBufferTracker";
*buffer_id = kInvalidId;
return VideoCaptureDevice::Client::ReserveResult::kAllocationFailed;
}
tracker->SetHeldByProducer(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