| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" | 5 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/stl_util.h" |
| 10 #include "media/base/video_frame.h" | 12 #include "media/base/video_frame.h" |
| 11 #include "media/base/video_util.h" | 13 #include "media/base/video_util.h" |
| 12 | 14 |
| 13 namespace content { | 15 namespace content { |
| 14 | 16 |
| 15 VideoCaptureBufferPool::VideoCaptureBufferPool(size_t size, int count) | 17 const int VideoCaptureBufferPool::kInvalidId = -1; |
| 16 : size_(size), | 18 |
| 17 count_(count) { | 19 VideoCaptureBufferPool::VideoCaptureBufferPool(int count) |
| 20 : count_(count), |
| 21 next_buffer_id_(0) { |
| 18 } | 22 } |
| 19 | 23 |
| 20 VideoCaptureBufferPool::~VideoCaptureBufferPool() { | 24 VideoCaptureBufferPool::~VideoCaptureBufferPool() { |
| 21 } | 25 STLDeleteValues(&buffers_); |
| 22 | |
| 23 bool VideoCaptureBufferPool::Allocate() { | |
| 24 base::AutoLock lock(lock_); | |
| 25 DCHECK(!IsAllocated()); | |
| 26 buffers_.resize(count_); | |
| 27 for (int buffer_id = 0; buffer_id < count(); ++buffer_id) { | |
| 28 Buffer* buffer = new Buffer(); | |
| 29 buffers_[buffer_id] = buffer; | |
| 30 if (!buffer->shared_memory.CreateAndMapAnonymous(GetMemorySize())) | |
| 31 return false; | |
| 32 } | |
| 33 return true; | |
| 34 } | 26 } |
| 35 | 27 |
| 36 base::SharedMemoryHandle VideoCaptureBufferPool::ShareToProcess( | 28 base::SharedMemoryHandle VideoCaptureBufferPool::ShareToProcess( |
| 37 int buffer_id, | 29 int buffer_id, |
| 38 base::ProcessHandle process_handle) { | 30 base::ProcessHandle process_handle, |
| 31 size_t* memory_size) { |
| 39 base::AutoLock lock(lock_); | 32 base::AutoLock lock(lock_); |
| 40 DCHECK(IsAllocated()); | 33 |
| 41 DCHECK(buffer_id >= 0); | 34 Buffer* buffer = GetBuffer(buffer_id); |
| 42 DCHECK(buffer_id < count_); | 35 if (!buffer) { |
| 43 Buffer* buffer = buffers_[buffer_id]; | 36 NOTREACHED() << "Invalid buffer_id."; |
| 37 return base::SharedMemory::NULLHandle(); |
| 38 } |
| 44 base::SharedMemoryHandle remote_handle; | 39 base::SharedMemoryHandle remote_handle; |
| 45 buffer->shared_memory.ShareToProcess(process_handle, &remote_handle); | 40 buffer->shared_memory.ShareToProcess(process_handle, &remote_handle); |
| 41 *memory_size = buffer->shared_memory.requested_size(); |
| 46 return remote_handle; | 42 return remote_handle; |
| 47 } | 43 } |
| 48 | 44 |
| 49 base::SharedMemoryHandle VideoCaptureBufferPool::GetHandle(int buffer_id) { | 45 int VideoCaptureBufferPool::ReserveForProducer(size_t size, |
| 46 int* buffer_id_to_drop) { |
| 50 base::AutoLock lock(lock_); | 47 base::AutoLock lock(lock_); |
| 51 DCHECK(IsAllocated()); | 48 return ReserveForProducerInternal(size, buffer_id_to_drop); |
| 52 DCHECK(buffer_id >= 0); | |
| 53 DCHECK(buffer_id < count_); | |
| 54 return buffers_[buffer_id]->shared_memory.handle(); | |
| 55 } | |
| 56 | |
| 57 void* VideoCaptureBufferPool::GetMemory(int buffer_id) { | |
| 58 base::AutoLock lock(lock_); | |
| 59 DCHECK(IsAllocated()); | |
| 60 DCHECK(buffer_id >= 0); | |
| 61 DCHECK(buffer_id < count_); | |
| 62 return buffers_[buffer_id]->shared_memory.memory(); | |
| 63 } | |
| 64 | |
| 65 int VideoCaptureBufferPool::ReserveForProducer() { | |
| 66 base::AutoLock lock(lock_); | |
| 67 return ReserveForProducerInternal(); | |
| 68 } | 49 } |
| 69 | 50 |
| 70 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id) { | 51 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id) { |
| 71 base::AutoLock lock(lock_); | 52 base::AutoLock lock(lock_); |
| 72 DCHECK(buffer_id >= 0); | 53 Buffer* buffer = GetBuffer(buffer_id); |
| 73 DCHECK(buffer_id < count()); | 54 if (!buffer) { |
| 74 Buffer* buffer = buffers_[buffer_id]; | 55 NOTREACHED() << "Invalid buffer_id."; |
| 56 return; |
| 57 } |
| 75 DCHECK(buffer->held_by_producer); | 58 DCHECK(buffer->held_by_producer); |
| 76 buffer->held_by_producer = false; | 59 buffer->held_by_producer = false; |
| 77 } | 60 } |
| 78 | 61 |
| 79 void VideoCaptureBufferPool::HoldForConsumers( | 62 void VideoCaptureBufferPool::HoldForConsumers( |
| 80 int buffer_id, | 63 int buffer_id, |
| 81 int num_clients) { | 64 int num_clients) { |
| 82 base::AutoLock lock(lock_); | 65 base::AutoLock lock(lock_); |
| 83 DCHECK(buffer_id >= 0); | 66 Buffer* buffer = GetBuffer(buffer_id); |
| 84 DCHECK(buffer_id < count()); | 67 if (!buffer) { |
| 85 DCHECK(IsAllocated()); | 68 NOTREACHED() << "Invalid buffer_id."; |
| 86 Buffer* buffer = buffers_[buffer_id]; | 69 return; |
| 70 } |
| 87 DCHECK(buffer->held_by_producer); | 71 DCHECK(buffer->held_by_producer); |
| 88 DCHECK(!buffer->consumer_hold_count); | 72 DCHECK(!buffer->consumer_hold_count); |
| 89 | 73 |
| 90 buffer->consumer_hold_count = num_clients; | 74 buffer->consumer_hold_count = num_clients; |
| 91 // Note: |held_by_producer| will stay true until | 75 // Note: |held_by_producer| will stay true until |
| 92 // RelinquishProducerReservation() (usually called by destructor of the object | 76 // RelinquishProducerReservation() (usually called by destructor of the object |
| 93 // wrapping this buffer, e.g. a media::VideoFrame | 77 // wrapping this buffer, e.g. a media::VideoFrame). |
| 94 } | 78 } |
| 95 | 79 |
| 96 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id, | 80 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id, |
| 97 int num_clients) { | 81 int num_clients) { |
| 98 base::AutoLock lock(lock_); | 82 base::AutoLock lock(lock_); |
| 99 DCHECK(buffer_id >= 0); | 83 Buffer* buffer = GetBuffer(buffer_id); |
| 100 DCHECK(buffer_id < count()); | 84 if (!buffer) { |
| 101 DCHECK_GT(num_clients, 0); | 85 NOTREACHED() << "Invalid buffer_id."; |
| 102 DCHECK(IsAllocated()); | 86 return; |
| 103 Buffer* buffer = buffers_[buffer_id]; | 87 } |
| 104 DCHECK_GE(buffer->consumer_hold_count, num_clients); | 88 DCHECK_GE(buffer->consumer_hold_count, num_clients); |
| 105 | 89 |
| 106 buffer->consumer_hold_count -= num_clients; | 90 buffer->consumer_hold_count -= num_clients; |
| 107 } | 91 } |
| 108 | 92 |
| 109 // State query functions. | |
| 110 size_t VideoCaptureBufferPool::GetMemorySize() const { | |
| 111 // No need to take |lock_| currently. | |
| 112 return size_; | |
| 113 } | |
| 114 | |
| 115 int VideoCaptureBufferPool::RecognizeReservedBuffer( | 93 int VideoCaptureBufferPool::RecognizeReservedBuffer( |
| 116 base::SharedMemoryHandle maybe_belongs_to_pool) { | 94 base::SharedMemoryHandle maybe_belongs_to_pool) { |
| 117 base::AutoLock lock(lock_); | 95 base::AutoLock lock(lock_); |
| 118 for (int buffer_id = 0; buffer_id < count(); ++buffer_id) { | 96 for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); it++) { |
| 119 Buffer* buffer = buffers_[buffer_id]; | 97 if (it->second->shared_memory.handle() == maybe_belongs_to_pool) { |
| 120 if (buffer->shared_memory.handle() == maybe_belongs_to_pool) { | 98 DCHECK(it->second->held_by_producer); |
| 121 DCHECK(buffer->held_by_producer); | 99 return it->first; |
| 122 return buffer_id; | |
| 123 } | 100 } |
| 124 } | 101 } |
| 125 return -1; // Buffer is not from our pool. | 102 return kInvalidId; // Buffer is not from our pool. |
| 126 } | 103 } |
| 127 | 104 |
| 128 scoped_refptr<media::VideoFrame> VideoCaptureBufferPool::ReserveI420VideoFrame( | 105 scoped_refptr<media::VideoFrame> VideoCaptureBufferPool::ReserveI420VideoFrame( |
| 129 const gfx::Size& size, | 106 const gfx::Size& size, |
| 130 int rotation) { | 107 int rotation, |
| 131 if (GetMemorySize() != | 108 int* buffer_id_to_drop) { |
| 132 media::VideoFrame::AllocationSize(media::VideoFrame::I420, size)) { | |
| 133 DCHECK_EQ(GetMemorySize(), | |
| 134 media::VideoFrame::AllocationSize(media::VideoFrame::I420, size)); | |
| 135 return NULL; | |
| 136 } | |
| 137 | |
| 138 base::AutoLock lock(lock_); | 109 base::AutoLock lock(lock_); |
| 139 | 110 |
| 140 int buffer_id = ReserveForProducerInternal(); | 111 size_t frame_bytes = |
| 141 if (buffer_id < 0) | 112 media::VideoFrame::AllocationSize(media::VideoFrame::I420, size); |
| 113 |
| 114 int buffer_id = ReserveForProducerInternal(frame_bytes, buffer_id_to_drop); |
| 115 if (buffer_id == kInvalidId) |
| 142 return NULL; | 116 return NULL; |
| 143 | 117 |
| 144 base::Closure disposal_handler = base::Bind( | 118 base::Closure disposal_handler = base::Bind( |
| 145 &VideoCaptureBufferPool::RelinquishProducerReservation, | 119 &VideoCaptureBufferPool::RelinquishProducerReservation, |
| 146 this, | 120 this, |
| 147 buffer_id); | 121 buffer_id); |
| 148 | 122 |
| 149 Buffer* buffer = buffers_[buffer_id]; | 123 Buffer* buffer = GetBuffer(buffer_id); |
| 150 // Wrap the buffer in a VideoFrame container. | 124 // Wrap the buffer in a VideoFrame container. |
| 151 scoped_refptr<media::VideoFrame> frame = | 125 scoped_refptr<media::VideoFrame> frame = |
| 152 media::VideoFrame::WrapExternalSharedMemory( | 126 media::VideoFrame::WrapExternalSharedMemory( |
| 153 media::VideoFrame::I420, | 127 media::VideoFrame::I420, |
| 154 size, | 128 size, |
| 155 gfx::Rect(size), | 129 gfx::Rect(size), |
| 156 size, | 130 size, |
| 157 static_cast<uint8*>(buffer->shared_memory.memory()), | 131 static_cast<uint8*>(buffer->shared_memory.memory()), |
| 158 GetMemorySize(), | 132 frame_bytes, |
| 159 buffer->shared_memory.handle(), | 133 buffer->shared_memory.handle(), |
| 160 base::TimeDelta(), | 134 base::TimeDelta(), |
| 161 disposal_handler); | 135 disposal_handler); |
| 162 | 136 |
| 163 if (buffer->rotation != rotation) { | 137 if (buffer->rotation != rotation) { |
| 164 // TODO(nick): Generalize the |rotation| mechanism. | 138 // TODO(jiayl): Generalize the |rotation| mechanism. |
| 165 media::FillYUV(frame.get(), 0, 128, 128); | 139 media::FillYUV(frame.get(), 0, 128, 128); |
| 166 buffer->rotation = rotation; | 140 buffer->rotation = rotation; |
| 167 } | 141 } |
| 168 | 142 |
| 169 return frame; | 143 return frame; |
| 170 } | 144 } |
| 171 | 145 |
| 172 bool VideoCaptureBufferPool::IsAnyBufferHeldForConsumers() { | |
| 173 base::AutoLock lock(lock_); | |
| 174 for (int buffer_id = 0; buffer_id < count(); ++buffer_id) { | |
| 175 Buffer* buffer = buffers_[buffer_id]; | |
| 176 if (buffer->consumer_hold_count > 0) | |
| 177 return true; | |
| 178 } | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 VideoCaptureBufferPool::Buffer::Buffer() | 146 VideoCaptureBufferPool::Buffer::Buffer() |
| 183 : rotation(0), | 147 : rotation(0), |
| 184 held_by_producer(false), | 148 held_by_producer(false), |
| 185 consumer_hold_count(0) {} | 149 consumer_hold_count(0) {} |
| 186 | 150 |
| 187 int VideoCaptureBufferPool::ReserveForProducerInternal() { | 151 int VideoCaptureBufferPool::ReserveForProducerInternal(size_t size, |
| 152 int* buffer_id_to_drop) { |
| 188 lock_.AssertAcquired(); | 153 lock_.AssertAcquired(); |
| 189 DCHECK(IsAllocated()); | |
| 190 | 154 |
| 191 int buffer_id = -1; | 155 // Look for a buffer that's allocated, big enough, and not in use. |
| 192 for (int candidate_id = 0; candidate_id < count(); ++candidate_id) { | 156 *buffer_id_to_drop = kInvalidId; |
| 193 Buffer* candidate = buffers_[candidate_id]; | 157 for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); it++) { |
| 194 if (!candidate->consumer_hold_count && !candidate->held_by_producer) { | 158 Buffer* buffer = it->second; |
| 195 buffer_id = candidate_id; | 159 if (!buffer->consumer_hold_count && !buffer->held_by_producer) { |
| 160 if (buffer->shared_memory.requested_size() >= size) { |
| 161 // Existing buffer is big enough. Reuse it. |
| 162 buffer->held_by_producer = true; |
| 163 return it->first; |
| 164 } |
| 165 } |
| 166 } |
| 167 |
| 168 // Look for a buffer that's not in use, that we can reallocate. |
| 169 for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); it++) { |
| 170 Buffer* buffer = it->second; |
| 171 if (!buffer->consumer_hold_count && !buffer->held_by_producer) { |
| 172 // Existing buffer is too small. Free it so we can allocate a new one |
| 173 // after the loop. |
| 174 *buffer_id_to_drop = it->first; |
| 175 buffers_.erase(it); |
| 176 delete buffer; |
| 196 break; | 177 break; |
| 197 } | 178 } |
| 198 } | 179 } |
| 199 if (buffer_id == -1) | |
| 200 return -1; | |
| 201 | 180 |
| 202 Buffer* buffer = buffers_[buffer_id]; | 181 // If possible, grow the pool by creating a new buffer. |
| 203 CHECK_GE(buffer->shared_memory.requested_size(), size_); | 182 if (static_cast<int>(buffers_.size()) < count_) { |
| 204 buffer->held_by_producer = true; | 183 int buffer_id = next_buffer_id_++; |
| 205 return buffer_id; | 184 scoped_ptr<Buffer> buffer(new Buffer()); |
| 185 if (!buffer->shared_memory.CreateAndMapAnonymous(size)) |
| 186 return kInvalidId; |
| 187 buffer->held_by_producer = true; |
| 188 buffers_[buffer_id] = buffer.release(); |
| 189 return buffer_id; |
| 190 } |
| 191 |
| 192 // The pool is at its size limit, and all buffers are in use. |
| 193 return kInvalidId; |
| 206 } | 194 } |
| 207 | 195 |
| 208 bool VideoCaptureBufferPool::IsAllocated() const { | 196 VideoCaptureBufferPool::Buffer* VideoCaptureBufferPool::GetBuffer( |
| 209 lock_.AssertAcquired(); | 197 int buffer_id) { |
| 210 return !buffers_.empty(); | 198 BufferMap::iterator it = buffers_.find(buffer_id); |
| 199 if (it == buffers_.end()) |
| 200 return NULL; |
| 201 return it->second; |
| 211 } | 202 } |
| 212 | 203 |
| 213 } // namespace content | 204 } // namespace content |
| 214 | 205 |
| OLD | NEW |