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