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"
8 #include <GLES2/gl2ext.h>
13 #include "base/bind.h"
14 #include "base/containers/stack_container.h"
15 #include "base/location.h"
16 #include "base/memory/linked_ptr.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/trace_event/trace_event.h"
19 #include "gpu/command_buffer/client/gles2_interface.h"
20 #include "media/renderers/gpu_video_accelerator_factories.h"
24 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
25 class GpuMemoryBufferVideoFramePool::PoolImpl
26 : public base::RefCountedThreadSafe
<
27 GpuMemoryBufferVideoFramePool::PoolImpl
> {
29 // |task_runner| is associated to the thread where the context of
30 // GLES2Interface returned by |gpu_factories| lives.
31 // |gpu_factories| is an interface to GPU related operation and can be
33 PoolImpl(const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
34 const scoped_refptr
<GpuVideoAcceleratorFactories
>& gpu_factories
)
35 : task_runner_(task_runner
),
36 gpu_factories_(gpu_factories
),
37 texture_target_(gpu_factories_
? gpu_factories_
->ImageTextureTarget()
40 // Takes a software VideoFrame and returns a VideoFrame backed by native
41 // textures if possible.
42 // The data contained in video_frame is copied into the returned frame.
43 scoped_refptr
<VideoFrame
> CreateHardwareFrame(
44 const scoped_refptr
<VideoFrame
>& video_frame
);
47 friend class base::RefCountedThreadSafe
<
48 GpuMemoryBufferVideoFramePool::PoolImpl
>;
51 // Resource to represent a plane.
52 struct PlaneResource
{
54 scoped_ptr
<gfx::GpuMemoryBuffer
> gpu_memory_buffer
;
55 unsigned texture_id
= 0u;
56 unsigned image_id
= 0u;
60 // All the resources needed to compose a frame.
61 struct FrameResources
{
62 FrameResources(VideoPixelFormat format
, const gfx::Size
& size
)
63 : format(format
), size(size
) {}
65 VideoPixelFormat format
;
67 PlaneResource plane_resources
[VideoFrame::kMaxPlanes
];
70 // Return true if |resources| can be used to represent a frame for
71 // specific |format| and |size|.
72 static bool AreFrameResourcesCompatible(const FrameResources
* resources
,
73 const gfx::Size
& size
,
74 VideoPixelFormat format
) {
75 return size
== resources
->size
&& format
== resources
->format
;
78 // Get the resources needed for a frame out of the pool, or create them if
80 // This also drops the LRU resources that can't be reuse for this frame.
81 FrameResources
* GetOrCreateFrameResources(const gfx::Size
& size
,
82 VideoPixelFormat format
);
84 // Callback called when a VideoFrame generated with GetFrameResources is no
86 // This could be called by any thread.
87 void MailboxHoldersReleased(FrameResources
* frame_resources
,
90 // Return frame resources to the pool. This has to be called on the thread
91 // where |task_runner| is current.
92 void ReturnFrameResources(FrameResources
* frame_resources
);
94 // Delete resources. This has to be called on the thread where |task_runner|
96 static void DeleteFrameResources(
97 const scoped_refptr
<GpuVideoAcceleratorFactories
>& gpu_factories
,
98 FrameResources
* frame_resources
);
100 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
101 scoped_refptr
<GpuVideoAcceleratorFactories
> gpu_factories_
;
103 // Pool of resources.
104 std::list
<FrameResources
*> resources_pool_
;
106 const unsigned texture_target_
;
107 DISALLOW_COPY_AND_ASSIGN(PoolImpl
);
112 // Copy a buffer info a GpuMemoryBuffer.
113 // |bytes_per_row| is expected to be less or equal than the strides of the two
115 void CopyPlaneToGpuMemoryBuffer(int rows
,
119 gfx::GpuMemoryBuffer
* buffer
) {
120 TRACE_EVENT2("media", "CopyPlaneToGpuMemoryBuffer", "bytes_per_row",
121 bytes_per_row
, "rows", rows
);
125 void* data
= nullptr;
126 CHECK(buffer
->Map(&data
));
127 uint8
* mapped_buffer
= static_cast<uint8
*>(data
);
129 buffer
->GetStride(&dest_stride
);
130 DCHECK_NE(dest_stride
, 0);
131 DCHECK_LE(bytes_per_row
, std::abs(dest_stride
));
132 DCHECK_LE(bytes_per_row
, source_stride
);
133 for (int row
= 0; row
< rows
; ++row
) {
134 memcpy(mapped_buffer
+ dest_stride
* row
, source
+ source_stride
* row
,
140 } // unnamed namespace
142 // Creates a VideoFrame backed by native textures starting from a software
144 // The data contained in video_frame is copied into the returned VideoFrame.
145 scoped_refptr
<VideoFrame
>
146 GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
147 const scoped_refptr
<VideoFrame
>& video_frame
) {
151 if (!gpu_factories_
->IsTextureRGSupported())
154 gpu::gles2::GLES2Interface
* gles2
= gpu_factories_
->GetGLES2Interface();
158 VideoPixelFormat format
= video_frame
->format();
159 size_t planes
= VideoFrame::NumPlanes(format
);
160 DCHECK(video_frame
->visible_rect().origin().IsOrigin());
161 gfx::Size size
= video_frame
->visible_rect().size();
162 gpu::MailboxHolder mailbox_holders
[VideoFrame::kMaxPlanes
];
164 // Acquire resources. Incompatible ones will be dropped from the pool.
165 FrameResources
* frame_resources
= GetOrCreateFrameResources(size
, format
);
167 // Set up the planes copying data into it and creating the mailboxes needed
168 // to refer to the textures.
169 for (size_t i
= 0; i
< planes
; ++i
) {
170 PlaneResource
& plane_resource
= frame_resources
->plane_resources
[i
];
171 CopyPlaneToGpuMemoryBuffer(VideoFrame::Rows(i
, format
, size
.height()),
172 VideoFrame::RowBytes(i
, format
, size
.width()),
173 video_frame
->data(i
), video_frame
->stride(i
),
174 plane_resource
.gpu_memory_buffer
.get());
176 // Bind the texture and create or rebind the image.
177 gles2
->BindTexture(texture_target_
, plane_resource
.texture_id
);
179 if (plane_resource
.gpu_memory_buffer
&& !plane_resource
.image_id
) {
180 const size_t width
= VideoFrame::Columns(i
, format
, size
.width());
181 const size_t height
= VideoFrame::Rows(i
, format
, size
.height());
182 plane_resource
.image_id
= gles2
->CreateImageCHROMIUM(
183 plane_resource
.gpu_memory_buffer
->AsClientBuffer(), width
, height
,
186 gles2
->ReleaseTexImage2DCHROMIUM(texture_target_
,
187 plane_resource
.image_id
);
189 gles2
->BindTexImage2DCHROMIUM(texture_target_
, plane_resource
.image_id
);
191 gpu::MailboxHolder(plane_resource
.mailbox
, texture_target_
, 0);
194 // Insert a sync_point, this is needed to make sure that the textures the
195 // mailboxes refer to will be used only after all the previous commands posted
196 // in the command buffer have been processed.
197 unsigned sync_point
= gles2
->InsertSyncPointCHROMIUM();
198 for (size_t i
= 0; i
< planes
; ++i
) {
199 mailbox_holders
[i
].sync_point
= sync_point
;
202 // Create the VideoFrame backed by native textures.
203 scoped_refptr
<VideoFrame
> frame
= VideoFrame::WrapYUV420NativeTextures(
204 mailbox_holders
[VideoFrame::kYPlane
],
205 mailbox_holders
[VideoFrame::kUPlane
],
206 mailbox_holders
[VideoFrame::kVPlane
],
207 base::Bind(&PoolImpl::MailboxHoldersReleased
, this, frame_resources
),
208 size
, video_frame
->visible_rect(), video_frame
->natural_size(),
209 video_frame
->timestamp());
210 if (video_frame
->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY
))
211 frame
->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY
, true);
215 // Destroy all the resources posting one task per FrameResources
216 // to the |task_runner_|.
217 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() {
218 // Delete all the resources on the media thread.
219 while (!resources_pool_
.empty()) {
220 FrameResources
* frame_resources
= resources_pool_
.front();
221 resources_pool_
.pop_front();
222 task_runner_
->PostTask(
223 FROM_HERE
, base::Bind(&PoolImpl::DeleteFrameResources
, gpu_factories_
,
224 base::Owned(frame_resources
)));
228 // Tries to find the resources in the pool or create them.
229 // Incompatible resources will be dropped.
230 GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources
*
231 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
232 const gfx::Size
& size
,
233 VideoPixelFormat format
) {
234 DCHECK(task_runner_
->BelongsToCurrentThread());
236 auto it
= resources_pool_
.begin();
237 while (it
!= resources_pool_
.end()) {
238 FrameResources
* frame_resources
= *it
;
239 if (!frame_resources
->in_use
) {
240 if (AreFrameResourcesCompatible(frame_resources
, size
, format
)) {
241 frame_resources
->in_use
= true;
242 return frame_resources
;
244 resources_pool_
.erase(it
++);
245 DeleteFrameResources(gpu_factories_
, frame_resources
);
246 delete frame_resources
;
253 // Create the resources.
254 gpu::gles2::GLES2Interface
* gles2
= gpu_factories_
->GetGLES2Interface();
256 gles2
->ActiveTexture(GL_TEXTURE0
);
257 size_t planes
= VideoFrame::NumPlanes(format
);
258 FrameResources
* frame_resources
= new FrameResources(format
, size
);
259 resources_pool_
.push_back(frame_resources
);
260 for (size_t i
= 0; i
< planes
; ++i
) {
261 PlaneResource
& plane_resource
= frame_resources
->plane_resources
[i
];
262 const size_t width
= VideoFrame::Columns(i
, format
, size
.width());
263 const size_t height
= VideoFrame::Rows(i
, format
, size
.height());
264 const gfx::Size
plane_size(width
, height
);
265 plane_resource
.gpu_memory_buffer
= gpu_factories_
->AllocateGpuMemoryBuffer(
266 plane_size
, gfx::BufferFormat::R_8
, gfx::BufferUsage::MAP
);
268 gles2
->GenTextures(1, &plane_resource
.texture_id
);
269 gles2
->BindTexture(texture_target_
, plane_resource
.texture_id
);
270 gles2
->TexParameteri(texture_target_
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
271 gles2
->TexParameteri(texture_target_
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
272 gles2
->TexParameteri(texture_target_
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
273 gles2
->TexParameteri(texture_target_
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
274 gles2
->GenMailboxCHROMIUM(plane_resource
.mailbox
.name
);
275 gles2
->ProduceTextureCHROMIUM(texture_target_
, plane_resource
.mailbox
.name
);
277 return frame_resources
;
281 void GpuMemoryBufferVideoFramePool::PoolImpl::DeleteFrameResources(
282 const scoped_refptr
<GpuVideoAcceleratorFactories
>& gpu_factories
,
283 FrameResources
* frame_resources
) {
284 // TODO(dcastagna): As soon as the context lost is dealt with in media,
285 // make sure that we won't execute this callback (use a weak pointer to
287 gpu::gles2::GLES2Interface
* gles2
= gpu_factories
->GetGLES2Interface();
291 for (PlaneResource
& plane_resource
: frame_resources
->plane_resources
) {
292 if (plane_resource
.image_id
)
293 gles2
->DestroyImageCHROMIUM(plane_resource
.image_id
);
294 if (plane_resource
.texture_id
)
295 gles2
->DeleteTextures(1, &plane_resource
.texture_id
);
299 // Called when a VideoFrame is no longer references.
300 void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased(
301 FrameResources
* frame_resources
,
303 // Return the resource on the media thread.
304 task_runner_
->PostTask(FROM_HERE
, base::Bind(&PoolImpl::ReturnFrameResources
,
305 this, frame_resources
));
308 // Put back the resoruces in the pool.
309 void GpuMemoryBufferVideoFramePool::PoolImpl::ReturnFrameResources(
310 FrameResources
* frame_resources
) {
311 DCHECK(task_runner_
->BelongsToCurrentThread());
313 auto it
= std::find(resources_pool_
.begin(), resources_pool_
.end(),
315 DCHECK(it
!= resources_pool_
.end());
316 // We want the pool to behave in a FIFO way.
317 // This minimizes the chances of locking the buffer that might be
318 // still needed for drawing.
319 std::swap(*it
, resources_pool_
.back());
320 frame_resources
->in_use
= false;
323 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool(
324 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
325 const scoped_refptr
<GpuVideoAcceleratorFactories
>& gpu_factories
)
326 : pool_impl_(new PoolImpl(task_runner
, gpu_factories
)) {
329 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() {
332 scoped_refptr
<VideoFrame
>
333 GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame(
334 const scoped_refptr
<VideoFrame
>& video_frame
) {
335 switch (video_frame
->format()) {
337 case PIXEL_FORMAT_YV12
:
338 case PIXEL_FORMAT_I420
:
339 return pool_impl_
->CreateHardwareFrame(video_frame
);
340 // Unsupported cases.
341 case PIXEL_FORMAT_YV12A
:
342 case PIXEL_FORMAT_YV16
:
343 case PIXEL_FORMAT_YV24
:
344 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
345 case PIXEL_FORMAT_NV12
:
347 case PIXEL_FORMAT_ARGB
:
348 case PIXEL_FORMAT_XRGB
:
349 case PIXEL_FORMAT_UNKNOWN
: