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"
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
{
22 SimpleBufferHandle(void* data
,
24 base::SharedMemoryHandle handle
)
26 mapped_size_(mapped_size
)
33 ~SimpleBufferHandle() override
{}
35 gfx::Size
dimensions() const override
{
39 size_t mapped_size() const override
{ return mapped_size_
; }
40 void* data(int plane
) override
{
44 ClientBuffer
AsClientBuffer(int plane
) override
{
49 base::FileDescriptor
AsPlatformFile() override
{
50 #if defined(OS_MACOSX)
51 return handle_
.GetFileDescriptor();
54 #endif // defined(OS_MACOSX)
60 const size_t mapped_size_
;
62 const base::SharedMemoryHandle handle_
;
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
{
71 GpuMemoryBufferBufferHandle(std::vector
<void*>* data
,
72 const gfx::Size
& dimensions
,
73 ScopedVector
<gfx::GpuMemoryBuffer
>* gmbs
)
74 : data_(data
), dimensions_(dimensions
), gmbs_(gmbs
) {
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
)
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();
98 base::FileDescriptor
AsPlatformFile() override
{
100 return base::FileDescriptor();
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
{
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
{
134 // The memory created to be shared with renderer processes.
135 base::SharedMemory shared_memory_
;
139 // Tracker specifics for GpuMemoryBuffer. Owns GpuMemoryBuffers and its
140 // associated pixel dimensions.
141 class VideoCaptureBufferPool::GpuMemoryBufferTracker final
: public Tracker
{
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
{
158 bool ShareToProcess2(int plane
,
159 base::ProcessHandle process_handle
,
160 gfx::GpuMemoryBufferHandle
* new_handle
) override
;
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());
181 media::VideoCaptureFormat(dimensions
, 0.0f
, format
, storage_type
)
182 .ImageAllocationSize();
185 return shared_memory_
.CreateAndMapAnonymous(mapped_size_
);
188 VideoCaptureBufferPool::GpuMemoryBufferTracker::GpuMemoryBufferTracker()
192 VideoCaptureBufferPool::GpuMemoryBufferTracker::~GpuMemoryBufferTracker() {
193 for (const auto& gmb
: gpu_memory_buffers_
) {
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
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)
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(
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
])
233 void* temp_data
= nullptr;
234 gpu_memory_buffers_
[i
]->Map(&temp_data
);
236 data_
.push_back(temp_data
);
241 bool VideoCaptureBufferPool::GpuMemoryBufferTracker::ShareToProcess2(
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
:
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
),
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
;
262 case gfx::IO_SURFACE_BUFFER
:
263 case gfx::SURFACE_TEXTURE_BUFFER
:
264 case gfx::OZONE_NATIVE_PIXMAP
:
265 *new_handle
= current_gmb_handle
;
273 scoped_ptr
<VideoCaptureBufferPool::Tracker
>
274 VideoCaptureBufferPool::Tracker::CreateTracker(bool use_gmb
) {
276 return make_scoped_ptr(new SharedMemTracker());
278 return make_scoped_ptr(new GpuMemoryBufferTracker());
281 VideoCaptureBufferPool::Tracker::~Tracker() {}
283 VideoCaptureBufferPool::VideoCaptureBufferPool(int count
)
289 VideoCaptureBufferPool::~VideoCaptureBufferPool() {
290 STLDeleteValues(&trackers_
);
293 bool VideoCaptureBufferPool::ShareToProcess(
295 base::ProcessHandle process_handle
,
296 base::SharedMemoryHandle
* new_handle
) {
297 base::AutoLock
lock(lock_
);
299 Tracker
* tracker
= GetTracker(buffer_id
);
301 NOTREACHED() << "Invalid buffer_id.";
304 if (tracker
->ShareToProcess(process_handle
, new_handle
))
306 DPLOG(ERROR
) << "Error mapping memory";
310 bool VideoCaptureBufferPool::ShareToProcess2(
313 base::ProcessHandle process_handle
,
314 gfx::GpuMemoryBufferHandle
* new_handle
) {
315 base::AutoLock
lock(lock_
);
317 Tracker
* tracker
= GetTracker(buffer_id
);
319 NOTREACHED() << "Invalid buffer_id.";
322 if (tracker
->ShareToProcess2(plane
, process_handle
, new_handle
))
324 DPLOG(ERROR
) << "Error mapping memory";
328 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
>
329 VideoCaptureBufferPool::GetBufferHandle(int buffer_id
) {
330 base::AutoLock
lock(lock_
);
332 Tracker
* tracker
= GetTracker(buffer_id
);
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
,
352 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id
) {
353 base::AutoLock
lock(lock_
);
354 Tracker
* tracker
= GetTracker(buffer_id
);
356 NOTREACHED() << "Invalid buffer_id.";
359 DCHECK(tracker
->held_by_producer());
360 tracker
->set_held_by_producer(false);
363 void VideoCaptureBufferPool::HoldForConsumers(
366 base::AutoLock
lock(lock_
);
367 Tracker
* tracker
= GetTracker(buffer_id
);
369 NOTREACHED() << "Invalid buffer_id.";
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
,
383 base::AutoLock
lock(lock_
);
384 Tracker
* tracker
= GetTracker(buffer_id
);
386 NOTREACHED() << "Invalid buffer_id.";
389 DCHECK_GE(tracker
->consumer_hold_count(), num_clients
);
391 tracker
->set_consumer_hold_count(tracker
->consumer_hold_count() -
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)
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();
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);
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.
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";
459 tracker
->set_held_by_producer(true);
460 trackers_
[buffer_id
] = tracker
.release();
465 VideoCaptureBufferPool::Tracker
* VideoCaptureBufferPool::GetTracker(
467 TrackerMap::const_iterator it
= trackers_
.find(buffer_id
);
468 return (it
== trackers_
.end()) ? NULL
: it
->second
;
471 } // namespace content