Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_buffer_pool.cc
blobf3a2c48055ac8458e148dc65ce39cbaa6428b470
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.
5 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h"
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/stl_util.h"
11 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "ui/gfx/buffer_format_util.h"
15 namespace content {
17 const int VideoCaptureBufferPool::kInvalidId = -1;
19 // A simple holder of a memory-backed buffer and accessors to it.
20 class SimpleBufferHandle final : public VideoCaptureBufferPool::BufferHandle {
21 public:
22 SimpleBufferHandle(void* data,
23 size_t mapped_size,
24 base::SharedMemoryHandle handle)
25 : data_(data),
26 mapped_size_(mapped_size)
27 #if defined(OS_POSIX)
29 handle_(handle)
30 #endif
33 ~SimpleBufferHandle() override {}
35 gfx::Size dimensions() const override {
36 NOTREACHED();
37 return gfx::Size();
39 size_t mapped_size() const override { return mapped_size_; }
40 void* data(int plane) override {
41 DCHECK_EQ(plane, 0);
42 return data_;
44 ClientBuffer AsClientBuffer(int plane) override {
45 NOTREACHED();
46 return nullptr;
48 #if defined(OS_POSIX)
49 base::FileDescriptor AsPlatformFile() override {
50 #if defined(OS_MACOSX)
51 return handle_.GetFileDescriptor();
52 #else
53 return handle_;
54 #endif // defined(OS_MACOSX)
56 #endif
58 private:
59 void* const data_;
60 const size_t mapped_size_;
61 #if defined(OS_POSIX)
62 const base::SharedMemoryHandle handle_;
63 #endif
66 // A holder of a GpuMemoryBuffer-backed buffer. Holds weak references to
67 // GpuMemoryBuffer-backed buffers and provides accessors to their data.
68 class GpuMemoryBufferBufferHandle final
69 : public VideoCaptureBufferPool::BufferHandle {
70 public:
71 GpuMemoryBufferBufferHandle(std::vector<void*>* data,
72 const gfx::Size& dimensions,
73 ScopedVector<gfx::GpuMemoryBuffer>* gmbs)
74 : data_(data), dimensions_(dimensions), gmbs_(gmbs) {
75 #ifndef NDEBUG
76 DCHECK_EQ(data->size(), gmbs->size());
77 for (const auto& gmb : *gmbs)
78 DCHECK(gmb && gmb->IsMapped());
79 for (const auto& data_ptr : *data)
80 DCHECK(data_ptr);
81 #endif
83 ~GpuMemoryBufferBufferHandle() override {}
85 gfx::Size dimensions() const override { return dimensions_; }
86 size_t mapped_size() const override { return dimensions_.GetArea(); }
87 void* data(int plane) override {
88 DCHECK_GE(plane, media::VideoFrame::kYPlane);
89 DCHECK_LT(plane, static_cast<int>(data_->size()));
90 return data_->at(plane);
92 ClientBuffer AsClientBuffer(int plane) override {
93 DCHECK_GE(plane, media::VideoFrame::kYPlane);
94 DCHECK_LT(plane, static_cast<int>(gmbs_->size()));
95 return (*gmbs_)[plane]->AsClientBuffer();
97 #if defined(OS_POSIX)
98 base::FileDescriptor AsPlatformFile() override {
99 NOTREACHED();
100 return base::FileDescriptor();
102 #endif
104 private:
105 std::vector<void*>* data_;
106 const gfx::Size dimensions_;
107 ScopedVector<gfx::GpuMemoryBuffer>* const gmbs_;
110 // Tracker specifics for SharedMemory.
111 class VideoCaptureBufferPool::SharedMemTracker final : public Tracker {
112 public:
113 SharedMemTracker();
114 bool Init(media::VideoPixelFormat format,
115 media::VideoPixelStorage storage_type,
116 const gfx::Size& dimensions) override;
118 scoped_ptr<BufferHandle> GetBufferHandle() override {
119 return make_scoped_ptr(new SimpleBufferHandle(
120 shared_memory_.memory(), mapped_size_, shared_memory_.handle()));
122 bool ShareToProcess(base::ProcessHandle process_handle,
123 base::SharedMemoryHandle* new_handle) override {
124 return shared_memory_.ShareToProcess(process_handle, new_handle);
126 bool ShareToProcess2(int plane,
127 base::ProcessHandle process_handle,
128 gfx::GpuMemoryBufferHandle* new_handle) override {
129 NOTREACHED();
130 return false;
133 private:
134 // The memory created to be shared with renderer processes.
135 base::SharedMemory shared_memory_;
136 size_t mapped_size_;
139 // Tracker specifics for GpuMemoryBuffer. Owns GpuMemoryBuffers and its
140 // associated pixel dimensions.
141 class VideoCaptureBufferPool::GpuMemoryBufferTracker final : public Tracker {
142 public:
143 GpuMemoryBufferTracker();
144 bool Init(media::VideoPixelFormat format,
145 media::VideoPixelStorage storage_type,
146 const gfx::Size& dimensions) override;
147 ~GpuMemoryBufferTracker() override;
149 scoped_ptr<BufferHandle> GetBufferHandle() override {
150 return make_scoped_ptr(new GpuMemoryBufferBufferHandle(
151 &data_, dimensions_, &gpu_memory_buffers_));
153 bool ShareToProcess(base::ProcessHandle process_handle,
154 base::SharedMemoryHandle* new_handle) override {
155 NOTREACHED();
156 return false;
158 bool ShareToProcess2(int plane,
159 base::ProcessHandle process_handle,
160 gfx::GpuMemoryBufferHandle* new_handle) override;
162 private:
163 std::vector<void*> data_;
164 gfx::Size dimensions_;
165 // Owned references to GpuMemoryBuffers.
166 ScopedVector<gfx::GpuMemoryBuffer> gpu_memory_buffers_;
169 VideoCaptureBufferPool::SharedMemTracker::SharedMemTracker() : Tracker() {}
171 bool VideoCaptureBufferPool::SharedMemTracker::Init(
172 media::VideoPixelFormat format,
173 media::VideoPixelStorage storage_type,
174 const gfx::Size& dimensions) {
175 DVLOG(2) << "allocating ShMem of " << dimensions.ToString();
176 set_pixel_format(format);
177 set_storage_type(storage_type);
178 // |dimensions| can be 0x0 for trackers that do not require memory backing.
179 set_pixel_count(dimensions.GetArea());
180 mapped_size_ =
181 media::VideoCaptureFormat(dimensions, 0.0f, format, storage_type)
182 .ImageAllocationSize();
183 if (!mapped_size_)
184 return true;
185 return shared_memory_.CreateAndMapAnonymous(mapped_size_);
188 VideoCaptureBufferPool::GpuMemoryBufferTracker::GpuMemoryBufferTracker()
189 : Tracker() {
192 VideoCaptureBufferPool::GpuMemoryBufferTracker::~GpuMemoryBufferTracker() {
193 for (const auto& gmb : gpu_memory_buffers_) {
194 if (gmb->IsMapped())
195 gmb->Unmap();
199 bool VideoCaptureBufferPool::GpuMemoryBufferTracker::Init(
200 media::VideoPixelFormat format,
201 media::VideoPixelStorage storage_type,
202 const gfx::Size& dimensions) {
203 DVLOG(2) << "allocating GMB for " << dimensions.ToString();
204 // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread.
205 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
206 DCHECK(BrowserGpuMemoryBufferManager::current());
207 // This class is only expected to be called with I420 buffer requests at this
208 // point.
209 DCHECK_EQ(format, media::PIXEL_FORMAT_I420);
210 set_pixel_format(format);
211 set_storage_type(storage_type);
212 set_pixel_count(dimensions.GetArea());
213 // |dimensions| can be 0x0 for trackers that do not require memory backing.
214 if (dimensions.GetArea() == 0u)
215 return true;
216 dimensions_ = dimensions;
218 const media::VideoPixelFormat video_format = media::PIXEL_FORMAT_I420;
219 const size_t num_planes = media::VideoFrame::NumPlanes(video_format);
220 for (size_t i = 0; i < num_planes; ++i) {
221 const gfx::Size& size =
222 media::VideoFrame::PlaneSize(video_format, i, dimensions);
223 gpu_memory_buffers_.push_back(
224 BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer(
225 size,
226 gfx::BufferFormat::R_8,
227 gfx::BufferUsage::MAP));
229 DLOG_IF(ERROR, !gpu_memory_buffers_[i]) << "Allocating GpuMemoryBuffer";
230 if (!gpu_memory_buffers_[i])
231 return false;
233 void* temp_data = nullptr;
234 gpu_memory_buffers_[i]->Map(&temp_data);
235 DCHECK(temp_data);
236 data_.push_back(temp_data);
238 return true;
241 bool VideoCaptureBufferPool::GpuMemoryBufferTracker::ShareToProcess2(
242 int plane,
243 base::ProcessHandle process_handle,
244 gfx::GpuMemoryBufferHandle* new_handle) {
245 DCHECK_LE(plane, static_cast<int>(gpu_memory_buffers_.size()));
247 const auto& current_gmb_handle = gpu_memory_buffers_[plane]->GetHandle();
248 switch (current_gmb_handle.type) {
249 case gfx::EMPTY_BUFFER:
250 NOTREACHED();
251 return false;
252 case gfx::SHARED_MEMORY_BUFFER: {
253 DCHECK(base::SharedMemory::IsHandleValid(current_gmb_handle.handle));
254 base::SharedMemory shared_memory(
255 base::SharedMemory::DuplicateHandle(current_gmb_handle.handle),
256 false);
257 shared_memory.ShareToProcess(process_handle, &new_handle->handle);
258 DCHECK(base::SharedMemory::IsHandleValid(new_handle->handle));
259 new_handle->type = gfx::SHARED_MEMORY_BUFFER;
260 return true;
262 case gfx::IO_SURFACE_BUFFER:
263 case gfx::SURFACE_TEXTURE_BUFFER:
264 case gfx::OZONE_NATIVE_PIXMAP:
265 *new_handle = current_gmb_handle;
266 return true;
268 NOTREACHED();
269 return true;
272 // static
273 scoped_ptr<VideoCaptureBufferPool::Tracker>
274 VideoCaptureBufferPool::Tracker::CreateTracker(bool use_gmb) {
275 if (!use_gmb)
276 return make_scoped_ptr(new SharedMemTracker());
277 else
278 return make_scoped_ptr(new GpuMemoryBufferTracker());
281 VideoCaptureBufferPool::Tracker::~Tracker() {}
283 VideoCaptureBufferPool::VideoCaptureBufferPool(int count)
284 : count_(count),
285 next_buffer_id_(0) {
286 DCHECK_GT(count, 0);
289 VideoCaptureBufferPool::~VideoCaptureBufferPool() {
290 STLDeleteValues(&trackers_);
293 bool VideoCaptureBufferPool::ShareToProcess(
294 int buffer_id,
295 base::ProcessHandle process_handle,
296 base::SharedMemoryHandle* new_handle) {
297 base::AutoLock lock(lock_);
299 Tracker* tracker = GetTracker(buffer_id);
300 if (!tracker) {
301 NOTREACHED() << "Invalid buffer_id.";
302 return false;
304 if (tracker->ShareToProcess(process_handle, new_handle))
305 return true;
306 DPLOG(ERROR) << "Error mapping memory";
307 return false;
310 bool VideoCaptureBufferPool::ShareToProcess2(
311 int buffer_id,
312 int plane,
313 base::ProcessHandle process_handle,
314 gfx::GpuMemoryBufferHandle* new_handle) {
315 base::AutoLock lock(lock_);
317 Tracker* tracker = GetTracker(buffer_id);
318 if (!tracker) {
319 NOTREACHED() << "Invalid buffer_id.";
320 return false;
322 if (tracker->ShareToProcess2(plane, process_handle, new_handle))
323 return true;
324 DPLOG(ERROR) << "Error mapping memory";
325 return false;
328 scoped_ptr<VideoCaptureBufferPool::BufferHandle>
329 VideoCaptureBufferPool::GetBufferHandle(int buffer_id) {
330 base::AutoLock lock(lock_);
332 Tracker* tracker = GetTracker(buffer_id);
333 if (!tracker) {
334 NOTREACHED() << "Invalid buffer_id.";
335 return scoped_ptr<BufferHandle>();
338 DCHECK(tracker->held_by_producer());
339 return tracker->GetBufferHandle();
342 int VideoCaptureBufferPool::ReserveForProducer(
343 media::VideoPixelFormat format,
344 media::VideoPixelStorage storage,
345 const gfx::Size& dimensions,
346 int* buffer_id_to_drop) {
347 base::AutoLock lock(lock_);
348 return ReserveForProducerInternal(format, storage, dimensions,
349 buffer_id_to_drop);
352 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id) {
353 base::AutoLock lock(lock_);
354 Tracker* tracker = GetTracker(buffer_id);
355 if (!tracker) {
356 NOTREACHED() << "Invalid buffer_id.";
357 return;
359 DCHECK(tracker->held_by_producer());
360 tracker->set_held_by_producer(false);
363 void VideoCaptureBufferPool::HoldForConsumers(
364 int buffer_id,
365 int num_clients) {
366 base::AutoLock lock(lock_);
367 Tracker* tracker = GetTracker(buffer_id);
368 if (!tracker) {
369 NOTREACHED() << "Invalid buffer_id.";
370 return;
372 DCHECK(tracker->held_by_producer());
373 DCHECK(!tracker->consumer_hold_count());
375 tracker->set_consumer_hold_count(num_clients);
376 // Note: |held_by_producer()| will stay true until
377 // RelinquishProducerReservation() (usually called by destructor of the object
378 // wrapping this tracker, e.g. a media::VideoFrame).
381 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id,
382 int num_clients) {
383 base::AutoLock lock(lock_);
384 Tracker* tracker = GetTracker(buffer_id);
385 if (!tracker) {
386 NOTREACHED() << "Invalid buffer_id.";
387 return;
389 DCHECK_GE(tracker->consumer_hold_count(), num_clients);
391 tracker->set_consumer_hold_count(tracker->consumer_hold_count() -
392 num_clients);
395 double VideoCaptureBufferPool::GetBufferPoolUtilization() const {
396 base::AutoLock lock(lock_);
397 int num_buffers_held = 0;
398 for (const auto& entry : trackers_) {
399 Tracker* const tracker = entry.second;
400 if (tracker->held_by_producer() || tracker->consumer_hold_count() > 0)
401 ++num_buffers_held;
403 return static_cast<double>(num_buffers_held) / count_;
406 int VideoCaptureBufferPool::ReserveForProducerInternal(
407 media::VideoPixelFormat pixel_format,
408 media::VideoPixelStorage storage_type,
409 const gfx::Size& dimensions,
410 int* buffer_id_to_drop) {
411 lock_.AssertAcquired();
412 *buffer_id_to_drop = kInvalidId;
414 const size_t size_in_pixels = dimensions.GetArea();
415 // Look for a tracker that's allocated, big enough, and not in use. Track the
416 // largest one that's not big enough, in case we have to reallocate a tracker.
417 *buffer_id_to_drop = kInvalidId;
418 size_t largest_size_in_pixels = 0;
419 TrackerMap::iterator tracker_to_drop = trackers_.end();
420 for (TrackerMap::iterator it = trackers_.begin(); it != trackers_.end();
421 ++it) {
422 Tracker* const tracker = it->second;
423 if (!tracker->consumer_hold_count() && !tracker->held_by_producer()) {
424 if (tracker->pixel_count() >= size_in_pixels &&
425 (tracker->pixel_format() == pixel_format) &&
426 (tracker->storage_type() == storage_type)) {
427 // Existing tracker is big enough and has correct format. Reuse it.
428 tracker->set_held_by_producer(true);
429 return it->first;
431 if (tracker->pixel_count() > largest_size_in_pixels) {
432 largest_size_in_pixels = tracker->pixel_count();
433 tracker_to_drop = it;
438 // Preferably grow the pool by creating a new tracker. If we're at maximum
439 // size, then reallocate by deleting an existing one instead.
440 if (trackers_.size() == static_cast<size_t>(count_)) {
441 if (tracker_to_drop == trackers_.end()) {
442 // We're out of space, and can't find an unused tracker to reallocate.
443 return kInvalidId;
445 *buffer_id_to_drop = tracker_to_drop->first;
446 delete tracker_to_drop->second;
447 trackers_.erase(tracker_to_drop);
450 // Create the new tracker.
451 const int buffer_id = next_buffer_id_++;
453 scoped_ptr<Tracker> tracker = Tracker::CreateTracker(
454 storage_type == media::PIXEL_STORAGE_GPUMEMORYBUFFER);
455 if (!tracker->Init(pixel_format, storage_type, dimensions)) {
456 DLOG(ERROR) << "Error initializing Tracker";
457 return kInvalidId;
459 tracker->set_held_by_producer(true);
460 trackers_[buffer_id] = tracker.release();
462 return buffer_id;
465 VideoCaptureBufferPool::Tracker* VideoCaptureBufferPool::GetTracker(
466 int buffer_id) {
467 TrackerMap::const_iterator it = trackers_.find(buffer_id);
468 return (it == trackers_.end()) ? NULL : it->second;
471 } // namespace content