OLD | NEW |
---|---|
(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 | |
OLD | NEW |