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

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

Issue 1874733002: media: split GpuMemoryBufferVideoFramePool into GpuMemoryBufferVideoFrameCopier/Pool Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add GpuMemoryBufferVideoFramePoolTest Created 4 years, 8 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
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/video/gpu_memory_buffer_video_frame_pool.h" 5 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
6 6
7 #include <GLES2/gl2.h>
8 #include <GLES2/gl2ext.h>
9 #include <stddef.h>
10 #include <stdint.h>
11
12 #include <algorithm> 7 #include <algorithm>
13 #include <list> 8 #include <list>
14 #include <utility>
15 9
16 #include "base/barrier_closure.h"
17 #include "base/bind.h" 10 #include "base/bind.h"
18 #include "base/containers/stack_container.h"
19 #include "base/location.h"
20 #include "base/macros.h"
21 #include "base/memory/linked_ptr.h"
22 #include "base/strings/stringprintf.h" 11 #include "base/strings/stringprintf.h"
23 #include "base/trace_event/memory_dump_provider.h" 12 #include "base/trace_event/memory_dump_provider.h"
24 #include "base/trace_event/trace_event.h"
25 #include "gpu/GLES2/gl2extchromium.h" 13 #include "gpu/GLES2/gl2extchromium.h"
26 #include "gpu/command_buffer/client/gles2_interface.h"
27 #include "media/base/bind_to_current_loop.h" 14 #include "media/base/bind_to_current_loop.h"
28 #include "media/renderers/gpu_video_accelerator_factories.h" 15 #include "media/renderers/gpu_video_accelerator_factories.h"
29 #include "third_party/libyuv/include/libyuv.h"
30 #include "ui/gfx/buffer_format_util.h" 16 #include "ui/gfx/buffer_format_util.h"
31 #include "ui/gl/trace_util.h"
32 17
33 namespace media { 18 namespace media {
34 19
35 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
36 class GpuMemoryBufferVideoFramePool::PoolImpl
37 : public base::RefCountedThreadSafe<
38 GpuMemoryBufferVideoFramePool::PoolImpl>,
39 public base::trace_event::MemoryDumpProvider {
40 public:
41 // |media_task_runner| is the media task runner associated with the
42 // GL context provided by |gpu_factories|
43 // |worker_task_runner| is a task runner used to asynchronously copy
44 // video frame's planes.
45 // |gpu_factories| is an interface to GPU related operation and can be
46 // null if a GL context is not available.
47 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
48 const scoped_refptr<base::TaskRunner>& worker_task_runner,
49 GpuVideoAcceleratorFactories* gpu_factories)
50 : media_task_runner_(media_task_runner),
51 worker_task_runner_(worker_task_runner),
52 gpu_factories_(gpu_factories),
53 output_format_(PIXEL_FORMAT_UNKNOWN) {
54 DCHECK(media_task_runner_);
55 DCHECK(worker_task_runner_);
56 }
57
58 // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame
59 // backed by native textures if possible.
60 // The data contained in video_frame is copied into the returned frame
61 // asynchronously posting tasks to |worker_task_runner_|, while
62 // |frame_ready_cb| will be called on |media_task_runner_| once all the data
63 // has been copied.
64 void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame,
65 const FrameReadyCB& cb);
66
67 bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
68 base::trace_event::ProcessMemoryDump* pmd) override;
69
70 private:
71 friend class base::RefCountedThreadSafe<
72 GpuMemoryBufferVideoFramePool::PoolImpl>;
73 ~PoolImpl() override;
74
75 // Resource to represent a plane.
76 struct PlaneResource {
77 gfx::Size size;
78 scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
79 unsigned texture_id = 0u;
80 unsigned image_id = 0u;
81 gpu::Mailbox mailbox;
82 };
83
84 // All the resources needed to compose a frame.
85 struct FrameResources {
86 explicit FrameResources(const gfx::Size& size) : size(size) {}
87 void SetIsInUse(bool in_use) { in_use_ = in_use; }
88 bool IsInUse() const {
89 if (in_use_)
90 return true;
91 for (const PlaneResource& plane_resource : plane_resources) {
92 if (plane_resource.gpu_memory_buffer &&
93 plane_resource.gpu_memory_buffer->IsInUseByMacOSWindowServer()) {
94 return true;
95 }
96 }
97 return false;
98 }
99
100 const gfx::Size size;
101 PlaneResource plane_resources[VideoFrame::kMaxPlanes];
102
103 private:
104 bool in_use_ = true;
105 };
106
107 // Copy |video_frame| data into |frame_resouces|
108 // and calls |done| when done.
109 void CopyVideoFrameToGpuMemoryBuffers(
110 const scoped_refptr<VideoFrame>& video_frame,
111 FrameResources* frame_resources,
112 const FrameReadyCB& frame_ready_cb);
113
114 // Called when all the data has been copied.
115 void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame,
116 FrameResources* frame_resources,
117 const FrameReadyCB& frame_ready_cb);
118
119 // Prepares GL resources, mailboxes and calls |frame_ready_cb| with the new
120 // VideoFrame.
121 // This has to be run on |media_task_runner_| where |frame_ready_cb| will also
122 // be run.
123 void BindAndCreateMailboxesHardwareFrameResources(
124 const scoped_refptr<VideoFrame>& video_frame,
125 FrameResources* frame_resources,
126 const FrameReadyCB& frame_ready_cb);
127
128 // Return true if |resources| can be used to represent a frame for
129 // specific |format| and |size|.
130 static bool AreFrameResourcesCompatible(const FrameResources* resources,
131 const gfx::Size& size) {
132 return size == resources->size;
133 }
134
135 // Get the resources needed for a frame out of the pool, or create them if
136 // necessary.
137 // This also drops the LRU resources that can't be reuse for this frame.
138 FrameResources* GetOrCreateFrameResources(const gfx::Size& size,
139 VideoPixelFormat format);
140
141 // Callback called when a VideoFrame generated with GetFrameResources is no
142 // longer referenced.
143 // This must be called on the thread where |media_task_runner_| is current.
144 void MailboxHoldersReleased(FrameResources* frame_resources,
145 const gpu::SyncToken& sync_token);
146
147 // Delete resources. This has to be called on the thread where |task_runner|
148 // is current.
149 static void DeleteFrameResources(GpuVideoAcceleratorFactories* gpu_factories,
150 FrameResources* frame_resources);
151
152 // Task runner associated to the GL context provided by |gpu_factories_|.
153 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
154 // Task runner used to asynchronously copy planes.
155 scoped_refptr<base::TaskRunner> worker_task_runner_;
156
157 // Interface to GPU related operations.
158 GpuVideoAcceleratorFactories* gpu_factories_;
159
160 // Pool of resources.
161 std::list<FrameResources*> resources_pool_;
162
163 // TODO(dcastagna): change the following type from VideoPixelFormat to
164 // BufferFormat.
165 VideoPixelFormat output_format_;
166
167 DISALLOW_COPY_AND_ASSIGN(PoolImpl);
168 };
169
170 namespace { 20 namespace {
171 21
172 // VideoFrame copies to GpuMemoryBuffers will be split in copies where the
173 // output size is |kBytesPerCopyTarget| bytes and run in parallel.
174 const size_t kBytesPerCopyTarget = 1024 * 1024; // 1MB
175
176 // Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat 22 // Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat
177 // and plane. 23 // and plane.
178 gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) { 24 gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) {
179 switch (format) { 25 switch (format) {
180 case PIXEL_FORMAT_I420: 26 case PIXEL_FORMAT_I420:
27 case PIXEL_FORMAT_YV12:
181 DCHECK_LE(plane, 2u); 28 DCHECK_LE(plane, 2u);
182 return gfx::BufferFormat::R_8; 29 return gfx::BufferFormat::R_8;
183 case PIXEL_FORMAT_NV12: 30 case PIXEL_FORMAT_NV12:
184 DCHECK_LE(plane, 1u); 31 DCHECK_LE(plane, 1u);
185 return gfx::BufferFormat::YUV_420_BIPLANAR; 32 return gfx::BufferFormat::YUV_420_BIPLANAR;
186 case PIXEL_FORMAT_UYVY: 33 case PIXEL_FORMAT_UYVY:
187 DCHECK_EQ(0u, plane); 34 DCHECK_EQ(0u, plane);
188 return gfx::BufferFormat::UYVY_422; 35 return gfx::BufferFormat::UYVY_422;
189 default: 36 default:
190 NOTREACHED(); 37 NOTREACHED();
191 return gfx::BufferFormat::BGRA_8888; 38 return gfx::BufferFormat::BGRA_8888;
192 } 39 }
193 } 40 }
194 41
195 unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) { 42 unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) {
196 switch (format) { 43 switch (format) {
197 case PIXEL_FORMAT_I420: 44 case PIXEL_FORMAT_I420:
45 case PIXEL_FORMAT_YV12:
198 DCHECK_LE(plane, 2u); 46 DCHECK_LE(plane, 2u);
199 return GL_RED_EXT; 47 return GL_RED_EXT;
200 case PIXEL_FORMAT_NV12: 48 case PIXEL_FORMAT_NV12:
49 // TODO(dshwang): support ChromeOS using GL_RG_EXT. crbug.com/356871
201 DCHECK_LE(plane, 1u); 50 DCHECK_LE(plane, 1u);
202 return GL_RGB_YCBCR_420V_CHROMIUM; 51 return GL_RGB_YCBCR_420V_CHROMIUM;
203 case PIXEL_FORMAT_UYVY: 52 case PIXEL_FORMAT_UYVY:
204 DCHECK_EQ(0u, plane); 53 DCHECK_EQ(0u, plane);
205 return GL_RGB_YCBCR_422_CHROMIUM; 54 return GL_RGB_YCBCR_422_CHROMIUM;
206 default: 55 default:
207 NOTREACHED(); 56 NOTREACHED();
208 return 0; 57 return 0;
209 } 58 }
210 } 59 }
211 60
212 // The number of output planes to be copied in each iteration. 61 bool IsValidPlane(size_t plane, VideoPixelFormat format) {
213 size_t PlanesPerCopy(VideoPixelFormat format) { 62 DCHECK_LE(VideoFrame::NumPlanes(format),
63 static_cast<size_t>(VideoFrame::kMaxPlanes));
64 return (plane < VideoFrame::NumPlanes(format));
65 }
66
67 bool IsPowerOfTwo(size_t x) {
68 return x != 0 && (x & (x - 1)) == 0;
69 }
70
71 inline size_t RoundUp(size_t value, size_t alignment) {
72 DCHECK(IsPowerOfTwo(alignment));
73 return ((value + (alignment - 1)) & ~(alignment - 1));
74 }
75
76 gfx::Size CodedSize(const gfx::Size& size, VideoPixelFormat format) {
214 switch (format) { 77 switch (format) {
215 case PIXEL_FORMAT_I420: 78 case PIXEL_FORMAT_I420:
79 case PIXEL_FORMAT_YV12:
80 case PIXEL_FORMAT_NV12:
81 return gfx::Size(RoundUp(size.width(), 2), RoundUp(size.height(), 2));
216 case PIXEL_FORMAT_UYVY: 82 case PIXEL_FORMAT_UYVY:
217 return 1; 83 return gfx::Size(RoundUp(size.width(), 2), size.height());
218 case PIXEL_FORMAT_NV12:
219 return 2;
220 default: 84 default:
221 NOTREACHED(); 85 NOTREACHED();
222 return 0; 86 }
223 } 87 return gfx::Size();
224 } 88 }
225 89
226 // The number of output rows to be copied in each iteration. 90 // All the resources needed to compose a frame.
227 int RowsPerCopy(size_t plane, VideoPixelFormat format, int width) { 91 class FrameResources : public base::RefCountedThreadSafe<FrameResources> {
228 int bytes_per_row = VideoFrame::RowBytes(plane, format, width); 92 public:
229 if (format == PIXEL_FORMAT_NV12) { 93 static scoped_refptr<FrameResources> Create(
230 DCHECK_EQ(0u, plane); 94 GpuVideoAcceleratorFactories* gpu_factories,
231 bytes_per_row += VideoFrame::RowBytes(1, format, width); 95 VideoPixelFormat format,
232 } 96 const gfx::Size& size) {
233 // Copy an even number of lines, and at least one. 97 // Create the resources.
234 return std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1); 98 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
235 } 99 gpu_factories->GetGLContextLock());
236 100 if (!lock)
237 void CopyRowsToI420Buffer(int first_row, 101 return nullptr;
238 int rows, 102
239 int bytes_per_row, 103 scoped_refptr<FrameResources> frame_resources(
240 const uint8_t* source, 104 new FrameResources(gpu_factories, format, size));
241 int source_stride, 105
242 uint8_t* output, 106 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
243 int dest_stride, 107 gles2->ActiveTexture(GL_TEXTURE0);
244 const base::Closure& done) { 108 size_t num_planes = VideoFrame::NumPlanes(format);
245 TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row, 109 for (size_t i = 0; i < num_planes;
246 "rows", rows); 110 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) {
247 if (output) { 111 PlaneResource& plane_resource = frame_resources->plane_resources_[i];
248 DCHECK_NE(dest_stride, 0); 112 const size_t width = VideoFrame::Columns(i, format, size.width());
249 DCHECK_LE(bytes_per_row, std::abs(dest_stride)); 113 const size_t height = VideoFrame::Rows(i, format, size.height());
250 DCHECK_LE(bytes_per_row, source_stride); 114 plane_resource.size = gfx::Size(width, height);
251 115
252 libyuv::CopyPlane(source + source_stride * first_row, source_stride, 116 const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i);
253 output + dest_stride * first_row, dest_stride, 117 plane_resource.gpu_memory_buffer = gpu_factories->AllocateGpuMemoryBuffer(
254 bytes_per_row, rows); 118 plane_resource.size, buffer_format,
255 } 119 gfx::BufferUsage::GPU_READ_CPU_READ_WRITE);
256 done.Run(); 120 if (!plane_resource.gpu_memory_buffer) {
257 } 121 DLOG(ERROR) << "Could not create GpuMemoryBuffer.";
258 122 return nullptr;
259 void CopyRowsToNV12Buffer(int first_row, 123 }
260 int rows, 124 unsigned texture_target =
261 int bytes_per_row, 125 gpu_factories->ImageTextureTarget(buffer_format);
262 const scoped_refptr<VideoFrame>& source_frame, 126 gles2->GenTextures(1, &plane_resource.texture_id);
263 uint8_t* dest_y, 127 gles2->BindTexture(texture_target, plane_resource.texture_id);
264 int dest_stride_y, 128 gles2->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
265 uint8_t* dest_uv, 129 gles2->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
266 int dest_stride_uv, 130 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
267 const base::Closure& done) { 131 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
268 TRACE_EVENT2("media", "CopyRowsToNV12Buffer", "bytes_per_row", bytes_per_row, 132 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name);
269 "rows", rows); 133 gles2->ProduceTextureCHROMIUM(texture_target,
270 if (dest_y && dest_uv) { 134 plane_resource.mailbox.name);
271 DCHECK_NE(dest_stride_y, 0); 135 }
272 DCHECK_NE(dest_stride_uv, 0); 136 return frame_resources;
273 DCHECK_LE(bytes_per_row, std::abs(dest_stride_y)); 137 }
274 DCHECK_LE(bytes_per_row, std::abs(dest_stride_uv)); 138
275 DCHECK_EQ(0, first_row % 2); 139 VideoPixelFormat format() const { return format_; }
276 140 const gfx::Size& size() const { return size_; }
277 libyuv::I420ToNV12( 141
278 source_frame->visible_data(VideoFrame::kYPlane) + 142 // Resource to represent a plane.
279 first_row * source_frame->stride(VideoFrame::kYPlane), 143 struct PlaneResource {
280 source_frame->stride(VideoFrame::kYPlane), 144 gfx::Size size;
281 source_frame->visible_data(VideoFrame::kUPlane) + 145 std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
282 first_row / 2 * source_frame->stride(VideoFrame::kUPlane), 146 unsigned texture_id = 0u;
283 source_frame->stride(VideoFrame::kUPlane), 147 unsigned image_id = 0u;
284 source_frame->visible_data(VideoFrame::kVPlane) + 148 gpu::Mailbox mailbox;
285 first_row / 2 * source_frame->stride(VideoFrame::kVPlane), 149 };
286 source_frame->stride(VideoFrame::kVPlane), 150 PlaneResource& GetPlaneResource(size_t plane) {
287 dest_y + first_row * dest_stride_y, dest_stride_y, 151 DCHECK(IsValidPlane(plane, format_));
288 dest_uv + first_row / 2 * dest_stride_uv, dest_stride_uv, bytes_per_row, 152 return plane_resources_[plane];
289 rows); 153 }
290 } 154
291 done.Run(); 155 void SetIsInUse(bool in_use) { in_use_ = in_use; }
292 } 156 bool IsInUse() const {
293 157 if (in_use_)
294 void CopyRowsToUYVYBuffer(int first_row, 158 return true;
295 int rows, 159 for (const PlaneResource& plane_resource : plane_resources_) {
296 int width, 160 if (plane_resource.gpu_memory_buffer->IsInUseByMacOSWindowServer()) {
297 const scoped_refptr<VideoFrame>& source_frame, 161 return true;
298 uint8_t* output, 162 }
299 int dest_stride, 163 }
300 const base::Closure& done) { 164 return false;
301 TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2, 165 }
302 "rows", rows); 166
303 if (output) { 167 bool IsCompatible(VideoPixelFormat format, const gfx::Size& size) {
304 DCHECK_NE(dest_stride, 0); 168 return format == format_ && size == size_;
305 DCHECK_LE(width, std::abs(dest_stride / 2)); 169 }
306 DCHECK_EQ(0, first_row % 2); 170
307 libyuv::I420ToUYVY( 171 void OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
308 source_frame->visible_data(VideoFrame::kYPlane) + 172 base::trace_event::ProcessMemoryDump* pmd) {
309 first_row * source_frame->stride(VideoFrame::kYPlane), 173 const uint64_t tracing_process_id =
310 source_frame->stride(VideoFrame::kYPlane), 174 base::trace_event::MemoryDumpManager::GetInstance()
311 source_frame->visible_data(VideoFrame::kUPlane) + 175 ->GetTracingProcessId();
312 first_row / 2 * source_frame->stride(VideoFrame::kUPlane), 176 const int kImportance = 2;
313 source_frame->stride(VideoFrame::kUPlane), 177 for (const PlaneResource& plane_resource : plane_resources_) {
314 source_frame->visible_data(VideoFrame::kVPlane) + 178 gfx::GpuMemoryBufferId buffer_id =
315 first_row / 2 * source_frame->stride(VideoFrame::kVPlane), 179 plane_resource.gpu_memory_buffer->GetId();
316 source_frame->stride(VideoFrame::kVPlane), 180 std::string dump_name = base::StringPrintf(
317 output + first_row * dest_stride, dest_stride, width, rows); 181 "media/video_frame_memory/buffer_%d", buffer_id.id);
318 } 182 base::trace_event::MemoryAllocatorDump* dump =
319 done.Run(); 183 pmd->CreateAllocatorDump(dump_name);
320 } 184 size_t buffer_size_in_bytes = gfx::BufferSizeForBufferFormat(
321 185 plane_resource.size, plane_resource.gpu_memory_buffer->GetFormat());
322 gfx::Size CodedSize(const scoped_refptr<VideoFrame>& video_frame, 186 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
323 VideoPixelFormat output_format) { 187 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
324 DCHECK(gfx::Rect(video_frame->coded_size()) 188 buffer_size_in_bytes);
325 .Contains(video_frame->visible_rect())); 189 dump->AddScalar("free_size",
326 DCHECK((video_frame->visible_rect().x() & 1) == 0); 190 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
327 gfx::Size output; 191 IsInUse() ? 0 : buffer_size_in_bytes);
328 switch (output_format) { 192 base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid =
329 case PIXEL_FORMAT_I420: 193 gfx::GetGpuMemoryBufferGUIDForTracing(tracing_process_id, buffer_id);
330 case PIXEL_FORMAT_NV12: 194 pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
331 DCHECK((video_frame->visible_rect().y() & 1) == 0); 195 pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid, kImportance);
332 output = gfx::Size((video_frame->visible_rect().width() + 1) & ~1, 196 }
333 (video_frame->visible_rect().height() + 1) & ~1); 197 }
334 break; 198
335 case PIXEL_FORMAT_UYVY: 199 private:
336 output = gfx::Size((video_frame->visible_rect().width() + 1) & ~1, 200 friend class base::RefCountedThreadSafe<FrameResources>;
337 video_frame->visible_rect().height()); 201 FrameResources(GpuVideoAcceleratorFactories* gpu_factories,
338 break; 202 VideoPixelFormat format,
339 default: 203 const gfx::Size& size)
204 : gpu_factories_(gpu_factories),
205 format_(format),
206 size_(size),
207 plane_resources_(NumPlanResources(format)) {}
208
209 ~FrameResources() {
210 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
211 gpu_factories_->GetGLContextLock());
212 DCHECK(lock);
213 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
214
215 for (PlaneResource& plane_resource : plane_resources_) {
216 if (plane_resource.image_id)
217 gles2->DestroyImageCHROMIUM(plane_resource.image_id);
218 if (plane_resource.texture_id)
219 gles2->DeleteTextures(1, &plane_resource.texture_id);
220 }
221 }
222
223 static int NumPlanResources(VideoPixelFormat format) {
224 return VideoFrame::NumPlanes(format) /
225 GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, 0);
226 }
227
228 GpuVideoAcceleratorFactories* const gpu_factories_;
229 VideoPixelFormat format_;
230 const gfx::Size size_;
231
232 std::vector<PlaneResource> plane_resources_;
233 bool in_use_ = true;
234 };
235
236 class GpuMemoryBufferVideoFrameFuture : public VideoFrameFuture {
237 public:
238 typedef base::Callback<void(const scoped_refptr<FrameResources>&,
239 const gpu::SyncToken&)>
240 ReturnFrameResourcesCB;
241
242 static std::unique_ptr<GpuMemoryBufferVideoFrameFuture> Create(
243 GpuVideoAcceleratorFactories* gpu_factories,
244 const scoped_refptr<FrameResources>& frame_resources,
245 const gfx::Rect& visible_rect,
246 const gfx::Size& natural_size,
247 base::TimeDelta timestamp,
248 const ReturnFrameResourcesCB& frame_resources_return_cb) {
249 std::unique_ptr<GpuMemoryBufferVideoFrameFuture> future(
250 new GpuMemoryBufferVideoFrameFuture(
251 gpu_factories, frame_resources, visible_rect, natural_size,
252 timestamp, frame_resources_return_cb));
253
254 const size_t num_planes = VideoFrame::NumPlanes(frame_resources->format());
255 for (size_t i = 0; i < num_planes;
256 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(
257 frame_resources->format(), i)) {
258 FrameResources::PlaneResource& plane_resource =
259 frame_resources->GetPlaneResource(i);
260 if (!plane_resource.gpu_memory_buffer->Map()) {
261 // Prevent the destructor from unmapping the |gpu_memory_buffer|.
262 future->released_video_frame_ = VideoFrame::CreateEOSFrame();
263 DLOG(ERROR) << "Could not Map() buffer";
264 return nullptr;
265 }
266 }
267
268 return future;
269 }
270
271 ~GpuMemoryBufferVideoFrameFuture() override {
272 if (released_video_frame_)
273 return;
274
275 // Decoder can be destructed before completing to decode this frame.
276 VideoPixelFormat format = frame_resources_->format();
277 const size_t num_planes = VideoFrame::NumPlanes(format);
278 for (size_t i = 0; i < num_planes;
279 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) {
280 FrameResources::PlaneResource& plane_resource =
281 frame_resources_->GetPlaneResource(i);
282 plane_resource.gpu_memory_buffer->Unmap();
283 }
284 }
285
286 scoped_refptr<VideoFrame> Release() override {
287 DCHECK(!released_video_frame_);
288 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
289 gpu_factories_->GetGLContextLock());
290 DCHECK(lock);
291 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
292
293 VideoPixelFormat format = frame_resources_->format();
294 const size_t num_planes = VideoFrame::NumPlanes(format);
295 const gfx::Size coded_size = frame_resources_->size();
296 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
297 // Set up the planes creating the mailboxes needed to refer to the textures.
298 for (size_t i = 0; i < num_planes;
299 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) {
300 FrameResources::PlaneResource& plane_resource =
301 frame_resources_->GetPlaneResource(i);
302 plane_resource.gpu_memory_buffer->Unmap();
303 const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i);
304 unsigned texture_target =
305 gpu_factories_->ImageTextureTarget(buffer_format);
306 // Bind the texture and create or rebind the image.
307 gles2->BindTexture(texture_target, plane_resource.texture_id);
308
309 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) {
310 plane_resource.image_id = gles2->CreateImageCHROMIUM(
311 plane_resource.gpu_memory_buffer->AsClientBuffer(),
312 plane_resource.gpu_memory_buffer->GetSize().width(),
313 plane_resource.gpu_memory_buffer->GetSize().height(),
314 ImageInternalFormat(format, i));
315 } else if (plane_resource.image_id) {
316 gles2->ReleaseTexImage2DCHROMIUM(texture_target,
317 plane_resource.image_id);
318 }
319 if (plane_resource.image_id)
320 gles2->BindTexImage2DCHROMIUM(texture_target, plane_resource.image_id);
321 mailbox_holders[i] = gpu::MailboxHolder(plane_resource.mailbox,
322 gpu::SyncToken(), texture_target);
323 }
324
325 // Insert a sync_token, this is needed to make sure that the textures the
326 // mailboxes refer to will be used only after all the previous commands
327 // posted
328 // in the command buffer have been processed.
329 const GLuint64 fence_sync = gles2->InsertFenceSyncCHROMIUM();
330 gles2->OrderingBarrierCHROMIUM();
331
332 gpu::SyncToken sync_token;
333 gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
334 for (size_t i = 0; i < num_planes;
335 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i))
336 mailbox_holders[i].sync_token = sync_token;
337
338 auto release_video_frame_callback = BindToCurrentLoop(
339 base::Bind(&GpuMemoryBufferVideoFrameFuture::VideoFrameReleased,
340 frame_resources_return_cb_, frame_resources_));
341
342 // Create the VideoFrame backed by native textures.
343 switch (format) {
344 case PIXEL_FORMAT_I420:
345 case PIXEL_FORMAT_YV12:
346 released_video_frame_ = VideoFrame::WrapYUV420NativeTextures(
347 mailbox_holders[VideoFrame::kYPlane],
348 mailbox_holders[VideoFrame::kUPlane],
349 mailbox_holders[VideoFrame::kVPlane], release_video_frame_callback,
350 coded_size, visible_rect_, natural_size_, timestamp_);
351 break;
352 case PIXEL_FORMAT_NV12:
353 case PIXEL_FORMAT_UYVY:
354 released_video_frame_ = VideoFrame::WrapNativeTexture(
355 format, mailbox_holders[VideoFrame::kYPlane],
356 release_video_frame_callback, coded_size, visible_rect_,
357 natural_size_, timestamp_);
358 released_video_frame_->metadata()->SetBoolean(
359 VideoFrameMetadata::ALLOW_OVERLAY, true);
360 break;
361 default:
362 NOTREACHED();
363 }
364
365 released_video_frame_->metadata()->SetBoolean(
366 VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true);
367
368 frame_resources_ = nullptr;
369 return released_video_frame_;
370 }
371
372 // static because GpuMemoryBufferVideoFrameFuture can be destroyed before
373 // this callback.
374 static void VideoFrameReleased(
375 const ReturnFrameResourcesCB& frame_resources_return_cb,
376 const scoped_refptr<FrameResources>& frame_resources,
377 const gpu::SyncToken& release_sync_token) {
378 frame_resources_return_cb.Run(frame_resources, release_sync_token);
379 }
380
381 uint8_t* data(size_t plane) const override {
382 DCHECK(!released_video_frame_);
383 DCHECK(IsValidPlane(plane, frame_resources_->format()));
384 switch (frame_resources_->format()) {
385 case PIXEL_FORMAT_I420:
386 case PIXEL_FORMAT_YV12:
387 case PIXEL_FORMAT_UYVY:
388 return static_cast<uint8_t*>(
389 frame_resources_->GetPlaneResource(plane).gpu_memory_buffer->memory(
390 0));
391 case PIXEL_FORMAT_NV12:
392 return static_cast<uint8_t*>(
393 frame_resources_->GetPlaneResource(0).gpu_memory_buffer->memory(
394 plane));
395 default:
396 NOTREACHED();
397 return 0;
398 }
399 }
400
401 int stride(size_t plane) const override {
402 DCHECK(!released_video_frame_);
403 DCHECK(IsValidPlane(plane, frame_resources_->format()));
404 switch (frame_resources_->format()) {
405 case PIXEL_FORMAT_I420:
406 case PIXEL_FORMAT_YV12:
407 case PIXEL_FORMAT_UYVY:
408 return frame_resources_->GetPlaneResource(plane)
409 .gpu_memory_buffer->stride(0);
410 case PIXEL_FORMAT_NV12:
411 return frame_resources_->GetPlaneResource(0).gpu_memory_buffer->stride(
412 plane);
413 default:
414 NOTREACHED();
415 return 0;
416 }
417 }
418
419 const gfx::Size& coded_size() const override {
420 DCHECK(!released_video_frame_);
421 return frame_resources_->size();
422 }
423
424 private:
425 GpuMemoryBufferVideoFrameFuture(
426 GpuVideoAcceleratorFactories* gpu_factories,
427 const scoped_refptr<FrameResources>& frame_resources,
428 const gfx::Rect& visible_rect,
429 const gfx::Size& natural_size,
430 base::TimeDelta timestamp,
431 const ReturnFrameResourcesCB& frame_resources_return_cb)
432 : VideoFrameFuture(),
433 gpu_factories_(gpu_factories),
434 frame_resources_(frame_resources),
435 visible_rect_(visible_rect),
436 natural_size_(natural_size),
437 timestamp_(timestamp),
438 frame_resources_return_cb_(frame_resources_return_cb) {}
439
440 GpuVideoAcceleratorFactories* const gpu_factories_;
441 scoped_refptr<FrameResources> frame_resources_;
442
443 const gfx::Rect visible_rect_;
444 const gfx::Size natural_size_;
445 base::TimeDelta timestamp_;
446 ReturnFrameResourcesCB frame_resources_return_cb_;
447
448 // FrameResources must be recycled after GpuMemoryBufferVideoFrameFuture is
449 // destructed.
450 scoped_refptr<VideoFrame> released_video_frame_;
451 };
452
453 } // unnamed namespace
454
455 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
456 class GpuMemoryBufferVideoFramePool::PoolImpl
457 : public base::RefCountedThreadSafe<
458 GpuMemoryBufferVideoFramePool::PoolImpl>,
459 public base::trace_event::MemoryDumpProvider {
460 public:
461 // |media_task_runner| is the media task runner associated with the
462 // GL context provided by |gpu_factories|
463 // |gpu_factories| is an interface to GPU related operation and can be
464 // null if a GL context is not available.
465 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
466 GpuVideoAcceleratorFactories* gpu_factories)
467 : media_task_runner_(media_task_runner), gpu_factories_(gpu_factories) {
468 DCHECK(media_task_runner_);
469 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
470 this, "GpuMemoryBufferVideoFramePool", media_task_runner_);
471 }
472
473 std::unique_ptr<VideoFrameFuture> CreateFrame(VideoPixelFormat format,
474 const gfx::Size& coded_size,
475 const gfx::Rect& visible_rect,
476 const gfx::Size& natural_size,
477 base::TimeDelta timestamp) {
478 DCHECK(media_task_runner_->BelongsToCurrentThread());
479 if (!IsSupported(format)) {
340 NOTREACHED(); 480 NOTREACHED();
341 } 481 return nullptr;
342 DCHECK(gfx::Rect(video_frame->coded_size()).Contains(gfx::Rect(output))); 482 }
343 return output; 483
344 } 484 gfx::Size new_coded_size = CodedSize(coded_size, format);
345 } // unnamed namespace 485 scoped_refptr<FrameResources> frame_resources =
346 486 GetOrCreateFrameResources(format, new_coded_size);
347 // Creates a VideoFrame backed by native textures starting from a software 487 if (!frame_resources) {
348 // VideoFrame. 488 return nullptr;
349 // The data contained in |video_frame| is copied into the VideoFrame passed to 489 }
350 // |frame_ready_cb|. 490
351 // This has to be called on the thread where |media_task_runner_| is current. 491 return GpuMemoryBufferVideoFrameFuture::Create(
352 void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame( 492 gpu_factories_, frame_resources, visible_rect, natural_size, timestamp,
353 const scoped_refptr<VideoFrame>& video_frame, 493 base::Bind(&PoolImpl::FrameResourcesReturned, this));
354 const FrameReadyCB& frame_ready_cb) { 494 }
355 DCHECK(media_task_runner_->BelongsToCurrentThread()); 495
356 // Lazily initialize output_format_ since VideoFrameOutputFormat() has to be 496 // Put back the resources in the pool.
357 // called on the media_thread while this object might be instantiated on any. 497 void FrameResourcesReturned(
358 if (output_format_ == PIXEL_FORMAT_UNKNOWN) 498 const scoped_refptr<FrameResources>& frame_resources,
359 output_format_ = gpu_factories_->VideoFrameOutputFormat(); 499 const gpu::SyncToken& release_sync_token) {
360 500 DCHECK(media_task_runner_->BelongsToCurrentThread());
361 if (output_format_ == PIXEL_FORMAT_UNKNOWN) { 501 auto it = std::find(resources_pool_.begin(), resources_pool_.end(),
362 frame_ready_cb.Run(video_frame); 502 frame_resources);
363 return; 503 DCHECK(it != resources_pool_.end());
364 } 504 // We want the pool to behave in a FIFO way.
365 switch (video_frame->format()) { 505 // This minimizes the chances of locking the buffer that might be
366 // Supported cases. 506 // still needed for drawing.
367 case PIXEL_FORMAT_YV12: 507 std::swap(*it, resources_pool_.back());
368 case PIXEL_FORMAT_I420: 508 frame_resources->SetIsInUse(false);
369 break; 509 }
370 // Unsupported cases. 510
371 case PIXEL_FORMAT_YV12A: 511 bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
372 case PIXEL_FORMAT_YV16: 512 base::trace_event::ProcessMemoryDump* pmd) override {
373 case PIXEL_FORMAT_YV24: 513 for (auto frame_resources : resources_pool_) {
374 case PIXEL_FORMAT_NV12: 514 frame_resources->OnMemoryDump(args, pmd);
375 case PIXEL_FORMAT_NV21: 515 }
376 case PIXEL_FORMAT_UYVY: 516 return true;
377 case PIXEL_FORMAT_YUY2: 517 }
378 case PIXEL_FORMAT_ARGB: 518
379 case PIXEL_FORMAT_XRGB: 519 size_t GetPoolSizeForTesting() const { return resources_pool_.size(); }
380 case PIXEL_FORMAT_RGB24: 520
381 case PIXEL_FORMAT_RGB32: 521 private:
382 case PIXEL_FORMAT_MJPEG: 522 friend class base::RefCountedThreadSafe<
383 case PIXEL_FORMAT_MT21: 523 GpuMemoryBufferVideoFramePool::PoolImpl>;
384 case PIXEL_FORMAT_YUV420P9: 524
385 case PIXEL_FORMAT_YUV422P9: 525 // Destroy all the resources posting one task per FrameResources
386 case PIXEL_FORMAT_YUV444P9: 526 // to the |media_task_runner_|.
387 case PIXEL_FORMAT_YUV420P10: 527 ~PoolImpl() override {
388 case PIXEL_FORMAT_YUV422P10: 528 media_task_runner_->PostTask(
389 case PIXEL_FORMAT_YUV444P10: 529 FROM_HERE, base::Bind(&DeleteFrameResources,
390 case PIXEL_FORMAT_UNKNOWN: 530 base::Passed(std::move(resources_pool_))));
391 frame_ready_cb.Run(video_frame); 531 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
392 return; 532 this);
393 } 533 }
394 534
395 const gfx::Size coded_size = CodedSize(video_frame, output_format_); 535 static void DeleteFrameResources(
396 // Acquire resources. Incompatible ones will be dropped from the pool. 536 std::list<scoped_refptr<FrameResources>> resources_pool) {
397 FrameResources* frame_resources = 537 resources_pool.clear();
398 GetOrCreateFrameResources(coded_size, output_format_); 538 }
399 if (!frame_resources) { 539
400 frame_ready_cb.Run(video_frame); 540 // Get the resources needed for a frame out of the pool, or create them if
401 return; 541 // necessary.
402 } 542 // This also drops the LRU resources that can't be reuse for this frame.
403 543 scoped_refptr<FrameResources> GetOrCreateFrameResources(
404 worker_task_runner_->PostTask( 544 VideoPixelFormat format,
405 FROM_HERE, base::Bind(&PoolImpl::CopyVideoFrameToGpuMemoryBuffers, this, 545 const gfx::Size& size) {
406 video_frame, frame_resources, frame_ready_cb)); 546 auto it = resources_pool_.begin();
407 } 547 while (it != resources_pool_.end()) {
408 548 scoped_refptr<FrameResources> frame_resources = *it;
409 bool GpuMemoryBufferVideoFramePool::PoolImpl::OnMemoryDump( 549 if (!frame_resources->IsInUse()) {
410 const base::trace_event::MemoryDumpArgs& args, 550 if (frame_resources->IsCompatible(format, size)) {
411 base::trace_event::ProcessMemoryDump* pmd) { 551 frame_resources->SetIsInUse(true);
412 const uint64_t tracing_process_id = 552 return frame_resources;
413 base::trace_event::MemoryDumpManager::GetInstance() 553 } else {
414 ->GetTracingProcessId(); 554 resources_pool_.erase(it++);
415 const int kImportance = 2; 555 }
416 for (const FrameResources* frame_resources : resources_pool_) { 556 } else {
417 for (const PlaneResource& plane_resource : 557 it++;
418 frame_resources->plane_resources) {
419 if (plane_resource.gpu_memory_buffer) {
420 gfx::GpuMemoryBufferId buffer_id =
421 plane_resource.gpu_memory_buffer->GetId();
422 std::string dump_name = base::StringPrintf(
423 "media/video_frame_memory/buffer_%d", buffer_id.id);
424 base::trace_event::MemoryAllocatorDump* dump =
425 pmd->CreateAllocatorDump(dump_name);
426 size_t buffer_size_in_bytes = gfx::BufferSizeForBufferFormat(
427 plane_resource.size, plane_resource.gpu_memory_buffer->GetFormat());
428 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
429 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
430 buffer_size_in_bytes);
431 dump->AddScalar("free_size",
432 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
433 frame_resources->IsInUse() ? 0 : buffer_size_in_bytes);
434 base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid =
435 gfx::GetGpuMemoryBufferGUIDForTracing(tracing_process_id,
436 buffer_id);
437 pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
438 pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid, kImportance);
439 } 558 }
440 } 559 }
441 } 560
442 return true; 561 scoped_refptr<FrameResources> frame_resources =
443 } 562 FrameResources::Create(gpu_factories_, format, size);
444 563 resources_pool_.push_back(frame_resources);
445 void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone( 564 return frame_resources;
446 const scoped_refptr<VideoFrame>& video_frame, 565 }
447 FrameResources* frame_resources, 566
448 const FrameReadyCB& frame_ready_cb) { 567 static bool IsSupported(VideoPixelFormat format) {
449 for (const auto& plane_resource : frame_resources->plane_resources) { 568 switch (format) {
450 if (plane_resource.gpu_memory_buffer) 569 // TODO(dshwang): support more format. crbug.com/356871
451 plane_resource.gpu_memory_buffer->Unmap(); 570 case PIXEL_FORMAT_I420:
452 } 571 case PIXEL_FORMAT_YV12:
453 572 case PIXEL_FORMAT_NV12:
454 media_task_runner_->PostTask( 573 case PIXEL_FORMAT_UYVY:
455 FROM_HERE, 574 return true;
456 base::Bind(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources, this, 575 default:
457 video_frame, frame_resources, frame_ready_cb)); 576 return false;
458 } 577 }
459 578 }
460 // Copies |video_frame| into |frame_resources| asynchronously, posting n tasks 579
461 // that will be synchronized by a barrier. 580 // Task runner associated to the GL context provided by |gpu_factories_|.
462 // After the barrier is passed OnCopiesDone will be called. 581 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
463 void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers( 582
464 const scoped_refptr<VideoFrame>& video_frame, 583 // Interface to GPU related operations.
465 FrameResources* frame_resources, 584 GpuVideoAcceleratorFactories* gpu_factories_;
466 const FrameReadyCB& frame_ready_cb) { 585
467 // Compute the number of tasks to post and create the barrier. 586 // Pool of resources.
468 const size_t num_planes = VideoFrame::NumPlanes(output_format_); 587 std::list<scoped_refptr<FrameResources>> resources_pool_;
469 const size_t planes_per_copy = PlanesPerCopy(output_format_); 588
470 const gfx::Size coded_size = CodedSize(video_frame, output_format_); 589 DISALLOW_COPY_AND_ASSIGN(PoolImpl);
471 size_t copies = 0; 590 };
472 for (size_t i = 0; i < num_planes; i += planes_per_copy) {
473 const int rows = VideoFrame::Rows(i, output_format_, coded_size.height());
474 const int rows_per_copy =
475 RowsPerCopy(i, output_format_, coded_size.width());
476 copies += rows / rows_per_copy;
477 if (rows % rows_per_copy)
478 ++copies;
479 }
480 const base::Closure copies_done =
481 base::Bind(&PoolImpl::OnCopiesDone, this, video_frame, frame_resources,
482 frame_ready_cb);
483 const base::Closure barrier = base::BarrierClosure(copies, copies_done);
484
485 // Post all the async tasks.
486 for (size_t i = 0; i < num_planes; i += planes_per_copy) {
487 gfx::GpuMemoryBuffer* buffer =
488 frame_resources->plane_resources[i].gpu_memory_buffer.get();
489
490 if (!buffer || !buffer->Map()) {
491 DLOG(ERROR) << "Could not get or Map() buffer";
492 return;
493 }
494 DCHECK_EQ(planes_per_copy,
495 gfx::NumberOfPlanesForBufferFormat(buffer->GetFormat()));
496
497 const int rows = VideoFrame::Rows(i, output_format_, coded_size.height());
498 const int rows_per_copy =
499 RowsPerCopy(i, output_format_, coded_size.width());
500
501 for (int row = 0; row < rows; row += rows_per_copy) {
502 const int rows_to_copy = std::min(rows_per_copy, rows - row);
503 switch (output_format_) {
504 case PIXEL_FORMAT_I420: {
505 const int bytes_per_row =
506 VideoFrame::RowBytes(i, output_format_, coded_size.width());
507 worker_task_runner_->PostTask(
508 FROM_HERE, base::Bind(&CopyRowsToI420Buffer, row, rows_to_copy,
509 bytes_per_row, video_frame->visible_data(i),
510 video_frame->stride(i),
511 static_cast<uint8_t*>(buffer->memory(0)),
512 buffer->stride(0), barrier));
513 break;
514 }
515 case PIXEL_FORMAT_NV12:
516 worker_task_runner_->PostTask(
517 FROM_HERE, base::Bind(&CopyRowsToNV12Buffer, row, rows_to_copy,
518 coded_size.width(), video_frame,
519 static_cast<uint8_t*>(buffer->memory(0)),
520 buffer->stride(0),
521 static_cast<uint8_t*>(buffer->memory(1)),
522 buffer->stride(1), barrier));
523 break;
524 case PIXEL_FORMAT_UYVY:
525 worker_task_runner_->PostTask(
526 FROM_HERE, base::Bind(&CopyRowsToUYVYBuffer, row, rows_to_copy,
527 coded_size.width(), video_frame,
528 static_cast<uint8_t*>(buffer->memory(0)),
529 buffer->stride(0), barrier));
530 break;
531 default:
532 NOTREACHED();
533 }
534 }
535 }
536 }
537
538 void GpuMemoryBufferVideoFramePool::PoolImpl::
539 BindAndCreateMailboxesHardwareFrameResources(
540 const scoped_refptr<VideoFrame>& video_frame,
541 FrameResources* frame_resources,
542 const FrameReadyCB& frame_ready_cb) {
543 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
544 gpu_factories_->GetGLContextLock());
545 if (!lock) {
546 frame_ready_cb.Run(video_frame);
547 return;
548 }
549 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
550
551 const size_t num_planes = VideoFrame::NumPlanes(output_format_);
552 const size_t planes_per_copy = PlanesPerCopy(output_format_);
553 const gfx::Size coded_size = CodedSize(video_frame, output_format_);
554 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
555 // Set up the planes creating the mailboxes needed to refer to the textures.
556 for (size_t i = 0; i < num_planes; i += planes_per_copy) {
557 PlaneResource& plane_resource = frame_resources->plane_resources[i];
558 const gfx::BufferFormat buffer_format =
559 GpuMemoryBufferFormat(output_format_, i);
560 unsigned texture_target = gpu_factories_->ImageTextureTarget(buffer_format);
561 // Bind the texture and create or rebind the image.
562 gles2->BindTexture(texture_target, plane_resource.texture_id);
563
564 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) {
565 const size_t width =
566 VideoFrame::Columns(i, output_format_, coded_size.width());
567 const size_t height =
568 VideoFrame::Rows(i, output_format_, coded_size.height());
569 plane_resource.image_id = gles2->CreateImageCHROMIUM(
570 plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height,
571 ImageInternalFormat(output_format_, i));
572 } else if (plane_resource.image_id) {
573 gles2->ReleaseTexImage2DCHROMIUM(texture_target, plane_resource.image_id);
574 }
575 if (plane_resource.image_id)
576 gles2->BindTexImage2DCHROMIUM(texture_target, plane_resource.image_id);
577 mailbox_holders[i] = gpu::MailboxHolder(plane_resource.mailbox,
578 gpu::SyncToken(), texture_target);
579 }
580
581 // Insert a sync_token, this is needed to make sure that the textures the
582 // mailboxes refer to will be used only after all the previous commands posted
583 // in the command buffer have been processed.
584 const GLuint64 fence_sync = gles2->InsertFenceSyncCHROMIUM();
585 gles2->OrderingBarrierCHROMIUM();
586
587 gpu::SyncToken sync_token;
588 gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
589 for (size_t i = 0; i < num_planes; i += planes_per_copy)
590 mailbox_holders[i].sync_token = sync_token;
591
592 scoped_refptr<VideoFrame> frame;
593
594 auto release_mailbox_callback = BindToCurrentLoop(
595 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources));
596
597 // Create the VideoFrame backed by native textures.
598 gfx::Size visible_size = video_frame->visible_rect().size();
599 switch (output_format_) {
600 case PIXEL_FORMAT_I420:
601 frame = VideoFrame::WrapYUV420NativeTextures(
602 mailbox_holders[VideoFrame::kYPlane],
603 mailbox_holders[VideoFrame::kUPlane],
604 mailbox_holders[VideoFrame::kVPlane], release_mailbox_callback,
605 coded_size, gfx::Rect(visible_size), video_frame->natural_size(),
606 video_frame->timestamp());
607 if (frame &&
608 video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY))
609 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
610 break;
611 case PIXEL_FORMAT_NV12:
612 case PIXEL_FORMAT_UYVY:
613 frame = VideoFrame::WrapNativeTexture(
614 output_format_, mailbox_holders[VideoFrame::kYPlane],
615 release_mailbox_callback, coded_size, gfx::Rect(visible_size),
616 video_frame->natural_size(), video_frame->timestamp());
617 if (frame)
618 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
619 break;
620 default:
621 NOTREACHED();
622 }
623
624 if (!frame) {
625 release_mailbox_callback.Run(gpu::SyncToken());
626 frame_ready_cb.Run(video_frame);
627 return;
628 }
629
630 base::TimeTicks render_time;
631 if (video_frame->metadata()->GetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
632 &render_time)) {
633 frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
634 render_time);
635 }
636
637 frame->metadata()->SetBoolean(VideoFrameMetadata::READ_LOCK_FENCES_ENABLED,
638 true);
639
640 frame_ready_cb.Run(frame);
641 }
642
643 // Destroy all the resources posting one task per FrameResources
644 // to the |media_task_runner_|.
645 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() {
646 // Delete all the resources on the media thread.
647 while (!resources_pool_.empty()) {
648 FrameResources* frame_resources = resources_pool_.front();
649 resources_pool_.pop_front();
650 media_task_runner_->PostTask(
651 FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_,
652 base::Owned(frame_resources)));
653 }
654 }
655
656 // Tries to find the resources in the pool or create them.
657 // Incompatible resources will be dropped.
658 GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources*
659 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
660 const gfx::Size& size,
661 VideoPixelFormat format) {
662 auto it = resources_pool_.begin();
663 while (it != resources_pool_.end()) {
664 FrameResources* frame_resources = *it;
665 if (!frame_resources->IsInUse()) {
666 if (AreFrameResourcesCompatible(frame_resources, size)) {
667 frame_resources->SetIsInUse(true);
668 return frame_resources;
669 } else {
670 resources_pool_.erase(it++);
671 DeleteFrameResources(gpu_factories_, frame_resources);
672 delete frame_resources;
673 }
674 } else {
675 it++;
676 }
677 }
678
679 // Create the resources.
680 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
681 gpu_factories_->GetGLContextLock());
682 if (!lock)
683 return nullptr;
684
685 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
686 gles2->ActiveTexture(GL_TEXTURE0);
687 size_t num_planes = VideoFrame::NumPlanes(format);
688 FrameResources* frame_resources = new FrameResources(size);
689 resources_pool_.push_back(frame_resources);
690 for (size_t i = 0; i < num_planes; i += PlanesPerCopy(format)) {
691 PlaneResource& plane_resource = frame_resources->plane_resources[i];
692 const size_t width = VideoFrame::Columns(i, format, size.width());
693 const size_t height = VideoFrame::Rows(i, format, size.height());
694 plane_resource.size = gfx::Size(width, height);
695
696 const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i);
697 plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer(
698 plane_resource.size, buffer_format,
699 gfx::BufferUsage::GPU_READ_CPU_READ_WRITE);
700
701 unsigned texture_target = gpu_factories_->ImageTextureTarget(buffer_format);
702 gles2->GenTextures(1, &plane_resource.texture_id);
703 gles2->BindTexture(texture_target, plane_resource.texture_id);
704 gles2->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
705 gles2->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
706 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
707 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
708 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name);
709 gles2->ProduceTextureCHROMIUM(texture_target, plane_resource.mailbox.name);
710 }
711 return frame_resources;
712 }
713
714 // static
715 void GpuMemoryBufferVideoFramePool::PoolImpl::DeleteFrameResources(
716 GpuVideoAcceleratorFactories* gpu_factories,
717 FrameResources* frame_resources) {
718 // TODO(dcastagna): As soon as the context lost is dealt with in media,
719 // make sure that we won't execute this callback (use a weak pointer to
720 // the old context).
721
722 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
723 gpu_factories->GetGLContextLock());
724 if (!lock)
725 return;
726 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
727
728 for (PlaneResource& plane_resource : frame_resources->plane_resources) {
729 if (plane_resource.image_id)
730 gles2->DestroyImageCHROMIUM(plane_resource.image_id);
731 if (plane_resource.texture_id)
732 gles2->DeleteTextures(1, &plane_resource.texture_id);
733 }
734 }
735
736 // Called when a VideoFrame is no longer referenced.
737 // Put back the resources in the pool.
738 void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased(
739 FrameResources* frame_resources,
740 const gpu::SyncToken& release_sync_token) {
741 DCHECK(media_task_runner_->BelongsToCurrentThread());
742 auto it = std::find(resources_pool_.begin(), resources_pool_.end(),
743 frame_resources);
744 DCHECK(it != resources_pool_.end());
745 // We want the pool to behave in a FIFO way.
746 // This minimizes the chances of locking the buffer that might be
747 // still needed for drawing.
748 std::swap(*it, resources_pool_.back());
749 frame_resources->SetIsInUse(false);
750 }
751
752 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool() {}
753 591
754 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool( 592 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool(
755 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, 593 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
756 const scoped_refptr<base::TaskRunner>& worker_task_runner,
757 GpuVideoAcceleratorFactories* gpu_factories) 594 GpuVideoAcceleratorFactories* gpu_factories)
758 : pool_impl_( 595 : pool_impl_(new PoolImpl(media_task_runner, gpu_factories)) {}
759 new PoolImpl(media_task_runner, worker_task_runner, gpu_factories)) {
760 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
761 pool_impl_.get(), "GpuMemoryBufferVideoFramePool", media_task_runner);
762 }
763 596
764 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() { 597 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() {
765 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( 598 }
766 pool_impl_.get()); 599
767 } 600 std::unique_ptr<VideoFrameFuture> GpuMemoryBufferVideoFramePool::CreateFrame(
768 601 VideoPixelFormat format,
769 void GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame( 602 const gfx::Size& coded_size,
770 const scoped_refptr<VideoFrame>& video_frame, 603 const gfx::Rect& visible_rect,
771 const FrameReadyCB& frame_ready_cb) { 604 const gfx::Size& natural_size,
772 DCHECK(video_frame); 605 base::TimeDelta timestamp) {
773 pool_impl_->CreateHardwareFrame(video_frame, frame_ready_cb); 606 return pool_impl_->CreateFrame(format, coded_size, visible_rect, natural_size,
607 timestamp);
608 }
609
610 size_t GpuMemoryBufferVideoFramePool::GetPoolSizeForTesting() const {
611 return pool_impl_->GetPoolSizeForTesting();
612 }
613
614 // static
615 size_t GpuMemoryBufferVideoFramePool::PlanesPerCopy(VideoPixelFormat format,
616 size_t plane) {
617 return gfx::NumberOfPlanesForBufferFormat(
618 GpuMemoryBufferFormat(format, plane));
774 } 619 }
775 620
776 } // namespace media 621 } // namespace media
OLDNEW
« no previous file with comments | « media/video/gpu_memory_buffer_video_frame_pool.h ('k') | media/video/gpu_memory_buffer_video_frame_pool_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698