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