| Index: media/video/gpu_memory_buffer_video_frame_copier.cc
 | 
| diff --git a/media/video/gpu_memory_buffer_video_frame_copier.cc b/media/video/gpu_memory_buffer_video_frame_copier.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..0557d20750b1d4d5bb792db1cb057f4653a2b312
 | 
| --- /dev/null
 | 
| +++ b/media/video/gpu_memory_buffer_video_frame_copier.cc
 | 
| @@ -0,0 +1,393 @@
 | 
| +// Copyright 2015 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "media/video/gpu_memory_buffer_video_frame_copier.h"
 | 
| +
 | 
| +#include "base/barrier_closure.h"
 | 
| +#include "base/bind.h"
 | 
| +#include "media/renderers/gpu_video_accelerator_factories.h"
 | 
| +#include "media/video/gpu_memory_buffer_video_frame_pool.h"
 | 
| +#include "third_party/libyuv/include/libyuv.h"
 | 
| +
 | 
| +namespace media {
 | 
| +
 | 
| +class GpuMemoryBufferVideoFrameCopier::CopierImpl
 | 
| +    : public base::RefCountedThreadSafe<
 | 
| +          GpuMemoryBufferVideoFrameCopier::CopierImpl> {
 | 
| + public:
 | 
| +  // |media_task_runner| is the media task runner associated with the
 | 
| +  // GL context provided by |gpu_factories|
 | 
| +  // |worker_task_runner| is a task runner used to asynchronously copy
 | 
| +  // video frame's planes.
 | 
| +  // |gpu_factories| is an interface to GPU related operation and can be
 | 
| +  // null if a GL context is not available.
 | 
| +  CopierImpl(
 | 
| +      const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
 | 
| +      const scoped_refptr<base::TaskRunner>& worker_task_runner,
 | 
| +      GpuVideoAcceleratorFactories* gpu_factories)
 | 
| +      : media_task_runner_(media_task_runner),
 | 
| +        worker_task_runner_(worker_task_runner),
 | 
| +        gpu_factories_(gpu_factories),
 | 
| +        video_frame_pool_(new GpuMemoryBufferVideoFramePool(media_task_runner_,
 | 
| +                                                            gpu_factories)),
 | 
| +        output_format_(PIXEL_FORMAT_UNKNOWN) {
 | 
| +    DCHECK(media_task_runner_);
 | 
| +    DCHECK(worker_task_runner_);
 | 
| +  }
 | 
| +
 | 
| +  // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame
 | 
| +  // backed by native textures if possible.
 | 
| +  // The data contained in video_frame is copied into the returned frame
 | 
| +  // asynchronously posting tasks to |worker_task_runner_|, while
 | 
| +  // |frame_ready_cb| will be called on |media_task_runner_| once all the data
 | 
| +  // has been copied.
 | 
| +  void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame,
 | 
| +                           const FrameReadyCB& cb);
 | 
| +
 | 
| + private:
 | 
| +  friend class base::RefCountedThreadSafe<
 | 
| +      GpuMemoryBufferVideoFrameCopier::CopierImpl>;
 | 
| +  ~CopierImpl();
 | 
| +
 | 
| +  // Copy |video_frame| data into |video_frame_future|
 | 
| +  // and calls |frame_ready_cb| when done.
 | 
| +  void CopyVideoFrameToGpuMemoryBuffers(
 | 
| +      const scoped_refptr<VideoFrame>& video_frame,
 | 
| +      std::unique_ptr<VideoFrameFuture> video_frame_future,
 | 
| +      const FrameReadyCB& frame_ready_cb);
 | 
| +
 | 
| +  // Called when all the data has been copied.
 | 
| +  void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame,
 | 
| +                    std::unique_ptr<VideoFrameFuture> video_frame_future,
 | 
| +                    const FrameReadyCB& frame_ready_cb);
 | 
| +
 | 
| +  // Create new VideoFrame by |video_frame_future| and calls |frame_ready_cb|.
 | 
| +  // This has to be run on |media_task_runner_| where |frame_ready_cb| will also
 | 
| +  // be run.
 | 
| +  void CreateGpuMemoryBufferVideoFrame(
 | 
| +      const scoped_refptr<VideoFrame>& video_frame,
 | 
| +      std::unique_ptr<VideoFrameFuture> video_frame_future,
 | 
| +      const FrameReadyCB& frame_ready_cb);
 | 
