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 <cerrno>
9 
10 #include <libyuv.h>
11 #include "arc/common.h"
12 
13 namespace arc {
14 
15 using android::CameraMetadata;
16 
CachedFrame()17 CachedFrame::CachedFrame()
18     : source_frame_(nullptr),
19       cropped_buffer_capacity_(0),
20       yu12_frame_(new AllocatedFrameBuffer(0)),
21       scaled_frame_(new AllocatedFrameBuffer(0)) {}
22 
~CachedFrame()23 CachedFrame::~CachedFrame() { UnsetSource(); }
24 
SetSource(const FrameBuffer * frame,int rotate_degree)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 
UnsetSource()38 void CachedFrame::UnsetSource() { source_frame_ = nullptr; }
39 
GetSourceBuffer() const40 uint8_t* CachedFrame::GetSourceBuffer() const {
41   return source_frame_->GetData();
42 }
43 
GetSourceDataSize() const44 size_t CachedFrame::GetSourceDataSize() const {
45   return source_frame_->GetDataSize();
46 }
47 
GetSourceFourCC() const48 uint32_t CachedFrame::GetSourceFourCC() const {
49   return source_frame_->GetFourcc();
50 }
51 
GetCachedBuffer() const52 uint8_t* CachedFrame::GetCachedBuffer() const { return yu12_frame_->GetData(); }
53 
GetCachedFourCC() const54 uint32_t CachedFrame::GetCachedFourCC() const {
55   return yu12_frame_->GetFourcc();
56 }
57 
GetWidth() const58 uint32_t CachedFrame::GetWidth() const { return yu12_frame_->GetWidth(); }
59 
GetHeight() const60 uint32_t CachedFrame::GetHeight() const { return yu12_frame_->GetHeight(); }
61 
GetConvertedSize(int fourcc) const62 size_t CachedFrame::GetConvertedSize(int fourcc) const {
63   return ImageProcessor::GetConvertedSize(fourcc, yu12_frame_->GetWidth(),
64                                           yu12_frame_->GetHeight());
65 }
66 
Convert(const CameraMetadata & metadata,FrameBuffer * out_frame,bool video_hack)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 
ConvertToYU12()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 
CropRotateScale(int rotate_degree)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