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/stl_util.h"
10 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
11 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
12 #include "content/public/browser/browser_thread.h"
16 const int VideoCaptureBufferPool::kInvalidId
= -1;
18 // A simple holder of a memory-backed buffer and accesors to it.
19 class SimpleBufferHandle final
: public VideoCaptureBufferPool::BufferHandle
{
21 SimpleBufferHandle(void* data
, size_t size
, base::SharedMemoryHandle handle
)
29 ~SimpleBufferHandle() override
{}
31 size_t size() const override
{ return size_
; }
32 void* data() override
{ return data_
; }
33 ClientBuffer
AsClientBuffer() override
{ return nullptr; }
35 base::FileDescriptor
AsPlatformFile() override
{
36 #if defined(OS_MACOSX)
37 return handle_
.GetFileDescriptor();
40 #endif // defined(OS_MACOSX)
48 const base::SharedMemoryHandle handle_
;
52 // A holder of a GpuMemoryBuffer-backed buffer, Map()ed on ctor and Unmap()ed on
53 // dtor. Holds a weak reference to its GpuMemoryBuffer.
54 // TODO(mcasas) Map()ed on ctor, or on first use?
55 class GpuMemoryBufferBufferHandle
56 final
: public VideoCaptureBufferPool::BufferHandle
{
58 GpuMemoryBufferBufferHandle(gfx::GpuMemoryBuffer
* gmb
, size_t size
)
60 data_(new void* [GpuMemoryBufferImpl::
61 NumberOfPlanesForGpuMemoryBufferFormat(
64 DCHECK(gmb
&& !gmb_
->IsMapped());
65 gmb_
->Map(data_
.get());
67 ~GpuMemoryBufferBufferHandle() override
{ gmb_
->Unmap(); }
69 size_t size() const override
{ return size_
; }
70 void* data() override
{ return data_
[0]; }
71 ClientBuffer
AsClientBuffer() override
{ return gmb_
->AsClientBuffer(); }
73 base::FileDescriptor
AsPlatformFile() override
{
74 #if defined(OS_MACOSX)
75 return gmb_
->GetHandle().handle
.GetFileDescriptor();
77 return gmb_
->GetHandle().handle
;
78 #endif // defined(OS_MACOSX)
83 gfx::GpuMemoryBuffer
* const gmb_
;
84 scoped_ptr
<void*[]> data_
;
88 // Tracker specifics for SharedMemory.
89 class VideoCaptureBufferPool::SharedMemTracker final
: public Tracker
{
92 bool Init(media::VideoCapturePixelFormat format
,
93 media::VideoPixelStorage storage_type
,
94 const gfx::Size
& dimensions
) override
;
96 size_t mapped_size() const override
{ return shared_memory_
.mapped_size(); }
98 scoped_ptr
<BufferHandle
> GetBufferHandle() override
{
99 return make_scoped_ptr(new SimpleBufferHandle(
100 shared_memory_
.memory(), mapped_size(), shared_memory_
.handle()));
103 bool ShareToProcess(base::ProcessHandle process_handle
,
104 base::SharedMemoryHandle
* new_handle
) override
{
105 return shared_memory_
.ShareToProcess(process_handle
, new_handle
);
109 // The memory created to be shared with renderer processes.
110 base::SharedMemory shared_memory_
;
113 // Tracker specifics for GpuMemoryBuffer. Owns one GpuMemoryBuffer and its
114 // associated pixel dimensions.
115 class VideoCaptureBufferPool::GpuMemoryBufferTracker final
: public Tracker
{
117 GpuMemoryBufferTracker();
118 bool Init(media::VideoCapturePixelFormat format
,
119 media::VideoPixelStorage storage_type
,
120 const gfx::Size
& dimensions
) override
;
121 ~GpuMemoryBufferTracker() override
;
123 size_t mapped_size() const override
{ return packed_size_
; }
124 scoped_ptr
<BufferHandle
> GetBufferHandle() override
{
125 return make_scoped_ptr(new GpuMemoryBufferBufferHandle(
126 gpu_memory_buffer_
.get(), packed_size_
));
129 bool ShareToProcess(base::ProcessHandle process_handle
,
130 base::SharedMemoryHandle
* new_handle
) override
{
136 scoped_ptr
<gfx::GpuMemoryBuffer
> gpu_memory_buffer_
;
139 VideoCaptureBufferPool::SharedMemTracker::SharedMemTracker() : Tracker() {
142 bool VideoCaptureBufferPool::SharedMemTracker::Init(
143 media::VideoCapturePixelFormat format
,
144 media::VideoPixelStorage storage_type
,
145 const gfx::Size
& dimensions
) {
146 DVLOG(2) << "allocating ShMem of " << dimensions
.ToString();
147 set_pixel_format(format
);
148 set_storage_type(storage_type
);
149 // |dimensions| can be 0x0 for trackers that do not require memory backing.
150 set_pixel_count(dimensions
.GetArea());
151 const size_t byte_count
=
152 media::VideoCaptureFormat(dimensions
, 0.0f
, format
, storage_type
)
153 .ImageAllocationSize();
156 return shared_memory_
.CreateAndMapAnonymous(byte_count
);
159 VideoCaptureBufferPool::GpuMemoryBufferTracker::GpuMemoryBufferTracker()
160 : Tracker(), packed_size_(0u), gpu_memory_buffer_(nullptr) {
163 VideoCaptureBufferPool::GpuMemoryBufferTracker::~GpuMemoryBufferTracker() {
164 if (gpu_memory_buffer_
->IsMapped())
165 gpu_memory_buffer_
->Unmap();
168 bool VideoCaptureBufferPool::GpuMemoryBufferTracker::Init(
169 media::VideoCapturePixelFormat format
,
170 media::VideoPixelStorage storage_type
,
171 const gfx::Size
& dimensions
) {
172 DVLOG(2) << "allocating GMB for " << dimensions
.ToString();
173 // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread.
174 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO
));
175 DCHECK(BrowserGpuMemoryBufferManager::current());
176 set_pixel_format(format
);
177 set_storage_type(storage_type
);
178 set_pixel_count(dimensions
.GetArea());
179 // |dimensions| can be 0x0 for trackers that do not require memory backing.
180 if (dimensions
.GetArea() == 0u)
183 BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer(
185 gfx::GpuMemoryBuffer::BGRA_8888
,
186 gfx::GpuMemoryBuffer::MAP
);
187 DLOG_IF(ERROR
, !gpu_memory_buffer_
.get()) << "Allocating GpuMemoryBuffer";
188 if (!gpu_memory_buffer_
.get())
191 gpu_memory_buffer_
->GetStride(&plane_sizes
);
192 packed_size_
= plane_sizes
* dimensions
.height();
197 scoped_ptr
<VideoCaptureBufferPool::Tracker
>
198 VideoCaptureBufferPool::Tracker::CreateTracker(bool use_gmb
) {
200 return make_scoped_ptr(new SharedMemTracker());
202 return make_scoped_ptr(new GpuMemoryBufferTracker());
205 VideoCaptureBufferPool::Tracker::~Tracker() {}
207 VideoCaptureBufferPool::VideoCaptureBufferPool(int count
)
213 VideoCaptureBufferPool::~VideoCaptureBufferPool() {
214 STLDeleteValues(&trackers_
);
217 base::SharedMemoryHandle
VideoCaptureBufferPool::ShareToProcess(
219 base::ProcessHandle process_handle
,
220 size_t* memory_size
) {
221 base::AutoLock
lock(lock_
);
223 Tracker
* tracker
= GetTracker(buffer_id
);
225 NOTREACHED() << "Invalid buffer_id.";
226 return base::SharedMemory::NULLHandle();
228 base::SharedMemoryHandle remote_handle
;
229 if (tracker
->ShareToProcess(process_handle
, &remote_handle
)) {
230 *memory_size
= tracker
->mapped_size();
231 return remote_handle
;
233 DPLOG(ERROR
) << "Error mapping Shared Memory";
234 return base::SharedMemoryHandle();
237 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
>
238 VideoCaptureBufferPool::GetBufferHandle(int buffer_id
) {
239 base::AutoLock
lock(lock_
);
241 Tracker
* tracker
= GetTracker(buffer_id
);
243 NOTREACHED() << "Invalid buffer_id.";
244 return scoped_ptr
<BufferHandle
>();
247 DCHECK(tracker
->held_by_producer());
248 return tracker
->GetBufferHandle();
251 int VideoCaptureBufferPool::ReserveForProducer(
252 media::VideoCapturePixelFormat format
,
253 media::VideoPixelStorage storage
,
254 const gfx::Size
& dimensions
,
255 int* buffer_id_to_drop
) {
256 base::AutoLock
lock(lock_
);
257 return ReserveForProducerInternal(format
, storage
, dimensions
,
261 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id
) {
262 base::AutoLock
lock(lock_
);
263 Tracker
* tracker
= GetTracker(buffer_id
);
265 NOTREACHED() << "Invalid buffer_id.";
268 DCHECK(tracker
->held_by_producer());
269 tracker
->set_held_by_producer(false);
272 void VideoCaptureBufferPool::HoldForConsumers(
275 base::AutoLock
lock(lock_
);
276 Tracker
* tracker
= GetTracker(buffer_id
);
278 NOTREACHED() << "Invalid buffer_id.";
281 DCHECK(tracker
->held_by_producer());
282 DCHECK(!tracker
->consumer_hold_count());
284 tracker
->set_consumer_hold_count(num_clients
);
285 // Note: |held_by_producer()| will stay true until
286 // RelinquishProducerReservation() (usually called by destructor of the object
287 // wrapping this tracker, e.g. a media::VideoFrame).
290 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id
,
292 base::AutoLock
lock(lock_
);
293 Tracker
* tracker
= GetTracker(buffer_id
);
295 NOTREACHED() << "Invalid buffer_id.";
298 DCHECK_GE(tracker
->consumer_hold_count(), num_clients
);
300 tracker
->set_consumer_hold_count(tracker
->consumer_hold_count() -
304 double VideoCaptureBufferPool::GetBufferPoolUtilization() const {
305 base::AutoLock
lock(lock_
);
306 int num_buffers_held
= 0;
307 for (const auto& entry
: trackers_
) {
308 Tracker
* const tracker
= entry
.second
;
309 if (tracker
->held_by_producer() || tracker
->consumer_hold_count() > 0)
312 return static_cast<double>(num_buffers_held
) / count_
;
315 int VideoCaptureBufferPool::ReserveForProducerInternal(
316 media::VideoCapturePixelFormat pixel_format
,
317 media::VideoPixelStorage storage_type
,
318 const gfx::Size
& dimensions
,
319 int* buffer_id_to_drop
) {
320 lock_
.AssertAcquired();
321 *buffer_id_to_drop
= kInvalidId
;
323 const size_t size_in_pixels
= dimensions
.GetArea();
324 // Look for a tracker that's allocated, big enough, and not in use. Track the
325 // largest one that's not big enough, in case we have to reallocate a tracker.
326 *buffer_id_to_drop
= kInvalidId
;
327 size_t largest_size_in_pixels
= 0;
328 TrackerMap::iterator tracker_to_drop
= trackers_
.end();
329 for (TrackerMap::iterator it
= trackers_
.begin(); it
!= trackers_
.end();
331 Tracker
* const tracker
= it
->second
;
332 if (!tracker
->consumer_hold_count() && !tracker
->held_by_producer()) {
333 if (tracker
->pixel_count() >= size_in_pixels
&&
334 (tracker
->pixel_format() == pixel_format
) &&
335 (tracker
->storage_type() == storage_type
)) {
336 // Existing tracker is big enough and has correct format. Reuse it.
337 tracker
->set_held_by_producer(true);
340 if (tracker
->pixel_count() > largest_size_in_pixels
) {
341 largest_size_in_pixels
= tracker
->pixel_count();
342 tracker_to_drop
= it
;
347 // Preferably grow the pool by creating a new tracker. If we're at maximum
348 // size, then reallocate by deleting an existing one instead.
349 if (trackers_
.size() == static_cast<size_t>(count_
)) {
350 if (tracker_to_drop
== trackers_
.end()) {
351 // We're out of space, and can't find an unused tracker to reallocate.
354 *buffer_id_to_drop
= tracker_to_drop
->first
;
355 delete tracker_to_drop
->second
;
356 trackers_
.erase(tracker_to_drop
);
359 // Create the new tracker.
360 const int buffer_id
= next_buffer_id_
++;
362 scoped_ptr
<Tracker
> tracker
= Tracker::CreateTracker(
363 storage_type
== media::PIXEL_STORAGE_GPUMEMORYBUFFER
);
364 if (!tracker
->Init(pixel_format
, storage_type
, dimensions
)) {
365 DLOG(ERROR
) << "Error initializing Tracker";
368 tracker
->set_held_by_producer(true);
369 trackers_
[buffer_id
] = tracker
.release();
374 VideoCaptureBufferPool::Tracker
* VideoCaptureBufferPool::GetTracker(
376 TrackerMap::const_iterator it
= trackers_
.find(buffer_id
);
377 return (it
== trackers_
.end()) ? NULL
: it
->second
;
380 } // namespace content