| +
 | 
| +  // Task runner associated to the GL context provided by |gpu_factories_|.
 | 
| +  scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
 | 
| +  // Task runner used to asynchronously copy planes.
 | 
| +  scoped_refptr<base::TaskRunner> worker_task_runner_;
 | 
| +
 | 
| +  // Interface to GPU related operations.
 | 
| +  GpuVideoAcceleratorFactories* gpu_factories_;
 | 
| +
 | 
| +  std::unique_ptr<GpuMemoryBufferVideoFramePool> video_frame_pool_;
 | 
| +
 | 
| +  // TODO(dcastagna): change the following type from VideoPixelFormat to
 | 
| +  // BufferFormat.
 | 
| +  VideoPixelFormat output_format_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(CopierImpl);
 | 
| +};
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +// VideoFrame copies to GpuMemoryBuffers will be split in copies where the
 | 
| +// output size is |kBytesPerCopyTarget| bytes and run in parallel.
 | 
| +const size_t kBytesPerCopyTarget = 1024 * 1024;  // 1MB
 | 
| +
 | 
| +// The number of output rows to be copied in each iteration.
 | 
| +int RowsPerCopy(size_t plane, VideoPixelFormat format, int width) {
 | 
| +  int bytes_per_row = VideoFrame::RowBytes(plane, format, width);
 | 
| +  if (format == PIXEL_FORMAT_NV12) {
 | 
| +    DCHECK_EQ(0u, plane);
 | 
| +    bytes_per_row += VideoFrame::RowBytes(1, format, width);
 | 
| +  }
 | 
| +  // Copy an even number of lines, and at least one.
 | 
| +  return std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1);
 | 
| +}
 | 
| +
 | 
| +void CopyRowsToI420Buffer(int first_row,
 | 
| +                          int rows,
 | 
| +                          int bytes_per_row,
 | 
| +                          const uint8_t* source,
 | 
| +                          int source_stride,
 | 
| +                          uint8_t* output,
 | 
| +                          int dest_stride,
 | 
| +                          const base::Closure& done) {
 | 
| +  TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row,
 | 
| +               "rows", rows);
 | 
| +  if (output) {
 | 
| +    DCHECK_NE(dest_stride, 0);
 | 
| +    DCHECK_LE(bytes_per_row, std::abs(dest_stride));
 | 
| +    DCHECK_LE(bytes_per_row, source_stride);
 | 
| +
 | 
| +    libyuv::CopyPlane(source + source_stride * first_row, source_stride,
 | 
| +                      output + dest_stride * first_row, dest_stride,
 | 
| +                      bytes_per_row, rows);
 | 
| +  }
 | 
| +  done.Run();
 | 
| +}
 | 
| +
 | 
| +void CopyRowsToNV12Buffer(int first_row,
 | 
| +                          int rows,
 | 
| +                          int bytes_per_row,
 | 
| +                          const scoped_refptr<VideoFrame>& source_frame,
 | 
| +                          uint8_t* dest_y,
 | 
| +                          int dest_stride_y,
 | 
| +                          uint8_t* dest_uv,
 | 
| +                          int dest_stride_uv,
 | 
| +                          const base::Closure& done) {
 | 
| +  TRACE_EVENT2("media", "CopyRowsToNV12Buffer", "bytes_per_row", bytes_per_row,
 | 
| +               "rows", rows);
 | 
| +  if (dest_y && dest_uv) {
 | 
| +    DCHECK_NE(dest_stride_y, 0);
 | 
| +    DCHECK_NE(dest_stride_uv, 0);
 | 
| +    DCHECK_LE(bytes_per_row, std::abs(dest_stride_y));
 | 
| +    DCHECK_LE(bytes_per_row, std::abs(dest_stride_uv));
 | 
| +    DCHECK_EQ(0, first_row % 2);
 | 
| +
 | 
| +    libyuv::I420ToNV12(
 | 
| +        source_frame->visible_data(VideoFrame::kYPlane) +
 | 
| +            first_row * source_frame->stride(VideoFrame::kYPlane),
 | 
| +        source_frame->stride(VideoFrame::kYPlane),
 | 
| +        source_frame->visible_data(VideoFrame::kUPlane) +
 | 
| +            first_row / 2 * source_frame->stride(VideoFrame::kUPlane),
 | 
| +        source_frame->stride(VideoFrame::kUPlane),
 | 
| +        source_frame->visible_data(VideoFrame::kVPlane) +
 | 
| +            first_row / 2 * source_frame->stride(VideoFrame::kVPlane),
 | 
| +        source_frame->stride(VideoFrame::kVPlane),
 | 
| +        dest_y + first_row * dest_stride_y, dest_stride_y,
 | 
| +        dest_uv + first_row / 2 * dest_stride_uv, dest_stride_uv, bytes_per_row,
 | 
| +        rows);
 | 
| +  }
 | 
| +  done.Run();
 | 
| +}
 | 
