| /* Copyright 2017 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "arc/cached_frame.h" |
| |
| #include <cerrno> |
| |
| #include <libyuv.h> |
| #include "arc/common.h" |
| |
| namespace arc { |
| |
| using android::CameraMetadata; |
| |
| CachedFrame::CachedFrame() |
| : source_frame_(nullptr), |
| cropped_buffer_capacity_(0), |
| yu12_frame_(new AllocatedFrameBuffer(0)), |
| scaled_frame_(new AllocatedFrameBuffer(0)) {} |
| |
| CachedFrame::~CachedFrame() { UnsetSource(); } |
| |
| int CachedFrame::SetSource(const FrameBuffer* frame, int rotate_degree) { |
| source_frame_ = frame; |
| int res = ConvertToYU12(); |
| if (res != 0) { |
| return res; |
| } |
| |
| if (rotate_degree > 0) { |
| res = CropRotateScale(rotate_degree); |
| } |
| return res; |
| } |
| |
| void CachedFrame::UnsetSource() { source_frame_ = nullptr; } |
| |
| uint8_t* CachedFrame::GetSourceBuffer() const { |
| return source_frame_->GetData(); |
| } |
| |
| size_t CachedFrame::GetSourceDataSize() const { |
| return source_frame_->GetDataSize(); |
| } |
| |
| uint32_t CachedFrame::GetSourceFourCC() const { |
| return source_frame_->GetFourcc(); |
| } |
| |
| uint8_t* CachedFrame::GetCachedBuffer() const { return yu12_frame_->GetData(); } |
| |
| uint32_t CachedFrame::GetCachedFourCC() const { |
| return yu12_frame_->GetFourcc(); |
| } |
| |
| uint32_t CachedFrame::GetWidth() const { return yu12_frame_->GetWidth(); } |
| |
| uint32_t CachedFrame::GetHeight() const { return yu12_frame_->GetHeight(); } |
| |
| size_t CachedFrame::GetConvertedSize(int fourcc) const { |
| return ImageProcessor::GetConvertedSize(fourcc, yu12_frame_->GetWidth(), |
| yu12_frame_->GetHeight()); |
| } |
| |
| int CachedFrame::Convert(const CameraMetadata& metadata, FrameBuffer* out_frame, |
| bool video_hack) { |
| if (video_hack && out_frame->GetFourcc() == V4L2_PIX_FMT_YVU420) { |
| out_frame->SetFourcc(V4L2_PIX_FMT_YUV420); |
| } |
| |
| FrameBuffer* source_frame = yu12_frame_.get(); |
| if (GetWidth() != out_frame->GetWidth() || |
| GetHeight() != out_frame->GetHeight()) { |
| size_t cache_size = ImageProcessor::GetConvertedSize( |
| yu12_frame_->GetFourcc(), out_frame->GetWidth(), |
| out_frame->GetHeight()); |
| if (cache_size == 0) { |
| return -EINVAL; |
| } else if (cache_size > scaled_frame_->GetBufferSize()) { |
| scaled_frame_.reset(new AllocatedFrameBuffer(cache_size)); |
| } |
| scaled_frame_->SetWidth(out_frame->GetWidth()); |
| scaled_frame_->SetHeight(out_frame->GetHeight()); |
| ImageProcessor::Scale(*yu12_frame_.get(), scaled_frame_.get()); |
| |
| source_frame = scaled_frame_.get(); |
| } |
| return ImageProcessor::ConvertFormat(metadata, *source_frame, out_frame); |
| } |
| |
| int CachedFrame::ConvertToYU12() { |
| size_t cache_size = ImageProcessor::GetConvertedSize( |
| V4L2_PIX_FMT_YUV420, source_frame_->GetWidth(), |
| source_frame_->GetHeight()); |
| if (cache_size == 0) { |
| return -EINVAL; |
| } |
| yu12_frame_->SetDataSize(cache_size); |
| yu12_frame_->SetFourcc(V4L2_PIX_FMT_YUV420); |
| yu12_frame_->SetWidth(source_frame_->GetWidth()); |
| yu12_frame_->SetHeight(source_frame_->GetHeight()); |
| |
| int res = ImageProcessor::ConvertFormat(CameraMetadata(), *source_frame_, |
| yu12_frame_.get()); |
| if (res) { |
| LOGF(ERROR) << "Convert from " << FormatToString(source_frame_->GetFourcc()) |
| << " to YU12 fails."; |
| return res; |
| } |
| return 0; |
| } |
| |
| int CachedFrame::CropRotateScale(int rotate_degree) { |
| // TODO(henryhsu): Move libyuv part to ImageProcessor. |
| if (yu12_frame_->GetHeight() % 2 != 0 || yu12_frame_->GetWidth() % 2 != 0) { |
| LOGF(ERROR) << "yu12_frame_ has odd dimension: " << yu12_frame_->GetWidth() |
| << "x" << yu12_frame_->GetHeight(); |
| return -EINVAL; |
| } |
| |
| if (yu12_frame_->GetHeight() > yu12_frame_->GetWidth()) { |
| LOGF(ERROR) << "yu12_frame_ is tall frame already: " |
| << yu12_frame_->GetWidth() << "x" << yu12_frame_->GetHeight(); |
| return -EINVAL; |
| } |
| |
| // Step 1: Crop and rotate |
| // |
| // Original frame Cropped frame Rotated frame |
| // -------------------- -------- |
| // | | | | | | --------------- |
| // | | | | | | | | |
| // | | | | =======>> | | =======>> | | |
| // | | | | | | --------------- |
| // | | | | | | |
| // -------------------- -------- |
| // |
| int cropped_width = yu12_frame_->GetHeight() * yu12_frame_->GetHeight() / |
| yu12_frame_->GetWidth(); |
| if (cropped_width % 2 == 1) { |
| // Make cropped_width to the closest even number. |
| cropped_width++; |
| } |
| int cropped_height = yu12_frame_->GetHeight(); |
| int margin = (yu12_frame_->GetWidth() - cropped_width) / 2; |
| |
| int rotated_height = cropped_width; |
| int rotated_width = cropped_height; |
| |
| int rotated_y_stride = rotated_width; |
| int rotated_uv_stride = rotated_width / 2; |
| size_t rotated_size = |
| rotated_y_stride * rotated_height + rotated_uv_stride * rotated_height; |
| if (rotated_size > cropped_buffer_capacity_) { |
| cropped_buffer_.reset(new uint8_t[rotated_size]); |
| cropped_buffer_capacity_ = rotated_size; |
| } |
| uint8_t* rotated_y_plane = cropped_buffer_.get(); |
| uint8_t* rotated_u_plane = |
| rotated_y_plane + rotated_y_stride * rotated_height; |
| uint8_t* rotated_v_plane = |
| rotated_u_plane + rotated_uv_stride * rotated_height / 2; |
| libyuv::RotationMode rotation_mode = libyuv::RotationMode::kRotate90; |
| switch (rotate_degree) { |
| case 90: |
| rotation_mode = libyuv::RotationMode::kRotate90; |
| break; |
| case 270: |
| rotation_mode = libyuv::RotationMode::kRotate270; |
| break; |
| default: |
| LOGF(ERROR) << "Invalid rotation degree: " << rotate_degree; |
| return -EINVAL; |
| } |
| // This libyuv method first crops the frame and then rotates it 90 degrees |
| // clockwise. |
| int res = libyuv::ConvertToI420( |
| yu12_frame_->GetData(), yu12_frame_->GetDataSize(), rotated_y_plane, |
| rotated_y_stride, rotated_u_plane, rotated_uv_stride, rotated_v_plane, |
| rotated_uv_stride, margin, 0, yu12_frame_->GetWidth(), |
| yu12_frame_->GetHeight(), cropped_width, cropped_height, rotation_mode, |
| libyuv::FourCC::FOURCC_I420); |
| |
| if (res) { |
| LOGF(ERROR) << "ConvertToI420 failed: " << res; |
| return res; |
| } |
| |
| // Step 2: Scale |
| // |
| // Final frame |
| // Rotated frame --------------------- |
| // -------------- | | |
| // | | =====>> | | |
| // | | | | |
| // -------------- | | |
| // | | |
| // --------------------- |
| // |
| // |
| res = libyuv::I420Scale( |
| rotated_y_plane, rotated_y_stride, rotated_u_plane, rotated_uv_stride, |
| rotated_v_plane, rotated_uv_stride, rotated_width, rotated_height, |
| yu12_frame_->GetData(), yu12_frame_->GetWidth(), |
| yu12_frame_->GetData() + |
| yu12_frame_->GetWidth() * yu12_frame_->GetHeight(), |
| yu12_frame_->GetWidth() / 2, |
| yu12_frame_->GetData() + |
| yu12_frame_->GetWidth() * yu12_frame_->GetHeight() * 5 / 4, |
| yu12_frame_->GetWidth() / 2, yu12_frame_->GetWidth(), |
| yu12_frame_->GetHeight(), libyuv::FilterMode::kFilterNone); |
| LOGF_IF(ERROR, res) << "I420Scale failed: " << res; |
| return res; |
| } |
| |
| } // namespace arc |