summaryrefslogtreecommitdiff
path: root/Encoder_libjpeg.cpp (plain)
blob: 8b57aca879c8a13d01af9408597e6fa9a87f476c
1/*
2 * Copyright (C) 2011 The Android Open Source Project
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#define LOG_TAG "CameraHAL_Encderlibjpeg"
27
28#include "CameraHal.h"
29#include "Encoder_libjpeg.h"
30#include "NV12_resize.h"
31
32#include <stdlib.h>
33#include <unistd.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <fcntl.h>
37#include <stdio.h>
38#include <errno.h>
39#include <math.h>
40
41extern "C" {
42 #include "jpeglib.h"
43 #include "jerror.h"
44}
45
46
47#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
48
49namespace android {
50struct string_pair {
51 const char* string1;
52 const char* string2;
53};
54static string_pair degress_to_exif_lut [] = {
55 // degrees, exif_orientation
56 {"0", "1"},
57 {"90", "6"},
58 {"180", "3"},
59 {"270", "8"},
60};
61struct libjpeg_destination_mgr : jpeg_destination_mgr {
62 libjpeg_destination_mgr(uint8_t* input, int size);
63
64 uint8_t* buf;
65 int bufsize;
66 size_t jpegsize;
67};
68
69static void libjpeg_init_destination (j_compress_ptr cinfo) {
70 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
71
72 dest->next_output_byte = dest->buf;
73 dest->free_in_buffer = dest->bufsize;
74 dest->jpegsize = 0;
75}
76
77static boolean libjpeg_empty_output_buffer(j_compress_ptr cinfo) {
78 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
79
80 dest->next_output_byte = dest->buf;
81 dest->free_in_buffer = dest->bufsize;
82 return TRUE; // ?
83}
84
85static void libjpeg_term_destination (j_compress_ptr cinfo) {
86 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
87 dest->jpegsize = dest->bufsize - dest->free_in_buffer;
88}
89
90libjpeg_destination_mgr::libjpeg_destination_mgr(uint8_t* input, int size) {
91 this->init_destination = libjpeg_init_destination;
92 this->empty_output_buffer = libjpeg_empty_output_buffer;
93 this->term_destination = libjpeg_term_destination;
94
95 this->buf = input;
96 this->bufsize = size;
97
98 jpegsize = 0;
99}
100
101/* private static functions */
102static void nv21_to_yuv(uint8_t* dst, uint8_t* y, uint8_t* uv, int width) {
103 if (!dst || !y || !uv) {
104 return;
105 }
106
107 while ((width--) > 0) {
108 uint8_t y0 = y[0];
109 uint8_t v0 = uv[0];
110 uint8_t u0 = *(uv+1);
111 dst[0] = y0;
112 dst[1] = u0;
113 dst[2] = v0;
114 dst += 3;
115 y++;
116 if(!(width % 2)) uv+=2;
117 }
118}
119
120static void uyvy_to_yuv(uint8_t* dst, uint32_t* src, int width) {
121 if (!dst || !src) {
122 return;
123 }
124
125 if (width % 2) {
126 return; // not supporting odd widths
127 }
128
129 // currently, neon routine only supports multiple of 16 width
130 if (width % 16) {
131 while ((width-=2) >= 0) {
132 uint8_t u0 = (src[0] >> 0) & 0xFF;
133 uint8_t y0 = (src[0] >> 8) & 0xFF;
134 uint8_t v0 = (src[0] >> 16) & 0xFF;
135 uint8_t y1 = (src[0] >> 24) & 0xFF;
136 dst[0] = y0;
137 dst[1] = u0;
138 dst[2] = v0;
139 dst[3] = y1;
140 dst[4] = u0;
141 dst[5] = v0;
142 dst += 6;
143 src++;
144 }
145 } else {
146 int n = width;
147 asm volatile (
148 " pld [%[src], %[src_stride], lsl #2] \n\t"
149 " cmp %[n], #16 \n\t"
150 " blt 5f \n\t"
151 "0: @ 16 pixel swap \n\t"
152 " vld2.8 {q0, q1} , [%[src]]! @ q0 = uv q1 = y \n\t"
153 " vuzp.8 q0, q2 @ d0 = u d4 = v \n\t"
154 " vmov d1, d0 @ q0 = u0u1u2..u0u1u2... \n\t"
155 " vmov d5, d4 @ q2 = v0v1v2..v0v1v2... \n\t"
156 " vzip.8 d0, d1 @ q0 = u0u0u1u1u2u2... \n\t"
157 " vzip.8 d4, d5 @ q2 = v0v0v1v1v2v2... \n\t"
158 " vswp q0, q1 @ now q0 = y q1 = u q2 = v \n\t"
159 " vst3.8 {d0,d2,d4},[%[dst]]! \n\t"
160 " vst3.8 {d1,d3,d5},[%[dst]]! \n\t"
161 " sub %[n], %[n], #16 \n\t"
162 " cmp %[n], #16 \n\t"
163 " bge 0b \n\t"
164 "5: @ end \n\t"
165#ifdef NEEDS_ARM_ERRATA_754319_754320
166 " vmov s0,s0 @ add noop for errata item \n\t"
167#endif
168 : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n)
169 : [src_stride] "r" (width)
170 : "cc", "memory", "q0", "q1", "q2"
171 );
172 }
173}
174
175static void yuyv_to_yuv(uint8_t* dst, uint32_t* src, int width) {
176 if (!dst || !src) {
177 return;
178 }
179
180 if (width % 2) {
181 return; // not supporting odd widths
182 }
183
184 // currently, neon routine only supports multiple of 16 width
185 if (width % 16) {
186 while ((width-=2) >= 0) {
187 uint8_t y0 = (src[0] >> 0) & 0xFF;
188 uint8_t u0 = (src[0] >> 8) & 0xFF;
189 uint8_t y1 = (src[0] >> 16) & 0xFF;
190 uint8_t v0 = (src[0] >> 24) & 0xFF;
191 dst[0] = y0;
192 dst[1] = u0;
193 dst[2] = v0;
194 dst[3] = y1;
195 dst[4] = u0;
196 dst[5] = v0;
197 dst += 6;
198 src++;
199 }
200 } else {
201 int n = width;
202 asm volatile (
203 " pld [%[src], %[src_stride], lsl #2] \n\t"
204 " cmp %[n], #16 \n\t"
205 " blt 5f \n\t"
206 "0: @ 16 pixel swap \n\t"
207 " vld2.8 {q0, q1} , [%[src]]! @ q0 = y q1 = uv \n\t"
208 " vuzp.8 q1, q2 @ d2 = u d4 = v \n\t"
209 " vmov d3, d2 @ q1 = u0u1u2..u0u1u2... \n\t"
210 " vmov d5, d4 @ q2 = v0v1v2..v0v1v2... \n\t"
211 " vzip.8 d2, d3 @ q1 = u0u0u1u1u2u2... \n\t"
212 " vzip.8 d4, d5 @ q2 = v0v0v1v1v2v2... \n\t"
213 " vst3.8 {d0,d2,d4},[%[dst]]! \n\t"
214 " vst3.8 {d1,d3,d5},[%[dst]]! \n\t"
215 " sub %[n], %[n], #16 \n\t"
216 " cmp %[n], #16 \n\t"
217 " bge 0b \n\t"
218 "5: @ end \n\t"
219#ifdef NEEDS_ARM_ERRATA_754319_754320
220 " vmov s0,s0 @ add noop for errata item \n\t"
221#endif
222 : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n)
223 : [src_stride] "r" (width)
224 : "cc", "memory", "q0", "q1", "q2"
225 );
226 }
227}
228
229int extraSmallImg(unsigned char* SrcImg,int SrcW,int SrcH,
230 unsigned char* DstImg,int DstW,int DstH)
231{
232 int skipW = SrcW/DstW;
233 int skipH = SrcH/DstH;
234
235 unsigned char* dst = DstImg;
236 unsigned char* srcrow = SrcImg;
237 unsigned char* srcrowidx = srcrow;
238
239 int i = 0,j = 0;
240 for(;i<DstH;i++)
241 {
242 //LOGD("srcrow = %d,dst = %d",srcrow-SrcImg,dst-DstImg);
243 for(j = 0;j<DstW;j++)
244 {
245 dst[0] = srcrowidx[0];
246 dst[1] = srcrowidx[1];
247 dst[2] = srcrowidx[2];
248 dst+=3;
249 srcrowidx+=3*skipW;
250 }
251 // LOGD("srcrowidx end = %d",srcrowidx-SrcImg);
252
253 srcrow += skipH*SrcW*3;
254 srcrowidx = srcrow;
255 }
256
257 return 1;
258}
259
260static void resize_nv12(Encoder_libjpeg::params* params, uint8_t* dst_buffer) {
261 structConvImage o_img_ptr, i_img_ptr;
262
263 if (!params || !dst_buffer) {
264 return;
265 }
266
267 //input
268 i_img_ptr.uWidth = params->in_width;
269 i_img_ptr.uStride = i_img_ptr.uWidth;
270 i_img_ptr.uHeight = params->in_height;
271 i_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
272 i_img_ptr.imgPtr = (uint8_t*) params->src;
273 i_img_ptr.clrPtr = i_img_ptr.imgPtr + (i_img_ptr.uWidth * i_img_ptr.uHeight);
274
275 //ouput
276 o_img_ptr.uWidth = params->out_width;
277 o_img_ptr.uStride = o_img_ptr.uWidth;
278 o_img_ptr.uHeight = params->out_height;
279 o_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
280 o_img_ptr.imgPtr = dst_buffer;
281 o_img_ptr.clrPtr = o_img_ptr.imgPtr + (o_img_ptr.uWidth * o_img_ptr.uHeight);
282
283 VT_resizeFrame_Video_opt2_lp(&i_img_ptr, &o_img_ptr, NULL, 0);
284}
285
286/* public static functions */
287const char* ExifElementsTable::degreesToExifOrientation(const char* degrees) {
288 for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) {
289 if (!strcmp(degrees, degress_to_exif_lut[i].string1)) {
290 return degress_to_exif_lut[i].string2;
291 }
292 }
293 return NULL;
294}
295
296void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) {
297 int len;
298 char * tempVal = NULL;
299
300 if (str != NULL) {
301 len = strlen(str);
302 tempVal = (char*) malloc( sizeof(char) * (len + 1));
303 }
304
305 if (tempVal != NULL) {
306 // convert the decimal string into a rational
307 size_t den_len;
308 char *ctx;
309 unsigned int numerator = 0;
310 unsigned int denominator = 0;
311 char* temp = NULL;
312
313 memset(tempVal, '\0', len + 1);
314 strncpy(tempVal, str, len);
315 temp = strtok_r(tempVal, ".", &ctx);
316
317 if (temp != NULL)
318 numerator = atoi(temp);
319
320 if (!numerator)
321 numerator = 1;
322
323 temp = strtok_r(NULL, ".", &ctx);
324 if (temp != NULL) {
325 den_len = strlen(temp);
326 if(HUGE_VAL == den_len ) {
327 den_len = 0;
328 }
329
330 denominator = static_cast<unsigned int>(pow(10, den_len));
331 numerator = numerator * denominator + atoi(temp);
332 } else {
333 denominator = 1;
334 }
335
336 free(tempVal);
337
338 *num = numerator;
339 *den = denominator;
340 }
341}
342
343bool ExifElementsTable::isAsciiTag(const char* tag) {
344 // TODO(XXX): Add tags as necessary
345 return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0);
346}
347
348void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) {
349 ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE);
350
351 ResetJpgfile();
352 if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) {
353 jpeg_opened = true;
354 create_EXIF(table, exif_tag_count, gps_tag_count,true);
355 }
356}
357
358status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) {
359 status_t ret = NO_ERROR;
360
361 if ((len > 0) && jpeg_opened) {
362 ret = ReplaceThumbnailFromBuffer(thumb, len);
363 CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret);
364 }
365
366 return ret;
367}
368
369void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) {
370 if (jpeg_opened) {
371 WriteJpegToBuffer(jpeg, jpeg_size);
372 DiscardData();
373 jpeg_opened = false;
374 }
375}
376
377/* public functions */
378ExifElementsTable::~ExifElementsTable() {
379 int num_elements = gps_tag_count + exif_tag_count;
380
381 for (int i = 0; i < num_elements; i++) {
382 if (table[i].Value) {
383 free(table[i].Value);
384 }
385 }
386
387 if (jpeg_opened) {
388 DiscardData();
389 }
390}
391
392status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
393 int value_length = 0;
394 status_t ret = NO_ERROR;
395
396 if (!value || !tag) {
397 return -EINVAL;
398 }
399
400 if (position >= MAX_EXIF_TAGS_SUPPORTED) {
401 CAMHAL_LOGEA("Max number of EXIF elements already inserted");
402 return NO_MEMORY;
403 }
404
405 if (isAsciiTag(tag)) {
406 value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix));
407 } else {
408 value_length = strlen(value);
409 }
410
411 if (IsGpsTag(tag)) {
412 table[position].GpsTag = TRUE;
413 table[position].Tag = GpsTagNameToValue(tag);
414 gps_tag_count++;
415 } else {
416 table[position].GpsTag = FALSE;
417 table[position].Tag = TagNameToValue(tag);
418 exif_tag_count++;
419 }
420
421 table[position].DataLength = 0;
422 table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1));
423
424 if (table[position].Value) {
425 memcpy(table[position].Value, value, value_length + 1);
426 table[position].DataLength = value_length + 1;
427 }
428
429 position++;
430 return ret;
431}
432
433/* private member functions */
434size_t Encoder_libjpeg::encode(params* input) {
435 jpeg_compress_struct cinfo;
436 jpeg_error_mgr jerr;
437 jpeg_destination_mgr jdest;
438 uint8_t* src = NULL, *resize_src = NULL;
439 uint8_t* row_tmp = NULL;
440 uint8_t* row_src = NULL;
441 uint8_t* row_uv = NULL; // used only for NV12
442 int row_stride;
443 int out_width = 0, in_width = 0;
444 int out_height = 0, in_height = 0;
445 int bpp = 2; // for uyvy
446
447 Encoder_libjpeg::format informat = Encoder_libjpeg::YUV422I;
448
449 if (!input) {
450 return 0;
451 }
452
453 out_width = input->out_width;
454 in_width = input->in_width;
455 out_height = input->out_height;
456 in_height = input->in_height;
457 src = input->src;
458 input->jpeg_size = 0;
459 libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size);
460
461#ifdef AMLOGIC_HW_JPEGENC
462 if((out_width == in_width)&&(out_height == in_height)
463 &&(out_height%16 == 0)&&(out_width%16 == 0)){
464 goto HW_CASE;
465 }
466SOFTWARE_ENC:
467#endif
468
469 // param check...
470 if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) ||
471 (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) ||
472 (input->dst_size < 1) || (input->format == NULL)) {
473 goto exit;
474 }
475
476 if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
477 informat = Encoder_libjpeg::YUV420SP;
478 bpp = 1;
479 if ((in_width != out_width) || (in_height != out_height)) {
480 resize_src = (uint8_t*) malloc(input->dst_size);
481 resize_nv12(input, resize_src);
482 if (resize_src) src = resize_src;
483 }
484 } else if (strcmp(input->format, CameraProperties::PIXEL_FORMAT_RGB24) == 0) {
485 informat = Encoder_libjpeg::RGB24;
486 bpp = 1;
487 if ((in_width != out_width) || (in_height != out_height)) {
488 resize_src = (uint8_t*) malloc(input->dst_size);
489 if(NULL != resize_src){
490 extraSmallImg(input->src, in_width, in_height,
491 resize_src, out_width, out_height);
492 src = resize_src;
493 }else{
494 CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n");
495 goto exit;
496 }
497 }
498 } else if ((in_width != out_width) || (in_height != out_height)) {
499 CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %s", input->format);
500 goto exit;
501 } else if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV422I)) {
502 // we currently only support yuv422i and yuv420sp
503 CAMHAL_LOGEB("Encoder: format not supported: %s", input->format);
504 goto exit;
505 }
506
507 cinfo.err = jpeg_std_error(&jerr);
508
509 jpeg_create_compress(&cinfo);
510
511 CAMHAL_LOGDB("software encoding... \n\t"
512 "width: %d \n\t"
513 "height:%d \n\t"
514 "dest %p \n\t"
515 "dest size:%d \n\t"
516 "mSrc %p",
517 out_width, out_height, input->dst,
518 input->dst_size, src);
519
520 cinfo.dest = &dest_mgr;
521 cinfo.image_width = out_width;
522 cinfo.image_height = out_height;
523 cinfo.input_components = 3;
524 if (informat == Encoder_libjpeg::RGB24)
525 cinfo.in_color_space = JCS_RGB;
526 else
527 cinfo.in_color_space = JCS_YCbCr;
528 cinfo.input_gamma = 1;
529
530 jpeg_set_defaults(&cinfo);
531 jpeg_set_quality(&cinfo, input->quality, TRUE);
532 cinfo.dct_method = JDCT_IFAST;
533
534 jpeg_start_compress(&cinfo, TRUE);
535
536 row_tmp = (uint8_t*)malloc(out_width * 3);
537 row_src = src;
538 row_uv = src + out_width * out_height * bpp;
539 row_stride = out_width * 3;
540
541 while ((cinfo.next_scanline < cinfo.image_height) && !mCancelEncoding) {
542 JSAMPROW row[1]; /* pointer to JSAMPLE row[s] */
543
544 if (informat == Encoder_libjpeg::RGB24) {
545 row[0] = &src[cinfo.next_scanline * row_stride];
546 (void) jpeg_write_scanlines(&cinfo, row, 1);
547 } else {
548 // convert input yuv format to yuv444
549 if (informat == Encoder_libjpeg::YUV420SP) {
550 nv21_to_yuv(row_tmp, row_src, row_uv, out_width);
551 } else if (informat == Encoder_libjpeg::YUV422I) {
552 //uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width);
553 yuyv_to_yuv(row_tmp, (uint32_t*)row_src, out_width);
554 }
555
556 row[0] = row_tmp;
557 jpeg_write_scanlines(&cinfo, row, 1);
558 row_src = row_src + out_width*bpp;
559
560 // move uv row if input format needs it
561 if (informat == Encoder_libjpeg::YUV420SP) {
562 if (!(cinfo.next_scanline % 2))
563 row_uv = row_uv + out_width * bpp;
564 }
565 }
566 }
567
568 // no need to finish encoding routine if we are prematurely stopping
569 // we will end up crashing in dest_mgr since data is incomplete
570 if (!mCancelEncoding)
571 jpeg_finish_compress(&cinfo);
572 jpeg_destroy_compress(&cinfo);
573
574 if (resize_src) free(resize_src);
575 if (row_tmp) free(row_tmp);
576
577 exit:
578 input->jpeg_size = dest_mgr.jpegsize;
579 return dest_mgr.jpegsize;
580
581#ifdef AMLOGIC_HW_JPEGENC
582HW_CASE:
583 size_t jpeg_size = 0;
584 memset(&hw_info,0, sizeof(hw_info));
585 if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
586 hw_info.in_format = FMT_NV21;
587 } else if (strcmp(input->format, CameraProperties::PIXEL_FORMAT_RGB24) == 0) {
588 hw_info.in_format = FMT_RGB888;
589 } else if(strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV422I)== 0){
590 hw_info.in_format = FMT_YUV422_SINGLE;
591 } else{
592 hw_info.in_format = FMT_NV21;
593 }
594 hw_info.fd = -1;
595 hw_info.width = out_width;
596 hw_info.height = out_height;
597 hw_info.src = input->src;
598 hw_info.src_size= input->src_size;
599 hw_info.dst= input->dst;
600 hw_info.dst_size= input->dst_size;
601 hw_info.quality = input->quality;
602
603 CAMHAL_LOGDB("hardware encoding... \n\t"
604 "width: %d \n\t"
605 "height:%d \n\t"
606 "dest %p \n\t"
607 "dest size:%d \n\t"
608 "mSrc %p",
609 out_width, out_height, input->dst,
610 input->dst_size, input->src);
611 jpeg_size = hw_encode(&hw_info);
612 if(jpeg_size<=0){
613 CAMHAL_LOGEA("HW Encode fail, re-encode with software.");
614 goto SOFTWARE_ENC;
615 }
616 input->jpeg_size = jpeg_size;
617 return jpeg_size;
618#endif
619}
620
621} // namespace android
622