| // 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 |