blob: 8cbb35c4b5571b62fa4148d393589e29116e776a [file] [log] [blame]
// 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