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