summaryrefslogtreecommitdiff
path: root/v3/fake-pipeline2/JpegCompressor.cpp (plain)
blob: 66781164c440b3bffaa8dc32fbf49c68c18b6829
1/*
2 * Copyright (C) 2012 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//#define LOG_NDEBUG 0
18#define LOG_TAG "EmulatedCamera3_JpegCompressor"
19
20#include <utils/Log.h>
21#include <ui/GraphicBufferMapper.h>
22
23#include "JpegCompressor.h"
24#include "../EmulatedFakeCamera2.h"
25#include "../EmulatedFakeCamera3.h"
26#include <stdlib.h>
27#include <math.h>
28#include <sys/time.h>
29#include <cutils/properties.h>
30#include "NV12_resize.h"
31
32
33#define EXIF_MAKE_DEFAULT "default_make"
34#define EXIF_MODEL_DEFAULT "default_model"
35#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
36
37const size_t MARKER_LENGTH = 2; // length of a marker
38const uint8_t MARK = 0xFF;
39const uint8_t EOI = 0xD9;
40bool checkJpegEnd(uint8_t *buf) {
41 return buf[0] == MARK && buf[1] == EOI;
42}
43int extraSmallImg(unsigned char* SrcImg,int SrcW,int SrcH,
44 unsigned char* DstImg,int DstW,int DstH)
45{
46 int skipW = SrcW/DstW;
47 int skipH = SrcH/DstH;
48 unsigned char* dst = DstImg;
49 unsigned char* srcrow = SrcImg;
50 unsigned char* srcrowidx = srcrow;
51 int i = 0,j = 0;
52 for (; i<DstH; i++)
53 {
54 for (j = 0; j<DstW; j++)
55 {
56 dst[0] = srcrowidx[0];
57 dst[1] = srcrowidx[1];
58 dst[2] = srcrowidx[2];
59 dst += 3;
60 srcrowidx += 3*skipW;
61 }
62 srcrow += skipH*SrcW*3;
63 srcrowidx = srcrow;
64 }
65 return 1;
66}
67namespace android {
68
69struct string_pair {
70 const char* string1;
71 const char* string2;
72};
73static string_pair degress_to_exif_lut [] = {
74 {"0", "1"},
75 {"90", "6"},
76 {"180", "3"},
77 {"270", "8"},
78};
79
80struct params {
81 uint8_t* src;
82 int src_size;
83 uint8_t* dst;
84 int dst_size;
85 int quality;
86 int in_width;
87 int in_height;
88 int out_width;
89 int out_height;
90 int format;
91 size_t jpeg_size;
92};
93
94enum format {
95 YUV420SP,
96 YUV422I,
97 RGB24,
98};
99
100JpegCompressor::JpegCompressor():
101 Thread(false),
102 mIsBusy(false),
103 mSynchronous(false),
104 mExitJpegThread(false),
105 mNeedexif(true),
106 mMainJpegSize(0),
107 mThumbJpegSize(0),
108 mSrcThumbBuffer(NULL),
109 mDstThumbBuffer(NULL),
110 mBuffers(NULL),
111 mPendingrequest(0),
112 mListener(NULL) {
113 memset(&mInfo,0,sizeof(struct ExifInfo));
114}
115
116JpegCompressor::~JpegCompressor() {
117 Mutex::Autolock lock(mMutex);
118}
119
120void JpegCompressor::queueRequest(CaptureRequest &r) {
121 Mutex::Autolock lock(mMutex);
122
123 CaptureRequest* ri = new CaptureRequest();
124 ri->buf = new camera3_stream_buffer();
125 memcpy(ri->buf,r.buf,sizeof(camera3_stream_buffer_t));
126 ri->frameNumber = r.frameNumber;
127 ri->sensorBuffers = r.sensorBuffers;
128 ri->mNeedThumbnail = r.mNeedThumbnail;
129 mInJpegRequestQueue.push_back(ri);
130
131 mPendingrequest++;
132 mInJpegRequestSignal.signal();
133}
134
135status_t JpegCompressor::start() {
136 status_t res;
137 res = run("EmulatedFakeCamera2::JpegCompressor");
138 if (res != OK) {
139 ALOGE("%s: Unable to start up compression thread: %s (%d)",
140 __FUNCTION__, strerror(-res), res);
141 }
142 return res;
143}
144
145status_t JpegCompressor::setlistener(JpegListener *listener) {
146 status_t res = NO_ERROR;
147 if (listener == NULL) {
148 ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
149 return BAD_VALUE;
150 }
151 Mutex::Autolock lock(mMutex);
152 {
153 Mutex::Autolock busyLock(mBusyMutex);
154
155 if (mIsBusy) {
156 ALOGE("%s: Already processing a buffer!", __FUNCTION__);
157 return INVALID_OPERATION;
158 }
159
160 mSynchronous = false;
161 mListener = listener;
162 }
163 return res;
164}
165
166status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
167 status_t res;
168
169 Mutex::Autolock lock(mMutex);
170 {
171 Mutex::Autolock busyLock(mBusyMutex);
172
173 if (mIsBusy) {
174 ALOGE("%s: Already processing a buffer!", __FUNCTION__);
175 return INVALID_OPERATION;
176 }
177
178 mIsBusy = true;
179 mSynchronous = true;
180 mBuffers = buffers;
181 }
182
183 res = compress();
184
185 cleanUp();
186
187 return res;
188}
189
190status_t JpegCompressor::cancel() {
191 mMutex.lock();
192 mExitJpegThread = true;
193 mPendingrequest++;
194 mInJpegRequestSignal.signal();
195 mMutex.unlock();
196 requestExitAndWait();
197 for (List<CaptureRequest*>::iterator i = mInJpegRequestQueue.begin();
198 i != mInJpegRequestQueue.end(); i++) {
199 delete (*i)->buf;
200 delete (*i)->sensorBuffers;
201 }
202
203 return OK;
204}
205
206status_t JpegCompressor::readyToRun() {
207 return OK;
208}
209
210bool JpegCompressor::threadLoop() {
211 status_t res;
212 CaptureRequest* ri = NULL;
213 {
214 mMutex.lock();
215 if (mExitJpegThread) {
216 mMutex.unlock();
217 ALOGE("JpegCompressor Thread : exiting on request0");
218 return false;
219 }
220
221 while (mPendingrequest == 0) {
222 res = mInJpegRequestSignal.wait(mMutex);
223 }
224 mPendingrequest--;
225 if (mInJpegRequestQueue.size() > 0) {
226 List<CaptureRequest*>::iterator i = mInJpegRequestQueue.begin();
227 mJpegRequest.frameNumber = (*i)->frameNumber;
228 mJpegRequest.buf = (*i)->buf;
229 mJpegRequest.sensorBuffers = (*i)->sensorBuffers;
230 mJpegRequest.mNeedThumbnail = (*i)->mNeedThumbnail;
231 ri = *mInJpegRequestQueue.begin();
232 mInJpegRequestQueue.erase(mInJpegRequestQueue.begin());
233 mBuffers = mJpegRequest.sensorBuffers;
234 } else {
235 mMutex.unlock();
236 return true;
237 }
238 if (mExitJpegThread) {
239 mMutex.unlock();
240 ALOGE("JpegCompressor Thread : exiting on request1");
241 if (mBuffers != NULL) {
242 delete mBuffers;
243 mBuffers = NULL;
244 }
245 if (ri != NULL) {
246 if (ri->buf != NULL) {
247 delete ri->buf;
248 }
249 delete ri;
250 }
251 return false;
252 }
253 mMutex.unlock();
254 }
255 bool foundJpeg = false, mFoundAux = false;
256 for (size_t i = 0; i < mBuffers->size(); i++) {
257 const StreamBuffer &b = (*mBuffers)[i];
258 if (b.format == HAL_PIXEL_FORMAT_BLOB) {
259 mJpegBuffer = b;
260 mFoundJpeg = true;
261 } else if (b.streamId <= 0) {
262 mAuxBuffer = b;
263 mFoundAux = true;
264 }
265 if (mFoundJpeg && mFoundAux) break;
266 }
267 if (!mFoundJpeg || !mFoundAux) {
268 ALOGE("%s: Unable to find buffers for JPEG source/destination",
269 __FUNCTION__);
270 return BAD_VALUE;
271 }
272
273 struct timeval mTimeStart,mTimeend;
274 int intreval;
275 ExifElementsTable* exiftable = NULL;
276 struct camera2_jpeg_blob blob;
277 int offset;
278 ALOGV("%s: Starting compression thread", __FUNCTION__);
279
280 gettimeofday(&mTimeStart, NULL);
281 res = compress();
282 if (mNeedexif) {
283 memset(&blob,0,sizeof(struct camera2_jpeg_blob));
284 exiftable = new ExifElementsTable();
285 GenExif(exiftable);
286 }
287
288 if (mJpegRequest.mNeedThumbnail) {
289 res = thumbcompress();
290 }
291
292 if (exiftable) {
293 uint32_t realjpegsize = 0;
294 Section_t* exif_section = NULL;
295 ExifElementsTable* exif = exiftable;
296 exif->insertExifToJpeg((unsigned char*)mJpegBuffer.img,mMainJpegSize);
297 if ((mJpegRequest.mNeedThumbnail) && (mDstThumbBuffer != NULL)) {
298 exif->insertExifThumbnailImage((const char*)mDstThumbBuffer,mThumbJpegSize);
299 }
300 exif_section = FindSection(M_EXIF);
301 if (exif_section) {
302 exif->saveJpeg((unsigned char*) mJpegBuffer.img, mMainJpegSize + exif_section->Size);
303 }
304 for (uint32_t size = (mMainJpegSize + exif_section->Size - 2); size > 0; size--) {
305 if (checkJpegEnd(mJpegBuffer.img + size)) {
306 realjpegsize = (size + MARKER_LENGTH);
307 break;
308 }
309 }
310 int offset = mMaxbufsize-sizeof(struct camera2_jpeg_blob);
311 blob.jpeg_blob_id = 0x00FF;
312 blob.jpeg_size = realjpegsize;
313 memcpy(mJpegBuffer.img+offset, &blob, sizeof(struct camera2_jpeg_blob));
314 }
315 mListener->onJpegDone(mJpegBuffer, res == OK, mJpegRequest);
316 if (ri != NULL) {
317 if (ri->buf != NULL) {
318 delete ri->buf;
319 }
320 delete ri;
321 }
322
323 if (mNeedexif) {
324 if (exiftable != NULL) {
325 delete exiftable;
326 exiftable = NULL;
327 }
328 }
329
330 gettimeofday(&mTimeend, NULL);
331 intreval = (mTimeend.tv_sec - mTimeStart.tv_sec) * 1000 + ((mTimeend.tv_usec - mTimeStart.tv_usec))/1000;
332 ALOGD("jpeg compress cost time =%d ms",intreval);
333 cleanUp();
334
335 return true;
336}
337
338struct libjpeg_destination_mgr : jpeg_destination_mgr {
339 libjpeg_destination_mgr(uint8_t* input, int size);
340 uint8_t* buf;
341 int bufsize;
342 size_t jpegsize;
343};
344
345static void libjpeg_init_destination (j_compress_ptr cinfo) {
346 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
347
348 dest->next_output_byte = dest->buf;
349 dest->free_in_buffer = dest->bufsize;
350 dest->jpegsize = 0;
351}
352
353static boolean libjpeg_empty_output_buffer(j_compress_ptr cinfo) {
354 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
355
356 dest->next_output_byte = dest->buf;
357 dest->free_in_buffer = dest->bufsize;
358 return TRUE; // ?
359}
360
361static void libjpeg_term_destination (j_compress_ptr cinfo) {
362 libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
363 dest->jpegsize = dest->bufsize - dest->free_in_buffer;
364}
365
366libjpeg_destination_mgr::libjpeg_destination_mgr(uint8_t* input, int size) {
367 this->init_destination = libjpeg_init_destination;
368 this->empty_output_buffer = libjpeg_empty_output_buffer;
369 this->term_destination = libjpeg_term_destination;
370 this->buf = input;
371 this->bufsize = size;
372 jpegsize = 0;
373}
374
375static void resize_nv12(params* params, uint8_t* dst_buffer) {
376 structConvImage o_img_ptr, i_img_ptr;
377
378 if (!params || !dst_buffer) {
379 return;
380 }
381
382 //input
383 i_img_ptr.uWidth = (mmInt32)params->in_width;
384 i_img_ptr.uStride = (mmInt32)i_img_ptr.uWidth;
385 i_img_ptr.uHeight = (mmInt32)params->in_height;
386 i_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
387 i_img_ptr.imgPtr = (mmByte *) params->src;
388 i_img_ptr.clrPtr = (mmByte *)i_img_ptr.imgPtr + (i_img_ptr.uWidth * i_img_ptr.uHeight);
389 i_img_ptr.uOffset = 0;
390
391 //ouput
392 o_img_ptr.uWidth = (mmInt32)params->out_width;
393 o_img_ptr.uStride = (mmInt32)o_img_ptr.uWidth;
394 o_img_ptr.uHeight = (mmInt32)params->out_height;
395 o_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
396 o_img_ptr.imgPtr = (mmByte *)dst_buffer;
397 o_img_ptr.clrPtr = (mmByte *)o_img_ptr.imgPtr + (o_img_ptr.uWidth * o_img_ptr.uHeight);
398 o_img_ptr.uOffset = 0;
399
400 VT_resizeFrame_Video_opt2_lp(&i_img_ptr, &o_img_ptr, NULL, 0);
401}
402
403static void resize_yuyv(params* params, uint8_t* dst_buffer) {
404 int step_x, step_y;
405 int dst_pos, src_pos;
406 int src_y_start_pos;
407 step_x = params->in_width / params->out_width;
408 step_y = params->in_height / params->out_height;
409 dst_pos = 0;
410 for (int y = 0; y < params->out_height; y++) {
411 src_y_start_pos = (y * step_y * (params->in_width * 2));
412 for (int x = 0; x < params->out_width; x += 2) {
413 src_pos = src_y_start_pos + (x * (step_x * 2));
414 dst_buffer[dst_pos++] = params->src[src_pos];
415 dst_buffer[dst_pos++] = params->src[src_pos + 1];
416 dst_buffer[dst_pos++] = params->src[src_pos + 2];
417 dst_buffer[dst_pos++] = params->src[src_pos + 3];
418 }
419 }
420}
421
422/* private static functions */
423static void nv21_to_yuv(uint8_t* dst, uint8_t* y, uint8_t* uv, int width) {
424 if (!dst || !y || !uv) {
425 return;
426 }
427
428 while ((width--) > 0) {
429 uint8_t y0 = y[0];
430 uint8_t v0 = uv[0];
431 uint8_t u0 = *(uv+1);
432 dst[0] = y0;
433 dst[1] = u0;
434 dst[2] = v0;
435 dst += 3;
436 y++;
437 if (!(width % 2)) uv+=2;
438 }
439}
440
441static void yuyv_to_yuv(uint8_t* dst, uint32_t* src, int width) {
442 if (!dst || !src) {
443 return;
444 }
445
446 if (width % 2) {
447 return; // not supporting odd widths
448 }
449
450 // currently, neon routine only supports multiple of 16 width
451 if (width % 16) {
452 while ((width-=2) >= 0) {
453 uint8_t y0 = (src[0] >> 0) & 0xFF;
454 uint8_t u0 = (src[0] >> 8) & 0xFF;
455 uint8_t y1 = (src[0] >> 16) & 0xFF;
456 uint8_t v0 = (src[0] >> 24) & 0xFF;
457 dst[0] = y0;
458 dst[1] = u0;
459 dst[2] = v0;
460 dst[3] = y1;
461 dst[4] = u0;
462 dst[5] = v0;
463 dst += 6;
464 src++;
465 }
466 } else {
467 int n = width;
468 asm volatile (
469 " pld [%[src], %[src_stride], lsl #2] \n\t"
470 " cmp %[n], #16 \n\t"
471 " blt 5f \n\t"
472 "0: @ 16 pixel swap \n\t"
473 " vld2.8 {q0, q1} , [%[src]]! @ q0 = y q1 = uv \n\t"
474 " vuzp.8 q1, q2 @ d2 = u d4 = v \n\t"
475 " vmov d3, d2 @ q1 = u0u1u2..u0u1u2... \n\t"
476 " vmov d5, d4 @ q2 = v0v1v2..v0v1v2... \n\t"
477 " vzip.8 d2, d3 @ q1 = u0u0u1u1u2u2... \n\t"
478 " vzip.8 d4, d5 @ q2 = v0v0v1v1v2v2... \n\t"
479 " vst3.8 {d0,d2,d4},[%[dst]]! \n\t"
480 " vst3.8 {d1,d3,d5},[%[dst]]! \n\t"
481 " sub %[n], %[n], #16 \n\t"
482 " cmp %[n], #16 \n\t"
483 " bge 0b \n\t"
484 "5: @ end \n\t"
485#ifdef NEEDS_ARM_ERRATA_754319_754320
486 " vmov s0,s0 @ add noop for errata item \n\t"
487#endif
488 : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n)
489 : [src_stride] "r" (width)
490 : "cc", "memory", "q0", "q1", "q2"
491 );
492 }
493}
494
495static uint32_t calc_frame_length(int format, uint32_t width, uint32_t height)
496{
497 uint32_t length;
498 switch (format) {
499 case HAL_PIXEL_FORMAT_YCrCb_420_SP:
500 length = width * height * 3/2;
501 break;
502 case HAL_PIXEL_FORMAT_RGB_888:
503 length = width * height * 3;
504 break;
505 case HAL_PIXEL_FORMAT_YCbCr_422_I:
506 length = width * height * 2;
507 break;
508 default:
509 length = width * height * 3/2;
510 break;
511 }
512 return length;
513}
514
515size_t encode(params* input) {
516 jpeg_compress_struct cinfo;
517 jpeg_error_mgr jerr;
518 jpeg_destination_mgr jdest;
519 uint8_t* src = NULL;
520 uint8_t* resize_src = NULL;
521 uint8_t* row_tmp = NULL;
522 uint8_t* row_src = NULL;
523 uint8_t* row_uv = NULL; // used only for NV12
524 int row_stride;
525 int out_width = 0, in_width = 0;
526 int out_height = 0, in_height = 0;
527 int bpp = 2; // for uyvy
528
529 format informat = YUV422I;
530
531 if (!input) {
532 return 0;
533 }
534
535 out_width = input->out_width;
536 in_width = input->in_width;
537 out_height = input->out_height;
538 in_height = input->in_height;
539 src = input->src;
540 input->jpeg_size = 0;
541 libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size);
542
543 // param check...
544 if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) ||
545 (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) ||
546 (input->dst_size < 1) ) {
547 goto exit;
548 }
549
550 if (input->format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
551 informat = YUV420SP;
552 bpp = 1;
553 if ((in_width != out_width) || (in_height != out_height)) {
554 resize_src = (uint8_t*) malloc(out_width * out_height *3);
555 if (NULL != resize_src) {
556 resize_nv12(input, resize_src);
557 if (resize_src) src = resize_src;
558 } else {
559 CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n");
560 goto exit;
561 }
562 }
563 } else if ((input->format == HAL_PIXEL_FORMAT_RGB_888)) {
564 informat = RGB24;
565 bpp = 1;
566 if ((in_width != out_width) || (in_height != out_height)) {
567 resize_src = (uint8_t*) malloc(out_width * out_height *3);
568 if (NULL != resize_src) {
569 extraSmallImg(input->src, in_width, in_height,
570 resize_src, out_width, out_height);
571 src = resize_src;
572 } else {
573 CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n");
574 goto exit;
575 }
576 }
577 } else if (input->format == HAL_PIXEL_FORMAT_YCbCr_422_I) {
578 informat = YUV422I;
579 bpp = 2;
580 if ((in_width != out_width) || (in_height != out_height)) {
581 resize_src = (uint8_t*) malloc(out_width * out_height *3);
582 if (NULL != resize_src) {
583 resize_yuyv(input,resize_src);
584 if (resize_src) src = resize_src;
585 } else {
586 CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n");
587 goto exit;
588 }
589 }
590 } else if ((in_width != out_width) || (in_height != out_height)) {
591 CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %d", input->format);
592 goto exit;
593 } else if ((input->format != HAL_PIXEL_FORMAT_YCbCr_422_I)) {
594 // we currently only support yuv422i and yuv420sp
595 CAMHAL_LOGEB("Encoder: format not supported: %d", input->format);
596 goto exit;
597 }
598
599 cinfo.err = jpeg_std_error(&jerr);
600
601 jpeg_create_compress(&cinfo);
602
603 CAMHAL_LOGDB("software 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, src);
611
612 cinfo.dest = &dest_mgr;
613 cinfo.image_width = out_width;
614 cinfo.image_height = out_height;
615 cinfo.input_components = 3;
616 if (informat == RGB24)
617 cinfo.in_color_space = JCS_RGB;
618 else
619 cinfo.in_color_space = JCS_YCbCr;
620 cinfo.input_gamma = 1;
621
622 jpeg_set_defaults(&cinfo);
623 jpeg_set_quality(&cinfo, input->quality, TRUE);
624 cinfo.dct_method = JDCT_IFAST;
625
626 jpeg_start_compress(&cinfo, TRUE);
627
628 row_tmp = (uint8_t*)malloc(out_width * 3);
629 row_src = src;
630 row_uv = src + out_width * out_height * bpp;
631 row_stride = out_width * 3;
632
633 while ((cinfo.next_scanline < cinfo.image_height)) {
634 JSAMPROW row[1]; /* pointer to JSAMPLE row[s] */
635
636 if (informat == RGB24) {
637 row[0] = &src[cinfo.next_scanline * row_stride];
638 (void) jpeg_write_scanlines(&cinfo, row, 1);
639 } else {
640 // convert input yuv format to yuv444
641 if (informat == YUV420SP) {
642 nv21_to_yuv(row_tmp, row_src, row_uv, out_width);
643 } else if (informat == YUV422I) {
644 //uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width);
645 yuyv_to_yuv(row_tmp, (uint32_t*)row_src, out_width);
646 }
647
648 row[0] = row_tmp;
649 jpeg_write_scanlines(&cinfo, row, 1);
650 row_src = row_src + out_width*bpp;
651
652 // move uv row if input format needs it
653 if (informat == YUV420SP) {
654 if (!(cinfo.next_scanline % 2))
655 row_uv = row_uv + out_width * bpp;
656 }
657 }
658 }
659
660 jpeg_finish_compress(&cinfo);
661 jpeg_destroy_compress(&cinfo);
662
663 if (resize_src) free(resize_src);
664 if (row_tmp) free(row_tmp);
665
666 exit:
667 input->jpeg_size = dest_mgr.jpegsize;
668 return dest_mgr.jpegsize;
669}
670
671status_t JpegCompressor::compress() {
672 // Find source and target buffers. Assumes only one buffer matches
673 // each condition!
674 if (mJpegRequest.mNeedThumbnail == true) {
675 mSrcThumbBuffer = mAuxBuffer.img;
676 if (mDstThumbBuffer == NULL) {
677 mDstThumbBuffer = (uint8_t*)malloc(mInfo.thumbwidth * mInfo.thumbheight * 3);
678 }
679 }
680
681 params enc_params;
682 enc_params.src = mAuxBuffer.img;
683 enc_params.src_size = calc_frame_length(mAuxBuffer.format, mAuxBuffer.width, mAuxBuffer.height);
684 enc_params.dst = mJpegBuffer.img;
685 enc_params.dst_size = kMaxJpegSize;
686 enc_params.quality = 80;
687 enc_params.in_width = mAuxBuffer.width;
688 enc_params.in_height = mAuxBuffer.height;
689 enc_params.out_width= mAuxBuffer.width;
690 enc_params.out_height = mAuxBuffer.height;
691 enc_params.format = mAuxBuffer.format;
692 enc_params.jpeg_size = 0;
693
694 mMainJpegSize = encode(&enc_params);
695 ALOGD("mMainJpegSize = %d",mMainJpegSize);
696
697 return OK;
698}
699
700status_t JpegCompressor::thumbcompress() {
701 if ((mSrcThumbBuffer == NULL) || (mDstThumbBuffer == NULL))
702 return 0;
703
704 params enc_params;
705 enc_params.src = mSrcThumbBuffer;
706 enc_params.dst = mDstThumbBuffer;
707 enc_params.dst_size = kMaxJpegSize;
708 enc_params.quality = 70;
709 enc_params.src_size = calc_frame_length(mAuxBuffer.format, mAuxBuffer.width, mAuxBuffer.height);
710 enc_params.in_width = mAuxBuffer.width;
711 enc_params.in_height = mAuxBuffer.height;
712 enc_params.out_width= mInfo.thumbwidth;
713 enc_params.out_height = mInfo.thumbheight;
714 enc_params.format = mAuxBuffer.format;
715 enc_params.jpeg_size = 0;
716
717 mThumbJpegSize = encode(&enc_params);
718 ALOGD("mThumbJpegSize = %d",mThumbJpegSize);
719
720 return OK;
721}
722bool JpegCompressor::isBusy() {
723 Mutex::Autolock busyLock(mBusyMutex);
724 return mIsBusy;
725}
726
727bool JpegCompressor::isStreamInUse(uint32_t id) {
728 Mutex::Autolock lock(mBusyMutex);
729
730 if (mBuffers && mIsBusy) {
731 for (size_t i = 0; i < mBuffers->size(); i++) {
732 if ( (*mBuffers)[i].streamId == (int)id ) return true;
733 }
734 }
735 return false;
736}
737
738bool JpegCompressor::waitForDone(nsecs_t timeout) {
739 Mutex::Autolock lock(mBusyMutex);
740 status_t res = OK;
741 if (mIsBusy) {
742 res = mDone.waitRelative(mBusyMutex, timeout);
743 }
744 return (res == OK);
745}
746
747bool JpegCompressor::checkError(const char *msg) {
748 if (mJpegErrorInfo) {
749 char errBuffer[JMSG_LENGTH_MAX];
750 mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
751 ALOGE("%s: %s: %s",
752 __FUNCTION__, msg, errBuffer);
753 mJpegErrorInfo = NULL;
754 return true;
755 }
756 return false;
757}
758
759void JpegCompressor::cleanUp() {
760 status_t res;
761 Mutex::Autolock lock(mBusyMutex);
762 if (mJpegRequest.mNeedThumbnail == true) {
763 if (mSrcThumbBuffer != NULL) {
764 mSrcThumbBuffer = NULL;
765 }
766 if (mDstThumbBuffer != NULL) {
767 free(mDstThumbBuffer);
768 mDstThumbBuffer = NULL;
769 }
770 }
771 if (mFoundAux) {
772 if (mAuxBuffer.streamId == 0) {
773 delete[] mAuxBuffer.img;
774 } else if (!mSynchronous) {
775 mListener->onJpegInputDone(mAuxBuffer);
776 }
777 }
778 if (!mSynchronous) {
779 delete mBuffers;
780 }
781
782 mBuffers = NULL;
783
784 mIsBusy = false;
785 mDone.signal();
786}
787
788JpegCompressor::JpegListener::~JpegListener() {
789}
790
791void JpegCompressor::SetMaxJpegBufferSize(ssize_t size)
792{
793 mMaxbufsize = size;
794}
795ssize_t JpegCompressor::GetMaxJpegBufferSize()
796{
797 return mMaxbufsize;
798}
799void JpegCompressor::SetExifInfo(struct ExifInfo info)
800{
801 mInfo.mainwidth = info.mainwidth;
802 mInfo.mainheight = info.mainheight;
803 mInfo.thumbwidth = info.thumbwidth;
804 mInfo.thumbheight = info.thumbheight;
805 mInfo.gpsTimestamp = info.gpsTimestamp;
806 mInfo.latitude = info.latitude;
807 mInfo.longitude = info.longitude;
808 mInfo.altitude = info.altitude;
809 mInfo.gpsProcessingMethod = info.gpsProcessingMethod;
810 mInfo.focallen = info.focallen;
811 mInfo.orientation = info.orientation;
812 mInfo.has_latitude = info.has_latitude;
813 mInfo.has_longitude = info.has_longitude;
814 mInfo.has_altitude = info.has_altitude;
815 mInfo.has_gpsProcessingMethod = info.has_gpsProcessingMethod;
816 mInfo.has_gpsTimestamp = info.has_gpsTimestamp;
817 mInfo.has_focallen = info.has_focallen;
818
819}
820int JpegCompressor::GenExif(ExifElementsTable* exiftable)
821{
822 char exifcontent[256];
823 int width,height;
824 bool newexif = true; //add new exif tag for cts
825 float exposuretime = 1.0;
826 float ApertureValue = 1.0;
827 int flash = 0;
828 int whitebalance = 1;
829 int iso = 100;
830 char SubSecTime[10] = "63";
831 char SubSecTimeOrig[10]= "63";
832 char SubSecTimeDig[10]= "63";
833 char property[PROPERTY_VALUE_MAX];
834
835 property_get("ro.product.manufacturer", property, EXIF_MAKE_DEFAULT);
836 exiftable->insertElement("Make",property);
837 property_get("ro.product.model", property, EXIF_MODEL_DEFAULT);
838 exiftable->insertElement("Model",property);
839// int orientation = mInfo.orientation;
840 width = mInfo.mainwidth;
841 height = mInfo.mainheight;
842#if 0
843 if (orientation == 0)
844 orientation = 1;
845 else if (orientation == 90)
846 orientation = 6;
847 else if (orientation == 180)
848 orientation = 3;
849 else if (orientation == 270)
850 orientation = 8;
851 sprintf(exifcontent,"%d",orientation);
852 exiftable->insertElement("Orientation",(const char*)exifcontent);
853#endif
854 sprintf(exifcontent,"%d",width);
855 exiftable->insertElement("ImageWidth",(const char*)exifcontent);
856 sprintf(exifcontent,"%d",height);
857 exiftable->insertElement("ImageLength",(const char*)exifcontent);
858
859 sprintf(exifcontent,"%f",exposuretime);
860 exiftable->insertElement("ExposureTime",(const char*)exifcontent);
861 sprintf(exifcontent,"%f",ApertureValue);
862 exiftable->insertElement("ApertureValue",(const char*)exifcontent);
863 sprintf(exifcontent,"%d",flash);
864 exiftable->insertElement("Flash",(const char*)exifcontent);
865 sprintf(exifcontent,"%d",whitebalance);
866 exiftable->insertElement("WhiteBalance",(const char*)exifcontent);
867 sprintf(exifcontent,"%d",iso);
868 exiftable->insertElement("ISOSpeedRatings",(const char*)exifcontent);
869 if (newexif) {
870 time_t times;
871 {
872 time(&times);
873 struct tm tmstruct;
874 tmstruct = *(localtime(&times)); //convert to local time
875 strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct);
876 exiftable->insertElement("DateTimeDigitized",(const char*)exifcontent);
877 }
878 {
879 sprintf(exifcontent, "%s", SubSecTime);
880 exiftable->insertElement("SubSecTime",(const char*)exifcontent);
881 }
882 {
883
884 sprintf(exifcontent, "%s", SubSecTimeOrig);
885 exiftable->insertElement("SubSecTimeOriginal",(const char*)exifcontent);
886 }
887 {
888
889 sprintf(exifcontent, "%s", SubSecTimeDig);
890 exiftable->insertElement("SubSecTimeDigitized",(const char*)exifcontent);
891 }
892 }
893
894 if (mInfo.has_focallen) {
895 float focallen = mInfo.focallen;
896 if (focallen >= 0) {
897 int focalNum = focallen*1000;
898 int focalDen = 1000;
899 sprintf(exifcontent,"%d/%d",focalNum,focalDen);
900 exiftable->insertElement("FocalLength",(const char*)exifcontent);
901 }
902 }
903 time_t times;
904 {
905 time(&times);
906 struct tm tmstruct;
907 tmstruct = *(localtime(&times)); //convert to local time
908 strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct);
909 exiftable->insertElement("DateTime",(const char*)exifcontent);
910 }
911 if (mInfo.has_gpsTimestamp) {
912 times = mInfo.gpsTimestamp;
913 if (times != -1) {
914 struct tm tmstruct;
915 tmstruct = *(gmtime(&times));//convert to standard time
916 strftime(exifcontent, 20, "%Y:%m:%d", &tmstruct);
917 exiftable->insertElement("GPSDateStamp",(const char*)exifcontent);
918 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",tmstruct.tm_hour,1,tmstruct.tm_min,1,tmstruct.tm_sec,1);
919 exiftable->insertElement("GPSTimeStamp",(const char*)exifcontent);
920 }
921 }
922 if (mInfo.has_latitude) {
923 int offset = 0;
924 float latitude = mInfo.latitude;
925 if (latitude < 0.0) {
926 offset = 1;
927 latitude*= (float)(-1);
928 }
929 int latitudedegree = latitude;
930 float latitudeminuts = (latitude-(float)latitudedegree)*60;
931 int latitudeminuts_int = latitudeminuts;
932 float latituseconds = (latitudeminuts-(float)latitudeminuts_int)*60+0.5;
933 int latituseconds_int = latituseconds;
934 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",latitudedegree,1,latitudeminuts_int,1,latituseconds_int,1);
935 exiftable->insertElement("GPSLatitude",(const char*)exifcontent);
936 exiftable->insertElement("GPSLatitudeRef",(offset==1)?"S":"N");
937 }
938 if (mInfo.has_longitude) {
939 int offset = 0;
940 float longitude = mInfo.longitude;
941 if (longitude < 0.0) {
942 offset = 1;
943 longitude*= (float)(-1);
944 }
945 int longitudedegree = longitude;
946 float longitudeminuts = (longitude-(float)longitudedegree)*60;
947 int longitudeminuts_int = longitudeminuts;
948 float longitudeseconds = (longitudeminuts-(float)longitudeminuts_int)*60+0.5;
949 int longitudeseconds_int = longitudeseconds;
950 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",longitudedegree,1,longitudeminuts_int,1,longitudeseconds_int,1);
951 exiftable->insertElement("GPSLongitude",(const char*)exifcontent);
952 exiftable->insertElement("GPSLongitudeRef",(offset==1)?"S":"N");
953 }
954 if (mInfo.has_altitude) {
955 int offset = 0;
956 float altitude = mInfo.altitude;
957 if (altitude < 0.0) {
958 offset = 1;
959 altitude*= (float)(-1);
960 }
961 int altitudenum = altitude*1000;
962 int altitudedec= 1000;
963 sprintf(exifcontent,"%d/%d",altitudenum,altitudedec);
964 exiftable->insertElement("GPSAltitude",(const char*)exifcontent);
965 sprintf(exifcontent,"%d",offset);
966 exiftable->insertElement("GPSAltitudeRef",(const char*)exifcontent);
967 }
968 if (mInfo.has_gpsProcessingMethod) {
969 char* processmethod = (char*)mInfo.gpsProcessingMethod;
970 if (processmethod != NULL) {
971 memset(exifcontent,0,sizeof(exifcontent));
972 char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };//asicii
973 memcpy(exifcontent,ExifAsciiPrefix,8);
974 memcpy(exifcontent+8,processmethod,strlen(processmethod));
975 exiftable->insertElement("GPSProcessingMethod",(const char*)exifcontent);
976 }
977 }
978 return 1;
979}
980const char* ExifElementsTable::degreesToExifOrientation(const char* degrees) {
981 for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) {
982 if (!strcmp(degrees, degress_to_exif_lut[i].string1)) {
983 return degress_to_exif_lut[i].string2;
984 }
985 }
986 return NULL;
987}
988void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) {
989 int len;
990 char * tempVal = NULL;
991 if (str != NULL) {
992 len = strlen(str);
993 tempVal = (char*) malloc( sizeof(char) * (len + 1));
994 }
995 if (tempVal != NULL) {
996 size_t den_len;
997 char *ctx;
998 unsigned int numerator = 0;
999 unsigned int denominator = 0;
1000 char* temp = NULL;
1001 memset(tempVal, '\0', len + 1);
1002 strncpy(tempVal, str, len);
1003 temp = strtok_r(tempVal, ".", &ctx);
1004 if (temp != NULL)
1005 numerator = atoi(temp);
1006 if (!numerator)
1007 numerator = 1;
1008 temp = strtok_r(NULL, ".", &ctx);
1009 if (temp != NULL) {
1010 den_len = strlen(temp);
1011 if(HUGE_VAL == den_len ) {
1012 den_len = 0;
1013 }
1014 denominator = static_cast<unsigned int>(pow(10, den_len));
1015 numerator = numerator * denominator + atoi(temp);
1016 } else {
1017 denominator = 1;
1018 }
1019 free(tempVal);
1020 *num = numerator;
1021 *den = denominator;
1022 }
1023}
1024bool ExifElementsTable::isAsciiTag(const char* tag) {
1025 return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0);
1026}
1027status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
1028 int value_length = 0;
1029 status_t ret = NO_ERROR;
1030 if (!value || !tag) {
1031 return -EINVAL;
1032 }
1033 if (position >= MAX_EXIF_TAGS_SUPPORTED) {
1034 CAMHAL_LOGEA("Max number of EXIF elements already inserted");
1035 return NO_MEMORY;
1036 }
1037 if (isAsciiTag(tag)) {
1038 value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix));
1039 } else {
1040 value_length = strlen(value);
1041 }
1042 if (IsGpsTag(tag)) {
1043 table[position].GpsTag = TRUE;
1044 table[position].Tag = GpsTagNameToValue(tag);
1045 gps_tag_count++;
1046 } else {
1047 table[position].GpsTag = FALSE;
1048 table[position].Tag = TagNameToValue(tag);
1049 exif_tag_count++;
1050 }
1051 table[position].DataLength = 0;
1052 table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1));
1053 if (table[position].Value) {
1054 memcpy(table[position].Value, value, value_length + 1);
1055 table[position].DataLength = value_length + 1;
1056 }
1057 position++;
1058 return ret;
1059}
1060void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) {
1061 int ret;
1062 if (jpeg_opened) {
1063 ret = WriteJpegToBuffer(jpeg, jpeg_size);
1064 ALOGD("saveJpeg :: ret =%d",ret);
1065 DiscardData();
1066 jpeg_opened = false;
1067 }
1068}
1069void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) {
1070 ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE);
1071 ResetJpgfile();
1072 if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) {
1073 jpeg_opened = true;
1074 create_EXIF(table, exif_tag_count, gps_tag_count,true);
1075 }
1076}
1077status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) {
1078 status_t ret = NO_ERROR;
1079 if ((len > 0) && jpeg_opened) {
1080 ret = ReplaceThumbnailFromBuffer(thumb, len);
1081 CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret);
1082 }
1083 return ret;
1084}
1085ExifElementsTable::~ExifElementsTable() {
1086 int num_elements = gps_tag_count + exif_tag_count;
1087 for (int i = 0; i < num_elements; i++) {
1088 if (table[i].Value) {
1089 free(table[i].Value);
1090 }
1091 }
1092 if (jpeg_opened) {
1093 DiscardData();
1094 }
1095}
1096} // namespace android
1097