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