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 | |
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 | 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 | |
339 | struct 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 | |
346 | static 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 | |
354 | static 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 | |
362 | static 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 | |
367 | libjpeg_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 | |
376 | static 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 | |
404 | static 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 */ |
424 | static 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 | |
442 | static 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 | |
499 | static 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 | |
519 | size_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 | |
675 | status_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 | |
704 | status_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 | } |
726 | bool JpegCompressor::isBusy() { |
727 | Mutex::Autolock busyLock(mBusyMutex); |
728 | return mIsBusy; |
729 | } |
730 | |
731 | bool 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 | |
742 | bool 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 | |
751 | bool 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 | |
763 | void 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 | |
792 | JpegCompressor::JpegListener::~JpegListener() { |
793 | } |
794 | |
795 | void JpegCompressor::SetMaxJpegBufferSize(ssize_t size) |
796 | { |
797 | mMaxbufsize = size; |
798 | } |
799 | ssize_t JpegCompressor::GetMaxJpegBufferSize() |
800 | { |
801 | return mMaxbufsize; |
802 | } |
803 | void JpegCompressor::SetExifInfo(struct ExifInfo info) |
804 | { |
805 | memcpy(&mInfo, &info, sizeof(struct ExifInfo)); |
806 | } |
807 | int 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(×); |
860 | struct tm tmstruct; |
861 | tmstruct = *(localtime(×)); //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(×); |
893 | struct tm tmstruct; |
894 | tmstruct = *(localtime(×)); //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(×));//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 | } |
967 | const 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 | } |
975 | void 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 | } |
1011 | bool ExifElementsTable::isAsciiTag(const char* tag) { |
1012 | return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0); |
1013 | } |
1014 | status_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 | } |
1047 | void 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 | } |
1056 | void 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 | } |
1064 | status_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 | } |
1072 | ExifElementsTable::~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 |