| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/media/shell_video_data_allocator_common.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "cobalt/base/polymorphic_downcast.h" |
| |
| namespace media { |
| |
| using cobalt::render_tree::Image; |
| using cobalt::render_tree::ImageData; |
| using cobalt::render_tree::ImageDataDescriptor; |
| using cobalt::render_tree::kAlphaFormatPremultiplied; |
| using cobalt::render_tree::kAlphaFormatUnpremultiplied; |
| using cobalt::render_tree::kPixelFormatRGBA8; |
| using cobalt::render_tree::MultiPlaneImageDataDescriptor; |
| using cobalt::render_tree::ResourceProvider; |
| using cobalt::render_tree::kMultiPlaneImageFormatYUV2PlaneBT709; |
| using cobalt::render_tree::kMultiPlaneImageFormatYUV3PlaneBT709; |
| using cobalt::render_tree::kPixelFormatU8; |
| using cobalt::render_tree::kPixelFormatV8; |
| using cobalt::render_tree::kPixelFormatY8; |
| using cobalt::render_tree::kPixelFormatUV8; |
| |
| namespace { |
| void ReleaseImage(scoped_refptr<Image> /* image */) {} |
| } // namespace |
| |
| ShellVideoDataAllocatorCommon::ShellVideoDataAllocatorCommon( |
| ResourceProvider* resource_provider, size_t minimum_allocation_size, |
| size_t maximum_allocation_size, size_t minimum_alignment) |
| : resource_provider_(resource_provider), |
| minimum_allocation_size_(minimum_allocation_size), |
| maximum_allocation_size_(maximum_allocation_size), |
| minimum_alignment_(minimum_alignment) {} |
| |
| scoped_refptr<ShellVideoDataAllocator::FrameBuffer> |
| ShellVideoDataAllocatorCommon::AllocateFrameBuffer(size_t size, |
| size_t alignment) { |
| UNREFERENCED_PARAMETER(size); |
| UNREFERENCED_PARAMETER(alignment); |
| DCHECK_LE(size, maximum_allocation_size_); |
| if (size > maximum_allocation_size_) { |
| NOTREACHED(); |
| return NULL; |
| } |
| size = std::max(size, minimum_allocation_size_); |
| alignment = std::max(alignment, minimum_alignment_); |
| scoped_ptr<RawImageMemory> raw_image_memory = |
| resource_provider_->AllocateRawImageMemory(size, alignment); |
| |
| return raw_image_memory ? new FrameBufferCommon(raw_image_memory.Pass()) |
| : NULL; |
| } |
| |
| scoped_refptr<VideoFrame> ShellVideoDataAllocatorCommon::CreateYV12Frame( |
| const scoped_refptr<FrameBuffer>& frame_buffer, const YV12Param& param, |
| const base::TimeDelta& timestamp) { |
| scoped_refptr<FrameBufferCommon> frame_buffer_common = |
| base::polymorphic_downcast<FrameBufferCommon*>(frame_buffer.get()); |
| |
| DCHECK_LE(frame_buffer->data(), param.y_data()); |
| DCHECK_LE(param.y_data() + param.y_pitch() * param.decoded_height(), |
| param.u_data()); |
| DCHECK_LE(param.u_data() + param.uv_pitch() * param.decoded_height() / 2, |
| param.v_data()); |
| DCHECK_LE(param.v_data() + param.uv_pitch() * param.decoded_height() / 2, |
| frame_buffer->data() + frame_buffer->size()); |
| |
| // TODO: Ensure it work with visible_rect with non-zero left and |
| // top. Note that simply add offset to the image buffer may cause alignment |
| // issues. |
| gfx::Size plane_size(param.visible_rect().size()); |
| |
| // Create image data descriptor for the frame in I420. |
| MultiPlaneImageDataDescriptor descriptor( |
| kMultiPlaneImageFormatYUV3PlaneBT709); |
| descriptor.AddPlane( |
| param.y_data() - frame_buffer->data(), |
| ImageDataDescriptor(plane_size, kPixelFormatY8, |
| kAlphaFormatUnpremultiplied, param.y_pitch())); |
| plane_size.SetSize(plane_size.width() / 2, plane_size.height() / 2); |
| descriptor.AddPlane( |
| param.u_data() - frame_buffer->data(), |
| ImageDataDescriptor(plane_size, kPixelFormatU8, |
| kAlphaFormatUnpremultiplied, param.uv_pitch())); |
| descriptor.AddPlane( |
| param.v_data() - frame_buffer->data(), |
| ImageDataDescriptor(plane_size, kPixelFormatV8, |
| kAlphaFormatUnpremultiplied, param.uv_pitch())); |
| |
| scoped_refptr<Image> image = |
| resource_provider_->CreateMultiPlaneImageFromRawMemory( |
| frame_buffer_common->DetachRawImageMemory(), descriptor); |
| |
| gfx::Size visible_size(param.visible_rect().size()); |
| // The reference of the image is held by the closure that binds ReleaseImage, |
| // so it won't be freed before the ReleaseImage is called. |
| scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapNativeTexture( |
| reinterpret_cast<uintptr_t>(image.get()), 0, visible_size, |
| param.visible_rect(), visible_size, timestamp, VideoFrame::ReadPixelsCB(), |
| base::Bind(ReleaseImage, image)); |
| return video_frame; |
| } |
| |
| scoped_refptr<VideoFrame> ShellVideoDataAllocatorCommon::CreateNV12Frame( |
| const scoped_refptr<FrameBuffer>& frame_buffer, const NV12Param& param, |
| const base::TimeDelta& timestamp) { |
| scoped_refptr<FrameBufferCommon> frame_buffer_common = |
| base::polymorphic_downcast<FrameBufferCommon*>(frame_buffer.get()); |
| |
| // TODO: Ensure it work with visible_rect with non-zero left and |
| // top. Note that simply add offset to the image buffer may cause alignment |
| // issues. |
| gfx::Size plane_size(param.visible_rect().size()); |
| |
| // When plane_size.height() is only aligned to 2, the height of U/V plane |
| // will be odd, which is not supported on most platforms. Align height to 4 |
| // to work around this issue. |
| if (param.decoded_height() % 4 == 0 && plane_size.height() % 4 != 0) { |
| plane_size.set_height((plane_size.height() + 3) / 4 * 4); |
| } |
| |
| intptr_t offset = 0; |
| int pitch_in_bytes = param.y_pitch(); |
| |
| DCHECK_EQ(pitch_in_bytes % 2, 0) << pitch_in_bytes << " has to be even."; |
| |
| // Create image data descriptor for the frame in NV12. |
| MultiPlaneImageDataDescriptor descriptor( |
| kMultiPlaneImageFormatYUV2PlaneBT709); |
| descriptor.AddPlane( |
| offset, ImageDataDescriptor(plane_size, kPixelFormatY8, |
| kAlphaFormatPremultiplied, pitch_in_bytes)); |
| offset += pitch_in_bytes * param.decoded_height(); |
| plane_size.SetSize(plane_size.width() / 2, plane_size.height() / 2); |
| descriptor.AddPlane( |
| offset, ImageDataDescriptor(plane_size, kPixelFormatUV8, |
| kAlphaFormatPremultiplied, pitch_in_bytes)); |
| offset += pitch_in_bytes * param.decoded_height() / 2; |
| DCHECK_EQ(offset, pitch_in_bytes * param.decoded_height() * 3 / 2); |
| |
| scoped_refptr<Image> image = |
| resource_provider_->CreateMultiPlaneImageFromRawMemory( |
| frame_buffer_common->DetachRawImageMemory(), descriptor); |
| |
| gfx::Size visible_size(param.visible_rect().size()); |
| // The reference of the image is held by the closure that binds ReleaseImage, |
| // so it won't be freed before the ReleaseImage is called. |
| scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapNativeTexture( |
| reinterpret_cast<uintptr_t>(image.get()), 0, visible_size, |
| param.visible_rect(), visible_size, timestamp, VideoFrame::ReadPixelsCB(), |
| base::Bind(ReleaseImage, image)); |
| return video_frame; |
| } |
| |
| ShellVideoDataAllocatorCommon::FrameBufferCommon::FrameBufferCommon( |
| scoped_ptr<RawImageMemory> raw_image_memory) |
| : raw_image_memory_(raw_image_memory.Pass()) { |
| DCHECK(raw_image_memory_); |
| } |
| |
| scoped_ptr<ShellVideoDataAllocatorCommon::RawImageMemory> |
| ShellVideoDataAllocatorCommon::FrameBufferCommon::DetachRawImageMemory() { |
| DCHECK(raw_image_memory_); |
| return raw_image_memory_.Pass(); |
| } |
| |
| } // namespace media |