summaryrefslogtreecommitdiff
path: root/v3/fake-pipeline2/JpegCompressor.cpp (plain)
blob: 96290705f1644a3453e4eafd4ed621834ad279dc
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
31
32#define EXIF_MAKE_DEFAULT "default_make"
33#define EXIF_MODEL_DEFAULT "default_model"
34#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
35
36const size_t MARKER_LENGTH = 2; // length of a marker
37const uint8_t MARK = 0xFF;
38const uint8_t EOI = 0xD9;
39bool checkJpegEnd(uint8_t *buf) {
40 return buf[0] == MARK && buf[1] == EOI;
41}
42int extraSmallImg(unsigned char* SrcImg,int SrcW,int SrcH,
43 unsigned char* DstImg,int DstW,int DstH)
44{
45 int skipW = SrcW/DstW;
46 int skipH = SrcH/DstH;
47 unsigned char* dst = DstImg;
48 unsigned char* srcrow = SrcImg;
49 unsigned char* srcrowidx = srcrow;
50 int i = 0,j = 0;
51 for(;i<DstH;i++)
52 {
53 for(j = 0;j<DstW;j++)
54 {
55 dst[0] = srcrowidx[0];
56 dst[1] = srcrowidx[1];
57 dst[2] = srcrowidx[2];
58 dst+=3;
59 srcrowidx+=3*skipW;
60 }
61 srcrow += skipH*SrcW*3;
62 srcrowidx = srcrow;
63 }
64 return 1;
65}
66namespace android {
67
68struct string_pair {
69 const char* string1;
70 const char* string2;
71};
72static string_pair degress_to_exif_lut [] = {
73 {"0", "1"},
74 {"90", "6"},
75 {"180", "3"},
76 {"270", "8"},
77};
78JpegCompressor::JpegCompressor():
79 Thread(false),
80 mIsBusy(false),
81 mSynchronous(false),
82 mNeedexif(true),
83 mNeedThumbnail(false),
84 mMainJpegSize(0),
85 mThumbJpegSize(0),
86 mSrcThumbBuffer(NULL),
87 mDstThumbBuffer(NULL),
88 mBuffers(NULL),
89 mListener(NULL) {
90 memset(&mInfo,0,sizeof(struct ExifInfo));
91}
92
93JpegCompressor::~JpegCompressor() {
94 Mutex::Autolock lock(mMutex);
95}
96
97status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener) {
98 if (listener == NULL) {
99 ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
100 return BAD_VALUE;
101 }
102 Mutex::Autolock lock(mMutex);
103 {
104 Mutex::Autolock busyLock(mBusyMutex);
105
106 if (mIsBusy) {
107 ALOGE("%s: Already processing a buffer!", __FUNCTION__);
108 return INVALID_OPERATION;
109 }
110
111 mIsBusy = true;
112 mSynchronous = false;
113 mBuffers = buffers;
114 mListener = listener;
115 }
116
117 status_t res;
118 res = run("EmulatedFakeCamera2::JpegCompressor");
119 if (res != OK) {
120 ALOGE("%s: Unable to start up compression thread: %s (%d)",
121 __FUNCTION__, strerror(-res), res);
122 delete mBuffers;
123 }
124 return res;
125}
126
127status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
128 status_t res;
129
130 Mutex::Autolock lock(mMutex);
131 {
132 Mutex::Autolock busyLock(mBusyMutex);
133
134 if (mIsBusy) {
135 ALOGE("%s: Already processing a buffer!", __FUNCTION__);
136 return INVALID_OPERATION;
137 }
138
139 mIsBusy = true;
140 mSynchronous = true;
141 mBuffers = buffers;
142 }
143
144 res = compress();
145
146 cleanUp();
147
148 return res;
149}
150
151status_t JpegCompressor::cancel() {
152 requestExitAndWait();
153 return OK;
154}
155
156status_t JpegCompressor::readyToRun() {
157 return OK;
158}
159
160bool JpegCompressor::threadLoop() {
161 status_t res;
162 struct timeval mTimeStart,mTimeend;
163 int intreval;
164 ExifElementsTable* exiftable = NULL;
165 struct camera2_jpeg_blob blob;
166 int offset;
167 ALOGV("%s: Starting compression thread", __FUNCTION__);
168
169 gettimeofday(&mTimeStart, NULL);
170 res = compress();
171 if (mNeedexif) {
172 memset(&blob,0,sizeof(struct camera2_jpeg_blob));
173 exiftable = new ExifElementsTable();
174 GenExif(exiftable);
175 }
176 if (mNeedThumbnail) {
177 res = thumbcompress();
178 }
179
180 if (exiftable) {
181 uint32_t realjpegsize = 0;
182 Section_t* exif_section = NULL;
183 ExifElementsTable* exif = exiftable;
184 exif->insertExifToJpeg((unsigned char*)mJpegBuffer.img,mMainJpegSize);
185 if ((mNeedThumbnail)&&(mDstThumbBuffer != NULL)) {
186 exif->insertExifThumbnailImage((const char*)mDstThumbBuffer,mThumbJpegSize);
187 }
188 exif_section = FindSection(M_EXIF);
189 if (exif_section) {
190 exif->saveJpeg((unsigned char*) mJpegBuffer.img, mMainJpegSize + exif_section->Size);
191 }
192 for (uint32_t size = (mMainJpegSize + exif_section->Size - 2); size > 0; size--) {
193 if (checkJpegEnd(mJpegBuffer.img + size)) {
194 realjpegsize = (size + MARKER_LENGTH);
195 break;
196 }
197 }
198 int offset = mMaxbufsize-sizeof(struct camera2_jpeg_blob);
199 blob.jpeg_blob_id = 0x00FF;
200 blob.jpeg_size = realjpegsize;
201 memcpy(mJpegBuffer.img+offset, &blob, sizeof(struct camera2_jpeg_blob));
202 }
203 mListener->onJpegDone(mJpegBuffer, res == OK);
204
205 if (mNeedexif) {
206 if (exiftable != NULL) {
207 delete exiftable;
208 exiftable = NULL;
209 }
210 }
211 gettimeofday(&mTimeend, NULL);
212 intreval = (mTimeend.tv_sec - mTimeStart.tv_sec) * 1000 + ((mTimeend.tv_usec - mTimeStart.tv_usec))/1000;
213 ALOGD("jpeg compress cost time =%d ms",intreval);
214 cleanUp();
215
216 return false;
217}
218
219status_t JpegCompressor::compress() {
220 // Find source and target buffers. Assumes only one buffer matches
221 // each condition!
222 //Mutex::Autolock lock(mMutex);
223 bool foundJpeg = false, mFoundAux = false;
224 for (size_t i = 0; i < mBuffers->size(); i++) {
225 const StreamBuffer &b = (*mBuffers)[i];
226 if (b.format == HAL_PIXEL_FORMAT_BLOB) {
227 mJpegBuffer = b;
228 mFoundJpeg = true;
229 } else if (b.streamId <= 0) {
230 mAuxBuffer = b;
231 mFoundAux = true;
232 }
233 if (mFoundJpeg && mFoundAux) break;
234 }
235 if (!mFoundJpeg || !mFoundAux) {
236 ALOGE("%s: Unable to find buffers for JPEG source/destination",
237 __FUNCTION__);
238 return BAD_VALUE;
239 }
240
241 if (mNeedThumbnail == true) {
242 if (mSrcThumbBuffer == NULL) {
243 mSrcThumbBuffer = (uint8_t*)malloc(mInfo.thumbwidth*mInfo.thumbheight*3);
244 }
245 if (mDstThumbBuffer == NULL) {
246 mDstThumbBuffer = (uint8_t*)malloc(mInfo.thumbwidth*mInfo.thumbheight*3);
247 }
248 if (mSrcThumbBuffer) {
249 if (mAuxBuffer.format == HAL_PIXEL_FORMAT_RGB_888)
250 extraSmallImg(mAuxBuffer.img,mAuxBuffer.width,mAuxBuffer.height,
251 mSrcThumbBuffer,mInfo.thumbwidth,mInfo.thumbheight);
252 }
253 }
254
255 // Set up error management
256
257 mJpegErrorInfo = NULL;
258 JpegError error;
259 error.parent = this;
260
261 mCInfo.err = jpeg_std_error(&error);
262 mCInfo.err->error_exit = MainJpegErrorHandler;
263
264 jpeg_create_compress(&mCInfo);
265 if (checkError("Error initializing compression")) return NO_INIT;
266
267 // Route compressed data straight to output stream buffer
268
269 JpegDestination jpegDestMgr;
270 jpegDestMgr.parent = this;
271 jpegDestMgr.init_destination = MainJpegInitDestination;
272 jpegDestMgr.empty_output_buffer = MainJpegEmptyOutputBuffer;
273 jpegDestMgr.term_destination = MainJpegTermDestination;
274
275 mCInfo.dest = &jpegDestMgr;
276
277 // Set up compression parameters
278
279 mCInfo.image_width = mAuxBuffer.width;
280 mCInfo.image_height = mAuxBuffer.height;
281 mCInfo.input_components = 3;
282 mCInfo.in_color_space = JCS_RGB;
283
284 jpeg_set_defaults(&mCInfo);
285 if (checkError("Error configuring defaults")) return NO_INIT;
286
287 // Do compression
288
289 jpeg_start_compress(&mCInfo, TRUE);
290 if (checkError("Error starting compression")) return NO_INIT;
291
292 size_t rowStride = mAuxBuffer.stride * 3;
293 const size_t kChunkSize = 32;
294 while (mCInfo.next_scanline < mCInfo.image_height) {
295 JSAMPROW chunk[kChunkSize];
296 for (size_t i = 0 ; i < kChunkSize; i++) {
297 chunk[i] = (JSAMPROW)
298 (mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride);
299 }
300 jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
301 if (checkError("Error while compressing")) return NO_INIT;
302 if (exitPending()) {
303 ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
304 return TIMED_OUT;
305 }
306 }
307
308 jpeg_finish_compress(&mCInfo);
309 if (checkError("Error while finishing compression")) return NO_INIT;
310
311 // All done
312 mMainJpegSize = kMaxJpegSize - mCInfo.dest->free_in_buffer;
313 ALOGD("mMainJpegSize = %d",mMainJpegSize);
314
315
316 return OK;
317}
318
319status_t JpegCompressor::thumbcompress() {
320 if ((mSrcThumbBuffer == NULL)||(mDstThumbBuffer == NULL))
321 return 0;
322 //Mutex::Autolock lock(mMutex);
323 mJpegErrorInfo = NULL;
324 JpegError error;
325 error.parent = this;
326 mCInfo.err = jpeg_std_error(&error);
327 mCInfo.err->error_exit = ThumbJpegErrorHandler;
328
329 jpeg_create_compress(&mCInfo);
330 if (checkError("Error initializing compression")) return NO_INIT;
331 JpegDestination jpegDestMgr;
332 jpegDestMgr.parent = this;
333 jpegDestMgr.init_destination = ThumbJpegInitDestination;
334 jpegDestMgr.empty_output_buffer = ThumbJpegEmptyOutputBuffer;
335 jpegDestMgr.term_destination = ThumbJpegTermDestination;
336 mCInfo.dest = &jpegDestMgr;
337
338 // Set up compression parameters
339
340 mCInfo.image_width = mInfo.thumbwidth;
341 mCInfo.image_height = mInfo.thumbheight;
342 mCInfo.input_components = 3;
343 mCInfo.in_color_space = JCS_RGB;
344 jpeg_set_defaults(&mCInfo);
345 if (checkError("Error configuring defaults")) return NO_INIT;
346 jpeg_start_compress(&mCInfo, TRUE);
347 if (checkError("Error starting compression")) return NO_INIT;
348 size_t rowStride = mInfo.thumbwidth* 3;
349 const size_t kChunkSize = 32;
350 while (mCInfo.next_scanline < mCInfo.image_height) {
351 JSAMPROW chunk[kChunkSize];
352 for (size_t i = 0 ; i < kChunkSize; i++) {
353 chunk[i] = (JSAMPROW)
354 (mSrcThumbBuffer + (i + mCInfo.next_scanline) * rowStride);
355 }
356 jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
357 if (checkError("Error while compressing")) return NO_INIT;
358 if (exitPending()) {
359 ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
360 return TIMED_OUT;
361 }
362 }
363 jpeg_finish_compress(&mCInfo);
364 if (checkError("Error while finishing compression")) return NO_INIT;
365 mThumbJpegSize = kMaxJpegSize - mCInfo.dest->free_in_buffer;
366 ALOGD("mThumbJpegSize = %d",mThumbJpegSize);
367 return OK;
368}
369bool JpegCompressor::isBusy() {
370 Mutex::Autolock busyLock(mBusyMutex);
371 return mIsBusy;
372}
373
374bool JpegCompressor::isStreamInUse(uint32_t id) {
375 Mutex::Autolock lock(mBusyMutex);
376
377 if (mBuffers && mIsBusy) {
378 for (size_t i = 0; i < mBuffers->size(); i++) {
379 if ( (*mBuffers)[i].streamId == (int)id ) return true;
380 }
381 }
382 return false;
383}
384
385bool JpegCompressor::waitForDone(nsecs_t timeout) {
386 Mutex::Autolock lock(mBusyMutex);
387 status_t res = OK;
388 if (mIsBusy) {
389 res = mDone.waitRelative(mBusyMutex, timeout);
390 }
391 return (res == OK);
392}
393
394bool JpegCompressor::checkError(const char *msg) {
395 if (mJpegErrorInfo) {
396 char errBuffer[JMSG_LENGTH_MAX];
397 mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
398 ALOGE("%s: %s: %s",
399 __FUNCTION__, msg, errBuffer);
400 mJpegErrorInfo = NULL;
401 return true;
402 }
403 return false;
404}
405
406void JpegCompressor::cleanUp() {
407 status_t res;
408 jpeg_destroy_compress(&mCInfo);
409 Mutex::Autolock lock(mBusyMutex);
410 if (mNeedThumbnail == true) {
411 mNeedThumbnail = false;
412 if (mSrcThumbBuffer != NULL) {
413 free(mSrcThumbBuffer);
414 mSrcThumbBuffer = NULL;
415 }
416 if (mDstThumbBuffer != NULL) {
417 free(mDstThumbBuffer);
418 mDstThumbBuffer = NULL;
419 }
420 }
421 if (mFoundAux) {
422 if (mAuxBuffer.streamId == 0) {
423 delete[] mAuxBuffer.img;
424 } else if (!mSynchronous) {
425 mListener->onJpegInputDone(mAuxBuffer);
426 }
427 }
428 if (!mSynchronous) {
429 delete mBuffers;
430 }
431
432 mBuffers = NULL;
433
434 mIsBusy = false;
435 mDone.signal();
436}
437
438void JpegCompressor::MainJpegErrorHandler(j_common_ptr cinfo) {
439 JpegError *error = static_cast<JpegError*>(cinfo->err);
440 error->parent->mJpegErrorInfo = cinfo;
441}
442
443void JpegCompressor::MainJpegInitDestination(j_compress_ptr cinfo) {
444 JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest);
445 ALOGV("%s: Setting destination to %p, size %zu",
446 __FUNCTION__, dest->parent->mJpegBuffer.img, kMaxJpegSize);
447 dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer.img);
448 dest->free_in_buffer = kMaxJpegSize;
449}
450
451boolean JpegCompressor::MainJpegEmptyOutputBuffer(j_compress_ptr cinfo) {
452 ALOGE("%s: JPEG destination buffer overflow!",
453 __FUNCTION__);
454 return true;
455}
456
457void JpegCompressor::MainJpegTermDestination(j_compress_ptr cinfo) {
458 ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer",
459 __FUNCTION__, cinfo->dest->free_in_buffer);
460}
461
462void JpegCompressor::ThumbJpegErrorHandler(j_common_ptr cinfo) {
463 JpegError *error = static_cast<JpegError*>(cinfo->err);
464 error->parent->mJpegErrorInfo = cinfo;
465}
466void JpegCompressor::ThumbJpegInitDestination(j_compress_ptr cinfo) {
467 JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest);
468 ALOGV("%s: Setting destination to %p, size %zu",
469 __FUNCTION__, dest->parent->mDstThumbBuffer, kMaxJpegSize);
470 dest->next_output_byte = (JOCTET*)(dest->parent->mDstThumbBuffer);
471 dest->free_in_buffer = kMaxJpegSize;
472}
473boolean JpegCompressor::ThumbJpegEmptyOutputBuffer(j_compress_ptr cinfo) {
474 ALOGE("%s: Thumb JPEG destination buffer overflow!",
475 __FUNCTION__);
476 return true;
477}
478void JpegCompressor::ThumbJpegTermDestination(j_compress_ptr cinfo) {
479 ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer",
480 __FUNCTION__, cinfo->dest->free_in_buffer);
481}
482JpegCompressor::JpegListener::~JpegListener() {
483}
484
485void JpegCompressor::SetMaxJpegBufferSize(ssize_t size)
486{
487 mMaxbufsize = size;
488}
489ssize_t JpegCompressor::GetMaxJpegBufferSize()
490{
491 return mMaxbufsize;
492}
493void JpegCompressor::SetExifInfo(struct ExifInfo info)
494{
495 mInfo.mainwidth = info.mainwidth;
496 mInfo.mainheight = info.mainheight;
497 mInfo.thumbwidth = info.thumbwidth;
498 mInfo.thumbheight = info.thumbheight;
499 mInfo.gpsTimestamp = info.gpsTimestamp;
500 mInfo.latitude = info.latitude;
501 mInfo.longitude = info.longitude;
502 mInfo.altitude = info.altitude;
503 mInfo.gpsProcessingMethod = info.gpsProcessingMethod;
504 mInfo.focallen = info.focallen;
505 mInfo.orientation = info.orientation;
506 mInfo.has_latitude = info.has_latitude;
507 mInfo.has_longitude = info.has_longitude;
508 mInfo.has_altitude = info.has_altitude;
509 mInfo.has_gpsProcessingMethod = info.has_gpsProcessingMethod;
510 mInfo.has_gpsTimestamp = info.has_gpsTimestamp;
511 mInfo.has_focallen = info.has_focallen;
512 if ((mInfo.thumbwidth>0)&&(mInfo.thumbheight>0)) {
513 mNeedThumbnail = true;
514 }
515}
516int JpegCompressor::GenExif(ExifElementsTable* exiftable)
517{
518 char exifcontent[256];
519 int width,height;
520
521 bool newexif = true; //add new exif tag for cts
522 float exposuretime = 1.0;
523 float ApertureValue = 1.0;
524 int flash = 0;
525 int whitebalance = 1;
526 int iso = 100;
527 char SubSecTime[10] = "63";
528 char SubSecTimeOrig[10]= "63";
529 char SubSecTimeDig[10]= "63";
530 char property[PROPERTY_VALUE_MAX];
531
532 property_get("ro.product.manufacturer", property, EXIF_MAKE_DEFAULT);
533 exiftable->insertElement("Make",property);
534 property_get("ro.product.model", property, EXIF_MODEL_DEFAULT);
535 exiftable->insertElement("Model",property);
536// int orientation = mInfo.orientation;
537 width = mInfo.mainwidth;
538 height = mInfo.mainheight;
539#if 0
540 if(orientation == 0)
541 orientation = 1;
542 else if(orientation == 90)
543 orientation = 6;
544 else if(orientation == 180)
545 orientation = 3;
546 else if(orientation == 270)
547 orientation = 8;
548 sprintf(exifcontent,"%d",orientation);
549 exiftable->insertElement("Orientation",(const char*)exifcontent);
550#endif
551 sprintf(exifcontent,"%d",width);
552 exiftable->insertElement("ImageWidth",(const char*)exifcontent);
553 sprintf(exifcontent,"%d",height);
554 exiftable->insertElement("ImageLength",(const char*)exifcontent);
555
556 sprintf(exifcontent,"%f",exposuretime);
557 exiftable->insertElement("ExposureTime",(const char*)exifcontent);
558 sprintf(exifcontent,"%f",ApertureValue);
559 exiftable->insertElement("ApertureValue",(const char*)exifcontent);
560 sprintf(exifcontent,"%d",flash);
561 exiftable->insertElement("Flash",(const char*)exifcontent);
562 sprintf(exifcontent,"%d",whitebalance);
563 exiftable->insertElement("WhiteBalance",(const char*)exifcontent);
564 sprintf(exifcontent,"%d",iso);
565 exiftable->insertElement("ISOSpeedRatings",(const char*)exifcontent);
566 if (newexif) {
567 time_t times;
568 {
569 time(&times);
570 struct tm tmstruct;
571 tmstruct = *(localtime(&times)); //convert to local time
572 strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct);
573 exiftable->insertElement("DateTimeDigitized",(const char*)exifcontent);
574 }
575 {
576 sprintf(exifcontent, "%s", SubSecTime);
577 exiftable->insertElement("SubSecTime",(const char*)exifcontent);
578 }
579 {
580
581 sprintf(exifcontent, "%s", SubSecTimeOrig);
582 exiftable->insertElement("SubSecTimeOriginal",(const char*)exifcontent);
583 }
584 {
585
586 sprintf(exifcontent, "%s", SubSecTimeDig);
587 exiftable->insertElement("SubSecTimeDigitized",(const char*)exifcontent);
588 }
589 }
590
591 if (mInfo.has_focallen) {
592 float focallen = mInfo.focallen;
593 if(focallen >= 0){
594 int focalNum = focallen*1000;
595 int focalDen = 1000;
596 sprintf(exifcontent,"%d/%d",focalNum,focalDen);
597 exiftable->insertElement("FocalLength",(const char*)exifcontent);
598 }
599 }
600 time_t times;
601 {
602 time(&times);
603 struct tm tmstruct;
604 tmstruct = *(localtime(&times)); //convert to local time
605 strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct);
606 exiftable->insertElement("DateTime",(const char*)exifcontent);
607 }
608 if (mInfo.has_gpsTimestamp) {
609 times = mInfo.gpsTimestamp;
610 if(times != -1){
611 struct tm tmstruct;
612 tmstruct = *(gmtime(&times));//convert to standard time
613 strftime(exifcontent, 20, "%Y:%m:%d", &tmstruct);
614 exiftable->insertElement("GPSDateStamp",(const char*)exifcontent);
615 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",tmstruct.tm_hour,1,tmstruct.tm_min,1,tmstruct.tm_sec,1);
616 exiftable->insertElement("GPSTimeStamp",(const char*)exifcontent);
617 }
618 }
619 if (mInfo.has_latitude) {
620 int offset = 0;
621 float latitude = mInfo.latitude;
622 if(latitude < 0.0){
623 offset = 1;
624 latitude*= (float)(-1);
625 }
626 int latitudedegree = latitude;
627 float latitudeminuts = (latitude-(float)latitudedegree)*60;
628 int latitudeminuts_int = latitudeminuts;
629 float latituseconds = (latitudeminuts-(float)latitudeminuts_int)*60+0.5;
630 int latituseconds_int = latituseconds;
631 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",latitudedegree,1,latitudeminuts_int,1,latituseconds_int,1);
632 exiftable->insertElement("GPSLatitude",(const char*)exifcontent);
633 exiftable->insertElement("GPSLatitudeRef",(offset==1)?"S":"N");
634 }
635 if (mInfo.has_longitude) {
636 int offset = 0;
637 float longitude = mInfo.longitude;
638 if(longitude < 0.0){
639 offset = 1;
640 longitude*= (float)(-1);
641 }
642 int longitudedegree = longitude;
643 float longitudeminuts = (longitude-(float)longitudedegree)*60;
644 int longitudeminuts_int = longitudeminuts;
645 float longitudeseconds = (longitudeminuts-(float)longitudeminuts_int)*60+0.5;
646 int longitudeseconds_int = longitudeseconds;
647 sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",longitudedegree,1,longitudeminuts_int,1,longitudeseconds_int,1);
648 exiftable->insertElement("GPSLongitude",(const char*)exifcontent);
649 exiftable->insertElement("GPSLongitudeRef",(offset==1)?"S":"N");
650 }
651 if (mInfo.has_altitude) {
652 int offset = 0;
653 float altitude = mInfo.altitude;
654 if(altitude < 0.0){
655 offset = 1;
656 altitude*= (float)(-1);
657 }
658 int altitudenum = altitude*1000;
659 int altitudedec= 1000;
660 sprintf(exifcontent,"%d/%d",altitudenum,altitudedec);
661 exiftable->insertElement("GPSAltitude",(const char*)exifcontent);
662 sprintf(exifcontent,"%d",offset);
663 exiftable->insertElement("GPSAltitudeRef",(const char*)exifcontent);
664 }
665 if (mInfo.has_gpsProcessingMethod) {
666 char* processmethod = (char*)mInfo.gpsProcessingMethod;
667 if(processmethod!=NULL){
668 memset(exifcontent,0,sizeof(exifcontent));
669 char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };//asicii
670 memcpy(exifcontent,ExifAsciiPrefix,8);
671 memcpy(exifcontent+8,processmethod,strlen(processmethod));
672 exiftable->insertElement("GPSProcessingMethod",(const char*)exifcontent);
673 }
674 }
675 return 1;
676}
677const char* ExifElementsTable::degreesToExifOrientation(const char* degrees) {
678 for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) {
679 if (!strcmp(degrees, degress_to_exif_lut[i].string1)) {
680 return degress_to_exif_lut[i].string2;
681 }
682 }
683 return NULL;
684}
685void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) {
686 int len;
687 char * tempVal = NULL;
688 if (str != NULL) {
689 len = strlen(str);
690 tempVal = (char*) malloc( sizeof(char) * (len + 1));
691 }
692 if (tempVal != NULL) {
693 size_t den_len;
694 char *ctx;
695 unsigned int numerator = 0;
696 unsigned int denominator = 0;
697 char* temp = NULL;
698 memset(tempVal, '\0', len + 1);
699 strncpy(tempVal, str, len);
700 temp = strtok_r(tempVal, ".", &ctx);
701 if (temp != NULL)
702 numerator = atoi(temp);
703 if (!numerator)
704 numerator = 1;
705 temp = strtok_r(NULL, ".", &ctx);
706 if (temp != NULL) {
707 den_len = strlen(temp);
708 if(HUGE_VAL == den_len ) {
709 den_len = 0;
710 }
711 denominator = static_cast<unsigned int>(pow(10, den_len));
712 numerator = numerator * denominator + atoi(temp);
713 } else {
714 denominator = 1;
715 }
716 free(tempVal);
717 *num = numerator;
718 *den = denominator;
719 }
720}
721bool ExifElementsTable::isAsciiTag(const char* tag) {
722 return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0);
723}
724status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
725 int value_length = 0;
726 status_t ret = NO_ERROR;
727 if (!value || !tag) {
728 return -EINVAL;
729 }
730 if (position >= MAX_EXIF_TAGS_SUPPORTED) {
731 CAMHAL_LOGEA("Max number of EXIF elements already inserted");
732 return NO_MEMORY;
733 }
734 if (isAsciiTag(tag)) {
735 value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix));
736 } else {
737 value_length = strlen(value);
738 }
739 if (IsGpsTag(tag)) {
740 table[position].GpsTag = TRUE;
741 table[position].Tag = GpsTagNameToValue(tag);
742 gps_tag_count++;
743 } else {
744 table[position].GpsTag = FALSE;
745 table[position].Tag = TagNameToValue(tag);
746 exif_tag_count++;
747 }
748 table[position].DataLength = 0;
749 table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1));
750 if (table[position].Value) {
751 memcpy(table[position].Value, value, value_length + 1);
752 table[position].DataLength = value_length + 1;
753 }
754 position++;
755 return ret;
756}
757void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) {
758 int ret;
759 if (jpeg_opened) {
760 ret = WriteJpegToBuffer(jpeg, jpeg_size);
761 ALOGD("saveJpeg :: ret =%d",ret);
762 DiscardData();
763 jpeg_opened = false;
764 }
765}
766void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) {
767 ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE);
768 ResetJpgfile();
769 if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) {
770 jpeg_opened = true;
771 create_EXIF(table, exif_tag_count, gps_tag_count,true);
772 }
773}
774status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) {
775 status_t ret = NO_ERROR;
776 if ((len > 0) && jpeg_opened) {
777 ret = ReplaceThumbnailFromBuffer(thumb, len);
778 CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret);
779 }
780 return ret;
781}
782ExifElementsTable::~ExifElementsTable() {
783 int num_elements = gps_tag_count + exif_tag_count;
784 for (int i = 0; i < num_elements; i++) {
785 if (table[i].Value) {
786 free(table[i].Value);
787 }
788 }
789 if (jpeg_opened) {
790 DiscardData();
791 }
792}
793} // namespace android
794