C++程序  |  219行  |  7.43 KB

/* 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