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 | |
37 | const size_t MARKER_LENGTH = 2; // length of a marker |
38 | const uint8_t MARK = 0xFF; |
39 | const uint8_t EOI = 0xD9; |
40 | bool checkJpegEnd(uint8_t *buf) { |
41 | return buf[0] == MARK && buf[1] == EOI; |
42 | } |
43 | int 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 | } |
67 | namespace android { |
68 | |
69 | struct string_pair { |
70 | const char* string1; |
71 | const char* string2; |
72 | }; |
73 | static string_pair degress_to_exif_lut [] = { |
74 | {"0", "1"}, |
75 | {"90", "6"}, |
76 | {"180", "3"}, |
77 | {"270", "8"}, |
78 | }; |
79 | |
80 | struct 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 | |
94 | enum format { |
95 | YUV420SP, |
96 | YUV422I, |
97 | RGB24, |
98 | }; |
99 | |
100 | JpegCompressor::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 | |
116 | JpegCompressor::~JpegCompressor() { |
117 | Mutex::Autolock lock(mMutex); |
118 | } |
119 | |
120 | void 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 | |
135 | status_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 | |
145 | status_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 | |
166 | status_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 | |
190 | status_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 | |
206 | status_t JpegCompressor::readyToRun() { |
207 | return OK; |
208 | } |
209 | |
210 | bool 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 | |
338 | struct 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 | |
345 | static 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 | |
353 | static 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 | |
361 | static 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 | |
366 | libjpeg_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 | |
375 | static 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 | |
403 | static 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 */ |
423 | static 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 | |
441 | static 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 | |
495 | static 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 | |
515 | size_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 | |
671 | status_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 | |
700 | status_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 | } |
722 | bool JpegCompressor::isBusy() { |
723 | Mutex::Autolock busyLock(mBusyMutex); |
724 | return mIsBusy; |
725 | } |
726 | |
727 | bool 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 | |
738 | bool 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 | |
747 | bool 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 | |
759 | void 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 | |
788 | JpegCompressor::JpegListener::~JpegListener() { |
789 | } |
790 | |
791 | void JpegCompressor::SetMaxJpegBufferSize(ssize_t size) |
792 | { |
793 | mMaxbufsize = size; |
794 | } |
795 | ssize_t JpegCompressor::GetMaxJpegBufferSize() |
796 | { |
797 | return mMaxbufsize; |
798 | } |
799 | void 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 | } |
820 | int 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(×); |
873 | struct tm tmstruct; |
874 | tmstruct = *(localtime(×)); //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(×); |
906 | struct tm tmstruct; |
907 | tmstruct = *(localtime(×)); //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(×));//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 | } |
980 | const 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 | } |
988 | void 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 | } |
1024 | bool ExifElementsTable::isAsciiTag(const char* tag) { |
1025 | return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0); |
1026 | } |
1027 | status_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 | } |
1060 | void 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 | } |
1069 | void 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 | } |
1077 | status_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 | } |
1085 | ExifElementsTable::~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 |