| +
 | 
| +void CopyRowsToUYVYBuffer(int first_row,
 | 
| +                          int rows,
 | 
| +                          int width,
 | 
| +                          const scoped_refptr<VideoFrame>& source_frame,
 | 
| +                          uint8_t* output,
 | 
| +                          int dest_stride,
 | 
| +                          const base::Closure& done) {
 | 
| +  TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2,
 | 
| +               "rows", rows);
 | 
| +  if (output) {
 | 
| +    DCHECK_NE(dest_stride, 0);
 | 
| +    DCHECK_LE(width, std::abs(dest_stride / 2));
 | 
| +    DCHECK_EQ(0, first_row % 2);
 | 
| +    libyuv::I420ToUYVY(
 | 
| +        source_frame->visible_data(VideoFrame::kYPlane) +
 | 
| +            first_row * source_frame->stride(VideoFrame::kYPlane),
 | 
| +        source_frame->stride(VideoFrame::kYPlane),
 | 
| +        source_frame->visible_data(VideoFrame::kUPlane) +
 | 
| +            first_row / 2 * source_frame->stride(VideoFrame::kUPlane),
 | 
| +        source_frame->stride(VideoFrame::kUPlane),
 | 
| +        source_frame->visible_data(VideoFrame::kVPlane) +
 | 
| +            first_row / 2 * source_frame->stride(VideoFrame::kVPlane),
 | 
| +        source_frame->stride(VideoFrame::kVPlane),
 | 
| +        output + first_row * dest_stride, dest_stride, width, rows);
 | 
| +  }
 | 
| +  done.Run();
 | 
| +}
 | 
| +
 | 
| +}  // unnamed namespace
 | 
| +
 | 
| +// Creates a VideoFrame backed by native textures starting from a software
 | 
| +// VideoFrame.
 | 
| +// The data contained in |video_frame| is copied into the VideoFrame passed to
 | 
| +// |frame_ready_cb|.
 | 
| +// This has to be called on the thread where |media_task_runner_| is current.
 | 
