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