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

Unified Diff: media/video/gpu_memory_buffer_video_frame_pool.cc

Issue 1273943002: media: Make GpuMemoryBuffers VideoFrame copies asynchronous. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase on master. Created 5 years, 4 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_pool.cc
diff --git a/media/video/gpu_memory_buffer_video_frame_pool.cc b/media/video/gpu_memory_buffer_video_frame_pool.cc
index 2d6e57df284a82b10756759a1c8db04cc9901202..74ef74e0cb530c9521de91065a912e643e021f91 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool.cc
+++ b/media/video/gpu_memory_buffer_video_frame_pool.cc
@@ -7,14 +7,15 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <algorithm>
#include <list>
#include <utility>
+#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/containers/stack_container.h"
#include "base/location.h"
#include "base/memory/linked_ptr.h"
-#include "base/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "media/renderers/gpu_video_accelerator_factories.h"
@@ -26,22 +27,31 @@ class GpuMemoryBufferVideoFramePool::PoolImpl
: public base::RefCountedThreadSafe<
GpuMemoryBufferVideoFramePool::PoolImpl> {
public:
- // |task_runner| is associated to the thread where the context of
- // GLES2Interface returned by |gpu_factories| lives.
+ // |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.
- PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ // null if a GL context is not available.
+ PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+ const scoped_refptr<base::TaskRunner>& worker_task_runner,
const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
- : task_runner_(task_runner),
+ : media_task_runner_(media_task_runner),
+ worker_task_runner_(worker_task_runner),
gpu_factories_(gpu_factories),
- texture_target_(gpu_factories_ ? gpu_factories_->ImageTextureTarget()
- : GL_TEXTURE_2D) {}
+ texture_target_(gpu_factories ? gpu_factories->ImageTextureTarget()
+ : GL_TEXTURE_2D) {
+ DCHECK(media_task_runner_);
+ DCHECK(worker_task_runner_);
+ }
- // Takes a software VideoFrame and returns a VideoFrame backed by native
- // textures if possible.
- // The data contained in video_frame is copied into the returned frame.
- scoped_refptr<VideoFrame> CreateHardwareFrame(
- const scoped_refptr<VideoFrame>& video_frame);
+ // Takes a software VideoFrame and calls |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 |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<
@@ -67,6 +77,25 @@ class GpuMemoryBufferVideoFramePool::PoolImpl
PlaneResource plane_resources[VideoFrame::kMaxPlanes];
};
+ // Copy |video_frame| data into |frame_resouces|
+ // and calls |done| when done.
+ void CopyVideoFrameToGpuMemoryBuffers(
+ const scoped_refptr<VideoFrame>& video_frame,
+ FrameResources* frame_resources,
+ const FrameReadyCB& cb);
DaleCurtis 2015/08/17 18:01:30 Please use something more descriptive than |cb|.
Daniele Castagna 2015/08/19 21:31:25 Done. Change everywhere |cb| to |frame_ready_cb|
+
+ // Called when all the data has been copied.
+ void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame,
+ FrameResources* frame_resources,
+ const FrameReadyCB& cb);
+
+ // Prepares GL resources, mailboxes and calls |cb| with the new VideoFrame.
+ // This has to be run on |media_task_runner_| where |cb| will also be run.
+ void BindAndCreateMailboxesHardwareFrameResources(
+ const scoped_refptr<VideoFrame>& video_frame,
+ FrameResources* frame_resources,
+ const FrameReadyCB& cb);
+
// Return true if |resources| can be used to represent a frame for
// specific |format| and |size|.
static bool AreFrameResourcesCompatible(const FrameResources* resources,
@@ -88,7 +117,7 @@ class GpuMemoryBufferVideoFramePool::PoolImpl
uint32 sync_point);
// Return frame resources to the pool. This has to be called on the thread
- // where |task_runner| is current.
+ // where |media_task_runner_| is current.
void ReturnFrameResources(FrameResources* frame_resources);
// Delete resources. This has to be called on the thread where |task_runner|
@@ -97,7 +126,12 @@ class GpuMemoryBufferVideoFramePool::PoolImpl
const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
FrameResources* frame_resources);
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ // 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.
scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_;
// Pool of resources.
@@ -109,70 +143,155 @@ class GpuMemoryBufferVideoFramePool::PoolImpl
namespace {
-// Copy a buffer info a GpuMemoryBuffer.
-// |bytes_per_row| is expected to be less or equal than the strides of the two
-// buffers.
-void CopyPlaneToGpuMemoryBuffer(int rows,
- int bytes_per_row,
- const uint8* source,
- int source_stride,
- gfx::GpuMemoryBuffer* buffer) {
- TRACE_EVENT2("media", "CopyPlaneToGpuMemoryBuffer", "bytes_per_row",
- bytes_per_row, "rows", rows);
-
- DCHECK(buffer);
- DCHECK(source);
- void* data = nullptr;
- CHECK(buffer->Map(&data));
- uint8* mapped_buffer = static_cast<uint8*>(data);
- int dest_stride = 0;
- buffer->GetStride(&dest_stride);
+void CopyRowsToBuffer(int first_row,
+ int rows,
+ int bytes_per_row,
+ const uint8* source,
+ int source_stride,
+ uint8* output,
+ int dest_stride,
+ base::Closure done) {
DaleCurtis 2015/08/17 18:01:30 const&
Daniele Castagna 2015/08/19 21:31:25 Done.
+ TRACE_EVENT2("media", "CopyRowsToBuffer", "bytes_per_row", bytes_per_row,
+ "rows", rows);
DCHECK_NE(dest_stride, 0);
DCHECK_LE(bytes_per_row, std::abs(dest_stride));
DCHECK_LE(bytes_per_row, source_stride);
- for (int row = 0; row < rows; ++row) {
- memcpy(mapped_buffer + dest_stride * row, source + source_stride * row,
+ for (int row = first_row; row < first_row + rows; ++row) {
+ memcpy(output + dest_stride * row, source + source_stride * row,
bytes_per_row);
}
- buffer->Unmap();
+ 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 returned VideoFrame.
-scoped_refptr<VideoFrame>
-GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
- const scoped_refptr<VideoFrame>& video_frame) {
- if (!gpu_factories_)
- return video_frame;
-
- if (!gpu_factories_->IsTextureRGSupported())
- return video_frame;
-
- gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
- if (!gles2)
- return video_frame;
+// The data contained in |video_frame| is copied into the VideoFrame passed to
+// |cb|.
+// This has to be called on the thread where |media_task_runner_| is current.
+void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
+ const scoped_refptr<VideoFrame>& video_frame,
+ const FrameReadyCB& cb) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
+ if (!gpu_factories_ || !gpu_factories_->IsTextureRGSupported()) {
+ 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_ARGB:
+ case PIXEL_FORMAT_XRGB:
+ case PIXEL_FORMAT_UNKNOWN:
+ cb.Run(video_frame);
+ return;
+ }
VideoPixelFormat format = video_frame->format();
- size_t planes = VideoFrame::NumPlanes(format);
DCHECK(video_frame->visible_rect().origin().IsOrigin());
- gfx::Size size = video_frame->visible_rect().size();
- gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
+ const gfx::Size size = video_frame->visible_rect().size();
// Acquire resources. Incompatible ones will be dropped from the pool.
FrameResources* frame_resources = GetOrCreateFrameResources(size, format);
DaleCurtis 2015/08/17 18:01:30 How expensive is the create portion here? I.e. is
Daniele Castagna 2015/08/19 21:31:25 It should not be particularly expensive but it def
+ if (!frame_resources) {
+ cb.Run(video_frame);
+ return;
+ }
+
+ worker_task_runner_->PostTask(
DaleCurtis 2015/08/17 18:01:30 WDYT about doing an inline copy (i.e. w/o post tas
Daniele Castagna 2015/08/19 21:31:25 Sounds like a good idea. Is it OK if we do this af
+ FROM_HERE, base::Bind(&PoolImpl::CopyVideoFrameToGpuMemoryBuffers, this,
+ video_frame, frame_resources, cb));
+}
+
+void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone(
+ const scoped_refptr<VideoFrame>& video_frame,
+ FrameResources* frame_resources,
+ const FrameReadyCB& cb) {
+ const VideoPixelFormat format = video_frame->format();
+ const size_t planes = VideoFrame::NumPlanes(format);
+ for (size_t i = 0; i < planes; ++i) {
+ frame_resources->plane_resources[i].gpu_memory_buffer->Unmap();
+ }
+
+ media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources, this,
+ video_frame, frame_resources, cb));
+}
- // Set up the planes copying data into it and creating the mailboxes needed
- // to refer to the textures.
+// Copies |video_frame| into |frame_resources| asynchronously, posting n tasks
+// that will be synchronized by a barrier.
+// After the barrier is passed OnCopiesDone will be called.
+void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers(
+ const scoped_refptr<VideoFrame>& video_frame,
+ FrameResources* frame_resources,
+ const FrameReadyCB& cb) {
+ const size_t kBytesPerCopyTarget = 1 << 20;
reveman 2015/08/17 16:38:29 Can you add a comment that makes it clear exactly
Daniele Castagna 2015/08/19 21:31:25 Done.
+ const VideoPixelFormat format = video_frame->format();
+ const size_t planes = VideoFrame::NumPlanes(format);
+ gfx::Size size = video_frame->visible_rect().size();
+ size_t copies = 0;
for (size_t i = 0; i < planes; ++i) {
+ int rows = VideoFrame::Rows(i, format, size.height());
+ int bytes = VideoFrame::RowBytes(i, format, size.width());
reveman 2015/08/17 16:38:29 nit: s/bytes/bytes_per_row/ or row_bytes
Daniele Castagna 2015/08/19 21:31:25 Done.
+ int rows_per_copy = std::max<size_t>(kBytesPerCopyTarget / bytes, 1);
+ copies += rows / rows_per_copy;
+ if (rows % rows_per_copy)
+ ++copies;
+ }
+
+ base::Closure copies_done = base::Bind(&PoolImpl::OnCopiesDone, this,
+ video_frame, frame_resources, cb);
+ base::Closure barrier = base::BarrierClosure(copies, copies_done);
+
+ for (size_t i = 0; i < planes; ++i) {
+ int rows = VideoFrame::Rows(i, format, size.height());
+ int bytes = VideoFrame::RowBytes(i, format, size.width());
reveman 2015/08/17 16:38:29 nit: s/bytes/bytes_per_row/
Daniele Castagna 2015/08/19 21:31:25 Done.
+ int rows_per_copy = std::max<size_t>(kBytesPerCopyTarget / bytes, 1);
+
PlaneResource& plane_resource = frame_resources->plane_resources[i];
- CopyPlaneToGpuMemoryBuffer(VideoFrame::Rows(i, format, size.height()),
- VideoFrame::RowBytes(i, format, size.width()),
- video_frame->data(i), video_frame->stride(i),
- plane_resource.gpu_memory_buffer.get());
+ void* data = nullptr;
+ CHECK(plane_resource.gpu_memory_buffer->Map(&data));
+ uint8* mapped_buffer = static_cast<uint8*>(data);
+ int dest_stride = 0;
+ plane_resource.gpu_memory_buffer->GetStride(&dest_stride);
+
+ for (int row = 0; row < rows; row += rows_per_copy) {
+ worker_task_runner_->PostTask(
DaleCurtis 2015/08/17 18:01:30 Hmm, did you change the worker task runner to run
Daniele Castagna 2015/08/19 21:31:25 Correct, the worker task runner execute tasks in p
+ FROM_HERE, base::Bind(&CopyRowsToBuffer, row,
+ std::min(rows_per_copy, rows - row), bytes,
+ video_frame->data(i), video_frame->stride(i),
+ mapped_buffer, dest_stride, barrier));
+ }
+ }
+}
+void GpuMemoryBufferVideoFramePool::PoolImpl::
+ BindAndCreateMailboxesHardwareFrameResources(
+ const scoped_refptr<VideoFrame>& video_frame,
+ FrameResources* frame_resources,
+ const FrameReadyCB& cb) {
+ gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
+ if (!gles2) {
+ cb.Run(video_frame);
+ return;
+ }
+
+ const VideoPixelFormat format = video_frame->format();
+ const size_t planes = VideoFrame::NumPlanes(format);
+ const gfx::Size size = video_frame->visible_rect().size();
+ gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
+ // Set up the planes creating the mailboxes needed to refer to the textures.
+ for (size_t i = 0; i < planes; ++i) {
+ PlaneResource& plane_resource = frame_resources->plane_resources[i];
// Bind the texture and create or rebind the image.
gles2->BindTexture(texture_target_, plane_resource.texture_id);
@@ -209,17 +328,17 @@ GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
video_frame->timestamp());
if (video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY))
frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
- return frame;
+ cb.Run(frame);
}
// Destroy all the resources posting one task per FrameResources
-// to the |task_runner_|.
+// to the |media_task_runner_|.
GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() {
// Delete all the resources on the media thread.
while (!resources_pool_.empty()) {
FrameResources* frame_resources = resources_pool_.front();
resources_pool_.pop_front();
- task_runner_->PostTask(
+ media_task_runner_->PostTask(
FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_,
base::Owned(frame_resources)));
}
@@ -231,8 +350,6 @@ GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources*
GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
const gfx::Size& size,
VideoPixelFormat format) {
- DCHECK(task_runner_->BelongsToCurrentThread());
-
auto it = resources_pool_.begin();
while (it != resources_pool_.end()) {
FrameResources* frame_resources = *it;
@@ -252,7 +369,8 @@ GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
// Create the resources.
gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface();
- DCHECK(gles2);
+ if (!gles2)
+ return nullptr;
gles2->ActiveTexture(GL_TEXTURE0);
size_t planes = VideoFrame::NumPlanes(format);
FrameResources* frame_resources = new FrameResources(format, size);
@@ -301,14 +419,14 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased(
FrameResources* frame_resources,
uint32 sync_point) {
// Return the resource on the media thread.
- task_runner_->PostTask(FROM_HERE, base::Bind(&PoolImpl::ReturnFrameResources,
- this, frame_resources));
+ media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&PoolImpl::ReturnFrameResources, this, frame_resources));
}
// Put back the resoruces in the pool.
void GpuMemoryBufferVideoFramePool::PoolImpl::ReturnFrameResources(
FrameResources* frame_resources) {
- DCHECK(task_runner_->BelongsToCurrentThread());
auto it = std::find(resources_pool_.begin(), resources_pool_.end(),
frame_resources);
@@ -321,33 +439,20 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::ReturnFrameResources(
}
GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool(
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+ const scoped_refptr<base::TaskRunner>& worker_task_runner,
const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories)
- : pool_impl_(new PoolImpl(task_runner, gpu_factories)) {
-}
+ : pool_impl_(
+ new PoolImpl(media_task_runner, worker_task_runner, gpu_factories)) {}
GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() {
}
-scoped_refptr<VideoFrame>
-GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame(
- const scoped_refptr<VideoFrame>& video_frame) {
- switch (video_frame->format()) {
- // Supported cases.
- case PIXEL_FORMAT_YV12:
- case PIXEL_FORMAT_I420:
- return pool_impl_->CreateHardwareFrame(video_frame);
- // Unsupported cases.
- case PIXEL_FORMAT_YV12A:
- case PIXEL_FORMAT_YV16:
- case PIXEL_FORMAT_YV24:
- case PIXEL_FORMAT_NV12:
- case PIXEL_FORMAT_ARGB:
- case PIXEL_FORMAT_XRGB:
- case PIXEL_FORMAT_UNKNOWN:
- break;
- }
- return video_frame;
+void GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame(
+ const scoped_refptr<VideoFrame>& video_frame,
+ const FrameReadyCB& cb) {
+ DCHECK(video_frame);
+ pool_impl_->CreateHardwareFrame(video_frame, cb);
}
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698