1 /* 2 * Copyright (C) Texas Instruments - http://www.ti.com/ 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /** 18 * @file Encoder_libjpeg.cpp 19 * 20 * This file encodes a YUV422I buffer to a jpeg 21 * TODO(XXX): Need to support formats other than yuv422i 22 * Change interface to pre/post-proc algo framework 23 * 24 */ 25 26 #include "Encoder_libjpeg.h" 27 #include "NV12_resize.h" 28 #include "TICameraParameters.h" 29 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <stdio.h> 36 #include <errno.h> 37 #include <math.h> 38 39 extern "C" { 40 #include "jpeglib.h" 41 #include "jerror.h" 42 } 43 44 #define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0])) 45 #define MIN(x,y) ((x < y) ? x : y) 46 47 namespace Ti { 48 namespace Camera { 49 50 struct integer_string_pair { 51 unsigned int integer; 52 const char* string; 53 }; 54 55 static integer_string_pair degress_to_exif_lut [] = { 56 // degrees, exif_orientation 57 {0, "1"}, 58 {90, "6"}, 59 {180, "3"}, 60 {270, "8"}, 61 }; 62 struct libjpeg_destination_mgr : jpeg_destination_mgr { 63 libjpeg_destination_mgr(uint8_t* input, int size); 64 65 uint8_t* buf; 66 int bufsize; 67 size_t jpegsize; 68 }; 69 70 static void libjpeg_init_destination (j_compress_ptr cinfo) { 71 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest; 72 73 dest->next_output_byte = dest->buf; 74 dest->free_in_buffer = dest->bufsize; 75 dest->jpegsize = 0; 76 } 77 78 static boolean libjpeg_empty_output_buffer(j_compress_ptr cinfo) { 79 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest; 80 81 dest->next_output_byte = dest->buf; 82 dest->free_in_buffer = dest->bufsize; 83 return TRUE; // ? 84 } 85 86 static void libjpeg_term_destination (j_compress_ptr cinfo) { 87 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest; 88 dest->jpegsize = dest->bufsize - dest->free_in_buffer; 89 } 90 91 libjpeg_destination_mgr::libjpeg_destination_mgr(uint8_t* input, int size) { 92 this->init_destination = libjpeg_init_destination; 93 this->empty_output_buffer = libjpeg_empty_output_buffer; 94 this->term_destination = libjpeg_term_destination; 95 96 this->buf = input; 97 this->bufsize = size; 98 99 jpegsize = 0; 100 } 101 102 /* private static functions */ 103 static void nv21_to_yuv(uint8_t* dst, uint8_t* y, uint8_t* uv, int width) { 104 if (!dst || !y || !uv) { 105 return; 106 } 107 108 while ((width--) > 0) { 109 uint8_t y0 = y[0]; 110 uint8_t v0 = uv[0]; 111 uint8_t u0 = *(uv+1); 112 dst[0] = y0; 113 dst[1] = u0; 114 dst[2] = v0; 115 dst += 3; 116 y++; 117 if(!(width % 2)) uv+=2; 118 } 119 } 120 121 static void uyvy_to_yuv(uint8_t* dst, uint32_t* src, int width) { 122 if (!dst || !src) { 123 return; 124 } 125 126 if (width % 2) { 127 return; // not supporting odd widths 128 } 129 130 // currently, neon routine only supports multiple of 16 width 131 if (width % 16) { 132 while ((width-=2) >= 0) { 133 uint8_t u0 = (src[0] >> 0) & 0xFF; 134 uint8_t y0 = (src[0] >> 8) & 0xFF; 135 uint8_t v0 = (src[0] >> 16) & 0xFF; 136 uint8_t y1 = (src[0] >> 24) & 0xFF; 137 dst[0] = y0; 138 dst[1] = u0; 139 dst[2] = v0; 140 dst[3] = y1; 141 dst[4] = u0; 142 dst[5] = v0; 143 dst += 6; 144 src++; 145 } 146 } else { 147 int n = width; 148 asm volatile ( 149 " pld [%[src], %[src_stride], lsl #2] \n\t" 150 " cmp %[n], #16 \n\t" 151 " blt 5f \n\t" 152 "0: @ 16 pixel swap \n\t" 153 " vld2.8 {q0, q1} , [%[src]]! @ q0 = uv q1 = y \n\t" 154 " vuzp.8 q0, q2 @ d0 = u d4 = v \n\t" 155 " vmov d1, d0 @ q0 = u0u1u2..u0u1u2... \n\t" 156 " vmov d5, d4 @ q2 = v0v1v2..v0v1v2... \n\t" 157 " vzip.8 d0, d1 @ q0 = u0u0u1u1u2u2... \n\t" 158 " vzip.8 d4, d5 @ q2 = v0v0v1v1v2v2... \n\t" 159 " vswp q0, q1 @ now q0 = y q1 = u q2 = v \n\t" 160 " vst3.8 {d0,d2,d4},[%[dst]]! \n\t" 161 " vst3.8 {d1,d3,d5},[%[dst]]! \n\t" 162 " sub %[n], %[n], #16 \n\t" 163 " cmp %[n], #16 \n\t" 164 " bge 0b \n\t" 165 "5: @ end \n\t" 166 #ifdef NEEDS_ARM_ERRATA_754319_754320 167 " vmov s0,s0 @ add noop for errata item \n\t" 168 #endif 169 : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n) 170 : [src_stride] "r" (width) 171 : "cc", "memory", "q0", "q1", "q2" 172 ); 173 } 174 } 175 176 static void yuyv_to_yuv(uint8_t* dst, uint32_t* src, int width) { 177 if (!dst || !src) { 178 return; 179 } 180 181 if (width % 2) { 182 return; // not supporting odd widths 183 } 184 185 // currently, neon routine only supports multiple of 16 width 186 if (width % 16) { 187 while ((width-=2) >= 0) { 188 uint8_t y0 = (src[0] >> 0) & 0xFF; 189 uint8_t u0 = (src[0] >> 8) & 0xFF; 190 uint8_t y1 = (src[0] >> 16) & 0xFF; 191 uint8_t v0 = (src[0] >> 24) & 0xFF; 192 dst[0] = y0; 193 dst[1] = u0; 194 dst[2] = v0; 195 dst[3] = y1; 196 dst[4] = u0; 197 dst[5] = v0; 198 dst += 6; 199 src++; 200 } 201 } else { 202 int n = width; 203 asm volatile ( 204 " pld [%[src], %[src_stride], lsl #2] \n\t" 205 " cmp %[n], #16 \n\t" 206 " blt 5f \n\t" 207 "0: @ 16 pixel swap \n\t" 208 " vld2.8 {q0, q1} , [%[src]]! @ q0 = yyyy.. q1 = uvuv.. \n\t" 209 " vuzp.8 q1, q2 @ d2 = u d4 = v \n\t" 210 " vmov d3, d2 @ q1 = u0u1u2..u0u1u2... \n\t" 211 " vmov d5, d4 @ q2 = v0v1v2..v0v1v2... \n\t" 212 " vzip.8 d2, d3 @ q1 = u0u0u1u1u2u2... \n\t" 213 " vzip.8 d4, d5 @ q2 = v0v0v1v1v2v2... \n\t" 214 " @ now q0 = y q1 = u q2 = v \n\t" 215 " vst3.8 {d0,d2,d4},[%[dst]]! \n\t" 216 " vst3.8 {d1,d3,d5},[%[dst]]! \n\t" 217 " sub %[n], %[n], #16 \n\t" 218 " cmp %[n], #16 \n\t" 219 " bge 0b \n\t" 220 "5: @ end \n\t" 221 #ifdef NEEDS_ARM_ERRATA_754319_754320 222 " vmov s0,s0 @ add noop for errata item \n\t" 223 #endif 224 : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n) 225 : [src_stride] "r" (width) 226 : "cc", "memory", "q0", "q1", "q2" 227 ); 228 } 229 } 230 231 static void resize_nv12(Encoder_libjpeg::params* params, uint8_t* dst_buffer) { 232 structConvImage o_img_ptr, i_img_ptr; 233 234 if (!params || !dst_buffer) { 235 return; 236 } 237 238 //input 239 i_img_ptr.uWidth = params->in_width; 240 i_img_ptr.uStride = i_img_ptr.uWidth; 241 i_img_ptr.uHeight = params->in_height; 242 i_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp; 243 i_img_ptr.imgPtr = (uint8_t*) params->src; 244 i_img_ptr.clrPtr = i_img_ptr.imgPtr + (i_img_ptr.uWidth * i_img_ptr.uHeight); 245 i_img_ptr.uOffset = 0; 246 247 //ouput 248 o_img_ptr.uWidth = params->out_width; 249 o_img_ptr.uStride = o_img_ptr.uWidth; 250 o_img_ptr.uHeight = params->out_height; 251 o_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp; 252 o_img_ptr.imgPtr = dst_buffer; 253 o_img_ptr.clrPtr = o_img_ptr.imgPtr + (o_img_ptr.uWidth * o_img_ptr.uHeight); 254 o_img_ptr.uOffset = 0; 255 256 VT_resizeFrame_Video_opt2_lp(&i_img_ptr, &o_img_ptr, NULL, 0); 257 } 258 259 /* public static functions */ 260 const char* ExifElementsTable::degreesToExifOrientation(unsigned int degrees) { 261 for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) { 262 if (degrees == degress_to_exif_lut[i].integer) { 263 return degress_to_exif_lut[i].string; 264 } 265 } 266 return NULL; 267 } 268 269 void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) { 270 int len; 271 char * tempVal = NULL; 272 273 if (str != NULL) { 274 len = strlen(str); 275 tempVal = (char*) malloc( sizeof(char) * (len + 1)); 276 } 277 278 if (tempVal != NULL) { 279 // convert the decimal string into a rational 280 size_t den_len; 281 char *ctx; 282 unsigned int numerator = 0; 283 unsigned int denominator = 0; 284 char* temp = NULL; 285 286 memset(tempVal, '\0', len + 1); 287 strncpy(tempVal, str, len); 288 temp = strtok_r(tempVal, ".", &ctx); 289 290 if (temp != NULL) 291 numerator = atoi(temp); 292 293 if (!numerator) 294 numerator = 1; 295 296 temp = strtok_r(NULL, ".", &ctx); 297 if (temp != NULL) { 298 den_len = strlen(temp); 299 if(HUGE_VAL == den_len ) { 300 den_len = 0; 301 } 302 303 denominator = static_cast<unsigned int>(pow(10, den_len)); 304 numerator = numerator * denominator + atoi(temp); 305 } else { 306 denominator = 1; 307 } 308 309 free(tempVal); 310 311 *num = numerator; 312 *den = denominator; 313 } 314 } 315 316 bool ExifElementsTable::isAsciiTag(const char* tag) { 317 // TODO(XXX): Add tags as necessary 318 return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0); 319 } 320 321 void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) { 322 ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE); 323 324 ResetJpgfile(); 325 if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) { 326 jpeg_opened = true; 327 #ifdef ANDROID_API_JB_OR_LATER 328 create_EXIF(table, exif_tag_count, gps_tag_count, has_datetime_tag); 329 #else 330 create_EXIF(table, exif_tag_count, gps_tag_count); 331 #endif 332 } 333 } 334 335 status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) { 336 status_t ret = NO_ERROR; 337 338 if ((len > 0) && jpeg_opened) { 339 ret = ReplaceThumbnailFromBuffer(thumb, len) ? NO_ERROR : UNKNOWN_ERROR; 340 CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret); 341 } 342 343 return ret; 344 } 345 346 void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) { 347 if (jpeg_opened) { 348 WriteJpegToBuffer(jpeg, jpeg_size); 349 DiscardData(); 350 jpeg_opened = false; 351 } 352 } 353 354 /* public functions */ 355 ExifElementsTable::~ExifElementsTable() { 356 int num_elements = gps_tag_count + exif_tag_count; 357 358 for (int i = 0; i < num_elements; i++) { 359 if (table[i].Value) { 360 free(table[i].Value); 361 } 362 } 363 364 if (jpeg_opened) { 365 DiscardData(); 366 } 367 } 368 369 status_t ExifElementsTable::insertElement(const char* tag, const char* value) { 370 unsigned int value_length = 0; 371 status_t ret = NO_ERROR; 372 373 if (!value || !tag) { 374 return -EINVAL; 375 } 376 377 if (position >= MAX_EXIF_TAGS_SUPPORTED) { 378 CAMHAL_LOGEA("Max number of EXIF elements already inserted"); 379 return NO_MEMORY; 380 } 381 382 if (isAsciiTag(tag)) { 383 value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix)); 384 } else { 385 value_length = strlen(value); 386 } 387 388 if (IsGpsTag(tag)) { 389 table[position].GpsTag = TRUE; 390 table[position].Tag = GpsTagNameToValue(tag); 391 gps_tag_count++; 392 } else { 393 table[position].GpsTag = FALSE; 394 table[position].Tag = TagNameToValue(tag); 395 exif_tag_count++; 396 397 if (strcmp(tag, TAG_DATETIME) == 0) { 398 #ifdef ANDROID_API_JB_OR_LATER 399 has_datetime_tag = true; 400 #else 401 // jhead isn't taking datetime tag...this is a WA 402 ImageInfo.numDateTimeTags = 1; 403 memcpy(ImageInfo.DateTime, value, 404 MIN(ARRAY_SIZE(ImageInfo.DateTime), value_length + 1)); 405 #endif 406 } 407 } 408 409 table[position].DataLength = 0; 410 table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1)); 411 412 if (table[position].Value) { 413 memcpy(table[position].Value, value, value_length + 1); 414 table[position].DataLength = value_length + 1; 415 } 416 417 position++; 418 return ret; 419 } 420 421 /* private member functions */ 422 size_t Encoder_libjpeg::encode(params* input) { 423 jpeg_compress_struct cinfo; 424 jpeg_error_mgr jerr; 425 jpeg_destination_mgr jdest; 426 uint8_t* src = NULL, *resize_src = NULL; 427 uint8_t* row_tmp = NULL; 428 uint8_t* row_src = NULL; 429 uint8_t* row_uv = NULL; // used only for NV12 430 int out_width = 0, in_width = 0; 431 int out_height = 0, in_height = 0; 432 int bpp = 2; // for uyvy 433 int right_crop = 0, start_offset = 0; 434 435 if (!input) { 436 return 0; 437 } 438 439 out_width = input->out_width; 440 in_width = input->in_width; 441 out_height = input->out_height; 442 in_height = input->in_height; 443 right_crop = input->right_crop; 444 start_offset = input->start_offset; 445 src = input->src; 446 input->jpeg_size = 0; 447 448 libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size); 449 450 // param check... 451 if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) || 452 (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) || 453 (input->dst_size < 1) || (input->format == NULL)) { 454 goto exit; 455 } 456 457 if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) { 458 bpp = 1; 459 if ((in_width != out_width) || (in_height != out_height)) { 460 resize_src = (uint8_t*) malloc(input->dst_size); 461 resize_nv12(input, resize_src); 462 if (resize_src) src = resize_src; 463 } 464 } else if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV422I) && 465 strcmp(input->format, TICameraParameters::PIXEL_FORMAT_YUV422I_UYVY)) { 466 // we currently only support yuv422i and yuv420sp 467 CAMHAL_LOGEB("Encoder: format not supported: %s", input->format); 468 goto exit; 469 } else if ((in_width != out_width) || (in_height != out_height)) { 470 CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %s", input->format); 471 goto exit; 472 } 473 474 cinfo.err = jpeg_std_error(&jerr); 475 476 jpeg_create_compress(&cinfo); 477 478 CAMHAL_LOGDB("encoding... \n\t" 479 "width: %d \n\t" 480 "height:%d \n\t" 481 "dest %p \n\t" 482 "dest size:%d \n\t" 483 "mSrc %p \n\t" 484 "format: %s", 485 out_width, out_height, input->dst, 486 input->dst_size, src, input->format); 487 488 cinfo.dest = &dest_mgr; 489 cinfo.image_width = out_width - right_crop; 490 cinfo.image_height = out_height; 491 cinfo.input_components = 3; 492 cinfo.in_color_space = JCS_YCbCr; 493 cinfo.input_gamma = 1; 494 495 jpeg_set_defaults(&cinfo); 496 jpeg_set_quality(&cinfo, input->quality, TRUE); 497 cinfo.dct_method = JDCT_IFAST; 498 499 jpeg_start_compress(&cinfo, TRUE); 500 501 row_tmp = (uint8_t*)malloc((out_width - right_crop) * 3); 502 row_src = src + start_offset; 503 row_uv = src + out_width * out_height * bpp; 504 505 while ((cinfo.next_scanline < cinfo.image_height) && !mCancelEncoding) { 506 JSAMPROW row[1]; /* pointer to JSAMPLE row[s] */ 507 508 // convert input yuv format to yuv444 509 if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) { 510 nv21_to_yuv(row_tmp, row_src, row_uv, out_width - right_crop); 511 } else if (strcmp(input->format, TICameraParameters::PIXEL_FORMAT_YUV422I_UYVY) == 0) { 512 uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width - right_crop); 513 } else if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV422I) == 0) { 514 yuyv_to_yuv(row_tmp, (uint32_t*)row_src, out_width - right_crop); 515 } 516 517 row[0] = row_tmp; 518 jpeg_write_scanlines(&cinfo, row, 1); 519 row_src = row_src + out_width*bpp; 520 521 // move uv row if input format needs it 522 if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) { 523 if (!(cinfo.next_scanline % 2)) 524 row_uv = row_uv + out_width * bpp; 525 } 526 } 527 528 // no need to finish encoding routine if we are prematurely stopping 529 // we will end up crashing in dest_mgr since data is incomplete 530 if (!mCancelEncoding) 531 jpeg_finish_compress(&cinfo); 532 jpeg_destroy_compress(&cinfo); 533 534 if (resize_src) free(resize_src); 535 if (row_tmp) free(row_tmp); 536 537 exit: 538 input->jpeg_size = dest_mgr.jpegsize; 539 return dest_mgr.jpegsize; 540 } 541 542 } // namespace Camera 543 } // namespace Ti 544