1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/logging.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/stl_util.h" 12 #include "media/base/video_frame.h" 13 #include "media/base/video_util.h" 14 15 namespace content { 16 17 const int VideoCaptureBufferPool::kInvalidId = -1; 18 19 VideoCaptureBufferPool::VideoCaptureBufferPool(int count) 20 : count_(count), 21 next_buffer_id_(0) { 22 DCHECK_GT(count, 0); 23 } 24 25 VideoCaptureBufferPool::~VideoCaptureBufferPool() { 26 STLDeleteValues(&buffers_); 27 } 28 29 base::SharedMemoryHandle VideoCaptureBufferPool::ShareToProcess( 30 int buffer_id, 31 base::ProcessHandle process_handle, 32 size_t* memory_size) { 33 base::AutoLock lock(lock_); 34 35 Buffer* buffer = GetBuffer(buffer_id); 36 if (!buffer) { 37 NOTREACHED() << "Invalid buffer_id."; 38 return base::SharedMemory::NULLHandle(); 39 } 40 base::SharedMemoryHandle remote_handle; 41 buffer->shared_memory.ShareToProcess(process_handle, &remote_handle); 42 *memory_size = buffer->shared_memory.requested_size(); 43 return remote_handle; 44 } 45 46 bool VideoCaptureBufferPool::GetBufferInfo(int buffer_id, 47 void** memory, 48 size_t* size) { 49 base::AutoLock lock(lock_); 50 51 Buffer* buffer = GetBuffer(buffer_id); 52 if (!buffer) { 53 NOTREACHED() << "Invalid buffer_id."; 54 return false; 55 } 56 57 DCHECK(buffer->held_by_producer); 58 *memory = buffer->shared_memory.memory(); 59 *size = buffer->shared_memory.mapped_size(); 60 return true; 61 } 62 63 int VideoCaptureBufferPool::ReserveForProducer(size_t size, 64 int* buffer_id_to_drop) { 65 base::AutoLock lock(lock_); 66 return ReserveForProducerInternal(size, buffer_id_to_drop); 67 } 68 69 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id) { 70 base::AutoLock lock(lock_); 71 Buffer* buffer = GetBuffer(buffer_id); 72 if (!buffer) { 73 NOTREACHED() << "Invalid buffer_id."; 74 return; 75 } 76 DCHECK(buffer->held_by_producer); 77 buffer->held_by_producer = false; 78 } 79 80 void VideoCaptureBufferPool::HoldForConsumers( 81 int buffer_id, 82 int num_clients) { 83 base::AutoLock lock(lock_); 84 Buffer* buffer = GetBuffer(buffer_id); 85 if (!buffer) { 86 NOTREACHED() << "Invalid buffer_id."; 87 return; 88 } 89 DCHECK(buffer->held_by_producer); 90 DCHECK(!buffer->consumer_hold_count); 91 92 buffer->consumer_hold_count = num_clients; 93 // Note: |held_by_producer| will stay true until 94 // RelinquishProducerReservation() (usually called by destructor of the object 95 // wrapping this buffer, e.g. a media::VideoFrame). 96 } 97 98 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id, 99 int num_clients) { 100 base::AutoLock lock(lock_); 101 Buffer* buffer = GetBuffer(buffer_id); 102 if (!buffer) { 103 NOTREACHED() << "Invalid buffer_id."; 104 return; 105 } 106 DCHECK_GE(buffer->consumer_hold_count, num_clients); 107 108 buffer->consumer_hold_count -= num_clients; 109 } 110 111 VideoCaptureBufferPool::Buffer::Buffer() 112 : held_by_producer(false), consumer_hold_count(0) {} 113 114 int VideoCaptureBufferPool::ReserveForProducerInternal(size_t size, 115 int* buffer_id_to_drop) { 116 lock_.AssertAcquired(); 117 118 // Look for a buffer that's allocated, big enough, and not in use. Track the 119 // largest one that's not big enough, in case we have to reallocate a buffer. 120 *buffer_id_to_drop = kInvalidId; 121 size_t realloc_size = 0; 122 BufferMap::iterator realloc = buffers_.end(); 123 for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); ++it) { 124 Buffer* buffer = it->second; 125 if (!buffer->consumer_hold_count && !buffer->held_by_producer) { 126 if (buffer->shared_memory.requested_size() >= size) { 127 // Existing buffer is big enough. Reuse it. 128 buffer->held_by_producer = true; 129 return it->first; 130 } 131 if (buffer->shared_memory.requested_size() > realloc_size) { 132 realloc_size = buffer->shared_memory.requested_size(); 133 realloc = it; 134 } 135 } 136 } 137 138 // Preferentially grow the pool by creating a new buffer. If we're at maximum 139 // size, then reallocate by deleting an existing one instead. 140 if (buffers_.size() == static_cast<size_t>(count_)) { 141 if (realloc == buffers_.end()) { 142 // We're out of space, and can't find an unused buffer to reallocate. 143 return kInvalidId; 144 } 145 *buffer_id_to_drop = realloc->first; 146 delete realloc->second; 147 buffers_.erase(realloc); 148 } 149 150 // Create the new buffer. 151 int buffer_id = next_buffer_id_++; 152 scoped_ptr<Buffer> buffer(new Buffer()); 153 if (size) { 154 // |size| can be 0 for buffers that do not require memory backing. 155 if (!buffer->shared_memory.CreateAndMapAnonymous(size)) 156 return kInvalidId; 157 } 158 buffer->held_by_producer = true; 159 buffers_[buffer_id] = buffer.release(); 160 return buffer_id; 161 } 162 163 VideoCaptureBufferPool::Buffer* VideoCaptureBufferPool::GetBuffer( 164 int buffer_id) { 165 BufferMap::iterator it = buffers_.find(buffer_id); 166 if (it == buffers_.end()) 167 return NULL; 168 return it->second; 169 } 170 171 } // namespace content 172