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 739bcc709e9435e8b938aba3c5a56a3c772d7126..ec93342042195c02efd34d3a83c79b76b0d74b31 100644 |
--- a/media/video/gpu_memory_buffer_video_frame_pool.cc |
+++ b/media/video/gpu_memory_buffer_video_frame_pool.cc |
@@ -161,6 +161,9 @@ gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) { |
case PIXEL_FORMAT_I420: |
DCHECK_LE(plane, 2u); |
return gfx::BufferFormat::R_8; |
+ case PIXEL_FORMAT_NV12: |
+ DCHECK_LE(plane, 1u); |
+ return gfx::BufferFormat::YUV_420_BIPLANAR; |
case PIXEL_FORMAT_UYVY: |
DCHECK_EQ(0u, plane); |
return gfx::BufferFormat::UYVY_422; |
@@ -175,6 +178,10 @@ unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) { |
case PIXEL_FORMAT_I420: |
DCHECK_LE(plane, 2u); |
return GL_R8_EXT; |
+ case PIXEL_FORMAT_NV12: |
+ DCHECK_LE(plane, 1u); |
+ DLOG(WARNING) << "NV12 format not supported yet"; |
+ return 0; // TODO(andresantoso): Implement extension for NV12. |
case PIXEL_FORMAT_UYVY: |
DCHECK_EQ(0u, plane); |
return GL_RGB_YCBCR_422_CHROMIUM; |
@@ -184,6 +191,31 @@ unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) { |
} |
} |
+// The number of output planes to be copied in each iteration. |
+size_t PlanesPerCopy(VideoPixelFormat format) { |
+ switch (format) { |
+ case PIXEL_FORMAT_I420: |
+ case PIXEL_FORMAT_UYVY: |
+ return 1; |
+ case PIXEL_FORMAT_NV12: |
+ return 2; |
+ default: |
+ NOTREACHED(); |
+ return 0; |
+ } |
+} |
+ |
+// 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, |
@@ -204,6 +236,38 @@ void CopyRowsToI420Buffer(int first_row, |
done.Run(); |
} |
+void CopyRowsToNV12Buffer(int first_row, |
+ int rows, |
+ int bytes_per_row, |
+ const scoped_refptr<VideoFrame>& source_frame, |
+ uint8* dest_y, |
+ int dest_stride_y, |
+ uint8* dest_uv, |
+ int dest_stride_uv, |
+ const base::Closure& done) { |
+ TRACE_EVENT2("media", "CopyRowsToNV12Buffer", "bytes_per_row", bytes_per_row, |
+ "rows", rows); |
+ 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->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), |
+ 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, |
@@ -298,9 +362,9 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone( |
const scoped_refptr<VideoFrame>& video_frame, |
FrameResources* frame_resources, |
const FrameReadyCB& frame_ready_cb) { |
- const size_t planes = VideoFrame::NumPlanes(output_format_); |
- for (size_t i = 0; i < planes; ++i) { |
- frame_resources->plane_resources[i].gpu_memory_buffer->Unmap(); |
+ for (const auto& plane_resource : frame_resources->plane_resources) { |
+ if (plane_resource.gpu_memory_buffer) |
+ plane_resource.gpu_memory_buffer->Unmap(); |
} |
media_task_runner_->PostTask( |
@@ -317,15 +381,13 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers( |
FrameResources* frame_resources, |
const FrameReadyCB& frame_ready_cb) { |
// Compute the number of tasks to post and create the barrier. |
- const size_t dest_planes = VideoFrame::NumPlanes(output_format_); |
+ const size_t num_planes = VideoFrame::NumPlanes(output_format_); |
+ const size_t planes_per_copy = PlanesPerCopy(output_format_); |
gfx::Size size = video_frame->visible_rect().size(); |
size_t copies = 0; |
- for (size_t i = 0; i < dest_planes; ++i) { |
- int rows = VideoFrame::Rows(i, output_format_, size.height()); |
- int bytes_per_row = VideoFrame::RowBytes(i, output_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, 1); |
+ for (size_t i = 0; i < num_planes; i += planes_per_copy) { |
+ const int rows = VideoFrame::Rows(i, output_format_, size.height()); |
+ const int rows_per_copy = RowsPerCopy(i, output_format_, size.width()); |
copies += rows / rows_per_copy; |
if (rows % rows_per_copy) |
++copies; |
@@ -334,40 +396,51 @@ void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers( |
base::Bind(&PoolImpl::OnCopiesDone, this, video_frame, frame_resources, |
frame_ready_cb); |
base::Closure barrier = base::BarrierClosure(copies, copies_done); |
+ |
// Post all the async tasks. |
- for (size_t i = 0; i < dest_planes; ++i) { |
- int rows = VideoFrame::Rows(i, output_format_, size.height()); |
- int bytes_per_row = VideoFrame::RowBytes(i, output_format_, size.width()); |
- int rows_per_copy = |
- std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1); |
- |
- void* data = nullptr; |
- DCHECK_EQ(1u, gfx::NumberOfPlanesForBufferFormat( |
- GpuMemoryBufferFormat(output_format_, i))); |
- bool rv = frame_resources->plane_resources[i].gpu_memory_buffer->Map(&data); |
+ for (size_t i = 0; i < num_planes; i += planes_per_copy) { |
+ gfx::GpuMemoryBuffer* buffer = |
+ frame_resources->plane_resources[i].gpu_memory_buffer.get(); |
+ DCHECK(buffer); |
+ DCHECK_EQ(planes_per_copy, |
+ gfx::NumberOfPlanesForBufferFormat(buffer->GetFormat())); |
+ uint8* dest_buffers[VideoFrame::kMaxPlanes]; |
+ int dest_strides[VideoFrame::kMaxPlanes]; |
+ bool rv = buffer->Map(reinterpret_cast<void**>(dest_buffers)); |
DCHECK(rv); |
- uint8* mapped_buffer = static_cast<uint8*>(data); |
+ buffer->GetStride(dest_strides); |
- int dest_stride = 0; |
- frame_resources->plane_resources[i].gpu_memory_buffer->GetStride( |
- &dest_stride); |
+ const int rows = VideoFrame::Rows(i, output_format_, size.height()); |
+ const int rows_per_copy = RowsPerCopy(i, output_format_, 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: |
+ case PIXEL_FORMAT_I420: { |
+ const int bytes_per_row = |
+ VideoFrame::RowBytes(i, output_format_, size.width()); |
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)); |
+ base::Bind(&CopyRowsToI420Buffer, row, rows_to_copy, |
+ bytes_per_row, video_frame->data(i), |
+ video_frame->stride(i), dest_buffers[0], |
+ dest_strides[0], barrier)); |
+ break; |
+ } |
+ case PIXEL_FORMAT_NV12: |
+ worker_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&CopyRowsToNV12Buffer, row, rows_to_copy, |
+ size.width(), video_frame, dest_buffers[0], |
+ dest_strides[0], dest_buffers[1], dest_strides[1], |
+ 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)); |
+ base::Bind(&CopyRowsToUYVYBuffer, row, rows_to_copy, size.width(), |
+ video_frame, dest_buffers[0], dest_strides[0], |
+ barrier)); |
break; |
default: |
NOTREACHED(); |
@@ -387,16 +460,18 @@ void GpuMemoryBufferVideoFramePool::PoolImpl:: |
return; |
} |
- const size_t planes = VideoFrame::NumPlanes(output_format_); |
+ const size_t num_planes = VideoFrame::NumPlanes(output_format_); |
+ const size_t planes_per_copy = PlanesPerCopy(output_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) { |
+ for (size_t i = 0; i < num_planes; i += planes_per_copy) { |
PlaneResource& plane_resource = frame_resources->plane_resources[i]; |
+ DCHECK(plane_resource.gpu_memory_buffer); |
// Bind the texture and create or rebind the image. |
gles2->BindTexture(texture_target_, plane_resource.texture_id); |
- if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) { |
+ if (!plane_resource.image_id) { |
const size_t width = VideoFrame::Columns(i, output_format_, size.width()); |
const size_t height = VideoFrame::Rows(i, output_format_, size.height()); |
plane_resource.image_id = gles2->CreateImageCHROMIUM( |
@@ -415,9 +490,8 @@ void GpuMemoryBufferVideoFramePool::PoolImpl:: |
// mailboxes refer to will be used only after all the previous commands posted |
// in the command buffer have been processed. |
unsigned sync_point = gles2->InsertSyncPointCHROMIUM(); |
- for (size_t i = 0; i < planes; ++i) { |
+ for (size_t i = 0; i < num_planes; i += planes_per_copy) |
mailbox_holders[i].sync_point = sync_point; |
- } |
scoped_refptr<VideoFrame> frame; |
// Create the VideoFrame backed by native textures. |
@@ -433,9 +507,10 @@ void GpuMemoryBufferVideoFramePool::PoolImpl:: |
if (video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY)) |
frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); |
break; |
+ case PIXEL_FORMAT_NV12: |
case PIXEL_FORMAT_UYVY: |
frame = VideoFrame::WrapNativeTexture( |
- PIXEL_FORMAT_UYVY, mailbox_holders[VideoFrame::kYPlane], |
+ output_format_, mailbox_holders[VideoFrame::kYPlane], |
base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources), |
size, video_frame->visible_rect(), video_frame->natural_size(), |
video_frame->timestamp()); |
@@ -488,17 +563,18 @@ GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources( |
if (!gles2) |
return nullptr; |
gles2->ActiveTexture(GL_TEXTURE0); |
- size_t planes = VideoFrame::NumPlanes(format); |
+ size_t num_planes = VideoFrame::NumPlanes(format); |
FrameResources* frame_resources = new FrameResources(size); |
resources_pool_.push_back(frame_resources); |
- for (size_t i = 0; i < planes; ++i) { |
+ for (size_t i = 0; i < num_planes; i += PlanesPerCopy(format)) { |
PlaneResource& plane_resource = frame_resources->plane_resources[i]; |
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); |
+ const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i); |
plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer( |
- plane_size, GpuMemoryBufferFormat(format, i), gfx::BufferUsage::MAP); |
+ plane_size, buffer_format, gfx::BufferUsage::MAP); |
gles2->GenTextures(1, &plane_resource.texture_id); |
gles2->BindTexture(texture_target_, plane_resource.texture_id); |