Chromium Code Reviews| 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 a141be0d56d4e81f5bd630f477650071902a5fb5..88fb6a8204d996bcba70459f890de937bffebbce 100644 |
| --- a/media/video/gpu_memory_buffer_video_frame_pool.cc |
| +++ b/media/video/gpu_memory_buffer_video_frame_pool.cc |
| @@ -19,6 +19,7 @@ |
| #include "base/trace_event/trace_event.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "media/renderers/gpu_video_accelerator_factories.h" |
| +#include "third_party/libyuv/include/libyuv.h" |
| namespace media { |
| @@ -40,7 +41,9 @@ class GpuMemoryBufferVideoFramePool::PoolImpl |
| worker_task_runner_(worker_task_runner), |
| gpu_factories_(gpu_factories), |
| texture_target_(gpu_factories ? gpu_factories->ImageTextureTarget() |
| - : GL_TEXTURE_2D) { |
| + : GL_TEXTURE_2D), |
| + format_((gpu_factories ? gpu_factories->VideoFramePixelFormat() |
| + : PIXEL_FORMAT_I420)) { |
|
Andre
2015/08/21 18:18:36
nit: don't need double parenthesis.
Daniele Castagna
2015/08/21 18:49:02
Done.
|
| DCHECK(media_task_runner_); |
| DCHECK(worker_task_runner_); |
| } |
| @@ -141,24 +144,57 @@ class GpuMemoryBufferVideoFramePool::PoolImpl |
| std::list<FrameResources*> resources_pool_; |
| const unsigned texture_target_; |
| + const VideoPixelFormat format_; |
|
Andre
2015/08/21 18:18:36
Maybe call this output_format_ or hardware_format_
Daniele Castagna
2015/08/21 18:49:02
Good point. Removed.
|
| + |
| DISALLOW_COPY_AND_ASSIGN(PoolImpl); |
| }; |
| namespace { |
| -// VideoFrame copies to GpuMemoryBuffers will be split in |kBytesPerCopyTarget| |
| -// bytes copies and run in parallel. |
| +// VideoFrame copies to GpuMemoryBuffers will be split in copies where the |
| +// output |
|
Andre
2015/08/21 18:18:36
don't wrap before output.
Daniele Castagna
2015/08/21 18:49:02
Done.
|
| +// size is |kBytesPerCopyTarget| bytes and run in parallel. |
| const size_t kBytesPerCopyTarget = 1024 * 1024; // 1MB |
| -void CopyRowsToBuffer(int first_row, |
| - int rows, |
| - int bytes_per_row, |
| - const uint8* source, |
| - int source_stride, |
| - uint8* output, |
| - int dest_stride, |
| - const base::Closure& done) { |
| - TRACE_EVENT2("media", "CopyRowsToBuffer", "bytes_per_row", bytes_per_row, |
| +// Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat |
| +// and plane. |
| +gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) { |
| + switch (format) { |
| + case PIXEL_FORMAT_I420: |
| + DCHECK_LE(plane, 2u); |
| + return gfx::BufferFormat::R_8; |
| + case PIXEL_FORMAT_UYVY: |
| + DCHECK_EQ(0u, plane); |
| + return gfx::BufferFormat::UYVY_422; |
| + default: |
| + NOTREACHED(); |
| + return gfx::BufferFormat::BGRA_8888; |
| + } |
| +} |
| + |
| +unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) { |
| + switch (format) { |
| + case PIXEL_FORMAT_I420: |
| + DCHECK_LE(plane, 2u); |
| + return GL_R8_EXT; |
| + case PIXEL_FORMAT_UYVY: |
| + DCHECK_EQ(0u, plane); |
| + return GL_RGB; |
| + default: |
| + NOTREACHED(); |
| + return 0; |
| + } |
| +} |
| + |
| +void CopyRowsToI420Buffer(int first_row, |
| + int rows, |
| + int bytes_per_row, |
| + const uint8* source, |
| + int source_stride, |
| + uint8* output, |
| + int dest_stride, |
| + const base::Closure& done) { |
| + TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row, |
| "rows", rows); |
| DCHECK_NE(dest_stride, 0); |
| DCHECK_LE(bytes_per_row, std::abs(dest_stride)); |
| @@ -170,6 +206,32 @@ void CopyRowsToBuffer(int first_row, |
| done.Run(); |
| } |
| +void CopyRowsToUYVYBuffer(int first_row, |
| + int rows, |
| + int width, |
| + const scoped_refptr<VideoFrame>& source_frame, |
| + uint8* output, |
| + int dest_stride, |
| + const base::Closure& done) { |
| + TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2, |
| + "rows", rows); |
| + DCHECK_NE(dest_stride, 0); |
| + DCHECK_LE(width, std::abs(dest_stride / 2)); |
| + DCHECK_EQ(0, first_row % 2); |
| + libyuv::I420ToUYVY( |
| + source_frame->data(VideoFrame::kYPlane) + |
| + first_row * source_frame->stride(VideoFrame::kYPlane), |
| + source_frame->stride(VideoFrame::kYPlane), |
| + source_frame->data(VideoFrame::kUPlane) + |
| + first_row / 2 * source_frame->stride(VideoFrame::kUPlane), |
| + source_frame->stride(VideoFrame::kUPlane), |
| + source_frame->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 |
| @@ -181,7 +243,8 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame( |
| const scoped_refptr<VideoFrame>& video_frame, |
| const FrameReadyCB& frame_ready_cb) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - if (!gpu_factories_ || !gpu_factories_->IsTextureRGSupported()) { |
| + if (!gpu_factories_ || (format_ == PIXEL_FORMAT_I420 && |
| + !gpu_factories_->IsTextureRGSupported())) { |
| frame_ready_cb.Run(video_frame); |
| return; |
| } |
| @@ -203,12 +266,11 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame( |
| return; |
| } |
| - VideoPixelFormat format = video_frame->format(); |
| DCHECK(video_frame->visible_rect().origin().IsOrigin()); |
| 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); |
| + FrameResources* frame_resources = GetOrCreateFrameResources(size, format_); |
| if (!frame_resources) { |
| frame_ready_cb.Run(video_frame); |
| return; |
| @@ -223,8 +285,7 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone( |
| const scoped_refptr<VideoFrame>& video_frame, |
| FrameResources* frame_resources, |
| const FrameReadyCB& frame_ready_cb) { |
| - const VideoPixelFormat format = video_frame->format(); |
| - const size_t planes = VideoFrame::NumPlanes(format); |
| + const size_t planes = VideoFrame::NumPlanes(frame_resources->format); |
| for (size_t i = 0; i < planes; ++i) { |
| frame_resources->plane_resources[i].gpu_memory_buffer->Unmap(); |
| } |
| @@ -242,45 +303,60 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers( |
| const scoped_refptr<VideoFrame>& video_frame, |
| FrameResources* frame_resources, |
| const FrameReadyCB& frame_ready_cb) { |
| - const VideoPixelFormat format = video_frame->format(); |
| - const size_t planes = VideoFrame::NumPlanes(format); |
| + // Compute the number of tasks to post and create the barrier. |
| + const VideoPixelFormat dest_format = frame_resources->format; |
| + const size_t dest_planes = VideoFrame::NumPlanes(dest_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_per_row = VideoFrame::RowBytes(i, format, size.width()); |
| + for (size_t i = 0; i < dest_planes; ++i) { |
| + int rows = VideoFrame::Rows(i, dest_format, size.height()); |
| + int bytes_per_row = VideoFrame::RowBytes(i, dest_format, size.width()); |
| + // Copy a even number of lines, and at least one. |
| int rows_per_copy = |
| - std::max<size_t>(kBytesPerCopyTarget / bytes_per_row, 1); |
| + std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 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, |
| frame_ready_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_per_row = VideoFrame::RowBytes(i, format, size.width()); |
| + // Post all the async tasks. |
| + for (size_t i = 0; i < dest_planes; ++i) { |
| + int rows = VideoFrame::Rows(i, dest_format, size.height()); |
| + int bytes_per_row = VideoFrame::RowBytes(i, dest_format, size.width()); |
| int rows_per_copy = |
| - std::max<size_t>(kBytesPerCopyTarget / bytes_per_row, 1); |
| + std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1); |
| - PlaneResource& plane_resource = frame_resources->plane_resources[i]; |
| void* data = nullptr; |
|
Andre
2015/08/21 18:18:36
CHECK_EQ(1, gfx::NumberOfPlanesForBufferFormat(des
Daniele Castagna
2015/08/21 18:49:02
Done.
|
| - CHECK(plane_resource.gpu_memory_buffer->Map(&data)); |
| + CHECK(frame_resources->plane_resources[i].gpu_memory_buffer->Map(&data)); |
| uint8* mapped_buffer = static_cast<uint8*>(data); |
| + |
| int dest_stride = 0; |
| - plane_resource.gpu_memory_buffer->GetStride(&dest_stride); |
| + frame_resources->plane_resources[i].gpu_memory_buffer->GetStride( |
| + &dest_stride); |
| for (int row = 0; row < rows; row += rows_per_copy) { |
| - worker_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&CopyRowsToBuffer, row, |
| - std::min(rows_per_copy, rows - row), bytes_per_row, |
| - video_frame->data(i), video_frame->stride(i), |
| - mapped_buffer, dest_stride, barrier)); |
| + switch (dest_format) { |
| + case PIXEL_FORMAT_I420: |
| + worker_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&CopyRowsToI420Buffer, row, |
| + std::min(rows_per_copy, rows - row), bytes_per_row, |
| + video_frame->data(i), video_frame->stride(i), |
| + mapped_buffer, dest_stride, barrier)); |
| + break; |
| + case PIXEL_FORMAT_UYVY: |
| + worker_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&CopyRowsToUYVYBuffer, row, |
| + std::min(rows_per_copy, rows - row), size.width(), |
| + video_frame, mapped_buffer, dest_stride, barrier)); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| } |
| } |
| } |
| @@ -296,8 +372,7 @@ void GpuMemoryBufferVideoFramePool::PoolImpl:: |
| return; |
| } |
| - const VideoPixelFormat format = video_frame->format(); |
| - const size_t planes = VideoFrame::NumPlanes(format); |
| + const size_t planes = VideoFrame::NumPlanes(frame_resources->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. |
| @@ -307,11 +382,13 @@ void GpuMemoryBufferVideoFramePool::PoolImpl:: |
| gles2->BindTexture(texture_target_, plane_resource.texture_id); |
| if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) { |
| - const size_t width = VideoFrame::Columns(i, format, size.width()); |
| - const size_t height = VideoFrame::Rows(i, format, size.height()); |
| + const size_t width = |
| + VideoFrame::Columns(i, frame_resources->format, size.width()); |
| + const size_t height = |
| + VideoFrame::Rows(i, frame_resources->format, size.height()); |
| plane_resource.image_id = gles2->CreateImageCHROMIUM( |
| plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height, |
| - GL_R8_EXT); |
| + ImageInternalFormat(frame_resources->format, i)); |
| } else { |
| gles2->ReleaseTexImage2DCHROMIUM(texture_target_, |
| plane_resource.image_id); |
| @@ -329,16 +406,31 @@ void GpuMemoryBufferVideoFramePool::PoolImpl:: |
| mailbox_holders[i].sync_point = sync_point; |
| } |
| + scoped_refptr<VideoFrame> frame; |
| // Create the VideoFrame backed by native textures. |
| - scoped_refptr<VideoFrame> frame = VideoFrame::WrapYUV420NativeTextures( |
| - mailbox_holders[VideoFrame::kYPlane], |
| - mailbox_holders[VideoFrame::kUPlane], |
| - mailbox_holders[VideoFrame::kVPlane], |
| - base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources), |
| - size, video_frame->visible_rect(), video_frame->natural_size(), |
| - video_frame->timestamp()); |
| + switch (frame_resources->format) { |
| + case PIXEL_FORMAT_I420: |
| + frame = VideoFrame::WrapYUV420NativeTextures( |
| + mailbox_holders[VideoFrame::kYPlane], |
| + mailbox_holders[VideoFrame::kUPlane], |
| + mailbox_holders[VideoFrame::kVPlane], |
| + base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources), |
| + size, video_frame->visible_rect(), video_frame->natural_size(), |
| + video_frame->timestamp()); |
| if (video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY)) |
|
Andre
2015/08/21 18:18:36
indent +4
Daniele Castagna
2015/08/21 18:49:02
Done.
|
| frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); |
| + break; |
| + case PIXEL_FORMAT_UYVY: |
| + frame = VideoFrame::WrapNativeTexture( |
| + PIXEL_FORMAT_UYVY, mailbox_holders[VideoFrame::kYPlane], |
| + base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources), |
| + size, video_frame->visible_rect(), video_frame->natural_size(), |
| + video_frame->timestamp()); |
| + frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| frame_ready_cb.Run(frame); |
| } |
| @@ -391,8 +483,9 @@ GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources( |
| const size_t width = VideoFrame::Columns(i, format, size.width()); |
| const size_t height = VideoFrame::Rows(i, format, size.height()); |
| const gfx::Size plane_size(width, height); |
| + |
| plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer( |
| - plane_size, gfx::BufferFormat::R_8, gfx::BufferUsage::MAP); |
| + plane_size, GpuMemoryBufferFormat(format, i), gfx::BufferUsage::MAP); |
| gles2->GenTextures(1, &plane_resource.texture_id); |
| gles2->BindTexture(texture_target_, plane_resource.texture_id); |