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"
13 #include "media/base/video_frame.h"
15 using media::VideoFrame
;
19 const int VideoCaptureBufferPool::kInvalidId
= -1;
21 VideoFrame::Format
VideoPixelFormatToVideoFrameFormat(
22 media::VideoPixelFormat pixel_format
) {
24 media::VideoPixelFormat pixel_format
;
25 VideoFrame::Format frame_format
;
26 } const kVideoPixelFormatToVideoFrameFormat
[] = {
27 {media::PIXEL_FORMAT_I420
, VideoFrame::I420
},
28 {media::PIXEL_FORMAT_TEXTURE
, VideoFrame::NATIVE_TEXTURE
},
29 {media::PIXEL_FORMAT_GPUMEMORYBUFFER
, VideoFrame::NATIVE_TEXTURE
},
32 for (const auto& format_pair
: kVideoPixelFormatToVideoFrameFormat
) {
33 if (format_pair
.pixel_format
== pixel_format
)
34 return format_pair
.frame_format
;
36 LOG(ERROR
) << "Unsupported VideoPixelFormat "
37 << media::VideoCaptureFormat::PixelFormatToString(pixel_format
);
38 return VideoFrame::UNKNOWN
;
41 // A simple holder of a memory-backed buffer and accesors to it.
42 class SimpleBufferHandle final
: public VideoCaptureBufferPool::BufferHandle
{
44 SimpleBufferHandle(void* data
, size_t size
) : data_(data
), size_(size
) {}
45 ~SimpleBufferHandle() override
{}
47 size_t size() const override
{ return size_
; }
48 void* data() override
{ return data_
; }
49 ClientBuffer
AsClientBuffer() override
{ return nullptr; }
56 // A holder of a GpuMemoryBuffer-backed buffer, Map()ed on ctor and Unmap()ed on
57 // dtor. Holds a weak reference to its GpuMemoryBuffer.
58 // TODO(mcasas) Map()ed on ctor, or on first use?
59 class GpuMemoryBufferBufferHandle
60 final
: public VideoCaptureBufferPool::BufferHandle
{
62 GpuMemoryBufferBufferHandle(gfx::GpuMemoryBuffer
* gmb
, size_t size
)
64 data_(new void* [GpuMemoryBufferImpl::
65 NumberOfPlanesForGpuMemoryBufferFormat(
68 DCHECK(gmb
&& !gmb_
->IsMapped());
69 gmb_
->Map(data_
.get());
71 ~GpuMemoryBufferBufferHandle() override
{ gmb_
->Unmap(); }
73 size_t size() const override
{ return size_
; }
74 void* data() override
{ return data_
[0]; }
75 ClientBuffer
AsClientBuffer() override
{ return gmb_
->AsClientBuffer(); }
78 gfx::GpuMemoryBuffer
* const gmb_
;
79 scoped_ptr
<void*[]> data_
;
83 // Tracker specifics for SharedMemory.
84 class VideoCaptureBufferPool::SharedMemTracker final
: public Tracker
{
87 bool Init(VideoFrame::Format format
, const gfx::Size
& dimensions
) override
;
89 size_t mapped_size() const override
{ return shared_memory_
.mapped_size(); }
91 scoped_ptr
<BufferHandle
> GetBufferHandle() override
{
92 return make_scoped_ptr(
93 new SimpleBufferHandle(shared_memory_
.memory(), mapped_size()));
96 bool ShareToProcess(base::ProcessHandle process_handle
,
97 base::SharedMemoryHandle
* new_handle
) override
{
98 return shared_memory_
.ShareToProcess(process_handle
, new_handle
);
102 // The memory created to be shared with renderer processes.
103 base::SharedMemory shared_memory_
;
106 // Tracker specifics for GpuMemoryBuffer. Owns one GpuMemoryBuffer and its
107 // associated pixel dimensions.
108 class VideoCaptureBufferPool::GpuMemoryBufferTracker final
: public Tracker
{
110 GpuMemoryBufferTracker();
111 bool Init(VideoFrame::Format format
, const gfx::Size
& dimensions
) override
;
112 ~GpuMemoryBufferTracker() override
;
114 size_t mapped_size() const override
{ return packed_size_
; }
115 scoped_ptr
<BufferHandle
> GetBufferHandle() override
{
116 return make_scoped_ptr(new GpuMemoryBufferBufferHandle(
117 gpu_memory_buffer_
.get(), packed_size_
));
120 bool ShareToProcess(base::ProcessHandle process_handle
,
121 base::SharedMemoryHandle
* new_handle
) override
{
127 scoped_ptr
<gfx::GpuMemoryBuffer
> gpu_memory_buffer_
;
130 VideoCaptureBufferPool::SharedMemTracker::SharedMemTracker() : Tracker() {
133 bool VideoCaptureBufferPool::SharedMemTracker::Init(
134 VideoFrame::Format format
,
135 const gfx::Size
& dimensions
) {
136 DVLOG(2) << "allocating ShMem of " << dimensions
.ToString();
137 // Input |dimensions| can be 0x0 for trackers that do not require memory
138 // backing. The allocated size is calculated using VideoFrame methods since
139 // this will be the abstraction used to wrap the underlying data.
140 set_pixel_count(dimensions
.GetArea());
141 const size_t byte_count
= VideoFrame::AllocationSize(format
, dimensions
);
144 return shared_memory_
.CreateAndMapAnonymous(byte_count
);
147 VideoCaptureBufferPool::GpuMemoryBufferTracker::GpuMemoryBufferTracker()
148 : Tracker(), gpu_memory_buffer_(nullptr) {}
150 VideoCaptureBufferPool::GpuMemoryBufferTracker::~GpuMemoryBufferTracker() {
151 if (gpu_memory_buffer_
->IsMapped())
152 gpu_memory_buffer_
->Unmap();
155 bool VideoCaptureBufferPool::GpuMemoryBufferTracker::Init(
156 VideoFrame::Format format
,
157 const gfx::Size
& dimensions
) {
158 DVLOG(2) << "allocating GMB for " << dimensions
.ToString();
159 // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread.
160 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO
));
161 DCHECK(BrowserGpuMemoryBufferManager::current());
162 set_pixel_count(dimensions
.GetArea());
164 BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer(
166 gfx::GpuMemoryBuffer::BGRA_8888
,
167 gfx::GpuMemoryBuffer::MAP
);
168 DLOG_IF(ERROR
, !gpu_memory_buffer_
.get()) << "Allocating GpuMemoryBuffer";
169 if (!gpu_memory_buffer_
.get())
172 gpu_memory_buffer_
->GetStride(&plane_sizes
);
173 packed_size_
= plane_sizes
* dimensions
.height();
178 scoped_ptr
<VideoCaptureBufferPool::Tracker
>
179 VideoCaptureBufferPool::Tracker::CreateTracker(bool use_gmb
) {
181 return make_scoped_ptr(new SharedMemTracker());
183 return make_scoped_ptr(new GpuMemoryBufferTracker());
186 VideoCaptureBufferPool::Tracker::~Tracker() {}
188 VideoCaptureBufferPool::VideoCaptureBufferPool(int count
)
194 VideoCaptureBufferPool::~VideoCaptureBufferPool() {
195 STLDeleteValues(&trackers_
);
198 base::SharedMemoryHandle
VideoCaptureBufferPool::ShareToProcess(
200 base::ProcessHandle process_handle
,
201 size_t* memory_size
) {
202 base::AutoLock
lock(lock_
);
204 Tracker
* tracker
= GetTracker(buffer_id
);
206 NOTREACHED() << "Invalid buffer_id.";
207 return base::SharedMemory::NULLHandle();
209 base::SharedMemoryHandle remote_handle
;
210 if (tracker
->ShareToProcess(process_handle
, &remote_handle
)) {
211 *memory_size
= tracker
->mapped_size();
212 return remote_handle
;
214 DPLOG(ERROR
) << "Error mapping Shared Memory";
215 return base::SharedMemoryHandle();
218 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
>
219 VideoCaptureBufferPool::GetBufferHandle(int buffer_id
) {
220 base::AutoLock
lock(lock_
);
222 Tracker
* tracker
= GetTracker(buffer_id
);
224 NOTREACHED() << "Invalid buffer_id.";
225 return scoped_ptr
<BufferHandle
>();
228 DCHECK(tracker
->held_by_producer());
229 return tracker
->GetBufferHandle();
232 int VideoCaptureBufferPool::ReserveForProducer(media::VideoPixelFormat format
,
233 const gfx::Size
& dimensions
,
234 int* buffer_id_to_drop
) {
235 base::AutoLock
lock(lock_
);
236 return ReserveForProducerInternal(format
, dimensions
, buffer_id_to_drop
);
239 void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id
) {
240 base::AutoLock
lock(lock_
);
241 Tracker
* tracker
= GetTracker(buffer_id
);
243 NOTREACHED() << "Invalid buffer_id.";
246 DCHECK(tracker
->held_by_producer());
247 tracker
->set_held_by_producer(false);
250 void VideoCaptureBufferPool::HoldForConsumers(
253 base::AutoLock
lock(lock_
);
254 Tracker
* tracker
= GetTracker(buffer_id
);
256 NOTREACHED() << "Invalid buffer_id.";
259 DCHECK(tracker
->held_by_producer());
260 DCHECK(!tracker
->consumer_hold_count());
262 tracker
->set_consumer_hold_count(num_clients
);
263 // Note: |held_by_producer()| will stay true until
264 // RelinquishProducerReservation() (usually called by destructor of the object
265 // wrapping this tracker, e.g. a media::VideoFrame).
268 void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id
,
270 base::AutoLock
lock(lock_
);
271 Tracker
* tracker
= GetTracker(buffer_id
);
273 NOTREACHED() << "Invalid buffer_id.";
276 DCHECK_GE(tracker
->consumer_hold_count(), num_clients
);
278 tracker
->set_consumer_hold_count(tracker
->consumer_hold_count() -
282 int VideoCaptureBufferPool::ReserveForProducerInternal(
283 media::VideoPixelFormat format
,
284 const gfx::Size
& dimensions
,
285 int* buffer_id_to_drop
) {
286 DCHECK(format
== media::PIXEL_FORMAT_I420
||
287 format
== media::PIXEL_FORMAT_TEXTURE
||
288 format
== media::PIXEL_FORMAT_GPUMEMORYBUFFER
);
289 lock_
.AssertAcquired();
290 *buffer_id_to_drop
= kInvalidId
;
292 const size_t size_in_pixels
= dimensions
.GetArea();
293 // Look for a tracker that's allocated, big enough, and not in use. Track the
294 // largest one that's not big enough, in case we have to reallocate a tracker.
295 *buffer_id_to_drop
= kInvalidId
;
296 size_t largest_size_in_pixels
= 0;
297 TrackerMap::iterator tracker_to_drop
= trackers_
.end();
298 for (TrackerMap::iterator it
= trackers_
.begin(); it
!= trackers_
.end();
300 Tracker
* const tracker
= it
->second
;
301 if (!tracker
->consumer_hold_count() && !tracker
->held_by_producer()) {
302 if (tracker
->pixel_count() >= size_in_pixels
) {
303 // Existing tracker is big enough. Reuse it.
304 tracker
->set_held_by_producer(true);
307 if (tracker
->pixel_count() > largest_size_in_pixels
) {
308 largest_size_in_pixels
= tracker
->pixel_count();
309 tracker_to_drop
= it
;
314 // Preferably grow the pool by creating a new tracker. If we're at maximum
315 // size, then reallocate by deleting an existing one instead.
316 if (trackers_
.size() == static_cast<size_t>(count_
)) {
317 if (tracker_to_drop
== trackers_
.end()) {
318 // We're out of space, and can't find an unused tracker to reallocate.
321 *buffer_id_to_drop
= tracker_to_drop
->first
;
322 delete tracker_to_drop
->second
;
323 trackers_
.erase(tracker_to_drop
);
326 // Create the new tracker.
327 const int buffer_id
= next_buffer_id_
++;
329 scoped_ptr
<Tracker
> tracker
=
330 Tracker::CreateTracker(format
== media::PIXEL_FORMAT_GPUMEMORYBUFFER
);
331 if (!tracker
->Init(VideoPixelFormatToVideoFrameFormat(format
), dimensions
)) {
332 DLOG(ERROR
) << "Error initializing Tracker";
335 tracker
->set_held_by_producer(true);
336 trackers_
[buffer_id
] = tracker
.release();
341 VideoCaptureBufferPool::Tracker
* VideoCaptureBufferPool::GetTracker(
343 TrackerMap::const_iterator it
= trackers_
.find(buffer_id
);
344 return (it
== trackers_
.end()) ? NULL
: it
->second
;
347 } // namespace content