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 #define LOG_TAG "CameraHAL"
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
41 extern "C" {
42 #include "jpeglib.h"
43 #include "jerror.h"
44 }
45
46 #define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
47 #define MIN(x,y) ((x < y) ? x : y)
48
49 namespace android {
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 @ d1 = u d5 = 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
resize_nv12(Encoder_libjpeg::params * params,uint8_t * dst_buffer)176 static void resize_nv12(Encoder_libjpeg::params* params, uint8_t* dst_buffer) {
177 structConvImage o_img_ptr, i_img_ptr;
178
179 if (!params || !dst_buffer) {
180 return;
181 }
182
183 //input
184 i_img_ptr.uWidth = params->in_width;
185 i_img_ptr.uStride = i_img_ptr.uWidth;
186 i_img_ptr.uHeight = params->in_height;
187 i_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
188 i_img_ptr.imgPtr = (uint8_t*) params->src;
189 i_img_ptr.clrPtr = i_img_ptr.imgPtr + (i_img_ptr.uWidth * i_img_ptr.uHeight);
190
191 //ouput
192 o_img_ptr.uWidth = params->out_width;
193 o_img_ptr.uStride = o_img_ptr.uWidth;
194 o_img_ptr.uHeight = params->out_height;
195 o_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
196 o_img_ptr.imgPtr = dst_buffer;
197 o_img_ptr.clrPtr = o_img_ptr.imgPtr + (o_img_ptr.uWidth * o_img_ptr.uHeight);
198
199 VT_resizeFrame_Video_opt2_lp(&i_img_ptr, &o_img_ptr, NULL, 0);
200 }
201
202 /* public static functions */
degreesToExifOrientation(unsigned int degrees)203 const char* ExifElementsTable::degreesToExifOrientation(unsigned int degrees) {
204 for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) {
205 if (degrees == degress_to_exif_lut[i].integer) {
206 return degress_to_exif_lut[i].string;
207 }
208 }
209 return NULL;
210 }
211
stringToRational(const char * str,unsigned int * num,unsigned int * den)212 void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) {
213 int len;
214 char * tempVal = NULL;
215
216 if (str != NULL) {
217 len = strlen(str);
218 tempVal = (char*) malloc( sizeof(char) * (len + 1));
219 }
220
221 if (tempVal != NULL) {
222 // convert the decimal string into a rational
223 size_t den_len;
224 char *ctx;
225 unsigned int numerator = 0;
226 unsigned int denominator = 0;
227 char* temp = NULL;
228
229 memset(tempVal, '\0', len + 1);
230 strncpy(tempVal, str, len);
231 temp = strtok_r(tempVal, ".", &ctx);
232
233 if (temp != NULL)
234 numerator = atoi(temp);
235
236 if (!numerator)
237 numerator = 1;
238
239 temp = strtok_r(NULL, ".", &ctx);
240 if (temp != NULL) {
241 den_len = strlen(temp);
242 if(HUGE_VAL == den_len ) {
243 den_len = 0;
244 }
245
246 denominator = static_cast<unsigned int>(pow(10, den_len));
247 numerator = numerator * denominator + atoi(temp);
248 } else {
249 denominator = 1;
250 }
251
252 free(tempVal);
253
254 *num = numerator;
255 *den = denominator;
256 }
257 }
258
isAsciiTag(const char * tag)259 bool ExifElementsTable::isAsciiTag(const char* tag) {
260 // TODO(XXX): Add tags as necessary
261 return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0);
262 }
263
insertExifToJpeg(unsigned char * jpeg,size_t jpeg_size)264 void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) {
265 ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE);
266
267 ResetJpgfile();
268 if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) {
269 jpeg_opened = true;
270 create_EXIF(table, exif_tag_count, gps_tag_count, has_datetime_tag);
271 }
272 }
273
insertExifThumbnailImage(const char * thumb,int len)274 status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) {
275 status_t ret = NO_ERROR;
276
277 if ((len > 0) && jpeg_opened) {
278 ret = ReplaceThumbnailFromBuffer(thumb, len);
279 CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret);
280 }
281
282 return ret;
283 }
284
saveJpeg(unsigned char * jpeg,size_t jpeg_size)285 void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) {
286 if (jpeg_opened) {
287 WriteJpegToBuffer(jpeg, jpeg_size);
288 DiscardData();
289 jpeg_opened = false;
290 }
291 }
292
293 /* public functions */
~ExifElementsTable()294 ExifElementsTable::~ExifElementsTable() {
295 int num_elements = gps_tag_count + exif_tag_count;
296
297 for (int i = 0; i < num_elements; i++) {
298 if (table[i].Value) {
299 free(table[i].Value);
300 }
301 }
302
303 if (jpeg_opened) {
304 DiscardData();
305 }
306 }
307
insertElement(const char * tag,const char * value)308 status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
309 unsigned int value_length = 0;
310 status_t ret = NO_ERROR;
311
312 if (!value || !tag) {
313 return -EINVAL;
314 }
315
316 if (position >= MAX_EXIF_TAGS_SUPPORTED) {
317 CAMHAL_LOGEA("Max number of EXIF elements already inserted");
318 return NO_MEMORY;
319 }
320
321 if (isAsciiTag(tag)) {
322 value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix));
323 } else {
324 value_length = strlen(value);
325 }
326
327 if (IsGpsTag(tag)) {
328 table[position].GpsTag = TRUE;
329 table[position].Tag = GpsTagNameToValue(tag);
330 gps_tag_count++;
331 } else {
332 table[position].GpsTag = FALSE;
333 table[position].Tag = TagNameToValue(tag);
334 exif_tag_count++;
335
336 if (strcmp(tag, TAG_DATETIME) == 0) {
337 has_datetime_tag = true;
338 }
339 }
340
341 table[position].DataLength = 0;
342 table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1));
343
344 if (table[position].Value) {
345 memcpy(table[position].Value, value, value_length + 1);
346 table[position].DataLength = value_length + 1;
347 }
348
349 position++;
350 return ret;
351 }
352
353 /* private member functions */
encode(params * input)354 size_t Encoder_libjpeg::encode(params* input) {
355 jpeg_compress_struct cinfo;
356 jpeg_error_mgr jerr;
357 jpeg_destination_mgr jdest;
358 uint8_t* src = NULL, *resize_src = NULL;
359 uint8_t* row_tmp = NULL;
360 uint8_t* row_src = NULL;
361 uint8_t* row_uv = NULL; // used only for NV12
362 int out_width = 0, in_width = 0;
363 int out_height = 0, in_height = 0;
364 int bpp = 2; // for uyvy
365 int right_crop = 0, start_offset = 0;
366
367 if (!input) {
368 return 0;
369 }
370
371 out_width = input->out_width;
372 in_width = input->in_width;
373 out_height = input->out_height;
374 in_height = input->in_height;
375 right_crop = input->right_crop;
376 start_offset = input->start_offset;
377 src = input->src;
378 input->jpeg_size = 0;
379
380 libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size);
381
382 // param check...
383 if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) ||
384 (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) ||
385 (input->dst_size < 1) || (input->format == NULL)) {
386 goto exit;
387 }
388
389 if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
390 bpp = 1;
391 if ((in_width != out_width) || (in_height != out_height)) {
392 resize_src = (uint8_t*) malloc(input->dst_size);
393 resize_nv12(input, resize_src);
394 if (resize_src) src = resize_src;
395 }
396 } else if ((in_width != out_width) || (in_height != out_height)) {
397 CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %s", input->format);
398 goto exit;
399 } else if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV422I)) {
400 // we currently only support yuv422i and yuv420sp
401 CAMHAL_LOGEB("Encoder: format not supported: %s", input->format);
402 goto exit;
403 }
404
405 cinfo.err = jpeg_std_error(&jerr);
406
407 jpeg_create_compress(&cinfo);
408
409 CAMHAL_LOGDB("encoding... \n\t"
410 "width: %d \n\t"
411 "height:%d \n\t"
412 "dest %p \n\t"
413 "dest size:%d \n\t"
414 "mSrc %p",
415 out_width, out_height, input->dst,
416 input->dst_size, src);
417
418 cinfo.dest = &dest_mgr;
419 cinfo.image_width = out_width - right_crop;
420 cinfo.image_height = out_height;
421 cinfo.input_components = 3;
422 cinfo.in_color_space = JCS_YCbCr;
423 cinfo.input_gamma = 1;
424
425 jpeg_set_defaults(&cinfo);
426 jpeg_set_quality(&cinfo, input->quality, TRUE);
427 cinfo.dct_method = JDCT_IFAST;
428
429 jpeg_start_compress(&cinfo, TRUE);
430
431 row_tmp = (uint8_t*)malloc(out_width * 3);
432 row_src = src + start_offset;
433 row_uv = src + out_width * out_height * bpp;
434
435 while ((cinfo.next_scanline < cinfo.image_height) && !mCancelEncoding) {
436 JSAMPROW row[1]; /* pointer to JSAMPLE row[s] */
437
438 // convert input yuv format to yuv444
439 if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
440 nv21_to_yuv(row_tmp, row_src, row_uv, out_width - right_crop);
441 } else {
442 uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width - right_crop);
443 }
444
445 row[0] = row_tmp;
446 jpeg_write_scanlines(&cinfo, row, 1);
447 row_src = row_src + out_width*bpp;
448
449 // move uv row if input format needs it
450 if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
451 if (!(cinfo.next_scanline % 2))
452 row_uv = row_uv + out_width * bpp;
453 }
454 }
455
456 // no need to finish encoding routine if we are prematurely stopping
457 // we will end up crashing in dest_mgr since data is incomplete
458 if (!mCancelEncoding)
459 jpeg_finish_compress(&cinfo);
460 jpeg_destroy_compress(&cinfo);
461
462 if (resize_src) free(resize_src);
463 if (row_tmp) free(row_tmp);
464
465 exit:
466 input->jpeg_size = dest_mgr.jpegsize;
467 return dest_mgr.jpegsize;
468 }
469
470 } // namespace android
471