OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/video/gpu_memory_buffer_video_frame_copier.h" |
| 6 |
| 7 #include "base/barrier_closure.h" |
| 8 #include "base/bind.h" |
| 9 #include "media/renderers/gpu_video_accelerator_factories.h" |
| 10 #include "media/video/gpu_memory_buffer_video_frame_pool.h" |
| 11 #include "third_party/libyuv/include/libyuv.h" |
| 12 |
| 13 namespace media { |
| 14 |
| 15 class GpuMemoryBufferVideoFrameCopier::CopierImpl |
| 16 : public base::RefCountedThreadSafe< |
| 17 GpuMemoryBufferVideoFrameCopier::CopierImpl> { |
| 18 public: |
| 19 // |media_task_runner| is the media task runner associated with the |
| 20 // GL context provided by |gpu_factories| |
| 21 // |worker_task_runner| is a task runner used to asynchronously copy |
| 22 // video frame's planes. |
| 23 // |gpu_factories| is an interface to GPU related operation and can be |
| 24 // null if a GL context is not available. |
| 25 CopierImpl( |
| 26 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| 27 const scoped_refptr<base::TaskRunner>& worker_task_runner, |
| 28 GpuVideoAcceleratorFactories* gpu_factories) |
| 29 : media_task_runner_(media_task_runner), |
| 30 worker_task_runner_(worker_task_runner), |
| 31 gpu_factories_(gpu_factories), |
| 32 video_frame_pool_(new GpuMemoryBufferVideoFramePool(media_task_runner_, |
| 33 gpu_factories)), |
| 34 output_format_(PIXEL_FORMAT_UNKNOWN) { |
| 35 DCHECK(media_task_runner_); |
| 36 DCHECK(worker_task_runner_); |
| 37 } |
| 38 |
| 39 // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame |
| 40 // backed by native textures if possible. |
| 41 // The data contained in video_frame is copied into the returned frame |
| 42 // asynchronously posting tasks to |worker_task_runner_|, while |
| 43 // |frame_ready_cb| will be called on |media_task_runner_| once all the data |
| 44 // has been copied. |
| 45 void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame, |
| 46 const FrameReadyCB& cb); |
| 47 |
| 48 private: |
| 49 friend class base::RefCountedThreadSafe< |
| 50 GpuMemoryBufferVideoFrameCopier::CopierImpl>; |
| 51 ~CopierImpl(); |
| 52 |
| 53 // Copy |video_frame| data into |video_frame_future| |
| 54 // and calls |frame_ready_cb| when done. |
| 55 void CopyVideoFrameToGpuMemoryBuffers( |
| 56 const scoped_refptr<VideoFrame>& video_frame, |
| 57 std::unique_ptr<VideoFrameFuture> video_frame_future, |
| 58 const FrameReadyCB& frame_ready_cb); |
| 59 |
| 60 // Called when all the data has been copied. |
| 61 void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame, |
| 62 std::unique_ptr<VideoFrameFuture> video_frame_future, |
| 63 const FrameReadyCB& frame_ready_cb); |
| 64 |
| 65 // Create new VideoFrame by |video_frame_future| and calls |frame_ready_cb|. |
| 66 // This has to be run on |media_task_runner_| where |frame_ready_cb| will also |
| 67 // be run. |
| 68 void CreateGpuMemoryBufferVideoFrame( |
| 69 const scoped_refptr<VideoFrame>& video_frame, |
| 70 std::unique_ptr<VideoFrameFuture> video_frame_future, |
| 71 const FrameReadyCB& frame_ready_cb); |
| 72 |
| 73 // Task runner associated to the GL context provided by |gpu_factories_|. |
| 74 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
| 75 // Task runner used to asynchronously copy planes. |
| 76 scoped_refptr<base::TaskRunner> worker_task_runner_; |
| 77 |
| 78 // Interface to GPU related operations. |
| 79 GpuVideoAcceleratorFactories* gpu_factories_; |
| 80 |
| 81 std::unique_ptr<GpuMemoryBufferVideoFramePool> video_frame_pool_; |
| 82 |
| 83 // TODO(dcastagna): change the following type from VideoPixelFormat to |
| 84 // BufferFormat. |
| 85 VideoPixelFormat output_format_; |
| 86 |
| 87 DISALLOW_COPY_AND_ASSIGN(CopierImpl); |
| 88 }; |
| 89 |
| 90 namespace { |
| 91 |
| 92 // VideoFrame copies to GpuMemoryBuffers will be split in copies where the |
| 93 // output size is |kBytesPerCopyTarget| bytes and run in parallel. |
| 94 const size_t kBytesPerCopyTarget = 1024 * 1024; // 1MB |
| 95 |
| 96 // The number of output rows to be copied in each iteration. |
| 97 int RowsPerCopy(size_t plane, VideoPixelFormat format, int width) { |
| 98 int bytes_per_row = VideoFrame::RowBytes(plane, format, width); |
| 99 if (format == PIXEL_FORMAT_NV12) { |
| 100 DCHECK_EQ(0u, plane); |
| 101 bytes_per_row += VideoFrame::RowBytes(1, format, width); |
| 102 } |
| 103 // Copy an even number of lines, and at least one. |
| 104 return std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1); |
| 105 } |
| 106 |
| 107 void CopyRowsToI420Buffer(int first_row, |
| 108 int rows, |
| 109 int bytes_per_row, |
| 110 const uint8_t* source, |
| 111 int source_stride, |
| 112 uint8_t* output, |
| 113 int dest_stride, |
| 114 const base::Closure& done) { |
| 115 TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row, |
| 116 "rows", rows); |
| 117 if (output) { |
| 118 DCHECK_NE(dest_stride, 0); |
| 119 DCHECK_LE(bytes_per_row, std::abs(dest_stride)); |
| 120 DCHECK_LE(bytes_per_row, source_stride); |
| 121 |
| 122 libyuv::CopyPlane(source + source_stride * first_row, source_stride, |
| 123 output + dest_stride * first_row, dest_stride, |
| 124 bytes_per_row, rows); |
| 125 } |
| 126 done.Run(); |
| 127 } |
| 128 |
| 129 void CopyRowsToNV12Buffer(int first_row, |
| 130 int rows, |
| 131 int bytes_per_row, |
| 132 const scoped_refptr<VideoFrame>& source_frame, |
| 133 uint8_t* dest_y, |
| 134 int dest_stride_y, |
| 135 uint8_t* dest_uv, |
| 136 int dest_stride_uv, |
| 137 const base::Closure& done) { |
| 138 TRACE_EVENT2("media", "CopyRowsToNV12Buffer", "bytes_per_row", bytes_per_row, |
| 139 "rows", rows); |
| 140 if (dest_y && dest_uv) { |
| 141 DCHECK_NE(dest_stride_y, 0); |
| 142 DCHECK_NE(dest_stride_uv, 0); |
| 143 DCHECK_LE(bytes_per_row, std::abs(dest_stride_y)); |
| 144 DCHECK_LE(bytes_per_row, std::abs(dest_stride_uv)); |
| 145 DCHECK_EQ(0, first_row % 2); |
| 146 |
| 147 libyuv::I420ToNV12( |
| 148 source_frame->visible_data(VideoFrame::kYPlane) + |
| 149 first_row * source_frame->stride(VideoFrame::kYPlane), |
| 150 source_frame->stride(VideoFrame::kYPlane), |
| 151 source_frame->visible_data(VideoFrame::kUPlane) + |
| 152 first_row / 2 * source_frame->stride(VideoFrame::kUPlane), |
| 153 source_frame->stride(VideoFrame::kUPlane), |
| 154 source_frame->visible_data(VideoFrame::kVPlane) + |
| 155 first_row / 2 * source_frame->stride(VideoFrame::kVPlane), |
| 156 source_frame->stride(VideoFrame::kVPlane), |
| 157 dest_y + first_row * dest_stride_y, dest_stride_y, |
| 158 dest_uv + first_row / 2 * dest_stride_uv, dest_stride_uv, bytes_per_row, |
| 159 rows); |
| 160 } |
| 161 done.Run(); |
| 162 } |
| 163 |
| 164 void CopyRowsToUYVYBuffer(int first_row, |
| 165 int rows, |
| 166 int width, |
| 167 const scoped_refptr<VideoFrame>& source_frame, |
| 168 uint8_t* output, |
| 169 int dest_stride, |
| 170 const base::Closure& done) { |
| 171 TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2, |
| 172 "rows", rows); |
| 173 if (output) { |
| 174 DCHECK_NE(dest_stride, 0); |
| 175 DCHECK_LE(width, std::abs(dest_stride / 2)); |
| 176 DCHECK_EQ(0, first_row % 2); |
| 177 libyuv::I420ToUYVY( |
| 178 source_frame->visible_data(VideoFrame::kYPlane) + |
| 179 first_row * source_frame->stride(VideoFrame::kYPlane), |
| 180 source_frame->stride(VideoFrame::kYPlane), |
| 181 source_frame->visible_data(VideoFrame::kUPlane) + |
| 182 first_row / 2 * source_frame->stride(VideoFrame::kUPlane), |
| 183 source_frame->stride(VideoFrame::kUPlane), |
| 184 source_frame->visible_data(VideoFrame::kVPlane) + |
| 185 first_row / 2 * source_frame->stride(VideoFrame::kVPlane), |
| 186 source_frame->stride(VideoFrame::kVPlane), |
| 187 output + first_row * dest_stride, dest_stride, width, rows); |
| 188 } |
| 189 done.Run(); |
| 190 } |
| 191 |
| 192 } // unnamed namespace |
| 193 |
| 194 // Creates a VideoFrame backed by native textures starting from a software |
| 195 // VideoFrame. |
| 196 // The data contained in |video_frame| is copied into the VideoFrame passed to |
| 197 // |frame_ready_cb|. |
| 198 // This has to be called on the thread where |media_task_runner_| is current. |
| 199 void GpuMemoryBufferVideoFrameCopier::CopierImpl::CreateHardwareFrame( |
| 200 const scoped_refptr<VideoFrame>& video_frame, |
| 201 const FrameReadyCB& frame_ready_cb) { |
| 202 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 203 // Lazily initialize output_format_ since VideoFrameOutputFormat() has to be |
| 204 // called on the media_thread while this object might be instantiated on any. |
| 205 if (output_format_ == PIXEL_FORMAT_UNKNOWN) |
| 206 output_format_ = gpu_factories_->VideoFrameOutputFormat(); |
| 207 |
| 208 if (output_format_ == PIXEL_FORMAT_UNKNOWN) { |
| 209 frame_ready_cb.Run(video_frame); |
| 210 return; |
| 211 } |
| 212 |
| 213 if (video_frame->HasTextures()) { |
| 214 frame_ready_cb.Run(video_frame); |
| 215 return; |
| 216 } |
| 217 |
| 218 switch (video_frame->format()) { |
| 219 // Supported cases. |
| 220 case PIXEL_FORMAT_YV12: |
| 221 case PIXEL_FORMAT_I420: |
| 222 break; |
| 223 // Unsupported cases. |
| 224 case PIXEL_FORMAT_YV12A: |
| 225 case PIXEL_FORMAT_YV16: |
| 226 case PIXEL_FORMAT_YV24: |
| 227 case PIXEL_FORMAT_NV12: |
| 228 case PIXEL_FORMAT_NV21: |
| 229 case PIXEL_FORMAT_UYVY: |
| 230 case PIXEL_FORMAT_YUY2: |
| 231 case PIXEL_FORMAT_ARGB: |
| 232 case PIXEL_FORMAT_XRGB: |
| 233 case PIXEL_FORMAT_RGB24: |
| 234 case PIXEL_FORMAT_RGB32: |
| 235 case PIXEL_FORMAT_MJPEG: |
| 236 case PIXEL_FORMAT_MT21: |
| 237 case PIXEL_FORMAT_YUV420P9: |
| 238 case PIXEL_FORMAT_YUV422P9: |
| 239 case PIXEL_FORMAT_YUV444P9: |
| 240 case PIXEL_FORMAT_YUV420P10: |
| 241 case PIXEL_FORMAT_YUV422P10: |
| 242 case PIXEL_FORMAT_YUV444P10: |
| 243 case PIXEL_FORMAT_UNKNOWN: |
| 244 frame_ready_cb.Run(video_frame); |
| 245 return; |
| 246 } |
| 247 |
| 248 std::unique_ptr<VideoFrameFuture> video_frame_future = |
| 249 video_frame_pool_->CreateFrame( |
| 250 output_format_, video_frame->visible_rect().size(), |
| 251 video_frame->visible_rect(), video_frame->natural_size(), |
| 252 video_frame->timestamp()); |
| 253 if (!video_frame_future) { |
| 254 frame_ready_cb.Run(video_frame); |
| 255 return; |
| 256 } |
| 257 |
| 258 worker_task_runner_->PostTask( |
| 259 FROM_HERE, |
| 260 base::Bind(&CopierImpl::CopyVideoFrameToGpuMemoryBuffers, this, |
| 261 video_frame, base::Passed(std::move(video_frame_future)), |
| 262 frame_ready_cb)); |
| 263 } |
| 264 |
| 265 void GpuMemoryBufferVideoFrameCopier::CopierImpl::OnCopiesDone( |
| 266 const scoped_refptr<VideoFrame>& video_frame, |
| 267 std::unique_ptr<VideoFrameFuture> video_frame_future, |
| 268 const FrameReadyCB& frame_ready_cb) { |
| 269 media_task_runner_->PostTask( |
| 270 FROM_HERE, |
| 271 base::Bind(&CopierImpl::CreateGpuMemoryBufferVideoFrame, this, |
| 272 video_frame, base::Passed(std::move(video_frame_future)), |
| 273 frame_ready_cb)); |
| 274 } |
| 275 |
| 276 // Copies |video_frame| into |video_frame_future| asynchronously, posting n |
| 277 // tasks that will be synchronized by a barrier. |
| 278 // After the barrier is passed OnCopiesDone will be called. |
| 279 void GpuMemoryBufferVideoFrameCopier::CopierImpl:: |
| 280 CopyVideoFrameToGpuMemoryBuffers( |
| 281 const scoped_refptr<VideoFrame>& video_frame, |
| 282 std::unique_ptr<VideoFrameFuture> video_frame_future, |
| 283 const FrameReadyCB& frame_ready_cb) { |
| 284 // Compute the number of tasks to post and create the barrier. |
| 285 const size_t num_planes = VideoFrame::NumPlanes(output_format_); |
| 286 const gfx::Size coded_size = video_frame_future->coded_size(); |
| 287 size_t copies = 0; |
| 288 for (size_t i = 0; i < num_planes; |
| 289 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(output_format_, i)) { |
| 290 const int rows = VideoFrame::Rows(i, output_format_, coded_size.height()); |
| 291 const int rows_per_copy = |
| 292 RowsPerCopy(i, output_format_, coded_size.width()); |
| 293 copies += rows / rows_per_copy; |
| 294 if (rows % rows_per_copy) |
| 295 ++copies; |
| 296 } |
| 297 |
| 298 VideoFrameFuture* future_ptr = video_frame_future.get(); |
| 299 const base::Closure copies_done = |
| 300 base::Bind(&CopierImpl::OnCopiesDone, this, video_frame, |
| 301 base::Passed(std::move(video_frame_future)), frame_ready_cb); |
| 302 const base::Closure barrier = base::BarrierClosure(copies, copies_done); |
| 303 |
| 304 // Post all the async tasks. |
| 305 for (size_t i = 0; i < num_planes; |
| 306 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(output_format_, i)) { |
| 307 const int rows = VideoFrame::Rows(i, output_format_, coded_size.height()); |
| 308 const int rows_per_copy = |
| 309 RowsPerCopy(i, output_format_, coded_size.width()); |
| 310 |
| 311 for (int row = 0; row < rows; row += rows_per_copy) { |
| 312 const int rows_to_copy = std::min(rows_per_copy, rows - row); |
| 313 switch (output_format_) { |
| 314 case PIXEL_FORMAT_I420: { |
| 315 const int bytes_per_row = |
| 316 VideoFrame::RowBytes(i, output_format_, coded_size.width()); |
| 317 worker_task_runner_->PostTask( |
| 318 FROM_HERE, base::Bind(&CopyRowsToI420Buffer, row, rows_to_copy, |
| 319 bytes_per_row, video_frame->visible_data(i), |
| 320 video_frame->stride(i), future_ptr->data(i), |
| 321 future_ptr->stride(i), barrier)); |
| 322 break; |
| 323 } |
| 324 case PIXEL_FORMAT_NV12: |
| 325 DCHECK(i == 0); |
| 326 worker_task_runner_->PostTask( |
| 327 FROM_HERE, |
| 328 base::Bind(&CopyRowsToNV12Buffer, row, rows_to_copy, |
| 329 coded_size.width(), video_frame, future_ptr->data(0), |
| 330 future_ptr->stride(0), future_ptr->data(1), |
| 331 future_ptr->stride(1), barrier)); |
| 332 break; |
| 333 case PIXEL_FORMAT_UYVY: |
| 334 worker_task_runner_->PostTask( |
| 335 FROM_HERE, |
| 336 base::Bind(&CopyRowsToUYVYBuffer, row, rows_to_copy, |
| 337 coded_size.width(), video_frame, future_ptr->data(i), |
| 338 future_ptr->stride(i), barrier)); |
| 339 break; |
| 340 default: |
| 341 NOTREACHED(); |
| 342 } |
| 343 } |
| 344 } |
| 345 } |
| 346 |
| 347 void GpuMemoryBufferVideoFrameCopier::CopierImpl:: |
| 348 CreateGpuMemoryBufferVideoFrame( |
| 349 const scoped_refptr<VideoFrame>& video_frame, |
| 350 std::unique_ptr<VideoFrameFuture> video_frame_future, |
| 351 const FrameReadyCB& frame_ready_cb) { |
| 352 scoped_refptr<VideoFrame> new_frame = video_frame_future->Release(); |
| 353 if (!new_frame) { |
| 354 frame_ready_cb.Run(video_frame); |
| 355 return; |
| 356 } |
| 357 |
| 358 if (new_frame && new_frame->format() == PIXEL_FORMAT_I420 && |
| 359 video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY)) |
| 360 new_frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); |
| 361 |
| 362 base::TimeTicks render_time; |
| 363 if (video_frame->metadata()->GetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, |
| 364 &render_time)) { |
| 365 new_frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, |
| 366 render_time); |
| 367 } |
| 368 |
| 369 frame_ready_cb.Run(new_frame); |
| 370 } |
| 371 |
| 372 GpuMemoryBufferVideoFrameCopier::CopierImpl::~CopierImpl() {} |
| 373 |
| 374 GpuMemoryBufferVideoFrameCopier::GpuMemoryBufferVideoFrameCopier() {} |
| 375 |
| 376 GpuMemoryBufferVideoFrameCopier::GpuMemoryBufferVideoFrameCopier( |
| 377 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| 378 const scoped_refptr<base::TaskRunner>& worker_task_runner, |
| 379 GpuVideoAcceleratorFactories* gpu_factories) |
| 380 : copier_impl_(new CopierImpl(media_task_runner, |
| 381 worker_task_runner, |
| 382 gpu_factories)) {} |
| 383 |
| 384 GpuMemoryBufferVideoFrameCopier::~GpuMemoryBufferVideoFrameCopier() {} |
| 385 |
| 386 void GpuMemoryBufferVideoFrameCopier::MaybeCreateHardwareFrame( |
| 387 const scoped_refptr<VideoFrame>& video_frame, |
| 388 const FrameReadyCB& frame_ready_cb) { |
| 389 DCHECK(video_frame); |
| 390 copier_impl_->CreateHardwareFrame(video_frame, frame_ready_cb); |
| 391 } |
| 392 |
| 393 } // namespace media |
OLD | NEW |