Home | History | Annotate | Download | only in arc
      1 /* Copyright 2017 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include "arc/cached_frame.h"
      7 
      8 #include <errno.h>
      9 #include <libyuv.h>
     10 
     11 #include "arc/common.h"
     12 #include "arc/common_types.h"
     13 
     14 namespace arc {
     15 
     16 using android::CameraMetadata;
     17 
     18 CachedFrame::CachedFrame()
     19     : source_frame_(nullptr),
     20       cropped_buffer_capacity_(0),
     21       yu12_frame_(new AllocatedFrameBuffer(0)) {}
     22 
     23 CachedFrame::~CachedFrame() { UnsetSource(); }
     24 
     25 int CachedFrame::SetSource(const FrameBuffer* frame, int rotate_degree) {
     26   source_frame_ = frame;
     27   int res = ConvertToYU12();
     28   if (res != 0) {
     29     return res;
     30   }
     31 
     32   if (rotate_degree > 0) {
     33     res = CropRotateScale(rotate_degree);
     34   }
     35   return res;
     36 }
     37 
     38 void CachedFrame::UnsetSource() { source_frame_ = nullptr; }
     39 
     40 uint8_t* CachedFrame::GetSourceBuffer() const {
     41   return source_frame_->GetData();
     42 }
     43 
     44 size_t CachedFrame::GetSourceDataSize() const {
     45   return source_frame_->GetDataSize();
     46 }
     47 
     48 uint32_t CachedFrame::GetSourceFourCC() const {
     49   return source_frame_->GetFourcc();
     50 }
     51 
     52 uint8_t* CachedFrame::GetCachedBuffer() const { return yu12_frame_->GetData(); }
     53 
     54 uint32_t CachedFrame::GetCachedFourCC() const {
     55   return yu12_frame_->GetFourcc();
     56 }
     57 
     58 uint32_t CachedFrame::GetWidth() const { return yu12_frame_->GetWidth(); }
     59 
     60 uint32_t CachedFrame::GetHeight() const { return yu12_frame_->GetHeight(); }
     61 
     62 size_t CachedFrame::GetConvertedSize(int fourcc) const {
     63   return ImageProcessor::GetConvertedSize(fourcc, yu12_frame_->GetWidth(),
     64                                           yu12_frame_->GetHeight());
     65 }
     66 
     67 int CachedFrame::Convert(const CameraMetadata& metadata, FrameBuffer* out_frame,
     68                          bool video_hack) {
     69   if (video_hack && out_frame->GetFourcc() == V4L2_PIX_FMT_YVU420) {
     70     out_frame->SetFourcc(V4L2_PIX_FMT_YUV420);
     71   }
     72 
     73   FrameBuffer* source_frame = yu12_frame_.get();
     74   if (GetWidth() != out_frame->GetWidth() ||
     75       GetHeight() != out_frame->GetHeight()) {
     76     size_t cache_size = ImageProcessor::GetConvertedSize(
     77         yu12_frame_->GetFourcc(), out_frame->GetWidth(),
     78         out_frame->GetHeight());
     79     if (cache_size == 0) {
     80       return -EINVAL;
     81     } else if (cache_size > scaled_frame_->GetBufferSize()) {
     82       scaled_frame_.reset(new AllocatedFrameBuffer(cache_size));
     83     }
     84     scaled_frame_->SetWidth(out_frame->GetWidth());
     85     scaled_frame_->SetHeight(out_frame->GetHeight());
     86     ImageProcessor::Scale(*yu12_frame_.get(), scaled_frame_.get());
     87 
     88     source_frame = scaled_frame_.get();
     89   }
     90   return ImageProcessor::ConvertFormat(metadata, *source_frame, out_frame);
     91 }
     92 
     93 int CachedFrame::ConvertToYU12() {
     94   size_t cache_size = ImageProcessor::GetConvertedSize(
     95       V4L2_PIX_FMT_YUV420, source_frame_->GetWidth(),
     96       source_frame_->GetHeight());
     97   if (cache_size == 0) {
     98     return -EINVAL;
     99   }
    100   yu12_frame_->SetDataSize(cache_size);
    101   yu12_frame_->SetFourcc(V4L2_PIX_FMT_YUV420);
    102   yu12_frame_->SetWidth(source_frame_->GetWidth());
    103   yu12_frame_->SetHeight(source_frame_->GetHeight());
    104 
    105   int res = ImageProcessor::ConvertFormat(CameraMetadata(), *source_frame_,
    106                                           yu12_frame_.get());
    107   if (res) {
    108     LOGF(ERROR) << "Convert from " << FormatToString(source_frame_->GetFourcc())
    109                 << " to YU12 fails.";
    110     return res;
    111   }
    112   return 0;
    113 }
    114 
    115 int CachedFrame::CropRotateScale(int rotate_degree) {
    116   // TODO(henryhsu): Move libyuv part to ImageProcessor.
    117   if (yu12_frame_->GetHeight() % 2 != 0 || yu12_frame_->GetWidth() % 2 != 0) {
    118     LOGF(ERROR) << "yu12_frame_ has odd dimension: " << yu12_frame_->GetWidth()
    119                 << "x" << yu12_frame_->GetHeight();
    120     return -EINVAL;
    121   }
    122 
    123   if (yu12_frame_->GetHeight() > yu12_frame_->GetWidth()) {
    124     LOGF(ERROR) << "yu12_frame_ is tall frame already: "
    125                 << yu12_frame_->GetWidth() << "x" << yu12_frame_->GetHeight();
    126     return -EINVAL;
    127   }
    128 
    129   // Step 1: Crop and rotate
    130   //
    131   //   Original frame                  Cropped frame              Rotated frame
    132   // --------------------               --------
    133   // |     |      |     |               |      |                 ---------------
    134   // |     |      |     |               |      |                 |             |
    135   // |     |      |     |   =======>>   |      |     =======>>   |             |
    136   // |     |      |     |               |      |                 ---------------
    137   // |     |      |     |               |      |
    138   // --------------------               --------
    139   //
    140   int cropped_width = yu12_frame_->GetHeight() * yu12_frame_->GetHeight() /
    141                       yu12_frame_->GetWidth();
    142   if (cropped_width % 2 == 1) {
    143     // Make cropped_width to the closest even number.
    144     cropped_width++;
    145   }
    146   int cropped_height = yu12_frame_->GetHeight();
    147   int margin = (yu12_frame_->GetWidth() - cropped_width) / 2;
    148 
    149   int rotated_height = cropped_width;
    150   int rotated_width = cropped_height;
    151 
    152   int rotated_y_stride = rotated_width;
    153   int rotated_uv_stride = rotated_width / 2;
    154   size_t rotated_size =
    155       rotated_y_stride * rotated_height + rotated_uv_stride * rotated_height;
    156   if (rotated_size > cropped_buffer_capacity_) {
    157     cropped_buffer_.reset(new uint8_t[rotated_size]);
    158     cropped_buffer_capacity_ = rotated_size;
    159   }
    160   uint8_t* rotated_y_plane = cropped_buffer_.get();
    161   uint8_t* rotated_u_plane =
    162       rotated_y_plane + rotated_y_stride * rotated_height;
    163   uint8_t* rotated_v_plane =
    164       rotated_u_plane + rotated_uv_stride * rotated_height / 2;
    165   libyuv::RotationMode rotation_mode = libyuv::RotationMode::kRotate90;
    166   switch (rotate_degree) {
    167     case 90:
    168       rotation_mode = libyuv::RotationMode::kRotate90;
    169       break;
    170     case 270:
    171       rotation_mode = libyuv::RotationMode::kRotate270;
    172       break;
    173     default:
    174       LOGF(ERROR) << "Invalid rotation degree: " << rotate_degree;
    175       return -EINVAL;
    176   }
    177   // This libyuv method first crops the frame and then rotates it 90 degrees
    178   // clockwise.
    179   int res = libyuv::ConvertToI420(
    180       yu12_frame_->GetData(), yu12_frame_->GetDataSize(), rotated_y_plane,
    181       rotated_y_stride, rotated_u_plane, rotated_uv_stride, rotated_v_plane,
    182       rotated_uv_stride, margin, 0, yu12_frame_->GetWidth(),
    183       yu12_frame_->GetHeight(), cropped_width, cropped_height, rotation_mode,
    184       libyuv::FourCC::FOURCC_I420);
    185 
    186   if (res) {
    187     LOGF(ERROR) << "ConvertToI420 failed: " << res;
    188     return res;
    189   }
    190 
    191   // Step 2: Scale
    192   //
    193   //                               Final frame
    194   //  Rotated frame            ---------------------
    195   // --------------            |                   |
    196   // |            |  =====>>   |                   |
    197   // |            |            |                   |
    198   // --------------            |                   |
    199   //                           |                   |
    200   //                           ---------------------
    201   //
    202   //
    203   res = libyuv::I420Scale(
    204       rotated_y_plane, rotated_y_stride, rotated_u_plane, rotated_uv_stride,
    205       rotated_v_plane, rotated_uv_stride, rotated_width, rotated_height,
    206       yu12_frame_->GetData(), yu12_frame_->GetWidth(),
    207       yu12_frame_->GetData() +
    208           yu12_frame_->GetWidth() * yu12_frame_->GetHeight(),
    209       yu12_frame_->GetWidth() / 2,
    210       yu12_frame_->GetData() +
    211           yu12_frame_->GetWidth() * yu12_frame_->GetHeight() * 5 / 4,
    212       yu12_frame_->GetWidth() / 2, yu12_frame_->GetWidth(),
    213       yu12_frame_->GetHeight(), libyuv::FilterMode::kFilterNone);
    214   LOGF_IF(ERROR, res) << "I420Scale failed: " << res;
    215   return res;
    216 }
    217 
    218 }  // namespace arc
    219