blob: f583a94adaa638396431ecda7017aa787d99aaa2 [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 "content/browser/renderer_host/media/video_capture_buffer_pool.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
namespace content {
const int VideoCaptureBufferPool::kInvalidId = -1;
VideoCaptureBufferPool::VideoCaptureBufferPool(int count)
: count_(count),
next_buffer_id_(0) {
DCHECK_GT(count, 0);
}
VideoCaptureBufferPool::~VideoCaptureBufferPool() {
STLDeleteValues(&buffers_);
}
base::SharedMemoryHandle VideoCaptureBufferPool::ShareToProcess(
int buffer_id,
base::ProcessHandle process_handle,
size_t* memory_size) {
base::AutoLock lock(lock_);
Buffer* buffer = GetBuffer(buffer_id);
if (!buffer) {
NOTREACHED() << "Invalid buffer_id.";
return base::SharedMemory::NULLHandle();
}
base::SharedMemoryHandle remote_handle;
buffer->shared_memory.ShareToProcess(process_handle, &remote_handle);
*memory_size = buffer->shared_memory.requested_size();
return remote_handle;
}
bool VideoCaptureBufferPool::GetBufferInfo(int buffer_id,
void** memory,
size_t* size) {
base::AutoLock lock(lock_);
Buffer* buffer = GetBuffer(buffer_id);
if (!buffer) {
NOTREACHED() << "Invalid buffer_id.";
return false;
}
DCHECK(buffer->held_by_producer);
*memory = buffer->shared_memory.memory();
*size = buffer->shared_memory.mapped_size();
return true;
}
int VideoCaptureBufferPool::ReserveForProducer(size_t size,
int* buffer_id_to_drop) {
base::AutoLock lock(lock_);
return ReserveForProducerInternal(size, buffer_id_to_drop);
}
void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id) {
base::AutoLock lock(lock_);
Buffer* buffer = GetBuffer(buffer_id);
if (!buffer) {
NOTREACHED() << "Invalid buffer_id.";
return;
}
DCHECK(buffer->held_by_producer);
buffer->held_by_producer = false;
}
void VideoCaptureBufferPool::HoldForConsumers(
int buffer_id,
int num_clients) {
base::AutoLock lock(lock_);
Buffer* buffer = GetBuffer(buffer_id);
if (!buffer) {
NOTREACHED() << "Invalid buffer_id.";
return;
}
DCHECK(buffer->held_by_producer);
DCHECK(!buffer->consumer_hold_count);
buffer->consumer_hold_count = num_clients;
// Note: |held_by_producer| will stay true until
// RelinquishProducerReservation() (usually called by destructor of the object
// wrapping this buffer, e.g. a media::VideoFrame).
}
void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id,
int num_clients) {
base::AutoLock lock(lock_);
Buffer* buffer = GetBuffer(buffer_id);
if (!buffer) {
NOTREACHED() << "Invalid buffer_id.";
return;
}
DCHECK_GE(buffer->consumer_hold_count, num_clients);
buffer->consumer_hold_count -= num_clients;
}
VideoCaptureBufferPool::Buffer::Buffer()
: held_by_producer(false), consumer_hold_count(0) {}
int VideoCaptureBufferPool::ReserveForProducerInternal(size_t size,
int* buffer_id_to_drop) {
lock_.AssertAcquired();
// Look for a buffer 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 buffer.
*buffer_id_to_drop = kInvalidId;
size_t realloc_size = 0;
BufferMap::iterator realloc = buffers_.end();
for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); ++it) {
Buffer* buffer = it->second;
if (!buffer->consumer_hold_count && !buffer->held_by_producer) {
if (buffer->shared_memory.requested_size() >= size) {
// Existing buffer is big enough. Reuse it.
buffer->held_by_producer = true;
return it->first;
}
if (buffer->shared_memory.requested_size() > realloc_size) {
realloc_size = buffer->shared_memory.requested_size();
realloc = it;
}
}
}
// Preferentially grow the pool by creating a new buffer. If we're at maximum
// size, then reallocate by deleting an existing one instead.
if (buffers_.size() == static_cast<size_t>(count_)) {
if (realloc == buffers_.end()) {
// We're out of space, and can't find an unused buffer to reallocate.
return kInvalidId;
}
*buffer_id_to_drop = realloc->first;
delete realloc->second;
buffers_.erase(realloc);
}
// Create the new buffer.
int buffer_id = next_buffer_id_++;
scoped_ptr<Buffer> buffer(new Buffer());
if (size) {
// |size| can be 0 for buffers that do not require memory backing.
if (!buffer->shared_memory.CreateAndMapAnonymous(size))
return kInvalidId;
}
buffer->held_by_producer = true;
buffers_[buffer_id] = buffer.release();
return buffer_id;
}
VideoCaptureBufferPool::Buffer* VideoCaptureBufferPool::GetBuffer(
int buffer_id) {
BufferMap::iterator it = buffers_.find(buffer_id);
if (it == buffers_.end())
return NULL;
return it->second;
}
} // namespace content