Encoder_libjpeg.cpp revision d9520b9de06f01b9411307040cf245e6fc7fe361
1/*
2 * Copyright (C) Texas Instruments - http://www.ti.com/
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/**
18* @file Encoder_libjpeg.cpp
19*
20* This file encodes a YUV422I buffer to a jpeg
21* TODO(XXX): Need to support formats other than yuv422i
22*            Change interface to pre/post-proc algo framework
23*
24*/
25
26#define LOG_TAG "CameraHAL"
27
28#include "CameraHal.h"
29#include "Encoder_libjpeg.h"
30#include "NV12_resize.h"
31
32#include <stdlib.h>
33#include <unistd.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <fcntl.h>
37#include <stdio.h>
38#include <errno.h>
39#include <math.h>
40
41extern "C" {
42    #include "jpeglib.h"
43    #include "jerror.h"
44}
45
46#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
47
48namespace android {
49struct string_pair {
50    const char* string1;
51    const char* string2;
52};
53
54static string_pair degress_to_exif_lut [] = {
55    // degrees, exif_orientation
56    {"0",   "1"},
57    {"90",  "6"},
58    {"180", "3"},
59    {"270", "8"},
60};
61struct libjpeg_destination_mgr : jpeg_destination_mgr {
62    libjpeg_destination_mgr(uint8_t* input, int size);
63
64    uint8_t* buf;
65    int bufsize;
66    size_t jpegsize;
67};
68
69static void libjpeg_init_destination (j_compress_ptr cinfo) {
70    libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
71
72    dest->next_output_byte = dest->buf;
73    dest->free_in_buffer = dest->bufsize;
74    dest->jpegsize = 0;
75}
76
77static boolean libjpeg_empty_output_buffer(j_compress_ptr cinfo) {
78    libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
79
80    dest->next_output_byte = dest->buf;
81    dest->free_in_buffer = dest->bufsize;
82    return TRUE; // ?
83}
84
85static void libjpeg_term_destination (j_compress_ptr cinfo) {
86    libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
87    dest->jpegsize = dest->bufsize - dest->free_in_buffer;
88}
89
90libjpeg_destination_mgr::libjpeg_destination_mgr(uint8_t* input, int size) {
91    this->init_destination = libjpeg_init_destination;
92    this->empty_output_buffer = libjpeg_empty_output_buffer;
93    this->term_destination = libjpeg_term_destination;
94
95    this->buf = input;
96    this->bufsize = size;
97
98    jpegsize = 0;
99}
100
101/* private static functions */
102static void nv21_to_yuv(uint8_t* dst, uint8_t* y, uint8_t* uv, int width) {
103    if (!dst || !y || !uv) {
104        return;
105    }
106
107    while ((width--) > 0) {
108        uint8_t y0 = y[0];
109        uint8_t v0 = uv[0];
110        uint8_t u0 = *(uv+1);
111        dst[0] = y0;
112        dst[1] = u0;
113        dst[2] = v0;
114        dst += 3;
115        y++;
116        if(!(width % 2)) uv+=2;
117    }
118}
119
120static void uyvy_to_yuv(uint8_t* dst, uint32_t* src, int width) {
121    if (!dst || !src) {
122        return;
123    }
124
125    if (width % 2) {
126        return; // not supporting odd widths
127    }
128
129    // currently, neon routine only supports multiple of 16 width
130    if (width % 16) {
131        while ((width-=2) >= 0) {
132            uint8_t u0 = (src[0] >> 0) & 0xFF;
133            uint8_t y0 = (src[0] >> 8) & 0xFF;
134            uint8_t v0 = (src[0] >> 16) & 0xFF;
135            uint8_t y1 = (src[0] >> 24) & 0xFF;
136            dst[0] = y0;
137            dst[1] = u0;
138            dst[2] = v0;
139            dst[3] = y1;
140            dst[4] = u0;
141            dst[5] = v0;
142            dst += 6;
143            src++;
144        }
145    } else {
146        int n = width;
147        asm volatile (
148        "   pld [%[src], %[src_stride], lsl #2]                         \n\t"
149        "   cmp %[n], #16                                               \n\t"
150        "   blt 5f                                                      \n\t"
151        "0: @ 16 pixel swap                                             \n\t"
152        "   vld2.8  {q0, q1} , [%[src]]! @ q0 = uv q1 = y               \n\t"
153        "   vuzp.8 q0, q2                @ d1 = u d5 = v                \n\t"
154        "   vmov d1, d0                  @ q0 = u0u1u2..u0u1u2...       \n\t"
155        "   vmov d5, d4                  @ q2 = v0v1v2..v0v1v2...       \n\t"
156        "   vzip.8 d0, d1                @ q0 = u0u0u1u1u2u2...         \n\t"
157        "   vzip.8 d4, d5                @ q2 = v0v0v1v1v2v2...         \n\t"
158        "   vswp q0, q1                  @ now q0 = y q1 = u q2 = v     \n\t"
159        "   vst3.8  {d0,d2,d4},[%[dst]]!                                \n\t"
160        "   vst3.8  {d1,d3,d5},[%[dst]]!                                \n\t"
161        "   sub %[n], %[n], #16                                         \n\t"
162        "   cmp %[n], #16                                               \n\t"
163        "   bge 0b                                                      \n\t"
164        "5: @ end                                                       \n\t"
165#ifdef NEEDS_ARM_ERRATA_754319_754320
166        "   vmov s0,s0  @ add noop for errata item                      \n\t"
167#endif
168        : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n)
169        : [src_stride] "r" (width)
170        : "cc", "memory", "q0", "q1", "q2"
171        );
172    }
173}
174
175static void resize_nv12(Encoder_libjpeg::params* params, uint8_t* dst_buffer) {
176    structConvImage o_img_ptr, i_img_ptr;
177
178    if (!params || !dst_buffer) {
179        return;
180    }
181
182    //input
183    i_img_ptr.uWidth =  params->in_width;
184    i_img_ptr.uStride =  i_img_ptr.uWidth;
185    i_img_ptr.uHeight =  params->in_height;
186    i_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
187    i_img_ptr.imgPtr = (uint8_t*) params->src;
188    i_img_ptr.clrPtr = i_img_ptr.imgPtr + (i_img_ptr.uWidth * i_img_ptr.uHeight);
189
190    //ouput
191    o_img_ptr.uWidth = params->out_width;
192    o_img_ptr.uStride = o_img_ptr.uWidth;
193    o_img_ptr.uHeight = params->out_height;
194    o_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
195    o_img_ptr.imgPtr = dst_buffer;
196    o_img_ptr.clrPtr = o_img_ptr.imgPtr + (o_img_ptr.uWidth * o_img_ptr.uHeight);
197
198    VT_resizeFrame_Video_opt2_lp(&i_img_ptr, &o_img_ptr, NULL, 0);
199}
200
201/* public static functions */
202const char* ExifElementsTable::degreesToExifOrientation(const char* degrees) {
203    for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) {
204        if (!strcmp(degrees, degress_to_exif_lut[i].string1)) {
205            return degress_to_exif_lut[i].string2;
206        }
207    }
208    return NULL;
209}
210
211void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) {
212    int len;
213    char * tempVal = NULL;
214
215    if (str != NULL) {
216        len = strlen(str);
217        tempVal = (char*) malloc( sizeof(char) * (len + 1));
218    }
219
220    if (tempVal != NULL) {
221        // convert the decimal string into a rational
222        size_t den_len;
223        char *ctx;
224        unsigned int numerator = 0;
225        unsigned int denominator = 0;
226        char* temp = NULL;
227
228        memset(tempVal, '\0', len + 1);
229        strncpy(tempVal, str, len);
230        temp = strtok_r(tempVal, ".", &ctx);
231
232        if (temp != NULL)
233            numerator = atoi(temp);
234
235        if (!numerator)
236            numerator = 1;
237
238        temp = strtok_r(NULL, ".", &ctx);
239        if (temp != NULL) {
240            den_len = strlen(temp);
241            if(HUGE_VAL == den_len ) {
242                den_len = 0;
243            }
244
245            denominator = static_cast<unsigned int>(pow(10, den_len));
246            numerator = numerator * denominator + atoi(temp);
247        } else {
248            denominator = 1;
249        }
250
251        free(tempVal);
252
253        *num = numerator;
254        *den = denominator;
255    }
256}
257
258bool ExifElementsTable::isAsciiTag(const char* tag) {
259    // TODO(XXX): Add tags as necessary
260    return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0);
261}
262
263void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) {
264    ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE);
265
266    ResetJpgfile();
267    if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) {
268        jpeg_opened = true;
269        create_EXIF(table, exif_tag_count, gps_tag_count);
270    }
271}
272
273status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) {
274    status_t ret = NO_ERROR;
275
276    if ((len > 0) && jpeg_opened) {
277        ret = ReplaceThumbnailFromBuffer(thumb, len);
278        CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret);
279    }
280
281    return ret;
282}
283
284void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) {
285    if (jpeg_opened) {
286       WriteJpegToBuffer(jpeg, jpeg_size);
287       DiscardData();
288       jpeg_opened = false;
289    }
290}
291
292/* public functions */
293ExifElementsTable::~ExifElementsTable() {
294    int num_elements = gps_tag_count + exif_tag_count;
295
296    for (int i = 0; i < num_elements; i++) {
297        if (table[i].Value) {
298            free(table[i].Value);
299        }
300    }
301
302    if (jpeg_opened) {
303        DiscardData();
304    }
305}
306
307status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
308    int value_length = 0;
309    status_t ret = NO_ERROR;
310
311    if (!value || !tag) {
312        return -EINVAL;
313    }
314
315    if (position >= MAX_EXIF_TAGS_SUPPORTED) {
316        CAMHAL_LOGEA("Max number of EXIF elements already inserted");
317        return NO_MEMORY;
318    }
319
320    if (isAsciiTag(tag)) {
321        value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix));
322    } else {
323        value_length = strlen(value);
324    }
325
326    if (IsGpsTag(tag)) {
327        table[position].GpsTag = TRUE;
328        table[position].Tag = GpsTagNameToValue(tag);
329        gps_tag_count++;
330    } else {
331        table[position].GpsTag = FALSE;
332        table[position].Tag = TagNameToValue(tag);
333        exif_tag_count++;
334    }
335
336    table[position].DataLength = 0;
337    table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1));
338
339    if (table[position].Value) {
340        memcpy(table[position].Value, value, value_length + 1);
341        table[position].DataLength = value_length + 1;
342    }
343
344    position++;
345    return ret;
346}
347
348/* private member functions */
349size_t Encoder_libjpeg::encode(params* input) {
350    jpeg_compress_struct    cinfo;
351    jpeg_error_mgr jerr;
352    jpeg_destination_mgr jdest;
353    uint8_t* src = NULL, *resize_src = NULL;
354    uint8_t* row_tmp = NULL;
355    uint8_t* row_src = NULL;
356    uint8_t* row_uv = NULL; // used only for NV12
357    int out_width = 0, in_width = 0;
358    int out_height = 0, in_height = 0;
359    int bpp = 2; // for uyvy
360    int right_crop = 0, start_offset = 0;
361
362    if (!input) {
363        return 0;
364    }
365
366    out_width = input->out_width;
367    in_width = input->in_width;
368    out_height = input->out_height;
369    in_height = input->in_height;
370    right_crop = input->right_crop;
371    start_offset = input->start_offset;
372    src = input->src;
373    input->jpeg_size = 0;
374
375    libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size);
376
377    // param check...
378    if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) ||
379         (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) ||
380         (input->dst_size < 1) || (input->format == NULL)) {
381        goto exit;
382    }
383
384    if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
385        bpp = 1;
386        if ((in_width != out_width) || (in_height != out_height)) {
387            resize_src = (uint8_t*) malloc(input->dst_size);
388            resize_nv12(input, resize_src);
389            if (resize_src) src = resize_src;
390        }
391    } else if ((in_width != out_width) || (in_height != out_height)) {
392        CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %s", input->format);
393        goto exit;
394    } else if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV422I)) {
395        // we currently only support yuv422i and yuv420sp
396        CAMHAL_LOGEB("Encoder: format not supported: %s", input->format);
397        goto exit;
398    }
399
400    cinfo.err = jpeg_std_error(&jerr);
401
402    jpeg_create_compress(&cinfo);
403
404    CAMHAL_LOGDB("encoding...  \n\t"
405                 "width: %d    \n\t"
406                 "height:%d    \n\t"
407                 "dest %p      \n\t"
408                 "dest size:%d \n\t"
409                 "mSrc %p",
410                 out_width, out_height, input->dst,
411                 input->dst_size, src);
412
413    cinfo.dest = &dest_mgr;
414    cinfo.image_width = out_width - right_crop;
415    cinfo.image_height = out_height;
416    cinfo.input_components = 3;
417    cinfo.in_color_space = JCS_YCbCr;
418    cinfo.input_gamma = 1;
419
420    jpeg_set_defaults(&cinfo);
421    jpeg_set_quality(&cinfo, input->quality, TRUE);
422    cinfo.dct_method = JDCT_IFAST;
423
424    jpeg_start_compress(&cinfo, TRUE);
425
426    row_tmp = (uint8_t*)malloc(out_width * 3);
427    row_src = src + start_offset;
428    row_uv = src + out_width * out_height * bpp;
429
430    while ((cinfo.next_scanline < cinfo.image_height) && !mCancelEncoding) {
431        JSAMPROW row[1];    /* pointer to JSAMPLE row[s] */
432
433        // convert input yuv format to yuv444
434        if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
435            nv21_to_yuv(row_tmp, row_src, row_uv, out_width - right_crop);
436        } else {
437            uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width - right_crop);
438        }
439
440        row[0] = row_tmp;
441        jpeg_write_scanlines(&cinfo, row, 1);
442        row_src = row_src + out_width*bpp;
443
444        // move uv row if input format needs it
445        if (strcmp(input->format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
446            if (!(cinfo.next_scanline % 2))
447                row_uv = row_uv +  out_width * bpp;
448        }
449    }
450
451    // no need to finish encoding routine if we are prematurely stopping
452    // we will end up crashing in dest_mgr since data is incomplete
453    if (!mCancelEncoding)
454        jpeg_finish_compress(&cinfo);
455    jpeg_destroy_compress(&cinfo);
456
457    if (resize_src) free(resize_src);
458    if (row_tmp) free(row_tmp);
459
460 exit:
461    input->jpeg_size = dest_mgr.jpegsize;
462    return dest_mgr.jpegsize;
463}
464
465} // namespace android
466