Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(526)

Side by Side Diff: media/video/gpu_memory_buffer_video_frame_pool.cc

Issue 1133563010: Add a GpuMemoryBuffer pool that creates hardware backed VideoFrames. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 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.
4
5 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
6
7 #include <GLES2/gl2.h>
8 #include <GLES2/gl2ext.h>
9
10 #include <deque>
11
12 #include "base/bind.h"
13 #include "base/containers/stack_container.h"
14 #include "base/location.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/trace_event/trace_event.h"
18 #include "gpu/command_buffer/client/gles2_interface.h"
19 #include "media/renderers/gpu_video_accelerator_factories.h"
20
21 namespace media {
22
23 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
24 class GpuMemoryBufferVideoFramePool::PoolImpl
25 : public base::RefCountedThreadSafe<
26 GpuMemoryBufferVideoFramePool::PoolImpl> {
27 public:
28 // 'task_runner' is associated to the thread where the context of
29 // GLES2Interface returned by 'gpu_factories' lives.
30 // 'gpu_factories' is an interface to GPU related operation and can be
31 // null.
32 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
33 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
34 : task_runner_(task_runner), gpu_factories_(gpu_factories) {}
35
36 // Takes a software VideoFrame and returns a VideoFrame backed by native
37 // textures if possible.
38 // The data contained in video_frame is copied into the returned frame.
39 const scoped_refptr<VideoFrame> CreateHardwareFrame(
40 scoped_refptr<VideoFrame> video_frame);
41
42 private:
43 friend class base::RefCountedThreadSafe<
44 GpuMemoryBufferVideoFramePool::PoolImpl>;
45 ~PoolImpl();
46
47 // Resource to represent a plane.
48 struct PlaneResource {
49 gfx::Size size;
50 scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
51 unsigned texture_id = 0u;
DaleCurtis 2015/05/12 17:23:20 Use the type size, uint32_t or whatever.
Daniele Castagna 2015/05/12 21:21:30 In cc unsigned is used for resouces/textures/image
DaleCurtis 2015/05/13 02:52:14 We don't use 'unsigned' in media/ code, but if thi
Daniele Castagna 2015/05/13 04:29:45 It's your call. If you prefer a signed type I'll c
DaleCurtis 2015/05/14 00:58:57 I didn't mean use a signed type, I just meant give
Daniele Castagna 2015/05/14 17:30:25 Sorry, I meant sized not signed. Leaving it unsign
52 unsigned image_id = 0u;
53 gpu::Mailbox mailbox;
54 };
55
56 // All the resources needed to compose a frame.
57 struct FrameResources {
58 VideoFrame::Format format;
59 gfx::Size size;
60 PlaneResource plane_resources[VideoFrame::kMaxPlanes];
61 };
62
63 // Return true if 'resources' can be used to represent a frame for
64 // specific 'format' and 'size'.
65 static bool IsFrameResourcesCompatible(const FrameResources* resources,
66 const gfx::Size& size,
67 VideoFrame::Format format) {
68 return size == resources->size && format == resources->format;
69 }
70
71 // Get the resources needed for a frame out of the pool, or create them if
72 // necessary.
73 // This also drops the LRU resources that can't be reuse for this frame.
74 scoped_ptr<FrameResources> GetOrCreateFrameResources(const gfx::Size& size,
75 VideoFrame::Format);
reveman 2015/05/12 03:20:25 nit: missing param name
Daniele Castagna 2015/05/12 21:21:30 Done.
76
77 // Callback called when a VideoFrame generated with GetFrameResources is no
78 // longer referenced.
79 // This could be called by any thread.
80 void MailboxHoldersReleased(scoped_ptr<FrameResources> frame_resources,
81 uint32 sync_point);
82
83 // Return frame resources to the pool. This has to be called on the thread
84 // where 'task_runner' is current.
85 void ReturnFrameResources(scoped_ptr<FrameResources> frame_resources);
86
87 // Delete resources. This has to be called on the thread where 'task_runner'
88 // is current.
89 static void DeleteFrameResources(
90 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_,
reveman 2015/05/12 03:20:25 nit: remove '_' suffix. and again, dunno about med
DaleCurtis 2015/05/12 17:23:20 const&
Daniele Castagna 2015/05/12 21:21:29 Done.
Daniele Castagna 2015/05/12 21:21:30 _ removed. This is used as a callback, the scoped
91 scoped_ptr<FrameResources> frame_resources);
92
93 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
94 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_;
95
96 // Pool of resources, the pool behaves like a LIFO.
97 std::deque<linked_ptr<FrameResources>> resources_pool_;
DaleCurtis 2015/05/12 17:23:20 Can you avoid using linked_ptr here? It makes thin
Daniele Castagna 2015/05/12 21:21:29 I was using a ScopedVector but they suggested me t
98
99 unsigned texture_target_ = GL_TEXTURE_2D;
100 DISALLOW_COPY_AND_ASSIGN(PoolImpl);
101 };
102
103 namespace {
104
105 // Copy a buffer info a GpuMemoryBuffer.
106 // bytes_per_row is expected to be less or equal than the strides of the two
107 // buffers.
108 void CopyPlaneToGpuMemoryBuffer(int rows,
109 int bytes_per_row,
110 const uint8* source,
111 const int source_stride,
DaleCurtis 2015/05/12 17:23:20 Remove const for primitive types that aren't point
Daniele Castagna 2015/05/12 21:21:30 Done.
112 gfx::GpuMemoryBuffer* buffer) {
113 TRACE_EVENT2("media", "CopyPlaneToGpuMemoryBuffer", "bytes_per_row",
114 bytes_per_row, "rows", rows);
115
116 DCHECK(buffer);
117 DCHECK(source);
118 void* data = nullptr;
119 CHECK(buffer->Map(&data));
DaleCurtis 2015/05/12 17:23:20 Seems we are more likely to OOM GPU buffers, shoul
Daniele Castagna 2015/05/12 21:21:29 AFAIU Map doesn't fail with the current GpuMemoryB
120 uint8* mapped_buffer = static_cast<uint8*>(data);
121 int dest_stride = 0;
122 buffer->GetStride(&dest_stride);
123 DCHECK_NE(dest_stride, 0);
124 DCHECK_LE(bytes_per_row, static_cast<int>(dest_stride));
reveman 2015/05/12 03:20:25 what it dest_stride is negative?
Daniele Castagna 2015/05/12 21:21:29 Changed the DCHECK.
125 DCHECK_LE(bytes_per_row, source_stride);
reveman 2015/05/12 03:20:25 can source_stride be negative?
Daniele Castagna 2015/05/12 21:21:30 It can't, software videoframes always have positiv
126 for (int row = 0; row < rows; ++row) {
127 memcpy(mapped_buffer + dest_stride * row, source + source_stride * row,
128 bytes_per_row);
129 }
130 buffer->Unmap();
131 }
132
133 } // unnamed namespace
134
135 // Creates a VideoFrame backed by native textures starting from a software
DaleCurtis 2015/05/12 17:23:20 Comments should go with the function definition no
Daniele Castagna 2015/05/12 21:21:30 I'm OK moving it, but https://google-styleguide.go
DaleCurtis 2015/05/13 02:52:14 That seems to agree with me? Can you highlight the
Daniele Castagna 2015/05/13 04:29:45 "Declaration comments describe use of the function
DaleCurtis 2015/05/14 00:58:57 Hmm, I see; this is atypical for media/ style and
Daniele Castagna 2015/05/14 17:30:25 OK, better to be consistent with the rest of media
136 // VideoFrame.
137 // The data contained in video_frame is copied into the returned VideoFrame.
138 // This method first tries to reuse resources and drops the incompatible ones.
139 // If resources are not available, they will be created.
140 // The returned VideoFrame will call
141 // GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased once the
142 // VideoFrame is not referenced anymore and the resources will be put back
143 // in the pool.
144 const scoped_refptr<VideoFrame>
145 GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
146 scoped_refptr<VideoFrame> video_frame) {
147 gpu::gles2::GLES2Interface* gles2 =
148 gpu_factories_ ? gpu_factories_->GetGLES2Interface() : nullptr;
reveman 2015/05/12 03:20:25 Do both of these need to be allowed to be null? Ma
Daniele Castagna 2015/05/12 21:21:30 gpu_factories_ can be null, for example when #disa
149 if (!gles2)
150 return video_frame;
151
152 VideoFrame::Format format = video_frame->format();
153 size_t planes = VideoFrame::NumPlanes(format);
154 DCHECK(video_frame->visible_rect().origin().IsOrigin());
155 gfx::Size size = video_frame->visible_rect().size();
156 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
157 scoped_ptr<FrameResources> frame_resources =
158 GetOrCreateFrameResources(size, format);
159
160 for (size_t i = 0; i < planes; ++i) {
161 PlaneResource& plane_resource = frame_resources->plane_resources[i];
162 CopyPlaneToGpuMemoryBuffer(VideoFrame::Rows(i, format, size.height()),
163 VideoFrame::RowBytes(i, format, size.width()),
164 video_frame->data(i), video_frame->stride(i),
165 plane_resource.gpu_memory_buffer.get());
166
167 // Bind the texture and create or rebind the image.
168 gles2->BindTexture(texture_target_, plane_resource.texture_id);
169 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) {
170 const size_t width = VideoFrame::Columns(i, format, size.width());
171 const size_t height = VideoFrame::Rows(i, format, size.height());
172 const gfx::Size size(width, height);
reveman 2015/05/12 03:20:25 creating this seems useless and just confusing as
Daniele Castagna 2015/05/12 21:21:30 Done.
173 plane_resource.image_id = gles2->CreateImageCHROMIUM(
174 plane_resource.gpu_memory_buffer->AsClientBuffer(), size.width(),
175 size.height(), GL_R8_EXT);
reveman 2015/05/12 03:20:25 Do we need to check that GL_R8_EXT is supported?
Daniele Castagna 2015/05/12 21:21:30 Yes we do. I can follow up with another cl since t
176 } else {
177 gles2->ReleaseTexImage2DCHROMIUM(texture_target_,
178 plane_resource.image_id);
179 }
180 gles2->BindTexImage2DCHROMIUM(texture_target_, plane_resource.image_id);
181 mailbox_holders[i] =
182 gpu::MailboxHolder(plane_resource.mailbox, texture_target_, 0);
183 }
184
185 unsigned sync_point = gles2->InsertSyncPointCHROMIUM();
186 for (size_t i = 0; i < planes; ++i) {
187 mailbox_holders[i].sync_point = sync_point;
188 }
189
190 return VideoFrame::WrapYUV420NativeTextures(
191 mailbox_holders[VideoFrame::kYPlane],
192 mailbox_holders[VideoFrame::kUPlane],
193 mailbox_holders[VideoFrame::kVPlane],
194 base::Bind(&PoolImpl::MailboxHoldersReleased, this,
195 base::Passed(&frame_resources)),
reveman 2015/05/12 03:20:25 Not sure passing frame resources containing GMBs a
Daniele Castagna 2015/05/12 21:21:30 I'll follow up with another iteration on this CL k
196 size, video_frame->visible_rect(), video_frame->natural_size(),
197 video_frame->timestamp(), video_frame->allow_overlay());
198 }
199
200 // Destroy all the resources posting one task per FrameResources
201 // to the 'task_runner_'.
202 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() {
203 // Delete all the resources on the media thread.
204 auto resources_it = resources_pool_.begin();
reveman 2015/05/12 03:20:25 ?
Daniele Castagna 2015/05/12 21:21:30 Done.
205 while (!resources_pool_.empty()) {
206 auto frame_resources_linked_ptr = resources_pool_.front();
reveman 2015/05/12 03:20:25 scoped_ptr<FrameResources> frame_resources = make_
Daniele Castagna 2015/05/12 21:21:30 Done.
207 resources_pool_.pop_front();
208 scoped_ptr<FrameResources> frame_resources(
209 frame_resources_linked_ptr.release());
210 task_runner_->PostTask(
211 FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_,
212 base::Passed(&frame_resources)));
reveman 2015/05/12 03:20:25 pass frame_resource as linked_ptr and get rid of t
Daniele Castagna 2015/05/12 21:21:30 Done as above. I preferred to use scoped_ptr as so
213 }
214 }
215
216 // Tries to find the resources in the pool or create them.
217 // Incompatible resources will be dropped.
218 scoped_ptr<GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources>
219 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
220 const gfx::Size& size,
221 VideoFrame::Format format) {
222 DCHECK(task_runner_->BelongsToCurrentThread());
223
224 while (!resources_pool_.empty()) {
225 auto frame_resources_linked_ptr = resources_pool_.front();
reveman 2015/05/12 03:20:25 make consistent with above code
Daniele Castagna 2015/05/12 21:21:30 Done.
226 resources_pool_.pop_front();
227 scoped_ptr<FrameResources> frame_resources(
228 frame_resources_linked_ptr.release());
229 if (IsFrameResourcesCompatible(frame_resources.get(), size, format)) {
230 return frame_resources.Pass();
231 } else {
232 DeleteFrameResources(gpu_factories_, frame_resources.Pass());
233 }
234 }
235
236 // Create the resources.
237 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
238 DCHECK(gles2);
239 gles2->ActiveTexture(GL_TEXTURE0);
240 size_t planes = VideoFrame::NumPlanes(format);
241 auto frame_resources = make_scoped_ptr(new FrameResources());
242 frame_resources->format = format;
243 frame_resources->size = size;
244 for (size_t i = 0; i < planes; ++i) {
245 PlaneResource& plane_resource = frame_resources->plane_resources[i];
246 const size_t width = VideoFrame::Columns(i, format, size.width());
247 const size_t height = VideoFrame::Rows(i, format, size.height());
248 const gfx::Size plane_size(width, height);
249 plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer(
250 plane_size, gfx::GpuMemoryBuffer::R_8, gfx::GpuMemoryBuffer::MAP);
251
252 gles2->GenTextures(1, &plane_resource.texture_id);
253 gles2->BindTexture(texture_target_, plane_resource.texture_id);
254 gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
255 gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
256 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
257 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
258 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name);
259 gles2->ProduceTextureCHROMIUM(texture_target_, plane_resource.mailbox.name);
260 }
261 return frame_resources.Pass();
262 }
263
264 // static
265 void GpuMemoryBufferVideoFramePool::PoolImpl::DeleteFrameResources(
266 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories,
267 scoped_ptr<FrameResources> frame_resources) {
268 DCHECK(frame_resources);
reveman 2015/05/12 03:20:24 nit: why check frame_resources here? it seems a bi
Daniele Castagna 2015/05/12 21:21:30 Done.
269 gpu::gles2::GLES2Interface* gles2 = gpu_factories->GetGLES2Interface();
270 DCHECK(gles2);
271
272 for (size_t i = 0; i < arraysize(frame_resources->plane_resources); ++i) {
reveman 2015/05/12 03:20:25 for (PlaneResource& plane_resource : frame_resourc
Daniele Castagna 2015/05/12 21:21:30 Done.
273 PlaneResource& plane_resource = frame_resources->plane_resources[i];
274 if (plane_resource.image_id) {
275 gles2->DestroyImageCHROMIUM(plane_resource.image_id);
276 plane_resource.image_id = 0;
277 }
278 if (plane_resource.texture_id) {
279 gles2->DeleteTextures(1, &plane_resource.texture_id);
280 plane_resource.texture_id = 0;
281 }
282 plane_resource.gpu_memory_buffer.reset();
283 }
284 }
285
286 // Called when a VideoFrame is no longer references.
287 void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased(
288 scoped_ptr<FrameResources> frame_resources,
289 uint32 sync_point) {
290 // Return the resource on the media thread.
291 task_runner_->PostTask(FROM_HERE,
292 base::Bind(&PoolImpl::ReturnFrameResources, this,
293 base::Passed(&frame_resources)));
294 }
295
296 // Put back the resoruces in the pool.
297 void GpuMemoryBufferVideoFramePool::PoolImpl::ReturnFrameResources(
298 scoped_ptr<FrameResources> frame_resources) {
299 DCHECK(task_runner_->BelongsToCurrentThread());
300 resources_pool_.push_back(
301 linked_ptr<FrameResources>(frame_resources.release()));
reveman 2015/05/12 03:20:24 make_linked_ptr?
Daniele Castagna 2015/05/12 21:21:30 Done.
302 }
303
304 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool(
305 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
306 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
307 : pool_impl_(new PoolImpl(task_runner, gpu_factories)) {
308 }
309
310 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() {
311 }
312
313 const scoped_refptr<VideoFrame>
314 GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame(
315 scoped_refptr<VideoFrame> video_frame) {
316 switch (video_frame->format()) {
317 // Supported cases.
318 case VideoFrame::YV12:
319 case VideoFrame::I420:
320 return pool_impl_->CreateHardwareFrame(video_frame);
321 // Unsupported cases.
322 case media::VideoFrame::YV12A:
323 case media::VideoFrame::YV16:
324 case media::VideoFrame::YV12J:
325 case media::VideoFrame::YV12HD:
326 case media::VideoFrame::YV24:
327 #if defined(VIDEO_HOLE)
328 case media::VideoFrame::HOLE:
329 #endif // defined(VIDEO_HOLE)
330 case media::VideoFrame::ARGB:
331 return video_frame;
332
333 // Unacceptable inputs.
334 case media::VideoFrame::NATIVE_TEXTURE:
DaleCurtis 2015/05/12 17:23:20 I don't think this should have a NOTREACHED(), it
Daniele Castagna 2015/05/12 21:21:29 These formats are not supported by VideoResourceUp
DaleCurtis 2015/05/14 00:58:57 What I mean is this method is definitely going to
335 case media::VideoFrame::UNKNOWN:
336 case media::VideoFrame::NV12:
337 NOTREACHED();
338 }
339 NOTREACHED();
340 return scoped_refptr<VideoFrame>();
341 }
342
343 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698