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/image_processor.h" 7 8 #include <cerrno> 9 #include <ctime> 10 #include <string> 11 12 #include <libyuv.h> 13 #include "arc/common.h" 14 #include "arc/exif_utils.h" 15 #include "arc/jpeg_compressor.h" 16 17 namespace arc { 18 19 using android::CameraMetadata; 20 21 /* 22 * Formats have different names in different header files. Here is the mapping 23 * table: 24 * 25 * android_pixel_format_t videodev2.h FOURCC in libyuv 26 * ----------------------------------------------------------------------------- 27 * HAL_PIXEL_FORMAT_YV12 = V4L2_PIX_FMT_YVU420 = FOURCC_YV12 28 * HAL_PIXEL_FORMAT_YCrCb_420_SP = V4L2_PIX_FMT_NV21 = FOURCC_NV21 29 * HAL_PIXEL_FORMAT_RGBA_8888 = V4L2_PIX_FMT_RGB32 = FOURCC_BGR4 30 * HAL_PIXEL_FORMAT_YCbCr_422_I = V4L2_PIX_FMT_YUYV = FOURCC_YUYV 31 * = FOURCC_YUY2 32 * V4L2_PIX_FMT_YUV420 = FOURCC_I420 33 * = FOURCC_YU12 34 * V4L2_PIX_FMT_MJPEG = FOURCC_MJPG 35 * 36 * Camera device generates FOURCC_YUYV and FOURCC_MJPG. 37 * Preview needs FOURCC_ARGB format. 38 * Software video encoder needs FOURCC_YU12. 39 * CTS requires FOURCC_YV12 and FOURCC_NV21 for applications. 40 * 41 * Android stride requirement: 42 * YV12 horizontal stride should be a multiple of 16 pixels. See 43 * android.graphics.ImageFormat.YV12. 44 * The stride of ARGB, YU12, and NV21 are always equal to the width. 45 * 46 * Conversion Path: 47 * MJPG/YUYV (from camera) -> YU12 -> ARGB (preview) 48 * -> NV21 (apps) 49 * -> YV12 (apps) 50 * -> YU12 (video encoder) 51 */ 52 53 // YV12 horizontal stride should be a multiple of 16 pixels for each plane. 54 // |dst_stride_uv| is the pixel stride of u or v plane. 55 static int YU12ToYV12(const void* yv12, void* yu12, int width, int height, 56 int dst_stride_y, int dst_stride_uv); 57 static int YU12ToNV21(const void* yv12, void* nv21, int width, int height); 58 static bool ConvertToJpeg(const CameraMetadata& metadata, 59 const FrameBuffer& in_frame, FrameBuffer* out_frame); 60 static bool SetExifTags(const CameraMetadata& metadata, ExifUtils* utils); 61 62 // How precise the float-to-rational conversion for EXIF tags would be. 63 static const int kRationalPrecision = 10000; 64 65 // Default JPEG quality settings. 66 static const int DEFAULT_JPEG_QUALITY = 80; 67 68 inline static size_t Align16(size_t value) { return (value + 15) & ~15; } 69 70 size_t ImageProcessor::GetConvertedSize(int fourcc, uint32_t width, 71 uint32_t height) { 72 if ((width % 2) || (height % 2)) { 73 LOGF(ERROR) << "Width or height is not even (" << width << " x " << height 74 << ")"; 75 return 0; 76 } 77 78 switch (fourcc) { 79 case V4L2_PIX_FMT_YVU420: // YV12 80 return Align16(width) * height + Align16(width / 2) * height; 81 case V4L2_PIX_FMT_YUV420: // YU12 82 // Fall-through. 83 case V4L2_PIX_FMT_NV21: // NV21 84 return width * height * 3 / 2; 85 case V4L2_PIX_FMT_BGR32: 86 case V4L2_PIX_FMT_RGB32: 87 return width * height * 4; 88 case V4L2_PIX_FMT_JPEG: 89 return 0; // For JPEG real size will be calculated after conversion. 90 default: 91 LOGF(ERROR) << "Pixel format " << FormatToString(fourcc) 92 << " is unsupported."; 93 return 0; 94 } 95 } 96 97 bool ImageProcessor::SupportsConversion(uint32_t from_fourcc, 98 uint32_t to_fourcc) { 99 switch (from_fourcc) { 100 case V4L2_PIX_FMT_YUYV: 101 return (to_fourcc == V4L2_PIX_FMT_YUV420); 102 case V4L2_PIX_FMT_YUV420: 103 return ( 104 to_fourcc == V4L2_PIX_FMT_YUV420 || 105 to_fourcc == V4L2_PIX_FMT_YVU420 || to_fourcc == V4L2_PIX_FMT_NV21 || 106 to_fourcc == V4L2_PIX_FMT_RGB32 || to_fourcc == V4L2_PIX_FMT_BGR32 || 107 to_fourcc == V4L2_PIX_FMT_JPEG); 108 case V4L2_PIX_FMT_MJPEG: 109 return (to_fourcc == V4L2_PIX_FMT_YUV420); 110 default: 111 return false; 112 } 113 } 114 115 int ImageProcessor::ConvertFormat(const CameraMetadata& metadata, 116 const FrameBuffer& in_frame, 117 FrameBuffer* out_frame) { 118 if ((in_frame.GetWidth() % 2) || (in_frame.GetHeight() % 2)) { 119 LOGF(ERROR) << "Width or height is not even (" << in_frame.GetWidth() 120 << " x " << in_frame.GetHeight() << ")"; 121 return -EINVAL; 122 } 123 124 size_t data_size = GetConvertedSize( 125 out_frame->GetFourcc(), in_frame.GetWidth(), in_frame.GetHeight()); 126 127 if (out_frame->SetDataSize(data_size)) { 128 LOGF(ERROR) << "Set data size failed"; 129 return -EINVAL; 130 } 131 132 if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUYV) { 133 switch (out_frame->GetFourcc()) { 134 case V4L2_PIX_FMT_YUV420: // YU12 135 { 136 int res = libyuv::YUY2ToI420( 137 in_frame.GetData(), /* src_yuy2 */ 138 in_frame.GetWidth() * 2, /* src_stride_yuy2 */ 139 out_frame->GetData(), /* dst_y */ 140 out_frame->GetWidth(), /* dst_stride_y */ 141 out_frame->GetData() + 142 out_frame->GetWidth() * out_frame->GetHeight(), /* dst_u */ 143 out_frame->GetWidth() / 2, /* dst_stride_u */ 144 out_frame->GetData() + out_frame->GetWidth() * 145 out_frame->GetHeight() * 5 / 146 4, /* dst_v */ 147 out_frame->GetWidth() / 2, /* dst_stride_v */ 148 in_frame.GetWidth(), in_frame.GetHeight()); 149 LOGF_IF(ERROR, res) << "YUY2ToI420() for YU12 returns " << res; 150 return res ? -EINVAL : 0; 151 } 152 default: 153 LOGF(ERROR) << "Destination pixel format " 154 << FormatToString(out_frame->GetFourcc()) 155 << " is unsupported for YUYV source format."; 156 return -EINVAL; 157 } 158 } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420) { 159 // V4L2_PIX_FMT_YVU420 is YV12. I420 is usually referred to YU12 160 // (V4L2_PIX_FMT_YUV420), and YV12 is similar to YU12 except that U/V 161 // planes are swapped. 162 switch (out_frame->GetFourcc()) { 163 case V4L2_PIX_FMT_YVU420: // YV12 164 { 165 int ystride = Align16(in_frame.GetWidth()); 166 int uvstride = Align16(in_frame.GetWidth() / 2); 167 int res = YU12ToYV12(in_frame.GetData(), out_frame->GetData(), 168 in_frame.GetWidth(), in_frame.GetHeight(), ystride, 169 uvstride); 170 LOGF_IF(ERROR, res) << "YU12ToYV12() returns " << res; 171 return res ? -EINVAL : 0; 172 } 173 case V4L2_PIX_FMT_YUV420: // YU12 174 { 175 memcpy(out_frame->GetData(), in_frame.GetData(), 176 in_frame.GetDataSize()); 177 return 0; 178 } 179 case V4L2_PIX_FMT_NV21: // NV21 180 { 181 // TODO(henryhsu): Use libyuv::I420ToNV21. 182 int res = YU12ToNV21(in_frame.GetData(), out_frame->GetData(), 183 in_frame.GetWidth(), in_frame.GetHeight()); 184 LOGF_IF(ERROR, res) << "YU12ToNV21() returns " << res; 185 return res ? -EINVAL : 0; 186 } 187 case V4L2_PIX_FMT_BGR32: { 188 int res = libyuv::I420ToABGR( 189 in_frame.GetData(), /* src_y */ 190 in_frame.GetWidth(), /* src_stride_y */ 191 in_frame.GetData() + 192 in_frame.GetWidth() * in_frame.GetHeight(), /* src_u */ 193 in_frame.GetWidth() / 2, /* src_stride_u */ 194 in_frame.GetData() + 195 in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, /* src_v */ 196 in_frame.GetWidth() / 2, /* src_stride_v */ 197 out_frame->GetData(), /* dst_abgr */ 198 out_frame->GetWidth() * 4, /* dst_stride_abgr */ 199 in_frame.GetWidth(), in_frame.GetHeight()); 200 LOGF_IF(ERROR, res) << "I420ToABGR() returns " << res; 201 return res ? -EINVAL : 0; 202 } 203 case V4L2_PIX_FMT_RGB32: { 204 int res = libyuv::I420ToARGB( 205 in_frame.GetData(), /* src_y */ 206 in_frame.GetWidth(), /* src_stride_y */ 207 in_frame.GetData() + 208 in_frame.GetWidth() * in_frame.GetHeight(), /* src_u */ 209 in_frame.GetWidth() / 2, /* src_stride_u */ 210 in_frame.GetData() + 211 in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, /* src_v */ 212 in_frame.GetWidth() / 2, /* src_stride_v */ 213 out_frame->GetData(), /* dst_argb */ 214 out_frame->GetWidth() * 4, /* dst_stride_argb */ 215 in_frame.GetWidth(), in_frame.GetHeight()); 216 LOGF_IF(ERROR, res) << "I420ToARGB() returns " << res; 217 return res ? -EINVAL : 0; 218 } 219 case V4L2_PIX_FMT_JPEG: { 220 bool res = ConvertToJpeg(metadata, in_frame, out_frame); 221 LOGF_IF(ERROR, !res) << "ConvertToJpeg() returns " << res; 222 return res ? -EINVAL : 0; 223 } 224 default: 225 LOGF(ERROR) << "Destination pixel format " 226 << FormatToString(out_frame->GetFourcc()) 227 << " is unsupported for YU12 source format."; 228 return -EINVAL; 229 } 230 } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_MJPEG) { 231 switch (out_frame->GetFourcc()) { 232 case V4L2_PIX_FMT_YUV420: // YU12 233 { 234 int res = libyuv::MJPGToI420( 235 in_frame.GetData(), /* sample */ 236 in_frame.GetDataSize(), /* sample_size */ 237 out_frame->GetData(), /* dst_y */ 238 out_frame->GetWidth(), /* dst_stride_y */ 239 out_frame->GetData() + 240 out_frame->GetWidth() * out_frame->GetHeight(), /* dst_u */ 241 out_frame->GetWidth() / 2, /* dst_stride_u */ 242 out_frame->GetData() + out_frame->GetWidth() * 243 out_frame->GetHeight() * 5 / 244 4, /* dst_v */ 245 out_frame->GetWidth() / 2, /* dst_stride_v */ 246 in_frame.GetWidth(), in_frame.GetHeight(), out_frame->GetWidth(), 247 out_frame->GetHeight()); 248 LOGF_IF(ERROR, res) << "MJPEGToI420() returns " << res; 249 return res ? -EINVAL : 0; 250 } 251 default: 252 LOGF(ERROR) << "Destination pixel format " 253 << FormatToString(out_frame->GetFourcc()) 254 << " is unsupported for MJPEG source format."; 255 return -EINVAL; 256 } 257 } else { 258 LOGF(ERROR) << "Convert format doesn't support source format " 259 << FormatToString(in_frame.GetFourcc()); 260 return -EINVAL; 261 } 262 } 263 264 int ImageProcessor::Scale(const FrameBuffer& in_frame, FrameBuffer* out_frame) { 265 if (in_frame.GetFourcc() != V4L2_PIX_FMT_YUV420) { 266 LOGF(ERROR) << "Pixel format " << FormatToString(in_frame.GetFourcc()) 267 << " is unsupported."; 268 return -EINVAL; 269 } 270 271 size_t data_size = GetConvertedSize( 272 in_frame.GetFourcc(), out_frame->GetWidth(), out_frame->GetHeight()); 273 274 if (out_frame->SetDataSize(data_size)) { 275 LOGF(ERROR) << "Set data size failed"; 276 return -EINVAL; 277 } 278 out_frame->SetFourcc(in_frame.GetFourcc()); 279 280 VLOGF(1) << "Scale image from " << in_frame.GetWidth() << "x" 281 << in_frame.GetHeight() << " to " << out_frame->GetWidth() << "x" 282 << out_frame->GetHeight(); 283 284 int ret = libyuv::I420Scale( 285 in_frame.GetData(), in_frame.GetWidth(), 286 in_frame.GetData() + in_frame.GetWidth() * in_frame.GetHeight(), 287 in_frame.GetWidth() / 2, 288 in_frame.GetData() + in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, 289 in_frame.GetWidth() / 2, in_frame.GetWidth(), in_frame.GetHeight(), 290 out_frame->GetData(), out_frame->GetWidth(), 291 out_frame->GetData() + out_frame->GetWidth() * out_frame->GetHeight(), 292 out_frame->GetWidth() / 2, 293 out_frame->GetData() + 294 out_frame->GetWidth() * out_frame->GetHeight() * 5 / 4, 295 out_frame->GetWidth() / 2, out_frame->GetWidth(), out_frame->GetHeight(), 296 libyuv::FilterMode::kFilterNone); 297 LOGF_IF(ERROR, ret) << "I420Scale failed: " << ret; 298 return ret; 299 } 300 301 static int YU12ToYV12(const void* yu12, void* yv12, int width, int height, 302 int dst_stride_y, int dst_stride_uv) { 303 if ((width % 2) || (height % 2)) { 304 LOGF(ERROR) << "Width or height is not even (" << width << " x " << height 305 << ")"; 306 return -EINVAL; 307 } 308 if (dst_stride_y < width || dst_stride_uv < width / 2) { 309 LOGF(ERROR) << "Y plane stride (" << dst_stride_y 310 << ") or U/V plane stride (" << dst_stride_uv 311 << ") is invalid for width " << width; 312 return -EINVAL; 313 } 314 315 const uint8_t* src = reinterpret_cast<const uint8_t*>(yu12); 316 uint8_t* dst = reinterpret_cast<uint8_t*>(yv12); 317 const uint8_t* u_src = src + width * height; 318 uint8_t* u_dst = dst + dst_stride_y * height + dst_stride_uv * height / 2; 319 const uint8_t* v_src = src + width * height * 5 / 4; 320 uint8_t* v_dst = dst + dst_stride_y * height; 321 322 return libyuv::I420Copy(src, width, u_src, width / 2, v_src, width / 2, dst, 323 dst_stride_y, u_dst, dst_stride_uv, v_dst, 324 dst_stride_uv, width, height); 325 } 326 327 static int YU12ToNV21(const void* yu12, void* nv21, int width, int height) { 328 if ((width % 2) || (height % 2)) { 329 LOGF(ERROR) << "Width or height is not even (" << width << " x " << height 330 << ")"; 331 return -EINVAL; 332 } 333 334 const uint8_t* src = reinterpret_cast<const uint8_t*>(yu12); 335 uint8_t* dst = reinterpret_cast<uint8_t*>(nv21); 336 const uint8_t* u_src = src + width * height; 337 const uint8_t* v_src = src + width * height * 5 / 4; 338 uint8_t* vu_dst = dst + width * height; 339 340 memcpy(dst, src, width * height); 341 342 for (int i = 0; i < height / 2; i++) { 343 for (int j = 0; j < width / 2; j++) { 344 *vu_dst++ = *v_src++; 345 *vu_dst++ = *u_src++; 346 } 347 } 348 return 0; 349 } 350 351 static bool ConvertToJpeg(const CameraMetadata& metadata, 352 const FrameBuffer& in_frame, FrameBuffer* out_frame) { 353 ExifUtils utils; 354 int jpeg_quality, thumbnail_jpeg_quality; 355 camera_metadata_ro_entry entry; 356 357 if (metadata.exists(ANDROID_JPEG_QUALITY)) { 358 entry = metadata.find(ANDROID_JPEG_QUALITY); 359 jpeg_quality = entry.data.u8[0]; 360 } else { 361 LOGF(ERROR) << "Could not find jpeg quality in metadata, defaulting to " 362 << DEFAULT_JPEG_QUALITY; 363 jpeg_quality = DEFAULT_JPEG_QUALITY; 364 } 365 if (metadata.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) { 366 entry = metadata.find(ANDROID_JPEG_THUMBNAIL_QUALITY); 367 thumbnail_jpeg_quality = entry.data.u8[0]; 368 } else { 369 thumbnail_jpeg_quality = jpeg_quality; 370 } 371 372 if (!utils.Initialize(in_frame.GetData(), in_frame.GetWidth(), 373 in_frame.GetHeight(), thumbnail_jpeg_quality)) { 374 LOGF(ERROR) << "ExifUtils initialization failed."; 375 return false; 376 } 377 if (!SetExifTags(metadata, &utils)) { 378 LOGF(ERROR) << "Setting Exif tags failed."; 379 return false; 380 } 381 if (!utils.GenerateApp1()) { 382 LOGF(ERROR) << "Generating APP1 segment failed."; 383 return false; 384 } 385 JpegCompressor compressor; 386 if (!compressor.CompressImage(in_frame.GetData(), in_frame.GetWidth(), 387 in_frame.GetHeight(), jpeg_quality, 388 utils.GetApp1Buffer(), utils.GetApp1Length())) { 389 LOGF(ERROR) << "JPEG image compression failed"; 390 return false; 391 } 392 size_t buffer_length = compressor.GetCompressedImageSize(); 393 if (out_frame->SetDataSize(buffer_length)) { 394 return false; 395 } 396 memcpy(out_frame->GetData(), compressor.GetCompressedImagePtr(), 397 buffer_length); 398 return true; 399 } 400 401 static bool SetExifTags(const CameraMetadata& metadata, ExifUtils* utils) { 402 time_t raw_time = 0; 403 struct tm time_info; 404 bool time_available = time(&raw_time) != -1; 405 localtime_r(&raw_time, &time_info); 406 if (!utils->SetDateTime(time_info)) { 407 LOGF(ERROR) << "Setting data time failed."; 408 return false; 409 } 410 411 float focal_length; 412 camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH); 413 if (entry.count) { 414 focal_length = entry.data.f[0]; 415 } else { 416 LOGF(ERROR) << "Cannot find focal length in metadata."; 417 return false; 418 } 419 if (!utils->SetFocalLength( 420 static_cast<uint32_t>(focal_length * kRationalPrecision), 421 kRationalPrecision)) { 422 LOGF(ERROR) << "Setting focal length failed."; 423 return false; 424 } 425 426 if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) { 427 entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES); 428 if (entry.count < 3) { 429 LOGF(ERROR) << "Gps coordinates in metadata is not complete."; 430 return false; 431 } 432 if (!utils->SetGpsLatitude(entry.data.d[0])) { 433 LOGF(ERROR) << "Setting gps latitude failed."; 434 return false; 435 } 436 if (!utils->SetGpsLongitude(entry.data.d[1])) { 437 LOGF(ERROR) << "Setting gps longitude failed."; 438 return false; 439 } 440 if (!utils->SetGpsAltitude(entry.data.d[2])) { 441 LOGF(ERROR) << "Setting gps altitude failed."; 442 return false; 443 } 444 } 445 446 if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) { 447 entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD); 448 std::string method_str(reinterpret_cast<const char*>(entry.data.u8)); 449 if (!utils->SetGpsProcessingMethod(method_str)) { 450 LOGF(ERROR) << "Setting gps processing method failed."; 451 return false; 452 } 453 } 454 455 if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) { 456 entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP); 457 time_t timestamp = static_cast<time_t>(entry.data.i64[0]); 458 if (gmtime_r(×tamp, &time_info)) { 459 if (!utils->SetGpsTimestamp(time_info)) { 460 LOGF(ERROR) << "Setting gps timestamp failed."; 461 return false; 462 } 463 } else { 464 LOGF(ERROR) << "Time tranformation failed."; 465 return false; 466 } 467 } 468 469 if (metadata.exists(ANDROID_JPEG_ORIENTATION)) { 470 entry = metadata.find(ANDROID_JPEG_ORIENTATION); 471 if (!utils->SetOrientation(entry.data.i32[0])) { 472 LOGF(ERROR) << "Setting orientation failed."; 473 return false; 474 } 475 } 476 477 if (metadata.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) { 478 entry = metadata.find(ANDROID_JPEG_THUMBNAIL_SIZE); 479 if (entry.count < 2) { 480 LOGF(ERROR) << "Thumbnail size in metadata is not complete."; 481 return false; 482 } 483 int thumbnail_width = entry.data.i32[0]; 484 int thumbnail_height = entry.data.i32[1]; 485 if (thumbnail_width > 0 && thumbnail_height > 0) { 486 if (!utils->SetThumbnailSize(static_cast<uint16_t>(thumbnail_width), 487 static_cast<uint16_t>(thumbnail_height))) { 488 LOGF(ERROR) << "Setting thumbnail size failed."; 489 return false; 490 } 491 } 492 } 493 return true; 494 } 495 496 } // namespace arc 497