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
)
22 : data_(data
), size_(size
), handle_(handle
) {}
23 ~SimpleBufferHandle() override
{}
25 size_t size() const override
{ return size_
; }
26 void* data() override
{ return data_
; }
27 ClientBuffer
AsClientBuffer() override
{ return nullptr; }
29 base::FileDescriptor
AsPlatformFile() override
{
30 #if defined(OS_MACOSX)
31 return handle_
.GetFileDescriptor();
34 #endif // defined(OS_MACOSX)
41 const base::SharedMemoryHandle handle_
;
44 // A holder of a GpuMemoryBuffer-backed buffer, Map()ed on ctor and Unmap()ed on
45 // dtor. Holds a weak reference to its GpuMemoryBuffer.
46 // TODO(mcasas) Map()ed on ctor, or on first use?
47 class GpuMemoryBufferBufferHandle
48 final
: public VideoCaptureBufferPool::BufferHandle
{
50 GpuMemoryBufferBufferHandle(gfx::GpuMemoryBuffer
* gmb
, size_t size
)
52 data_(new void* [GpuMemoryBufferImpl::
53 NumberOfPlanesForGpuMemoryBufferFormat(
56 DCHECK(gmb
&& !gmb_
->IsMapped());
57 gmb_
->Map(data_
.get());
59 ~GpuMemoryBufferBufferHandle() override
{ gmb_
->Unmap(); }
61 size_t size() const override
{ return size_
; }
62 void* data() override
{ return data_
[0]; }
63 ClientBuffer
AsClientBuffer() override
{ return gmb_
->AsClientBuffer(); }
65 base::FileDescriptor
AsPlatformFile() override
{
66 #if defined(OS_MACOSX)
67 return gmb_
->GetHandle().handle
.GetFileDescriptor();
69 return gmb_
->GetHandle().handle
;
70 #endif // defined(OS_MACOSX)
75 gfx::GpuMemoryBuffer
* const gmb_
;
76 scoped_ptr
<void*[]> data_
;
80 // Tracker specifics for SharedMemory.
81 class VideoCaptureBufferPool::SharedMemTracker final
: public Tracker
{
84 bool Init(media::VideoPixelFormat format
,
85 media::VideoPixelStorage storage_type
,
86 const gfx::Size
& dimensions
) override
;
88 size_t mapped_size() const override
{ return shared_memory_
.mapped_size(); }
90 scoped_ptr
<BufferHandle
> GetBufferHandle() override
{
91 return make_scoped_ptr(new SimpleBufferHandle(
92 shared_memory_
.memory(), mapped_size(), shared_memory_
.handle()));
95 bool ShareToProcess(base::ProcessHandle process_handle
,
96 base::SharedMemoryHandle
* new_handle
) override
{
97 return shared_memory_
.ShareToProcess(process_handle
, new_handle
);
101 // The memory created to be shared with renderer processes.
102 base::SharedMemory shared_memory_
;
105 // Tracker specifics for GpuMemoryBuffer. Owns one GpuMemoryBuffer and its
106 // associated pixel dimensions.
107 class VideoCaptureBufferPool::GpuMemoryBufferTracker final
: public Tracker
{
109 GpuMemoryBufferTracker();
110 bool Init(media::VideoPixelFormat format
,
111 media::VideoPixelStorage storage_type
,
112 const gfx::Size
& dimensions
) override
;
113 ~GpuMemoryBufferTracker() override
;
115 size_t mapped_size() const override
{ return packed_size_
; }
116 scoped_ptr
<BufferHandle
> GetBufferHandle() override
{
117 return make_scoped_ptr(new GpuMemoryBufferBufferHandle(
118 gpu_memory_buffer_
.get(), packed_size_
));
121 bool ShareToProcess(base::ProcessHandle process_handle
,
122 base::SharedMemoryHandle
* new_handle
) override
{
128 scoped_ptr
<gfx::GpuMemoryBuffer
> gpu_memory_buffer_
;
131 VideoCaptureBufferPool::SharedMemTracker::SharedMemTracker() : Tracker() {
134 bool VideoCaptureBufferPool::SharedMemTracker::Init(
135 media::VideoPixelFormat format
,
136 media::VideoPixelStorage storage_type
,
137 const gfx::Size
& dimensions
) {
138 DVLOG(2) << "allocating ShMem of " << dimensions
.ToString();
139 set_pixel_format(format
);
140 set_storage_type(storage_type
);
141 // |dimensions| can be 0x0 for trackers that do not require memory backing.
142 set_pixel_count(dimensions
.GetArea());
143 const size_t byte_count
=
144 media::VideoCaptureFormat(dimensions
, 0.0f
, format
, storage_type
)
145 .ImageAllocationSize();
148 return shared_memory_
.CreateAndMapAnonymous(byte_count
);
151 VideoCaptureBufferPool::GpuMemoryBufferTracker::GpuMemoryBufferTracker()
152 : Tracker(), packed_size_(0u), gpu_memory_buffer_(nullptr) {
155 VideoCaptureBufferPool::GpuMemoryBufferTracker::~GpuMemoryBufferTracker() {
156 if (gpu_memory_buffer_
->IsMapped())
157 gpu_memory_buffer_
->Unmap();
160 bool VideoCaptureBufferPool::GpuMemoryBufferTracker::Init(
161 media::VideoPixelFormat format
,
162 media::VideoPixelStorage storage_type
,
163 const gfx::Size
& dimensions
) {
164 DVLOG(2) << "allocating GMB for " << dimensions
.ToString();
165 // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread.
166 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO
));
167 DCHECK(BrowserGpuMemoryBufferManager::current());
168 set_pixel_format(format
);
169 set_storage_type(storage_type
);
170 set_pixel_count(dimensions
.GetArea());
171 // |dimensions| can be 0x0 for trackers that do not require memory backing.
172 if (dimensions
.GetArea() == 0u)
175 BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer(
177 gfx::GpuMemoryBuffer::BGRA_8888
,
178 gfx::GpuMemoryBuffer::MAP
);
179 DLOG_IF(ERROR
, !gpu_memory_buffer_
.get()) << "Allocating GpuMemoryBuffer";
180 if (!gpu_memory_buffer_
.get())
183 gpu_memory_buffer_
->GetStride(&plane_sizes
);
184 packed_size_
= plane_sizes
* dimensions
.height();
189 scoped_ptr
<VideoCaptureBufferPool::Tracker
>
190 VideoCaptureBufferPool::Tracker::CreateTracker(bool use_gmb
) {
192 return make_scoped_ptr(new SharedMemTracker());
194 return make_scoped_ptr(new GpuMemoryBufferTracker());
197 VideoCaptureBufferPool::Tracker::~Tracker() {}
199 VideoCaptureBufferPool::VideoCaptureBufferPool(int count
)
205 VideoCaptureBufferPool::~VideoCaptureBufferPool() {
206 STLDeleteValues(&trackers_
);
209 base::SharedMemoryHandle
VideoCaptureBufferPool::ShareToProcess(
211 base::ProcessHandle process_handle
,
212 size_t* memory_size
) {
213 base::AutoLock
lock(lock_
);
215 Tracker
* tracker
= GetTracker(buffer_id
);
217 NOTREACHED() << "Invalid buffer_id.";
218 return base::SharedMemory::NULLHandle();
220 base::SharedMemoryHandle remote_handle
;
221 if (tracker
->ShareToProcess(process_handle
, &remote_handle
)) {
222 *memory_size
= tracker
->mapped_size();
223 return remote_handle
;
225 DPLOG(ERROR
) << "Error mapping Shared Memory";
226 return base::SharedMemoryHandle();
229 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
>
230 VideoCaptureBufferPool::GetBufferHandle(int buffer_id
) {
231 base::AutoLock
lock(lock_
);
233 Tracker
* tracker
= GetTracker(buffer_id
);
235 NOTREACHED() << "Invalid buffer_id.";
236 return scoped_ptr
<BufferHandle
>();
239 DCHECK(tracker
->held_by_producer());
240 return tracker
->GetBufferHandle();
243 int VideoCaptureBufferPool::ReserveForProducer(media::VideoPixelFormat format
,
244 media::VideoPixelStorage storage
,
245 const gfx::Size
& dimensions
,
246 int* buffer_id_to_drop
) {
247 base::AutoLock
lock(lock_
);
248 return ReserveForProducerInternal(format
, storage
, dimensions
,
252 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id
) {
253 base::AutoLock
lock(lock_
);
254 Tracker
* tracker
= GetTracker(buffer_id
);
256 NOTREACHED() << "Invalid buffer_id.";
259 DCHECK(tracker
->held_by_producer());
260 tracker
->set_held_by_producer(false);
263 void VideoCaptureBufferPool::HoldForConsumers(
266 base::AutoLock
lock(lock_
);
267 Tracker
* tracker
= GetTracker(buffer_id
);
269 NOTREACHED() << "Invalid buffer_id.";
272 DCHECK(tracker
->held_by_producer());
273 DCHECK(!tracker
->consumer_hold_count());
275 tracker
->set_consumer_hold_count(num_clients
);
276 // Note: |held_by_producer()| will stay true until
277 // RelinquishProducerReservation() (usually called by destructor of the object
278 // wrapping this tracker, e.g. a media::VideoFrame).
281 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id
,
283 base::AutoLock
lock(lock_
);
284 Tracker
* tracker
= GetTracker(buffer_id
);
286 NOTREACHED() << "Invalid buffer_id.";
289 DCHECK_GE(tracker
->consumer_hold_count(), num_clients
);
291 tracker
->set_consumer_hold_count(tracker
->consumer_hold_count() -
295 double VideoCaptureBufferPool::GetBufferPoolUtilization() const {
296 base::AutoLock
lock(lock_
);
297 int num_buffers_held
= 0;
298 for (const auto& entry
: trackers_
) {
299 Tracker
* const tracker
= entry
.second
;
300 if (tracker
->held_by_producer() || tracker
->consumer_hold_count() > 0)
303 return static_cast<double>(num_buffers_held
) / count_
;
306 int VideoCaptureBufferPool::ReserveForProducerInternal(
307 media::VideoPixelFormat pixel_format
,
308 media::VideoPixelStorage storage_type
,
309 const gfx::Size
& dimensions
,
310 int* buffer_id_to_drop
) {
311 lock_
.AssertAcquired();
312 *buffer_id_to_drop
= kInvalidId
;
314 const size_t size_in_pixels
= dimensions
.GetArea();
315 // Look for a tracker that's allocated, big enough, and not in use. Track the
316 // largest one that's not big enough, in case we have to reallocate a tracker.
317 *buffer_id_to_drop
= kInvalidId
;
318 size_t largest_size_in_pixels
= 0;
319 TrackerMap::iterator tracker_to_drop
= trackers_
.end();
320 for (TrackerMap::iterator it
= trackers_
.begin(); it
!= trackers_
.end();
322 Tracker
* const tracker
= it
->second
;
323 if (!tracker
->consumer_hold_count() && !tracker
->held_by_producer()) {
324 if (tracker
->pixel_count() >= size_in_pixels
&&
325 (tracker
->pixel_format() == pixel_format
) &&
326 (tracker
->storage_type() == storage_type
)) {
327 // Existing tracker is big enough and has correct format. Reuse it.
328 tracker
->set_held_by_producer(true);
331 if (tracker
->pixel_count() > largest_size_in_pixels
) {
332 largest_size_in_pixels
= tracker
->pixel_count();
333 tracker_to_drop
= it
;
338 // Preferably grow the pool by creating a new tracker. If we're at maximum
339 // size, then reallocate by deleting an existing one instead.
340 if (trackers_
.size() == static_cast<size_t>(count_
)) {
341 if (tracker_to_drop
== trackers_
.end()) {
342 // We're out of space, and can't find an unused tracker to reallocate.
345 *buffer_id_to_drop
= tracker_to_drop
->first
;
346 delete tracker_to_drop
->second
;
347 trackers_
.erase(tracker_to_drop
);
350 // Create the new tracker.
351 const int buffer_id
= next_buffer_id_
++;
353 scoped_ptr
<Tracker
> tracker
= Tracker::CreateTracker(
354 storage_type
== media::PIXEL_STORAGE_GPUMEMORYBUFFER
);
355 if (!tracker
->Init(pixel_format
, storage_type
, dimensions
)) {
356 DLOG(ERROR
) << "Error initializing Tracker";
359 tracker
->set_held_by_producer(true);
360 trackers_
[buffer_id
] = tracker
.release();
365 VideoCaptureBufferPool::Tracker
* VideoCaptureBufferPool::GetTracker(
367 TrackerMap::const_iterator it
= trackers_
.find(buffer_id
);
368 return (it
== trackers_
.end()) ? NULL
: it
->second
;
371 } // namespace content