• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
libjpeg_init_destination(j_compress_ptr cinfo)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 
libjpeg_empty_output_buffer(j_compress_ptr cinfo)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 
libjpeg_term_destination(j_compress_ptr cinfo)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 
libjpeg_destination_mgr(uint8_t * input,int size)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 */
nv21_to_yuv(uint8_t * dst,uint8_t * y,uint8_t * uv,int width)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 
uyvy_to_yuv(uint8_t * dst,uint32_t * src,int width)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 
yuyv_to_yuv(uint8_t * dst,uint32_t * src,int width)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 
resize_nv12(Encoder_libjpeg::params * params,uint8_t * dst_buffer)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 */
degreesToExifOrientation(unsigned int degrees)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 
stringToRational(const char * str,unsigned int * num,unsigned int * den)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 
isAsciiTag(const char * tag)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 
insertExifToJpeg(unsigned char * jpeg,size_t jpeg_size)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 
insertExifThumbnailImage(const char * thumb,int len)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 
saveJpeg(unsigned char * jpeg,size_t jpeg_size)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 */
~ExifElementsTable()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 
insertElement(const char * tag,const char * value)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 */
encode(params * input)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