| +void GpuMemoryBufferVideoFrameCopier::CopierImpl::CreateHardwareFrame(
 | 
| +    const scoped_refptr<VideoFrame>& video_frame,
 | 
| +    const FrameReadyCB& frame_ready_cb) {
 | 
| +  DCHECK(media_task_runner_->BelongsToCurrentThread());
 | 
| +  // Lazily initialize output_format_ since VideoFrameOutputFormat() has to be
 | 
| +  // called on the media_thread while this object might be instantiated on any.
 | 
| +  if (output_format_ == PIXEL_FORMAT_UNKNOWN)
 | 
| +    output_format_ = gpu_factories_->VideoFrameOutputFormat();
 | 
| +
 | 
| +  if (output_format_ == PIXEL_FORMAT_UNKNOWN) {
 | 
| +    frame_ready_cb.Run(video_frame);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  if (video_frame->HasTextures()) {
 | 
| +    frame_ready_cb.Run(video_frame);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  switch (video_frame->format()) {
 | 
| +    // Supported cases.
 | 
| +    case PIXEL_FORMAT_YV12:
 | 
| +    case PIXEL_FORMAT_I420:
 | 
| +      break;
 | 
| +    // Unsupported cases.
 | 
| +    case PIXEL_FORMAT_YV12A:
 | 
| +    case PIXEL_FORMAT_YV16:
 | 
| +    case PIXEL_FORMAT_YV24:
 | 
| +    case PIXEL_FORMAT_NV12:
 | 
| +    case PIXEL_FORMAT_NV21:
 | 
| +    case PIXEL_FORMAT_UYVY:
 | 
| +    case PIXEL_FORMAT_YUY2:
 | 
| +    case PIXEL_FORMAT_ARGB:
 | 
| +    case PIXEL_FORMAT_XRGB:
 | 
| +    case PIXEL_FORMAT_RGB24:
 | 
| +    case PIXEL_FORMAT_RGB32:
 | 
| +    case PIXEL_FORMAT_MJPEG:
 | 
| +    case PIXEL_FORMAT_MT21:
 | 
| +    case PIXEL_FORMAT_YUV420P9:
 | 
| +    case PIXEL_FORMAT_YUV422P9:
 | 
| +    case PIXEL_FORMAT_YUV444P9:
 | 
| +    case PIXEL_FORMAT_YUV420P10:
 | 
| +    case PIXEL_FORMAT_YUV422P10:
 | 
| +    case PIXEL_FORMAT_YUV444P10:
 | 
| +    case PIXEL_FORMAT_UNKNOWN:
 | 
| +      frame_ready_cb.Run(video_frame);
 | 
| +      return;
 | 
| +  }
 | 
| +
 | 
| +  std::unique_ptr<VideoFrameFuture> video_frame_future =
 | 
| +      video_frame_pool_->CreateFrame(
 | 
| +          output_format_, video_frame->visible_rect().size(),
 | 
| +          video_frame->visible_rect(), video_frame->natural_size(),
 | 
| +          video_frame->timestamp());
 | 
| +  if (!video_frame_future) {
 | 
| +    frame_ready_cb.Run(video_frame);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  worker_task_runner_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&CopierImpl::CopyVideoFrameToGpuMemoryBuffers, this,
 | 
| +                 video_frame, base::Passed(std::move(video_frame_future)),
 | 
| +                 frame_ready_cb));
 | 
| +}
 | 
| +
 | 
| +void GpuMemoryBufferVideoFrameCopier::CopierImpl::OnCopiesDone(
 | 
| +    const scoped_refptr<VideoFrame>& video_frame,
 | 
| +    std::unique_ptr<VideoFrameFuture> video_frame_future,
 | 
| +    const FrameReadyCB& frame_ready_cb) {
 | 
| +  media_task_runner_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&CopierImpl::CreateGpuMemoryBufferVideoFrame, this,
 | 
| +                 video_frame, base::Passed(std::move(video_frame_future)),
 | 
| +                 frame_ready_cb));
 | 
| +}
 | 
| +
 | 
| +// Copies |video_frame| into |video_frame_future| asynchronously, posting n
 | 
| +// tasks that will be synchronized by a barrier.
 | 
| +// After the barrier is passed OnCopiesDone will be called.
 | 
| +void GpuMemoryBufferVideoFrameCopier::CopierImpl::
 | 
