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