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

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: Fix a mem leak. Remove TODO in VideoRendererImpl. 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 <list>
11 #include <utility>
12
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"
21
22 namespace media {
23
24 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
25 class GpuMemoryBufferVideoFramePool::PoolImpl
26 : public base::RefCountedThreadSafe<
27 GpuMemoryBufferVideoFramePool::PoolImpl> {
28 public:
29 // 'task_runner' is associated to the thread where the context of
DaleCurtis 2015/05/14 00:58:58 Style is || instead of '' for designating variable
Daniele Castagna 2015/05/14 17:30:26 Done.
30 // GLES2Interface returned by 'gpu_factories' lives.
31 // 'gpu_factories' is an interface to GPU related operation and can be
32 // null.
33 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
34 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
35 : task_runner_(task_runner), gpu_factories_(gpu_factories) {}
36
37 // Takes a software VideoFrame and returns a VideoFrame backed by native
38 // textures if possible.
39 // The data contained in video_frame is copied into the returned frame.
40 const scoped_refptr<VideoFrame> CreateHardwareFrame(
DaleCurtis 2015/05/14 00:58:57 Remove const
Daniele Castagna 2015/05/14 17:30:26 Done.
41 scoped_refptr<VideoFrame> video_frame);
DaleCurtis 2015/05/14 00:58:58 const&
Daniele Castagna 2015/05/14 17:30:26 Done.
42
43 private:
44 friend class base::RefCountedThreadSafe<
45 GpuMemoryBufferVideoFramePool::PoolImpl>;
46 ~PoolImpl();
47
48 // Resource to represent a plane.
49 struct PlaneResource {
50 gfx::Size size;
51 scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
52 unsigned texture_id = 0u;
53 unsigned image_id = 0u;
54 gpu::Mailbox mailbox;
55 };
56
57 // All the resources needed to compose a frame.
58 struct FrameResources {
59 FrameResources(VideoFrame::Format format, const gfx::Size& size)
60 : format(format), size(size) {}
61 bool in_use = true;
62 VideoFrame::Format format;
63 gfx::Size size;
64 PlaneResource plane_resources[VideoFrame::kMaxPlanes];
65 };
66
67 // Return true if 'resources' can be used to represent a frame for
68 // specific 'format' and 'size'.
69 static bool IsFrameResourcesCompatible(const FrameResources* resources,
70 const gfx::Size& size,
71 VideoFrame::Format format) {
72 return size == resources->size && format == resources->format;
73 }
74
75 // Get the resources needed for a frame out of the pool, or create them if
76 // necessary.
77 // This also drops the LRU resources that can't be reuse for this frame.
78 FrameResources* GetOrCreateFrameResources(const gfx::Size& size,
79 VideoFrame::Format format);
80
81 // Callback called when a VideoFrame generated with GetFrameResources is no
82 // longer referenced.
83 // This could be called by any thread.
84 void MailboxHoldersReleased(FrameResources* frame_resources,
85 uint32 sync_point);
86
87 // Return frame resources to the pool. This has to be called on the thread
88 // where 'task_runner' is current.
89 void ReturnFrameResources(FrameResources* frame_resources);
90
91 // Delete resources. This has to be called on the thread where 'task_runner'
92 // is current.
93 static void DeleteFrameResources(
94 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
95 FrameResources* frame_resources);
96
97 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
98 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_;
99
100 // Pool of resources.
101 std::list<FrameResources*> resources_pool_;
102
103 unsigned texture_target_ = GL_TEXTURE_2D;
104 DISALLOW_COPY_AND_ASSIGN(PoolImpl);
105 };
106
107 namespace {
108
109 // Copy a buffer info a GpuMemoryBuffer.
110 // bytes_per_row is expected to be less or equal than the strides of the two
DaleCurtis 2015/05/14 00:58:57 bytes_per_row and all other variables in comments,
Daniele Castagna 2015/05/14 17:30:26 Done.
111 // buffers.
112 void CopyPlaneToGpuMemoryBuffer(int rows,
113 int bytes_per_row,
114 const uint8* source,
115 int source_stride,
116 gfx::GpuMemoryBuffer* buffer) {
117 TRACE_EVENT2("media", "CopyPlaneToGpuMemoryBuffer", "bytes_per_row",
118 bytes_per_row, "rows", rows);
119
120 DCHECK(buffer);
121 DCHECK(source);
122 void* data = nullptr;
123 CHECK(buffer->Map(&data));
124 uint8* mapped_buffer = static_cast<uint8*>(data);
125 int dest_stride = 0;
126 buffer->GetStride(&dest_stride);
127 DCHECK_NE(dest_stride, 0);
128 DCHECK_LE(bytes_per_row, std::abs(dest_stride));
129 DCHECK_LE(bytes_per_row, source_stride);
130 for (int row = 0; row < rows; ++row) {
131 memcpy(mapped_buffer + dest_stride * row, source + source_stride * row,
132 bytes_per_row);
133 }
134 buffer->Unmap();
135 }
136
137 } // unnamed namespace
138
139 // Creates a VideoFrame backed by native textures starting from a software
DaleCurtis 2015/05/14 00:58:58 Again, strongly prefer if the comment was reflowed
Daniele Castagna 2015/05/14 17:30:26 Done.
140 // VideoFrame.
141 // The data contained in video_frame is copied into the returned VideoFrame.
142 // This method first tries to reuse resources and drops the incompatible ones.
143 // If resources are not available, they will be created.
144 // The returned VideoFrame will call
145 // GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased once the
146 // VideoFrame is not referenced anymore and the resources will be put back
147 // in the pool.
148 const scoped_refptr<VideoFrame>
149 GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
DaleCurtis 2015/05/14 00:58:57 Fix const, const& stuff
Daniele Castagna 2015/05/14 17:30:26 Done.
150 scoped_refptr<VideoFrame> video_frame) {
151 if (!gpu_factories_)
152 return video_frame;
153
154 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
155 if (!gles2)
156 return video_frame;
157
158 VideoFrame::Format 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];
163 FrameResources* frame_resources = GetOrCreateFrameResources(size, format);
164
165 for (size_t i = 0; i < planes; ++i) {
166 PlaneResource& plane_resource = frame_resources->plane_resources[i];
167 CopyPlaneToGpuMemoryBuffer(VideoFrame::Rows(i, format, size.height()),
168 VideoFrame::RowBytes(i, format, size.width()),
169 video_frame->data(i), video_frame->stride(i),
170 plane_resource.gpu_memory_buffer.get());
171
172 // Bind the texture and create or rebind the image.
173 gles2->BindTexture(texture_target_, plane_resource.texture_id);
174 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) {
175 const size_t width = VideoFrame::Columns(i, format, size.width());
176 const size_t height = VideoFrame::Rows(i, format, size.height());
177 plane_resource.image_id = gles2->CreateImageCHROMIUM(
178 plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height,
179 GL_R8_EXT);
180 } else {
181 gles2->ReleaseTexImage2DCHROMIUM(texture_target_,
182 plane_resource.image_id);
183 }
184 gles2->BindTexImage2DCHROMIUM(texture_target_, plane_resource.image_id);
185 mailbox_holders[i] =
186 gpu::MailboxHolder(plane_resource.mailbox, texture_target_, 0);
187 }
188
189 unsigned sync_point = gles2->InsertSyncPointCHROMIUM();
190 for (size_t i = 0; i < planes; ++i) {
191 mailbox_holders[i].sync_point = sync_point;
192 }
193
194 return VideoFrame::WrapYUV420NativeTextures(
195 mailbox_holders[VideoFrame::kYPlane],
196 mailbox_holders[VideoFrame::kUPlane],
197 mailbox_holders[VideoFrame::kVPlane],
198 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources),
199 size, video_frame->visible_rect(), video_frame->natural_size(),
200 video_frame->timestamp(), video_frame->allow_overlay());
201 }
202
203 // Destroy all the resources posting one task per FrameResources
204 // to the 'task_runner_'.
205 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() {
206 // Delete all the resources on the media thread.
207 while (!resources_pool_.empty()) {
208 FrameResources* frame_resources = resources_pool_.front();
209 resources_pool_.pop_front();
210 task_runner_->PostTask(
211 FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_,
212 base::Owned(frame_resources)));
213 }
214 }
215
216 // Tries to find the resources in the pool or create them.
217 // Incompatible resources will be dropped.
218 GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources*
DaleCurtis 2015/05/14 00:58:58 Why not scoped_ptr?
Daniele Castagna 2015/05/14 17:30:26 As per reveman's suggestion we decided to leave Fr
DaleCurtis 2015/05/14 18:52:13 I'm not sure about that logic, but up to you :) So
Daniele Castagna 2015/05/14 19:24:16 I agree that it can be confusing, at least this me
DaleCurtis 2015/05/14 20:04:49 I was expected that resources_pool_ would be a Sco
219 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
220 const gfx::Size& size,
221 VideoFrame::Format format) {
222 DCHECK(task_runner_->BelongsToCurrentThread());
223
224 auto it = resources_pool_.begin();
225 while (it != resources_pool_.end()) {
226 FrameResources* frame_resources = *it;
227 if (!frame_resources->in_use) {
228 if (IsFrameResourcesCompatible(frame_resources, size, format)) {
229 frame_resources->in_use = true;
230 return frame_resources;
231 } else {
232 resources_pool_.erase(it++);
233 DeleteFrameResources(gpu_factories_, frame_resources);
234 delete frame_resources;
235 }
236 } else {
237 it++;
238 }
239 }
240
241 // Create the resources.
242 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
243 DCHECK(gles2);
244 gles2->ActiveTexture(GL_TEXTURE0);
245 size_t planes = VideoFrame::NumPlanes(format);
246 FrameResources* frame_resources = new FrameResources(format, size);
247 resources_pool_.push_back(frame_resources);
248 for (size_t i = 0; i < planes; ++i) {
DaleCurtis 2015/05/14 00:58:57 Don't need i it looks like, so why not for (const
Daniele Castagna 2015/05/14 17:30:26 |planes| might be less than arraysize(frame_resour
249 PlaneResource& plane_resource = frame_resources->plane_resources[i];
250 const size_t width = VideoFrame::Columns(i, format, size.width());
251 const size_t height = VideoFrame::Rows(i, format, size.height());
252 const gfx::Size plane_size(width, height);
253 plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer(
254 plane_size, gfx::GpuMemoryBuffer::R_8, gfx::GpuMemoryBuffer::MAP);
255
256 gles2->GenTextures(1, &plane_resource.texture_id);
257 gles2->BindTexture(texture_target_, plane_resource.texture_id);
258 gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
259 gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
260 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
261 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
262 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name);
263 gles2->ProduceTextureCHROMIUM(texture_target_, plane_resource.mailbox.name);
264 }
265 return frame_resources;
266 }
267
268 // static
269 void GpuMemoryBufferVideoFramePool::PoolImpl::DeleteFrameResources(
270 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
271 FrameResources* frame_resources) {
272 // TODO(dcastagna): As soon as the context lost is dealt with in media,
DaleCurtis 2015/05/14 00:58:57 Seems pretty important to fix sooner than later :)
Daniele Castagna 2015/05/14 17:30:26 Absolutely. I talked with posciak@ about this and
DaleCurtis 2015/05/14 18:52:13 Depends on the failure mode, if it dangles use-aft
Daniele Castagna 2015/05/14 19:24:16 There is no use-after-frees and the resources are
273 // make sure that we won't execture this callback (use a weak pointer to
274 // the old context).
275 gpu::gles2::GLES2Interface* gles2 = gpu_factories->GetGLES2Interface();
276 if (!gles2)
277 return;
278
279 for (PlaneResource& plane_resource : frame_resources->plane_resources) {
280 if (plane_resource.image_id)
281 gles2->DestroyImageCHROMIUM(plane_resource.image_id);
282 if (plane_resource.texture_id)
283 gles2->DeleteTextures(1, &plane_resource.texture_id);
284 }
285 }
286
287 // Called when a VideoFrame is no longer references.
288 void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased(
289 FrameResources* frame_resources,
290 uint32 sync_point) {
291 // Return the resource on the media thread.
292 task_runner_->PostTask(FROM_HERE, base::Bind(&PoolImpl::ReturnFrameResources,
293 this, frame_resources));
294 }
295
296 // Put back the resoruces in the pool.
297 void GpuMemoryBufferVideoFramePool::PoolImpl::ReturnFrameResources(
298 FrameResources* frame_resources) {
299 DCHECK(task_runner_->BelongsToCurrentThread());
300
301 auto it = std::find(resources_pool_.begin(), resources_pool_.end(),
DaleCurtis 2015/05/14 00:58:58 These will be released extremely frequently, so yo
Daniele Castagna 2015/05/14 17:30:27 I'll take a look at this before enabling the pool.
302 frame_resources);
303 DCHECK(it != resources_pool_.end());
DaleCurtis 2015/05/14 00:58:57 DCHECK_NE?
Daniele Castagna 2015/05/14 17:30:26 That doesn't work with iterators. :(
304 // We want the pool to behave in a FIFO way.
305 // This minimizes the chances of locking the buffer that might be
306 // still needed for drawing.
307 std::swap(*it, resources_pool_.back());
308 frame_resources->in_use = false;
309 }
310
311 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool(
312 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
313 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
314 : pool_impl_(new PoolImpl(task_runner, gpu_factories)) {
315 }
316
317 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() {
318 }
319
320 scoped_refptr<VideoFrame>
321 GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame(
322 const scoped_refptr<VideoFrame>& video_frame) {
323 switch (video_frame->format()) {
324 // Supported cases.
325 case VideoFrame::YV12:
326 case VideoFrame::I420:
327 return pool_impl_->CreateHardwareFrame(video_frame);
328 // Unsupported cases.
329 case media::VideoFrame::YV12A:
330 case media::VideoFrame::YV16:
331 case media::VideoFrame::YV12J:
332 case media::VideoFrame::YV12HD:
333 case media::VideoFrame::YV24:
334 #if defined(VIDEO_HOLE)
335 case media::VideoFrame::HOLE:
336 #endif // defined(VIDEO_HOLE)
337 case media::VideoFrame::ARGB:
338 return video_frame;
DaleCurtis 2015/05/14 00:58:57 I'd just do nothing here and change the return at
Daniele Castagna 2015/05/14 17:30:26 Done.
339
340 // Unacceptable inputs.
341 case media::VideoFrame::NATIVE_TEXTURE:
342 case media::VideoFrame::UNKNOWN:
343 case media::VideoFrame::NV12:
344 NOTREACHED();
DaleCurtis 2015/05/14 00:58:58 Again remove this, these falls under the unsupport
Daniele Castagna 2015/05/14 17:30:26 Done.
345 }
346 NOTREACHED();
347 return scoped_refptr<VideoFrame>();
348 }
349
350 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698