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(
184 dimensions
, gfx::BufferFormat::BGRA_8888
, gfx::BufferUsage::MAP
);
185 DLOG_IF(ERROR
, !gpu_memory_buffer_
.get()) << "Allocating GpuMemoryBuffer";
186 if (!gpu_memory_buffer_
.get())
189 gpu_memory_buffer_
->GetStride(&plane_sizes
);
190 packed_size_
= plane_sizes
* dimensions
.height();
195 scoped_ptr
<VideoCaptureBufferPool::Tracker
>
196 VideoCaptureBufferPool::Tracker::CreateTracker(bool use_gmb
) {
198 return make_scoped_ptr(new SharedMemTracker());
200 return make_scoped_ptr(new GpuMemoryBufferTracker());
203 VideoCaptureBufferPool::Tracker::~Tracker() {}
205 VideoCaptureBufferPool::VideoCaptureBufferPool(int count
)
211 VideoCaptureBufferPool::~VideoCaptureBufferPool() {
212 STLDeleteValues(&trackers_
);
215 base::SharedMemoryHandle
VideoCaptureBufferPool::ShareToProcess(
217 base::ProcessHandle process_handle
,
218 size_t* memory_size
) {
219 base::AutoLock
lock(lock_
);
221 Tracker
* tracker
= GetTracker(buffer_id
);
223 NOTREACHED() << "Invalid buffer_id.";
224 return base::SharedMemory::NULLHandle();
226 base::SharedMemoryHandle remote_handle
;
227 if (tracker
->ShareToProcess(process_handle
, &remote_handle
)) {
228 *memory_size
= tracker
->mapped_size();
229 return remote_handle
;
231 DPLOG(ERROR
) << "Error mapping Shared Memory";
232 return base::SharedMemoryHandle();
235 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
>
236 VideoCaptureBufferPool::GetBufferHandle(int buffer_id
) {
237 base::AutoLock
lock(lock_
);
239 Tracker
* tracker
= GetTracker(buffer_id
);
241 NOTREACHED() << "Invalid buffer_id.";
242 return scoped_ptr
<BufferHandle
>();
245 DCHECK(tracker
->held_by_producer());
246 return tracker
->GetBufferHandle();
249 int VideoCaptureBufferPool::ReserveForProducer(
250 media::VideoCapturePixelFormat format
,
251 media::VideoPixelStorage storage
,
252 const gfx::Size
& dimensions
,
253 int* buffer_id_to_drop
) {
254 base::AutoLock
lock(lock_
);
255 return ReserveForProducerInternal(format
, storage
, dimensions
,
259 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id
) {
260 base::AutoLock
lock(lock_
);
261 Tracker
* tracker
= GetTracker(buffer_id
);
263 NOTREACHED() << "Invalid buffer_id.";
266 DCHECK(tracker
->held_by_producer());
267 tracker
->set_held_by_producer(false);
270 void VideoCaptureBufferPool::HoldForConsumers(
273 base::AutoLock
lock(lock_
);
274 Tracker
* tracker
= GetTracker(buffer_id
);
276 NOTREACHED() << "Invalid buffer_id.";
279 DCHECK(tracker
->held_by_producer());
280 DCHECK(!tracker
->consumer_hold_count());
282 tracker
->set_consumer_hold_count(num_clients
);
283 // Note: |held_by_producer()| will stay true until
284 // RelinquishProducerReservation() (usually called by destructor of the object
285 // wrapping this tracker, e.g. a media::VideoFrame).
288 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id
,
290 base::AutoLock
lock(lock_
);
291 Tracker
* tracker
= GetTracker(buffer_id
);
293 NOTREACHED() << "Invalid buffer_id.";
296 DCHECK_GE(tracker
->consumer_hold_count(), num_clients
);
298 tracker
->set_consumer_hold_count(tracker
->consumer_hold_count() -
302 double VideoCaptureBufferPool::GetBufferPoolUtilization() const {
303 base::AutoLock
lock(lock_
);
304 int num_buffers_held
= 0;
305 for (const auto& entry
: trackers_
) {
306 Tracker
* const tracker
= entry
.second
;
307 if (tracker
->held_by_producer() || tracker
->consumer_hold_count() > 0)
310 return static_cast<double>(num_buffers_held
) / count_
;
313 int VideoCaptureBufferPool::ReserveForProducerInternal(
314 media::VideoCapturePixelFormat pixel_format
,
315 media::VideoPixelStorage storage_type
,
316 const gfx::Size
& dimensions
,
317 int* buffer_id_to_drop
) {
318 lock_
.AssertAcquired();
319 *buffer_id_to_drop
= kInvalidId
;
321 const size_t size_in_pixels
= dimensions
.GetArea();
322 // Look for a tracker that's allocated, big enough, and not in use. Track the
323 // largest one that's not big enough, in case we have to reallocate a tracker.
324 *buffer_id_to_drop
= kInvalidId
;
325 size_t largest_size_in_pixels
= 0;
326 TrackerMap::iterator tracker_to_drop
= trackers_
.end();
327 for (TrackerMap::iterator it
= trackers_
.begin(); it
!= trackers_
.end();
329 Tracker
* const tracker
= it
->second
;
330 if (!tracker
->consumer_hold_count() && !tracker
->held_by_producer()) {
331 if (tracker
->pixel_count() >= size_in_pixels
&&
332 (tracker
->pixel_format() == pixel_format
) &&
333 (tracker
->storage_type() == storage_type
)) {
334 // Existing tracker is big enough and has correct format. Reuse it.
335 tracker
->set_held_by_producer(true);
338 if (tracker
->pixel_count() > largest_size_in_pixels
) {
339 largest_size_in_pixels
= tracker
->pixel_count();
340 tracker_to_drop
= it
;
345 // Preferably grow the pool by creating a new tracker. If we're at maximum
346 // size, then reallocate by deleting an existing one instead.
347 if (trackers_
.size() == static_cast<size_t>(count_
)) {
348 if (tracker_to_drop
== trackers_
.end()) {
349 // We're out of space, and can't find an unused tracker to reallocate.
352 *buffer_id_to_drop
= tracker_to_drop
->first
;
353 delete tracker_to_drop
->second
;
354 trackers_
.erase(tracker_to_drop
);
357 // Create the new tracker.
358 const int buffer_id
= next_buffer_id_
++;
360 scoped_ptr
<Tracker
> tracker
= Tracker::CreateTracker(
361 storage_type
== media::PIXEL_STORAGE_GPUMEMORYBUFFER
);
362 if (!tracker
->Init(pixel_format
, storage_type
, dimensions
)) {
363 DLOG(ERROR
) << "Error initializing Tracker";
366 tracker
->set_held_by_producer(true);
367 trackers_
[buffer_id
] = tracker
.release();
372 VideoCaptureBufferPool::Tracker
* VideoCaptureBufferPool::GetTracker(
374 TrackerMap::const_iterator it
= trackers_
.find(buffer_id
);
375 return (it
== trackers_
.end()) ? NULL
: it
->second
;
378 } // namespace content