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