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 |