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

Unified Diff: media/video/gpu_memory_buffer_video_frame_copier.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 side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « media/video/gpu_memory_buffer_video_frame_copier.h ('k') | media/video/gpu_memory_buffer_video_frame_copier_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698