| +    CopyVideoFrameToGpuMemoryBuffers(
 | 
| +        const scoped_refptr<VideoFrame>& video_frame,
 | 
| +        std::unique_ptr<VideoFrameFuture> video_frame_future,
 | 
| +        const FrameReadyCB& frame_ready_cb) {
 | 
| +  // Compute the number of tasks to post and create the barrier.
 | 
| +  const size_t num_planes = VideoFrame::NumPlanes(output_format_);
 | 
| +  const gfx::Size coded_size = video_frame_future->coded_size();
 | 
| +  size_t copies = 0;
 | 
| +  for (size_t i = 0; i < num_planes;
 | 
| +       i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(output_format_, i)) {
 | 
| +    const int rows = VideoFrame::Rows(i, output_format_, coded_size.height());
 | 
| +    const int rows_per_copy =
 | 
| +        RowsPerCopy(i, output_format_, coded_size.width());
 | 
| +    copies += rows / rows_per_copy;
 | 
| +    if (rows % rows_per_copy)
 | 
| +      ++copies;
 | 
| +  }
 | 
| +
 | 
| +  VideoFrameFuture* future_ptr = video_frame_future.get();
 | 
| +  const base::Closure copies_done =
 | 
| +      base::Bind(&CopierImpl::OnCopiesDone, this, video_frame,
 | 
| +                 base::Passed(std::move(video_frame_future)), frame_ready_cb);
 | 
| +  const base::Closure barrier = base::BarrierClosure(copies, copies_done);
 | 
| +
 | 
| +  // Post all the async tasks.
 | 
| +  for (size_t i = 0; i < num_planes;
 | 
| +       i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(output_format_, i)) {
 | 
| +    const int rows = VideoFrame::Rows(i, output_format_, coded_size.height());
 | 
| +    const int rows_per_copy =
 | 
| +        RowsPerCopy(i, output_format_, coded_size.width());
 | 
| +
 | 
| +    for (int row = 0; row < rows; row += rows_per_copy) {
 | 
| +      const int rows_to_copy = std::min(rows_per_copy, rows - row);
 | 
| +      switch (output_format_) {
 | 
| +        case PIXEL_FORMAT_I420: {
 | 
| +          const int bytes_per_row =
 | 
| +              VideoFrame::RowBytes(i, output_format_, coded_size.width());
 | 
| +          worker_task_runner_->PostTask(
 | 
| +              FROM_HERE, base::Bind(&CopyRowsToI420Buffer, row, rows_to_copy,
 | 
| +                                    bytes_per_row, video_frame->visible_data(i),
 | 
| +                                    video_frame->stride(i), future_ptr->data(i),
 | 
| +                                    future_ptr->stride(i), barrier));
 | 
| +          break;
 | 
| +        }
 | 
| +        case PIXEL_FORMAT_NV12:
 | 
| +          DCHECK(i == 0);
 | 
| +          worker_task_runner_->PostTask(
 | 
| +              FROM_HERE,
 | 
| +              base::Bind(&CopyRowsToNV12Buffer, row, rows_to_copy,
 | 
| +                         coded_size.width(), video_frame, future_ptr->data(0),
 | 
| +                         future_ptr->stride(0), future_ptr->data(1),
 | 
| +                         future_ptr->stride(1), barrier));
 | 
| +          break;
 | 
| +        case PIXEL_FORMAT_UYVY:
 | 
| +          worker_task_runner_->PostTask(
 | 
| +              FROM_HERE,
 | 
| +              base::Bind(&CopyRowsToUYVYBuffer, row, rows_to_copy,
 | 
| +                         coded_size.width(), video_frame, future_ptr->data(i),
 | 
| +                         future_ptr->stride(i), barrier));
 | 
| +          break;
 | 
| +        default:
 | 
| +          NOTREACHED();
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void GpuMemoryBufferVideoFrameCopier::CopierImpl::
 | 
| +    CreateGpuMemoryBufferVideoFrame(
 | 
| +        const scoped_refptr<VideoFrame>& video_frame,
 | 
| +        std::unique_ptr<VideoFrameFuture> video_frame_future,
 | 
| +        const FrameReadyCB& frame_ready_cb) {
 | 
| +  scoped_refptr<VideoFrame> new_frame = video_frame_future->Release();
 | 
| +  if (!new_frame) {
 | 
| +    frame_ready_cb.Run(video_frame);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  if (new_frame && new_frame->format() == PIXEL_FORMAT_I420 &&
 | 
| +      video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY))
 | 
| +    new_frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
 | 
| +
 | 
| +  base::TimeTicks render_time;
 | 
| +  if (video_frame->metadata()->GetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
 | 
| +                                            &render_time)) {
 | 
| +    new_frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
 | 
| +                                        render_time);
 | 
| +  }
 | 
| +
 | 
| +  frame_ready_cb.Run(new_frame);
 | 
| +}
 | 
| +
 | 
| +GpuMemoryBufferVideoFrameCopier::CopierImpl::~CopierImpl() {}
 | 
| +
 | 
| +GpuMemoryBufferVideoFrameCopier::GpuMemoryBufferVideoFrameCopier() {}
 | 
| +
 | 
| +GpuMemoryBufferVideoFrameCopier::GpuMemoryBufferVideoFrameCopier(
 | 
| +    const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
 | 
| +    const scoped_refptr<base::TaskRunner>& worker_task_runner,
 | 
| +    GpuVideoAcceleratorFactories* gpu_factories)
 | 
| +    : copier_impl_(new CopierImpl(media_task_runner,
 | 
| +                                  worker_task_runner,
 | 
| +                                  gpu_factories)) {}
 | 
| +
 | 
| +GpuMemoryBufferVideoFrameCopier::~GpuMemoryBufferVideoFrameCopier() {}
 | 
| +
 | 
| +void GpuMemoryBufferVideoFrameCopier::MaybeCreateHardwareFrame(
 | 
| +    const scoped_refptr<VideoFrame>& video_frame,
 | 
| +    const FrameReadyCB& frame_ready_cb) {
 | 
| +  DCHECK(video_frame);
 | 
| +  copier_impl_->CreateHardwareFrame(video_frame, frame_ready_cb);
 | 
| +}
 | 
| +
 | 
| +}  // namespace media
 | 
| 
 |