summaryrefslogtreecommitdiff
path: root/v3/fake-pipeline2/JpegCompressor.cpp (plain)
blob: 35730b84623bf805304b0ddd60865c1ca98e37b1
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#if defined(__arm__)
469 asm volatile (
470 " pld [%[src], %[src_stride], lsl #2] \n\t"
471 " cmp %[n], #16 \n\t"
472 " blt 5f \n\t"
473 "0: @ 16 pixel swap \n\t"
474 " vld2.8 {q0, q1} , [%[src]]! @ q0 = y q1 = uv \n\t"
475 " vuzp.8 q1, q2 @ d2 = u d4 = v \n\t"
476 " vmov d3, d2 @ q1 = u0u1u2..u0u1u2... \n\t"
477 " vmov d5, d4 @ q2 = v0v1v2..v0v1v2... \n\t"
478 " vzip.8 d2, d3 @ q1 = u0u0u1u1u2u2... \n\t"
479 " vzip.8 d4, d5 @ q2 = v0v0v1v1v2v2... \n\t"
480 " vst3.8 {d0,d2,d4},[%[dst]]! \n\t"
481 " vst3.8 {d1,d3,d5},[%[dst]]! \n\t"
482 " sub %[n], %[n], #16 \n\t"
483 " cmp %[n], #16 \n\t"
484 " bge 0b \n\t"
485 "5: @ end \n\t"
486#ifdef NEEDS_ARM_ERRATA_754319_754320
487 " vmov s0,s0 @ add noop for errata item \n\t"
488#endif
489 : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n)
490 : [src_stride] "r" (width)
491 : "cc", "memory", "q0", "q1", "q2"
492 );
493#elif defined(__aarch64__)
494#endif
495 }
496}
497
498static uint32_t calc_frame_length(int format, uint32_t width, uint32_t height)
499{
500 uint32_t length;
501 switch (format) {
502 case HAL_PIXEL_FORMAT_YCrCb_420_SP:
503 length = width * height * 3/2;
504 break;
505 case HAL_PIXEL_FORMAT_RGB_888:
506 length = width * height * 3;
507 break;
508 case HAL_PIXEL_FORMAT_YCbCr_422_I:
509 length = width * height * 2;
510 break;
511 default:
512 length = width * height * 3/2;
513 break;
514 }
515 return length;
516}
517
518size_t encode(params* input) {
519 jpeg_compress_struct cinfo;
520 jpeg_error_mgr jerr;
521 jpeg_destination_mgr jdest;
522 uint8_t* src = NULL;
523 uint8_t* resize_src = NULL;
524 uint8_t* row_tmp = NULL;
525 uint8_t* row_src = NULL;
526 uint8_t* row_uv = NULL; // used only for NV12
527 int row_stride;
528 int out_width = 0, in_width = 0;
529 int out_height = 0, in_height = 0;
530 int bpp = 2; // for uyvy
531
532 format informat = YUV422I;
533
534 if (!input) {
535 return 0;
536 }
537
538 out_width = input->out_width;
539 in_width = input->in_width;
540 out_height = input->out_height;
541 in_height = input->in_height;
542 src = input->src;
543 input->jpeg_size = 0;
544 libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size);
545
546 // param check...
547 if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) ||
548 (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) ||
549 (input->dst_size < 1) ) {
550 goto exit;
551 }
552
553 if (input->format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
554 informat = YUV420SP;
555 bpp = 1;
556 if ((in_width != out_width) || (in_height != out_height)) {
557 resize_src = (uint8_t*) malloc(out_width * out_height *3);
558 if (NULL != resize_src) {
559 resize_nv12(input, resize_src);
560 if (resize_src) src = resize_src;
561 } else {
562 CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n");
563 goto exit;
564 }
565 }
566 } else if ((input->format == HAL_PIXEL_FORMAT_RGB_888)) {
567 informat = RGB24;
568 bpp = 1;
569 if ((in_width != out_width) || (in_height != out_height)) {
570 resize_src = (uint8_t*) malloc(out_width * out_height *3);
571 if (NULL != resize_src) {
572 extraSmallImg(input->src, in_width, in_height,
573 resize_src, out_width, out_height);
574 src = resize_src;
575 } else {
576 CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n");
577 goto exit;
578 }
579 }
580 } else if (input->format == HAL_PIXEL_FORMAT_YCbCr_422_I) {
581 informat = YUV422I;
582 bpp = 2;
583 if ((in_width != out_width) || (in_height != out_height)) {
584 resize_src = (uint8_t*) malloc(out_width * out_height *3);
585 if (NULL != resize_src) {
586 resize_yuyv(input,resize_src);
587 if (resize_src) src = resize_src;
588 } else {
589 CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n");
590 goto exit;
591 }
592 }
593 } else if ((in_width != out_width) || (in_height != out_height)) {
594 CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %d", input->format);
595 goto exit;
596 } else if ((input->format != HAL_PIXEL_FORMAT_YCbCr_422_I)) {
597 // we currently only support yuv422i and yuv420sp
598 CAMHAL_LOGEB("Encoder: format not supported: %d", input->format);
599 goto exit;
600 }
601
602 cinfo.err = jpeg_std_error(&jerr);
603
604 jpeg_create_compress(&cinfo);
605
606 CAMHAL_LOGDB("software encoding... \n\t"
607 "width: %d \n\t"
608 "height:%d \n\t"
609 "dest %p \n\t"
610 "dest size:%d \n\t"
611 "mSrc %p",
612 out_width, out_height, input->dst,
613 input->dst_size, src);
614
615 cinfo.dest = &dest_mgr;
616 cinfo.image_width = out_width;
617 cinfo.image_height = out_height;
618 cinfo.input_components = 3;
619 if (informat == RGB24)
620 cinfo.in_color_space = JCS_RGB;
621 else
622 cinfo.in_color_space = JCS_YCbCr;
623 cinfo.input_gamma = 1;
624
625 jpeg_set_defaults(&cinfo);
626 jpeg_set_quality(&cinfo, input->quality, TRUE);
627 cinfo.dct_method = JDCT_IFAST;
628
629 jpeg_start_compress(&cinfo, TRUE);
630
631 row_tmp = (uint8_t*)malloc(out_width * 3);
632 row_src = src;
633 row_uv = src + out_width * out_height * bpp;
634 row_stride = out_width * 3;
635
636 while ((cinfo.next_scanline < cinfo.image_height)) {
637 JSAMPROW row[1]; /* pointer to JSAMPLE row[s] */
638
639 if (informat == RGB24) {
640 row[0] = &src[cinfo.next_scanline * row_stride];
641 (void) jpeg_write_scanlines(&cinfo, row, 1);
642 } else {
643 // convert input yuv format to yuv444
644 if (informat == YUV420SP) {
645 nv21_to_yuv(row_tmp, row_src, row_uv, out_width);
646 } else if (informat == YUV422I) {
647 //uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width);
648 yuyv_to_yuv(row_tmp, (uint32_t*)row_src, out_width);
649 }
650
651 row[0] = row_tmp;
652 jpeg_write_scanlines(&cinfo, row, 1);
653 row_src = row_src + out_width*bpp;
654
655 // move uv row if input format needs it
656 if (informat == YUV420SP) {
657 if (!(cinfo.next_scanline % 2))
658 row_uv = row_uv + out_width * bpp;
659 }
660 }
661 }
662
663 jpeg_finish_compress(&cinfo);
664 jpeg_destroy_compress(&cinfo);
665
666 if (resize_src) free(resize_src);
667 if (row_tmp) free(row_tmp);
668
669 exit:
670 input->jpeg_size = dest_mgr.jpegsize;
671 return dest_mgr.jpegsize;
672}
673
674status_t JpegCompressor::compress() {
675 // Find source and target buffers. Assumes only one buffer matches
676 // each condition!
677 if (mJpegRequest.mNeedThumbnail == true) {
678 mSrcThumbBuffer = mAuxBuffer.img;
679 if (mDstThumbBuffer == NULL) {
680 mDstThumbBuffer = (uint8_t*)malloc(mInfo.thumbwidth * mInfo.thumbheight * 3);
681 }
682 }
683
684 params enc_params;
685 enc_params.src = mAuxBuffer.img;
686 enc_params.src_size = calc_frame_length(mAuxBuffer.format, mAuxBuffer.width, mAuxBuffer.height);
687 enc_params.dst = mJpegBuffer.img;
688 enc_params.dst_size = kMaxJpegSize;
689 enc_params.quality = 80;
690 enc_params.in_width = mAuxBuffer.width;
691 enc_params.in_height = mAuxBuffer.height;
692 enc_params.out_width= mAuxBuffer.width;
693 enc_params.out_height = mAuxBuffer.height;
694 enc_params.format = mAuxBuffer.format;
695 enc_params.jpeg_size = 0;
696
697 mMainJpegSize = encode(&enc_params);
698 ALOGD("mMainJpegSize = %d",mMainJpegSize);
699
700 return OK;
701}
702
703status_t JpegCompressor::thumbcompress() {
704 if ((mSrcThumbBuffer == NULL) || (mDstThumbBuffer == NULL))
705 return 0;
706
707 params enc_params;
708 enc_params.src = mSrcThumbBuffer;
709 enc_params.dst = mDstThumbBuffer;
710 enc_params.dst_size = kMaxJpegSize;
711 enc_params.quality = 70;
712 enc_params.src_size = calc_frame_length(mAuxBuffer.format, mAuxBuffer.width, mAuxBuffer.height);
713 enc_params.in_width = mAuxBuffer.width;
714 enc_params.in_height = mAuxBuffer.height;
715 enc_params.out_width= mInfo.thumbwidth;
716 enc_params.out_height = mInfo.thumbheight;
717 enc_params.format = mAuxBuffer.format;
718 enc_params.jpeg_size = 0;
719
720 mThumbJpegSize = encode(&enc_params);
721 ALOGD("mThumbJpegSize = %d",mThumbJpegSize);
722
723 return OK;
724}
725bool JpegCompressor::isBusy() {
726 Mutex::Autolock busyLock(mBusyMutex);
727 return mIsBusy;
728}
729
730bool JpegCompressor::isStreamInUse(uint32_t id) {
731 Mutex::Autolock lock(mBusyMutex);
732
733 if (mBuffers && mIsBusy) {
734 for (size_t i = 0; i < mBuffers->size(); i++) {
735 if ( (*mBuffers)[i].streamId == (int)id ) return true;
736 }
737 }
738 return false;
739}
740
741bool JpegCompressor::waitForDone(nsecs_t timeout) {
742 Mutex::Autolock lock(mBusyMutex);
743 status_t res = OK;
744 if (mIsBusy) {
745 res = mDone.waitRelative(mBusyMutex, timeout);
746 }
747 return (res == OK);
748}
749
750bool JpegCompressor::checkError(const char *msg) {
751 if (mJpegErrorInfo) {
752 char errBuffer[JMSG_LENGTH_MAX];
753 mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
754 ALOGE("%s: %s: %s",
755 __FUNCTION__, msg, errBuffer);
756 mJpegErrorInfo = NULL;
757 return true;
758 }
759 return false;
760}
761
762void JpegCompressor::cleanUp() {
763 status_t res;
764 Mutex::Autolock lock(mBusyMutex);
765 if (mJpegRequest.mNeedThumbnail == true) {
766 if (mSrcThumbBuffer != NULL) {
767 mSrcThumbBuffer = NULL;
768 }
769 if (mDstThumbBuffer != NULL) {
770 free(mDstThumbBuffer);
771 mDstThumbBuffer = NULL;
772 }
773 }
774 if (mFoundAux) {
775 if (mAuxBuffer.streamId == 0) {
776 delete[] mAuxBuffer.img;
777 } else if (!mSynchronous) {
778 mListener->onJpegInputDone(mAuxBuffer);
779 }
780 }
781 if (!mSynchronous) {
782 delete mBuffers;
783 }
784
785 mBuffers = NULL;
786
787 mIsBusy = false;
788 mDone.signal();
789}
790
791JpegCompressor::JpegListener::~JpegListener() {
792}
793
794void JpegCompressor::SetMaxJpegBufferSize(ssize_t size)
795{
796 mMaxbufsize = size;
797}
798ssize_t JpegCompressor::GetMaxJpegBufferSize()
799{
800 return mMaxbufsize;
801}
802void JpegCompressor::SetExifInfo(struct ExifInfo info)
803{
804 mInfo.mainwidth = info.mainwidth;
805 mInfo.mainheight = info.mainheight;
806 mInfo.thumbwidth = info.thumbwidth;
807 mInfo.thumbheight = info.thumbheight;
808 mInfo.gpsTimestamp = info.gpsTimestamp;
809 mInfo.latitude = info.latitude;
810 mInfo.longitude = info.longitude;
811 mInfo.altitude = info.altitude;
812 mInfo.gpsProcessingMethod = info.gpsProcessingMethod;
813 mInfo.focallen = info.focallen;
814 mInfo.orientation = info.orientation;
815 mInfo.has_latitude = info.has_latitude;
816 mInfo.has_longitude = info.has_longitude;
817 mInfo.has_altitude = info.has_altitude;
818 mInfo.has_gpsProcessingMethod = info.has_gpsProcessingMethod;
819 mInfo.has_gpsTimestamp = info.has_gpsTimestamp;
820 mInfo.has_focallen = info.has_focallen;
821
822}
823int JpegCompressor::GenExif(ExifElementsTable* exiftable)
824{
825 char exifcontent[256];
826 int width,height;
827 bool newexif = true; //add new exif tag for cts
828 float exposuretime = 1.0;
829 float ApertureValue = 1.0;
830 int flash = 0;
831 int whitebalance = 1;
832 int iso = 100;
833 char SubSecTime[10] = "63";
834 char SubSecTimeOrig[10]= "63";
835 char SubSecTimeDig[10]= "63";
836 char property[PROPERTY_VALUE_MAX];
837
838 property_get("ro.product.manufacturer", property, EXIF_MAKE_DEFAULT);
839 exiftable->insertElement("Make",property);
840 property_get("ro.product.model", property, EXIF_MODEL_DEFAULT);
841 exiftable->insertElement("Model",property);
842// int orientation = mInfo.orientation;
843 width = mInfo.mainwidth;
844 height = mInfo.mainheight;
845#if 0
846 if (orientation == 0)
847 orientation = 1;
848 else if (orientation == 90)
849 orientation = 6;
850 else if (orientation == 180)
851 orientation = 3;
852 else if (orientation == 270)
853 orientation = 8;
854 sprintf(exifcontent,"%d",orientation);
855 exiftable->insertElement("Orientation",(const char*)exifcontent);
856#endif
857 sprintf(exifcontent,"%d",width);
858 exiftable->insertElement("ImageWidth",(const char*)exifcontent);
859 sprintf(exifcontent,"%d",height);
860 exiftable->insertElement("ImageLength",(const char*)exifcontent);
861
862 sprintf(exifcontent,"%f",exposuretime);
863 exiftable->insertElement("ExposureTime",(const char*)exifcontent);
864 sprintf(exifcontent,"%f",ApertureValue);
865 exiftable->insertElement("ApertureValue",(const char*)exifcontent);
866 sprintf(exifcontent,"%d",flash);
867 exiftable->insertElement("Flash",(const char*)exifcontent);
868 sprintf(exifcontent,"%d",whitebalance);
869 exiftable->insertElement("WhiteBalance",(const char*)exifcontent);
870 sprintf(exifcontent,"%d",iso);
871 exiftable->insertElement("ISOSpeedRatings",(const char*)exifcontent);
872 if (newexif) {
873 time_t times;
874 {
875 time(&times);
876 struct tm tmstruct;
877 tmstruct = *(localtime(&times)); //convert to local time
878 strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct);
879 exiftable->insertElement("DateTimeDigitized",(const char*)exifcontent);
880 }
881 {
882 sprintf(exifcontent, "%s", SubSecTime);
883 exiftable->insertElement("SubSecTime",(const char*)exifcontent);
884 }
885 {
886
887 sprintf(exifcontent, "%s", SubSecTimeOrig);
888 exiftable->insertElement("SubSecTimeOriginal",(const char*)exifcontent);
889 }
890 {
891
892 sprintf(exifcontent, "%s", SubSecTimeDig);
893 exiftable->insertElement("SubSecTimeDigitized",(const char*)exifcontent);
894 }
895 }
896
897 if (mInfo.has_focallen) {
898 float focallen = mInfo.focallen;
899 if (focallen >= 0) {
900 int focalNum = focallen*1000;
901 int focalDen = 1000;
902 sprintf(exifcontent,"%d/%d",focalNum,focalDen);
903 exiftable->insertElement("FocalLength",(const char*)exifcontent);
904 }
905 }
906 time_t times;
907 {
908 time(&times);
909 struct tm tmstruct;
910 tmstruct = *(localtime(&times)); //convert to local time
911 strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct);
912 exiftable->insertElement("DateTime",(const char*)exifcontent);
913 }
914 if (mInfo.has_gpsTimestamp) {
915 times = mInfo.gpsTimestamp;
916 if (times != -1) {
917 struct tm tmstruct;
918 tmstruct = *(gmtime(&times));//convert to standard time
919 strftime(exifcontent, 20, "%Y:%m:%d", &tmstruct);
920 exiftable->insertElement("GPSDateStamp",(const char*)exifcontent);
921 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",tmstruct.tm_hour,1,tmstruct.tm_min,1,tmstruct.tm_sec,1);
922 exiftable->insertElement("GPSTimeStamp",(const char*)exifcontent);
923 }
924 }
925 if (mInfo.has_latitude) {
926 int offset = 0;
927 float latitude = mInfo.latitude;
928 if (latitude < 0.0) {
929 offset = 1;
930 latitude*= (float)(-1);
931 }
932 int latitudedegree = latitude;
933 float latitudeminuts = (latitude-(float)latitudedegree)*60;
934 int latitudeminuts_int = latitudeminuts;
935 float latituseconds = (latitudeminuts-(float)latitudeminuts_int)*60+0.5;
936 int latituseconds_int = latituseconds;
937 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",latitudedegree,1,latitudeminuts_int,1,latituseconds_int,1);
938 exiftable->insertElement("GPSLatitude",(const char*)exifcontent);
939 exiftable->insertElement("GPSLatitudeRef",(offset==1)?"S":"N");
940 }
941 if (mInfo.has_longitude) {
942 int offset = 0;
943 float longitude = mInfo.longitude;
944 if (longitude < 0.0) {
945 offset = 1;
946 longitude*= (float)(-1);
947 }
948 int longitudedegree = longitude;
949 float longitudeminuts = (longitude-(float)longitudedegree)*60;
950 int longitudeminuts_int = longitudeminuts;
951 float longitudeseconds = (longitudeminuts-(float)longitudeminuts_int)*60+0.5;
952 int longitudeseconds_int = longitudeseconds;
953 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",longitudedegree,1,longitudeminuts_int,1,longitudeseconds_int,1);
954 exiftable->insertElement("GPSLongitude",(const char*)exifcontent);
955 exiftable->insertElement("GPSLongitudeRef",(offset==1)?"S":"N");
956 }
957 if (mInfo.has_altitude) {
958 int offset = 0;
959 float altitude = mInfo.altitude;
960 if (altitude < 0.0) {
961 offset = 1;
962 altitude*= (float)(-1);
963 }
964 int altitudenum = altitude*1000;
965 int altitudedec= 1000;
966 sprintf(exifcontent,"%d/%d",altitudenum,altitudedec);
967 exiftable->insertElement("GPSAltitude",(const char*)exifcontent);
968 sprintf(exifcontent,"%d",offset);
969 exiftable->insertElement("GPSAltitudeRef",(const char*)exifcontent);
970 }
971 if (mInfo.has_gpsProcessingMethod) {
972 char* processmethod = (char*)mInfo.gpsProcessingMethod;
973 if (processmethod != NULL) {
974 memset(exifcontent,0,sizeof(exifcontent));
975 char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };//asicii
976 memcpy(exifcontent,ExifAsciiPrefix,8);
977 memcpy(exifcontent+8,processmethod,strlen(processmethod));
978 exiftable->insertElement("GPSProcessingMethod",(const char*)exifcontent);
979 }
980 }
981 return 1;
982}
983const char* ExifElementsTable::degreesToExifOrientation(const char* degrees) {
984 for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) {
985 if (!strcmp(degrees, degress_to_exif_lut[i].string1)) {
986 return degress_to_exif_lut[i].string2;
987 }
988 }
989 return NULL;
990}
991void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) {
992 int len;
993 char * tempVal = NULL;
994 if (str != NULL) {
995 len = strlen(str);
996 tempVal = (char*) malloc( sizeof(char) * (len + 1));
997 }
998 if (tempVal != NULL) {
999 size_t den_len;
1000 char *ctx;
1001 unsigned int numerator = 0;
1002 unsigned int denominator = 0;
1003 char* temp = NULL;
1004 memset(tempVal, '\0', len + 1);
1005 strncpy(tempVal, str, len);
1006 temp = strtok_r(tempVal, ".", &ctx);
1007 if (temp != NULL)
1008 numerator = atoi(temp);
1009 if (!numerator)
1010 numerator = 1;
1011 temp = strtok_r(NULL, ".", &ctx);
1012 if (temp != NULL) {
1013 den_len = strlen(temp);
1014 if(HUGE_VAL == den_len ) {
1015 den_len = 0;
1016 }
1017 denominator = static_cast<unsigned int>(pow(10, den_len));
1018 numerator = numerator * denominator + atoi(temp);
1019 } else {
1020 denominator = 1;
1021 }
1022 free(tempVal);
1023 *num = numerator;
1024 *den = denominator;
1025 }
1026}
1027bool ExifElementsTable::isAsciiTag(const char* tag) {
1028 return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0);
1029}
1030status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
1031 int value_length = 0;
1032 status_t ret = NO_ERROR;
1033 if (!value || !tag) {
1034 return -EINVAL;
1035 }
1036 if (position >= MAX_EXIF_TAGS_SUPPORTED) {
1037 CAMHAL_LOGEA("Max number of EXIF elements already inserted");
1038 return NO_MEMORY;
1039 }
1040 if (isAsciiTag(tag)) {
1041 value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix));
1042 } else {
1043 value_length = strlen(value);
1044 }
1045 if (IsGpsTag(tag)) {
1046 table[position].GpsTag = TRUE;
1047 table[position].Tag = GpsTagNameToValue(tag);
1048 gps_tag_count++;
1049 } else {
1050 table[position].GpsTag = FALSE;
1051 table[position].Tag = TagNameToValue(tag);
1052 exif_tag_count++;
1053 }
1054 table[position].DataLength = 0;
1055 table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1));
1056 if (table[position].Value) {
1057 memcpy(table[position].Value, value, value_length + 1);
1058 table[position].DataLength = value_length + 1;
1059 }
1060 position++;
1061 return ret;
1062}
1063void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) {
1064 int ret;
1065 if (jpeg_opened) {
1066 ret = WriteJpegToBuffer(jpeg, jpeg_size);
1067 ALOGD("saveJpeg :: ret =%d",ret);
1068 DiscardData();
1069 jpeg_opened = false;
1070 }
1071}
1072void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) {
1073 ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE);
1074 ResetJpgfile();
1075 if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) {
1076 jpeg_opened = true;
1077 create_EXIF(table, exif_tag_count, gps_tag_count,true);
1078 }
1079}
1080status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) {
1081 status_t ret = NO_ERROR;
1082 if ((len > 0) && jpeg_opened) {
1083 ret = ReplaceThumbnailFromBuffer(thumb, len);
1084 CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret);
1085 }
1086 return ret;
1087}
1088ExifElementsTable::~ExifElementsTable() {
1089 int num_elements = gps_tag_count + exif_tag_count;
1090 for (int i = 0; i < num_elements; i++) {
1091 if (table[i].Value) {
1092 free(table[i].Value);
1093 }
1094 }
1095 if (jpeg_opened) {
1096 DiscardData();
1097 }
1098}
1099} // namespace android
1100