Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / video / gpu_memory_buffer_video_frame_pool.cc
blob8f1ecd0349cf81e18e2967213fe1f20d511781ad
1 // Copyright 2015 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 "media/video/gpu_memory_buffer_video_frame_pool.h"
7 #include <GLES2/gl2.h>
8 #include <GLES2/gl2ext.h>
10 #include <algorithm>
11 #include <list>
12 #include <utility>
14 #include "base/barrier_closure.h"
15 #include "base/bind.h"
16 #include "base/containers/stack_container.h"
17 #include "base/location.h"
18 #include "base/memory/linked_ptr.h"
19 #include "base/trace_event/trace_event.h"
20 #include "gpu/GLES2/gl2extchromium.h"
21 #include "gpu/command_buffer/client/gles2_interface.h"
22 #include "media/renderers/gpu_video_accelerator_factories.h"
23 #include "third_party/libyuv/include/libyuv.h"
24 #include "ui/gfx/buffer_format_util.h"
26 namespace media {
28 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
29 class GpuMemoryBufferVideoFramePool::PoolImpl
30 : public base::RefCountedThreadSafe<
31 GpuMemoryBufferVideoFramePool::PoolImpl> {
32 public:
33 // |media_task_runner| is the media task runner associated with the
34 // GL context provided by |gpu_factories|
35 // |worker_task_runner| is a task runner used to asynchronously copy
36 // video frame's planes.
37 // |gpu_factories| is an interface to GPU related operation and can be
38 // null if a GL context is not available.
39 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
40 const scoped_refptr<base::TaskRunner>& worker_task_runner,
41 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
42 : media_task_runner_(media_task_runner),
43 worker_task_runner_(worker_task_runner),
44 gpu_factories_(gpu_factories),
45 texture_target_(gpu_factories ? gpu_factories->ImageTextureTarget()
46 : GL_TEXTURE_2D),
47 output_format_(PIXEL_FORMAT_UNKNOWN) {
48 DCHECK(media_task_runner_);
49 DCHECK(worker_task_runner_);
52 // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame
53 // backed by native textures if possible.
54 // The data contained in video_frame is copied into the returned frame
55 // asynchronously posting tasks to |worker_task_runner_|, while
56 // |frame_ready_cb| will be called on |media_task_runner_| once all the data
57 // has been copied.
58 void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame,
59 const FrameReadyCB& cb);
61 private:
62 friend class base::RefCountedThreadSafe<
63 GpuMemoryBufferVideoFramePool::PoolImpl>;
64 ~PoolImpl();
66 // Resource to represent a plane.
67 struct PlaneResource {
68 gfx::Size size;
69 scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
70 unsigned texture_id = 0u;
71 unsigned image_id = 0u;
72 gpu::Mailbox mailbox;
75 // All the resources needed to compose a frame.
76 struct FrameResources {
77 explicit FrameResources(const gfx::Size& size) : size(size) {}
78 bool in_use = true;
79 gfx::Size size;
80 PlaneResource plane_resources[VideoFrame::kMaxPlanes];
83 // Copy |video_frame| data into |frame_resouces|
84 // and calls |done| when done.
85 void CopyVideoFrameToGpuMemoryBuffers(
86 const scoped_refptr<VideoFrame>& video_frame,
87 FrameResources* frame_resources,
88 const FrameReadyCB& frame_ready_cb);
90 // Called when all the data has been copied.
91 void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame,
92 FrameResources* frame_resources,
93 const FrameReadyCB& frame_ready_cb);
95 // Prepares GL resources, mailboxes and calls |frame_ready_cb| with the new
96 // VideoFrame.
97 // This has to be run on |media_task_runner_| where |frame_ready_cb| will also
98 // be run.
99 void BindAndCreateMailboxesHardwareFrameResources(
100 const scoped_refptr<VideoFrame>& video_frame,
101 FrameResources* frame_resources,
102 const FrameReadyCB& frame_ready_cb);
104 // Return true if |resources| can be used to represent a frame for
105 // specific |format| and |size|.
106 static bool AreFrameResourcesCompatible(const FrameResources* resources,
107 const gfx::Size& size) {
108 return size == resources->size;
111 // Get the resources needed for a frame out of the pool, or create them if
112 // necessary.
113 // This also drops the LRU resources that can't be reuse for this frame.
114 FrameResources* GetOrCreateFrameResources(const gfx::Size& size,
115 VideoPixelFormat format);
117 // Callback called when a VideoFrame generated with GetFrameResources is no
118 // longer referenced.
119 // This could be called by any thread.
120 void MailboxHoldersReleased(FrameResources* frame_resources,
121 uint32 sync_point);
123 // Return frame resources to the pool. This has to be called on the thread
124 // where |media_task_runner_| is current.
125 void ReturnFrameResources(FrameResources* frame_resources);
127 // Delete resources. This has to be called on the thread where |task_runner|
128 // is current.
129 static void DeleteFrameResources(
130 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
131 FrameResources* frame_resources);
133 // Task runner associated to the GL context provided by |gpu_factories_|.
134 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
135 // Task runner used to asynchronously copy planes.
136 scoped_refptr<base::TaskRunner> worker_task_runner_;
138 // Interface to GPU related operations.
139 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_;
141 // Pool of resources.
142 std::list<FrameResources*> resources_pool_;
144 const unsigned texture_target_;
145 // TODO(dcastagna): change the following type from VideoPixelFormat to
146 // BufferFormat.
147 VideoPixelFormat output_format_;
148 DISALLOW_COPY_AND_ASSIGN(PoolImpl);
151 namespace {
153 // VideoFrame copies to GpuMemoryBuffers will be split in copies where the
154 // output size is |kBytesPerCopyTarget| bytes and run in parallel.
155 const size_t kBytesPerCopyTarget = 1024 * 1024; // 1MB
157 // Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat
158 // and plane.
159 gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) {
160 switch (format) {
161 case PIXEL_FORMAT_I420:
162 DCHECK_LE(plane, 2u);
163 return gfx::BufferFormat::R_8;
164 case PIXEL_FORMAT_NV12:
165 DCHECK_LE(plane, 1u);
166 return gfx::BufferFormat::YUV_420_BIPLANAR;
167 case PIXEL_FORMAT_UYVY:
168 DCHECK_EQ(0u, plane);
169 return gfx::BufferFormat::UYVY_422;
170 default:
171 NOTREACHED();
172 return gfx::BufferFormat::BGRA_8888;
176 unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) {
177 switch (format) {
178 case PIXEL_FORMAT_I420:
179 DCHECK_LE(plane, 2u);
180 return GL_R8_EXT;
181 case PIXEL_FORMAT_NV12:
182 DCHECK_LE(plane, 1u);
183 DLOG(WARNING) << "NV12 format not supported yet";
184 return 0; // TODO(andresantoso): Implement extension for NV12.
185 case PIXEL_FORMAT_UYVY:
186 DCHECK_EQ(0u, plane);
187 return GL_RGB_YCBCR_422_CHROMIUM;
188 default:
189 NOTREACHED();
190 return 0;
194 // The number of output planes to be copied in each iteration.
195 size_t PlanesPerCopy(VideoPixelFormat format) {
196 switch (format) {
197 case PIXEL_FORMAT_I420:
198 case PIXEL_FORMAT_UYVY:
199 return 1;
200 case PIXEL_FORMAT_NV12:
201 return 2;
202 default:
203 NOTREACHED();
204 return 0;
208 // The number of output rows to be copied in each iteration.
209 int RowsPerCopy(size_t plane, VideoPixelFormat format, int width) {
210 int bytes_per_row = VideoFrame::RowBytes(plane, format, width);
211 if (format == PIXEL_FORMAT_NV12) {
212 DCHECK_EQ(0u, plane);
213 bytes_per_row += VideoFrame::RowBytes(1, format, width);
215 // Copy an even number of lines, and at least one.
216 return std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1);
219 void CopyRowsToI420Buffer(int first_row,
220 int rows,
221 int bytes_per_row,
222 const uint8* source,
223 int source_stride,
224 uint8* output,
225 int dest_stride,
226 const base::Closure& done) {
227 TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row,
228 "rows", rows);
229 if (output) {
230 DCHECK_NE(dest_stride, 0);
231 DCHECK_LE(bytes_per_row, std::abs(dest_stride));
232 DCHECK_LE(bytes_per_row, source_stride);
233 for (int row = first_row; row < first_row + rows; ++row) {
234 memcpy(output + dest_stride * row, source + source_stride * row,
235 bytes_per_row);
238 done.Run();
241 void CopyRowsToNV12Buffer(int first_row,
242 int rows,
243 int bytes_per_row,
244 const scoped_refptr<VideoFrame>& source_frame,
245 uint8* dest_y,
246 int dest_stride_y,
247 uint8* dest_uv,
248 int dest_stride_uv,
249 const base::Closure& done) {
250 TRACE_EVENT2("media", "CopyRowsToNV12Buffer", "bytes_per_row", bytes_per_row,
251 "rows", rows);
252 if (dest_y && dest_uv) {
253 DCHECK_NE(dest_stride_y, 0);
254 DCHECK_NE(dest_stride_uv, 0);
255 DCHECK_LE(bytes_per_row, std::abs(dest_stride_y));
256 DCHECK_LE(bytes_per_row, std::abs(dest_stride_uv));
257 DCHECK_EQ(0, first_row % 2);
259 libyuv::I420ToNV12(
260 source_frame->data(VideoFrame::kYPlane) +
261 first_row * source_frame->stride(VideoFrame::kYPlane),
262 source_frame->stride(VideoFrame::kYPlane),
263 source_frame->data(VideoFrame::kUPlane) +
264 first_row / 2 * source_frame->stride(VideoFrame::kUPlane),
265 source_frame->stride(VideoFrame::kUPlane),
266 source_frame->data(VideoFrame::kVPlane) +
267 first_row / 2 * source_frame->stride(VideoFrame::kVPlane),
268 source_frame->stride(VideoFrame::kVPlane),
269 dest_y + first_row * dest_stride_y, dest_stride_y,
270 dest_uv + first_row / 2 * dest_stride_uv, dest_stride_uv, bytes_per_row,
271 rows);
273 done.Run();
276 void CopyRowsToUYVYBuffer(int first_row,
277 int rows,
278 int width,
279 const scoped_refptr<VideoFrame>& source_frame,
280 uint8* output,
281 int dest_stride,
282 const base::Closure& done) {
283 TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2,
284 "rows", rows);
285 if (output) {
286 DCHECK_NE(dest_stride, 0);
287 DCHECK_LE(width, std::abs(dest_stride / 2));
288 DCHECK_EQ(0, first_row % 2);
289 libyuv::I420ToUYVY(
290 source_frame->data(VideoFrame::kYPlane) +
291 first_row * source_frame->stride(VideoFrame::kYPlane),
292 source_frame->stride(VideoFrame::kYPlane),
293 source_frame->data(VideoFrame::kUPlane) +
294 first_row / 2 * source_frame->stride(VideoFrame::kUPlane),
295 source_frame->stride(VideoFrame::kUPlane),
296 source_frame->data(VideoFrame::kVPlane) +
297 first_row / 2 * source_frame->stride(VideoFrame::kVPlane),
298 source_frame->stride(VideoFrame::kVPlane),
299 output + first_row * dest_stride, dest_stride, width, rows);
301 done.Run();
304 } // unnamed namespace
306 // Creates a VideoFrame backed by native textures starting from a software
307 // VideoFrame.
308 // The data contained in |video_frame| is copied into the VideoFrame passed to
309 // |frame_ready_cb|.
310 // This has to be called on the thread where |media_task_runner_| is current.
311 void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
312 const scoped_refptr<VideoFrame>& video_frame,
313 const FrameReadyCB& frame_ready_cb) {
314 DCHECK(media_task_runner_->BelongsToCurrentThread());
315 if (!gpu_factories_) {
316 frame_ready_cb.Run(video_frame);
317 return;
320 // Lazily initialize output_format_ since VideoFrameOutputFormat() has to be
321 // called on the media_thread while this object might be instantiated on any.
322 if (output_format_ == PIXEL_FORMAT_UNKNOWN)
323 output_format_ = gpu_factories_->VideoFrameOutputFormat();
325 if (output_format_ == PIXEL_FORMAT_UNKNOWN) {
326 frame_ready_cb.Run(video_frame);
327 return;
329 switch (video_frame->format()) {
330 // Supported cases.
331 case PIXEL_FORMAT_YV12:
332 case PIXEL_FORMAT_I420:
333 break;
334 // Unsupported cases.
335 case PIXEL_FORMAT_YV12A:
336 case PIXEL_FORMAT_YV16:
337 case PIXEL_FORMAT_YV24:
338 case PIXEL_FORMAT_NV12:
339 case PIXEL_FORMAT_NV21:
340 case PIXEL_FORMAT_UYVY:
341 case PIXEL_FORMAT_YUY2:
342 case PIXEL_FORMAT_ARGB:
343 case PIXEL_FORMAT_XRGB:
344 case PIXEL_FORMAT_RGB24:
345 case PIXEL_FORMAT_RGB32:
346 case PIXEL_FORMAT_MJPEG:
347 case PIXEL_FORMAT_MT21:
348 case PIXEL_FORMAT_UNKNOWN:
349 frame_ready_cb.Run(video_frame);
350 return;
353 DCHECK(video_frame->visible_rect().origin().IsOrigin());
354 const gfx::Size size = video_frame->visible_rect().size();
356 // Acquire resources. Incompatible ones will be dropped from the pool.
357 FrameResources* frame_resources =
358 GetOrCreateFrameResources(size, output_format_);
359 if (!frame_resources) {
360 frame_ready_cb.Run(video_frame);
361 return;
364 worker_task_runner_->PostTask(
365 FROM_HERE, base::Bind(&PoolImpl::CopyVideoFrameToGpuMemoryBuffers, this,
366 video_frame, frame_resources, frame_ready_cb));
369 void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone(
370 const scoped_refptr<VideoFrame>& video_frame,
371 FrameResources* frame_resources,
372 const FrameReadyCB& frame_ready_cb) {
373 for (const auto& plane_resource : frame_resources->plane_resources) {
374 if (plane_resource.gpu_memory_buffer)
375 plane_resource.gpu_memory_buffer->Unmap();
378 media_task_runner_->PostTask(
379 FROM_HERE,
380 base::Bind(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources, this,
381 video_frame, frame_resources, frame_ready_cb));
384 // Copies |video_frame| into |frame_resources| asynchronously, posting n tasks
385 // that will be synchronized by a barrier.
386 // After the barrier is passed OnCopiesDone will be called.
387 void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers(
388 const scoped_refptr<VideoFrame>& video_frame,
389 FrameResources* frame_resources,
390 const FrameReadyCB& frame_ready_cb) {
391 // Compute the number of tasks to post and create the barrier.
392 const size_t num_planes = VideoFrame::NumPlanes(output_format_);
393 const size_t planes_per_copy = PlanesPerCopy(output_format_);
394 gfx::Size size = video_frame->visible_rect().size();
395 size_t copies = 0;
396 for (size_t i = 0; i < num_planes; i += planes_per_copy) {
397 const int rows = VideoFrame::Rows(i, output_format_, size.height());
398 const int rows_per_copy = RowsPerCopy(i, output_format_, size.width());
399 copies += rows / rows_per_copy;
400 if (rows % rows_per_copy)
401 ++copies;
403 base::Closure copies_done =
404 base::Bind(&PoolImpl::OnCopiesDone, this, video_frame, frame_resources,
405 frame_ready_cb);
406 base::Closure barrier = base::BarrierClosure(copies, copies_done);
408 // Post all the async tasks.
409 for (size_t i = 0; i < num_planes; i += planes_per_copy) {
410 gfx::GpuMemoryBuffer* buffer =
411 frame_resources->plane_resources[i].gpu_memory_buffer.get();
412 uint8* dest_buffers[VideoFrame::kMaxPlanes] = {0};
413 int dest_strides[VideoFrame::kMaxPlanes] = {0};
414 if (buffer) {
415 DCHECK_EQ(planes_per_copy,
416 gfx::NumberOfPlanesForBufferFormat(buffer->GetFormat()));
417 bool rv = buffer->Map(reinterpret_cast<void**>(dest_buffers));
418 DCHECK(rv);
419 buffer->GetStride(dest_strides);
422 const int rows = VideoFrame::Rows(i, output_format_, size.height());
423 const int rows_per_copy = RowsPerCopy(i, output_format_, size.width());
425 for (int row = 0; row < rows; row += rows_per_copy) {
426 const int rows_to_copy = std::min(rows_per_copy, rows - row);
427 switch (output_format_) {
428 case PIXEL_FORMAT_I420: {
429 const int bytes_per_row =
430 VideoFrame::RowBytes(i, output_format_, size.width());
431 worker_task_runner_->PostTask(
432 FROM_HERE,
433 base::Bind(&CopyRowsToI420Buffer, row, rows_to_copy,
434 bytes_per_row, video_frame->data(i),
435 video_frame->stride(i), dest_buffers[0],
436 dest_strides[0], barrier));
437 break;
439 case PIXEL_FORMAT_NV12:
440 worker_task_runner_->PostTask(
441 FROM_HERE,
442 base::Bind(&CopyRowsToNV12Buffer, row, rows_to_copy,
443 size.width(), video_frame, dest_buffers[0],
444 dest_strides[0], dest_buffers[1], dest_strides[1],
445 barrier));
446 break;
447 case PIXEL_FORMAT_UYVY:
448 worker_task_runner_->PostTask(
449 FROM_HERE,
450 base::Bind(&CopyRowsToUYVYBuffer, row, rows_to_copy, size.width(),
451 video_frame, dest_buffers[0], dest_strides[0],
452 barrier));
453 break;
454 default:
455 NOTREACHED();
461 void GpuMemoryBufferVideoFramePool::PoolImpl::
462 BindAndCreateMailboxesHardwareFrameResources(
463 const scoped_refptr<VideoFrame>& video_frame,
464 FrameResources* frame_resources,
465 const FrameReadyCB& frame_ready_cb) {
466 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
467 if (!gles2) {
468 frame_ready_cb.Run(video_frame);
469 return;
472 const size_t num_planes = VideoFrame::NumPlanes(output_format_);
473 const size_t planes_per_copy = PlanesPerCopy(output_format_);
474 const gfx::Size size = video_frame->visible_rect().size();
475 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
476 // Set up the planes creating the mailboxes needed to refer to the textures.
477 for (size_t i = 0; i < num_planes; i += planes_per_copy) {
478 PlaneResource& plane_resource = frame_resources->plane_resources[i];
479 // Bind the texture and create or rebind the image.
480 gles2->BindTexture(texture_target_, plane_resource.texture_id);
482 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) {
483 const size_t width = VideoFrame::Columns(i, output_format_, size.width());
484 const size_t height = VideoFrame::Rows(i, output_format_, size.height());
485 plane_resource.image_id = gles2->CreateImageCHROMIUM(
486 plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height,
487 ImageInternalFormat(output_format_, i));
488 } else if (plane_resource.image_id) {
489 gles2->ReleaseTexImage2DCHROMIUM(texture_target_,
490 plane_resource.image_id);
492 if (plane_resource.image_id)
493 gles2->BindTexImage2DCHROMIUM(texture_target_, plane_resource.image_id);
494 mailbox_holders[i] =
495 gpu::MailboxHolder(plane_resource.mailbox, texture_target_, 0);
498 // Insert a sync_point, this is needed to make sure that the textures the
499 // mailboxes refer to will be used only after all the previous commands posted
500 // in the command buffer have been processed.
501 unsigned sync_point = gles2->InsertSyncPointCHROMIUM();
502 for (size_t i = 0; i < num_planes; i += planes_per_copy)
503 mailbox_holders[i].sync_point = sync_point;
505 scoped_refptr<VideoFrame> frame;
506 // Create the VideoFrame backed by native textures.
507 switch (output_format_) {
508 case PIXEL_FORMAT_I420:
509 frame = VideoFrame::WrapYUV420NativeTextures(
510 mailbox_holders[VideoFrame::kYPlane],
511 mailbox_holders[VideoFrame::kUPlane],
512 mailbox_holders[VideoFrame::kVPlane],
513 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources),
514 size, video_frame->visible_rect(), video_frame->natural_size(),
515 video_frame->timestamp());
516 if (video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY))
517 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
518 break;
519 case PIXEL_FORMAT_NV12:
520 case PIXEL_FORMAT_UYVY:
521 frame = VideoFrame::WrapNativeTexture(
522 output_format_, mailbox_holders[VideoFrame::kYPlane],
523 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources),
524 size, video_frame->visible_rect(), video_frame->natural_size(),
525 video_frame->timestamp());
526 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
527 break;
528 default:
529 NOTREACHED();
531 frame_ready_cb.Run(frame);
534 // Destroy all the resources posting one task per FrameResources
535 // to the |media_task_runner_|.
536 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() {
537 // Delete all the resources on the media thread.
538 while (!resources_pool_.empty()) {
539 FrameResources* frame_resources = resources_pool_.front();
540 resources_pool_.pop_front();
541 media_task_runner_->PostTask(
542 FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_,
543 base::Owned(frame_resources)));
547 // Tries to find the resources in the pool or create them.
548 // Incompatible resources will be dropped.
549 GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources*
550 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
551 const gfx::Size& size,
552 VideoPixelFormat format) {
553 auto it = resources_pool_.begin();
554 while (it != resources_pool_.end()) {
555 FrameResources* frame_resources = *it;
556 if (!frame_resources->in_use) {
557 if (AreFrameResourcesCompatible(frame_resources, size)) {
558 frame_resources->in_use = true;
559 return frame_resources;
560 } else {
561 resources_pool_.erase(it++);
562 DeleteFrameResources(gpu_factories_, frame_resources);
563 delete frame_resources;
565 } else {
566 it++;
570 // Create the resources.
571 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
572 if (!gles2)
573 return nullptr;
574 gles2->ActiveTexture(GL_TEXTURE0);
575 size_t num_planes = VideoFrame::NumPlanes(format);
576 FrameResources* frame_resources = new FrameResources(size);
577 resources_pool_.push_back(frame_resources);
578 for (size_t i = 0; i < num_planes; i += PlanesPerCopy(format)) {
579 PlaneResource& plane_resource = frame_resources->plane_resources[i];
580 const size_t width = VideoFrame::Columns(i, format, size.width());
581 const size_t height = VideoFrame::Rows(i, format, size.height());
582 const gfx::Size plane_size(width, height);
584 const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i);
585 plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer(
586 plane_size, buffer_format, gfx::BufferUsage::MAP);
588 gles2->GenTextures(1, &plane_resource.texture_id);
589 gles2->BindTexture(texture_target_, plane_resource.texture_id);
590 gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
591 gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
592 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
593 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
594 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name);
595 gles2->ProduceTextureCHROMIUM(texture_target_, plane_resource.mailbox.name);
597 return frame_resources;
600 // static
601 void GpuMemoryBufferVideoFramePool::PoolImpl::DeleteFrameResources(
602 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
603 FrameResources* frame_resources) {
604 // TODO(dcastagna): As soon as the context lost is dealt with in media,
605 // make sure that we won't execute this callback (use a weak pointer to
606 // the old context).
607 gpu::gles2::GLES2Interface* gles2 = gpu_factories->GetGLES2Interface();
608 if (!gles2)
609 return;
611 for (PlaneResource& plane_resource : frame_resources->plane_resources) {
612 if (plane_resource.image_id)
613 gles2->DestroyImageCHROMIUM(plane_resource.image_id);
614 if (plane_resource.texture_id)
615 gles2->DeleteTextures(1, &plane_resource.texture_id);
619 // Called when a VideoFrame is no longer references.
620 void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased(
621 FrameResources* frame_resources,
622 uint32 sync_point) {
623 // Return the resource on the media thread.
624 media_task_runner_->PostTask(
625 FROM_HERE,
626 base::Bind(&PoolImpl::ReturnFrameResources, this, frame_resources));
629 // Put back the resoruces in the pool.
630 void GpuMemoryBufferVideoFramePool::PoolImpl::ReturnFrameResources(
631 FrameResources* frame_resources) {
633 auto it = std::find(resources_pool_.begin(), resources_pool_.end(),
634 frame_resources);
635 DCHECK(it != resources_pool_.end());
636 // We want the pool to behave in a FIFO way.
637 // This minimizes the chances of locking the buffer that might be
638 // still needed for drawing.
639 std::swap(*it, resources_pool_.back());
640 frame_resources->in_use = false;
643 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool() {}
645 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool(
646 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
647 const scoped_refptr<base::TaskRunner>& worker_task_runner,
648 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
649 : pool_impl_(
650 new PoolImpl(media_task_runner, worker_task_runner, gpu_factories)) {}
652 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() {
655 void GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame(
656 const scoped_refptr<VideoFrame>& video_frame,
657 const FrameReadyCB& frame_ready_cb) {
658 DCHECK(video_frame);
659 pool_impl_->CreateHardwareFrame(video_frame, frame_ready_cb);
662 } // namespace media