C++程序  |  1014行  |  31.99 KB

/*
 * Copyright Samsung Electronics Co.,LTD.
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <utils/Log.h>

#include "ExynosJpegEncoderForCamera.h"

static const char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };

#define JPEG_ERROR_LOG ALOGE
#define LOG_TAG "ExynosJpegForCamera"

#define JPEG_THUMBNAIL_QUALITY (60)
#define EXIF_LIMIT_SIZE (64*1024)
#define THUMBNAIL_IMAGE_PIXEL_SIZE (4)
#define MAX_JPG_WIDTH (8192)
#define MAX_JPG_HEIGHT (8192)

#define MAX_INPUT_BUFFER_PLANE_NUM (1)
#define MAX_OUTPUT_BUFFER_PLANE_NUM (1)

ExynosJpegEncoderForCamera::ExynosJpegEncoderForCamera()
{
    m_flagCreate = false;
    m_jpegMain = NULL;
    m_jpegThumb = NULL;
    m_thumbnailW = 0;
    m_thumbnailH = 0;
    m_thumbnailQuality = JPEG_THUMBNAIL_QUALITY;
    m_ionJpegClient = 0;
    initJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM);
    initJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM);
    initJpegMemory(&m_stMainInBuf, MAX_IMAGE_PLANE_NUM);
    initJpegMemory(&m_stMainOutBuf, MAX_IMAGE_PLANE_NUM);
}

ExynosJpegEncoderForCamera::~ExynosJpegEncoderForCamera()
{
    if (m_flagCreate == true) {
        this->destroy();
    }
}

bool ExynosJpegEncoderForCamera::flagCreate(void)
{
    return m_flagCreate;
}

int ExynosJpegEncoderForCamera::create(void)
{
    int ret = ERROR_NONE;
    if (m_flagCreate == true) {
        return ERROR_ALREADY_CREATE;
    }

    if (m_jpegMain == NULL) {
        m_jpegMain = new ExynosJpegEncoder;

        if (m_jpegMain == NULL) {
            JPEG_ERROR_LOG("ERR(%s):Cannot create ExynosJpegEncoder class\n", __func__);
            return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
        }

        ret = m_jpegMain->create();
        if (ret) {
            return ret;
        }

        ret = m_jpegMain->setCache(JPEG_CACHE_ON);

        if (ret) {
            m_jpegMain->destroy();
            return ret;
        }
    }

    m_ionJpegClient = createIonClient(m_ionJpegClient);
    if(m_ionJpegClient == 0) {
        return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
    }
    m_stMainOutBuf.ionClient = m_stMainInBuf.ionClient = m_stThumbInBuf.ionClient = m_stThumbOutBuf.ionClient = m_ionJpegClient;

    m_flagCreate = true;

    return ERROR_NONE;
}

int ExynosJpegEncoderForCamera::destroy(void)
{
    if (m_flagCreate == false) {
        return ERROR_ALREADY_DESTROY;
    }

    if (m_jpegMain != NULL) {
        m_jpegMain->destroy();
        delete m_jpegMain;
        m_jpegMain = NULL;
    }

    if (m_jpegThumb != NULL) {
        int iSize = sizeof(char)*m_thumbnailW*m_thumbnailH*4;

        freeJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM);
        freeJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM);
        initJpegMemory(&m_stMainInBuf, MAX_IMAGE_PLANE_NUM);
        initJpegMemory(&m_stMainOutBuf, MAX_IMAGE_PLANE_NUM);
        m_ionJpegClient = deleteIonClient(m_ionJpegClient);
        m_stMainOutBuf.ionClient = m_stMainInBuf.ionClient = m_stThumbInBuf.ionClient = m_stThumbOutBuf.ionClient = m_ionJpegClient;
        m_jpegThumb->destroy();
        delete m_jpegThumb;
        m_jpegThumb = NULL;
    }

    m_flagCreate = false;
    m_thumbnailW = 0;
    m_thumbnailH = 0;
    m_thumbnailQuality = JPEG_THUMBNAIL_QUALITY;
    return ERROR_NONE;
}

int ExynosJpegEncoderForCamera::setSize(int w, int h)
{
    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }

    return m_jpegMain->setSize(w, h);
}


int ExynosJpegEncoderForCamera::setQuality(int quality)
{
    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }

    return m_jpegMain->setQuality(quality);
}

int ExynosJpegEncoderForCamera::setColorFormat(int colorFormat)
{
    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }

    return m_jpegMain->setColorFormat(colorFormat);
}

int ExynosJpegEncoderForCamera::setJpegFormat(int jpegFormat)
{
    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }

    return m_jpegMain->setJpegFormat(jpegFormat);
}

int ExynosJpegEncoderForCamera::updateConfig(void)
{
    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }

    return m_jpegMain->updateConfig();
}

int  ExynosJpegEncoderForCamera::setInBuf(int *buf, char** vBuf, int *size)
{
    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }

    if (buf == NULL) {
        return ERROR_BUFFR_IS_NULL;
    }

    if (size == NULL) {
        return ERROR_BUFFR_IS_NULL;
    }

    int ret = ERROR_NONE;

    ret = m_jpegMain->setInBuf(buf, size);
    if (ret) {
        JPEG_ERROR_LOG("%s::Fail to JPEG input buffer!!\n", __func__);
        return ret;
    }
    m_stMainInBuf.ionBuffer[0] = buf[0];
    m_stMainInBuf.iSize[0] = size[0];
    m_stMainInBuf.pcBuf[0] = vBuf[0];

    return ERROR_NONE;
}

int  ExynosJpegEncoderForCamera::setOutBuf(int buf, char* vBuf, int size)
{
    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }

    if (buf == NULL) {
        return ERROR_BUFFR_IS_NULL;
    }

    if (size<=0) {
        return ERROR_BUFFER_TOO_SMALL;
    }

    int ret = ERROR_NONE;
    ret = m_jpegMain->setOutBuf(buf, size);
    if (ret) {
        JPEG_ERROR_LOG("%s::Fail to JPEG output buffer!!\n", __func__);
        return ret;
    }
    m_stMainOutBuf.ionBuffer[0] = buf;
    m_stMainOutBuf.iSize[0] = size;
    m_stMainOutBuf.pcBuf[0] = vBuf;

    return ERROR_NONE;
}

int ExynosJpegEncoderForCamera::encode(int *size, exif_attribute_t *exifInfo)
{
    int ret = ERROR_NONE;
    unsigned char *exifOut = NULL;

    if (m_flagCreate == false) {
        return ERROR_NOT_YET_CREATED;
    }


    ret = m_jpegMain->encode();
    if (ret) {
        JPEG_ERROR_LOG("encode failed\n");
        return ret;
    }

    int iJpegSize = m_jpegMain->getJpegSize();

    if (iJpegSize<=0) {
        JPEG_ERROR_LOG("%s:: output_size is too small(%d)!!\n", __func__, iJpegSize);
        return ERROR_OUT_BUFFER_SIZE_TOO_SMALL;
    }

    int iOutputSize = m_stMainOutBuf.iSize[0];
    int iJpegBuffer = m_stMainOutBuf.ionBuffer[0];
    char *pcJpegBuffer = m_stMainOutBuf.pcBuf[0];

    if (pcJpegBuffer[0] == NULL) {
        JPEG_ERROR_LOG("%s::pcJpegBuffer[0] is null!!\n", __func__);
        return ERROR_OUT_BUFFER_CREATE_FAIL;
    }

    if (exifInfo != NULL) {
        unsigned int thumbLen, exifLen;

        unsigned int bufSize = 0;
        if (exifInfo->enableThumb) {
            if (encodeThumbnail(&thumbLen)) {
                bufSize = EXIF_FILE_SIZE;
                exifInfo->enableThumb = false;
            } else {
                if (thumbLen > EXIF_LIMIT_SIZE) {
                    bufSize = EXIF_FILE_SIZE;
                    exifInfo->enableThumb = false;
                }
                else {
                    bufSize = EXIF_FILE_SIZE + thumbLen;
                }
            }
        } else {
            bufSize = EXIF_FILE_SIZE;
            exifInfo->enableThumb = false;
        }

        exifOut = new unsigned char[bufSize];
        if (exifOut == NULL) {
            JPEG_ERROR_LOG("%s::Failed to allocate for exifOut\n", __func__);
            delete[] exifOut;
            return ERROR_EXIFOUT_ALLOC_FAIL;
        }
        memset(exifOut, 0, bufSize);

        if (makeExif (exifOut, exifInfo, &exifLen)) {
            JPEG_ERROR_LOG("%s::Failed to make EXIF\n", __func__);
            delete[] exifOut;
            return ERROR_MAKE_EXIF_FAIL;
        }

        if (exifLen <= EXIF_LIMIT_SIZE) {
            memmove(pcJpegBuffer+exifLen+2, pcJpegBuffer+2, iJpegSize - 2);
            memcpy(pcJpegBuffer+2, exifOut, exifLen);
            iJpegSize += exifLen;
        }

        delete[] exifOut;
    }

    *size = iJpegSize;

    return ERROR_NONE;
}

int ExynosJpegEncoderForCamera::makeExif (unsigned char *exifOut,
                              exif_attribute_t *exifInfo,
                              unsigned int *size,
                              bool useMainbufForThumb)
{
    unsigned char *pCur, *pApp1Start, *pIfdStart, *pGpsIfdPtr, *pNextIfdOffset;
    unsigned int tmp, LongerTagOffest = 0, exifSizeExceptThumb;
    pApp1Start = pCur = exifOut;

    //2 Exif Identifier Code & TIFF Header
    pCur += 4;  // Skip 4 Byte for APP1 marker and length
    unsigned char ExifIdentifierCode[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
    memcpy(pCur, ExifIdentifierCode, 6);
    pCur += 6;

    /* Byte Order - little endian, Offset of IFD - 0x00000008.H */
    unsigned char TiffHeader[8] = { 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00 };
    memcpy(pCur, TiffHeader, 8);
    pIfdStart = pCur;
    pCur += 8;

    //2 0th IFD TIFF Tags
    if (exifInfo->enableGps)
        tmp = NUM_0TH_IFD_TIFF;
    else
        tmp = NUM_0TH_IFD_TIFF - 1;

    memcpy(pCur, &tmp, NUM_SIZE);
    pCur += NUM_SIZE;

    LongerTagOffest += 8 + NUM_SIZE + tmp*IFD_SIZE + OFFSET_SIZE;

    writeExifIfd(&pCur, EXIF_TAG_IMAGE_WIDTH, EXIF_TYPE_LONG,
                 1, exifInfo->width);
    writeExifIfd(&pCur, EXIF_TAG_IMAGE_HEIGHT, EXIF_TYPE_LONG,
                 1, exifInfo->height);
    writeExifIfd(&pCur, EXIF_TAG_MAKE, EXIF_TYPE_ASCII,
                 strlen((char *)exifInfo->maker) + 1, exifInfo->maker, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_MODEL, EXIF_TYPE_ASCII,
                 strlen((char *)exifInfo->model) + 1, exifInfo->model, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_ORIENTATION, EXIF_TYPE_SHORT,
                 1, exifInfo->orientation);
    writeExifIfd(&pCur, EXIF_TAG_SOFTWARE, EXIF_TYPE_ASCII,
                 strlen((char *)exifInfo->software) + 1, exifInfo->software, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_DATE_TIME, EXIF_TYPE_ASCII,
                 20, exifInfo->date_time, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_YCBCR_POSITIONING, EXIF_TYPE_SHORT,
                 1, exifInfo->ycbcr_positioning);
    writeExifIfd(&pCur, EXIF_TAG_EXIF_IFD_POINTER, EXIF_TYPE_LONG,
                 1, LongerTagOffest);
    if (exifInfo->enableGps) {
        pGpsIfdPtr = pCur;
        pCur += IFD_SIZE;   // Skip a ifd size for gps IFD pointer
    }

    pNextIfdOffset = pCur;  // Skip a offset size for next IFD offset
    pCur += OFFSET_SIZE;

    //2 0th IFD Exif Private Tags
    pCur = pIfdStart + LongerTagOffest;

    tmp = NUM_0TH_IFD_EXIF;
    memcpy(pCur, &tmp , NUM_SIZE);
    pCur += NUM_SIZE;

    LongerTagOffest += NUM_SIZE + NUM_0TH_IFD_EXIF*IFD_SIZE + OFFSET_SIZE;

    writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_TIME, EXIF_TYPE_RATIONAL,
                 1, &exifInfo->exposure_time, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_FNUMBER, EXIF_TYPE_RATIONAL,
                 1, &exifInfo->fnumber, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_PROGRAM, EXIF_TYPE_SHORT,
                 1, exifInfo->exposure_program);
    writeExifIfd(&pCur, EXIF_TAG_ISO_SPEED_RATING, EXIF_TYPE_SHORT,
                 1, exifInfo->iso_speed_rating);
    writeExifIfd(&pCur, EXIF_TAG_EXIF_VERSION, EXIF_TYPE_UNDEFINED,
                 4, exifInfo->exif_version);
    writeExifIfd(&pCur, EXIF_TAG_DATE_TIME_ORG, EXIF_TYPE_ASCII,
                 20, exifInfo->date_time, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_DATE_TIME_DIGITIZE, EXIF_TYPE_ASCII,
                 20, exifInfo->date_time, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_SHUTTER_SPEED, EXIF_TYPE_SRATIONAL,
                 1, (rational_t *)&exifInfo->shutter_speed, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_APERTURE, EXIF_TYPE_RATIONAL,
                 1, &exifInfo->aperture, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_BRIGHTNESS, EXIF_TYPE_SRATIONAL,
                 1, (rational_t *)&exifInfo->brightness, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_BIAS, EXIF_TYPE_SRATIONAL,
                 1, (rational_t *)&exifInfo->exposure_bias, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_MAX_APERTURE, EXIF_TYPE_RATIONAL,
                 1, &exifInfo->max_aperture, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_METERING_MODE, EXIF_TYPE_SHORT,
                 1, exifInfo->metering_mode);
    writeExifIfd(&pCur, EXIF_TAG_FLASH, EXIF_TYPE_SHORT,
                 1, exifInfo->flash);
    writeExifIfd(&pCur, EXIF_TAG_FOCAL_LENGTH, EXIF_TYPE_RATIONAL,
                 1, &exifInfo->focal_length, &LongerTagOffest, pIfdStart);
    char code[8] = { 0x00, 0x00, 0x00, 0x49, 0x49, 0x43, 0x53, 0x41 };
    int commentsLen = strlen((char *)exifInfo->user_comment) + 1;
    memmove(exifInfo->user_comment + sizeof(code), exifInfo->user_comment, commentsLen);
    memcpy(exifInfo->user_comment, code, sizeof(code));
    writeExifIfd(&pCur, EXIF_TAG_USER_COMMENT, EXIF_TYPE_UNDEFINED,
                 commentsLen + sizeof(code), exifInfo->user_comment, &LongerTagOffest, pIfdStart);
    writeExifIfd(&pCur, EXIF_TAG_COLOR_SPACE, EXIF_TYPE_SHORT,
                 1, exifInfo->color_space);
    writeExifIfd(&pCur, EXIF_TAG_PIXEL_X_DIMENSION, EXIF_TYPE_LONG,
                 1, exifInfo->width);
    writeExifIfd(&pCur, EXIF_TAG_PIXEL_Y_DIMENSION, EXIF_TYPE_LONG,
                 1, exifInfo->height);
    writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_MODE, EXIF_TYPE_LONG,
                 1, exifInfo->exposure_mode);
    writeExifIfd(&pCur, EXIF_TAG_WHITE_BALANCE, EXIF_TYPE_LONG,
                 1, exifInfo->white_balance);
    writeExifIfd(&pCur, EXIF_TAG_SCENCE_CAPTURE_TYPE, EXIF_TYPE_LONG,
                 1, exifInfo->scene_capture_type);
    tmp = 0;
    memcpy(pCur, &tmp, OFFSET_SIZE); // next IFD offset
    pCur += OFFSET_SIZE;

    //2 0th IFD GPS Info Tags
    if (exifInfo->enableGps) {
        writeExifIfd(&pGpsIfdPtr, EXIF_TAG_GPS_IFD_POINTER, EXIF_TYPE_LONG,
                     1, LongerTagOffest); // GPS IFD pointer skipped on 0th IFD

        pCur = pIfdStart + LongerTagOffest;

        if (exifInfo->gps_processing_method[0] == 0) {
            // don't create GPS_PROCESSING_METHOD tag if there isn't any
            tmp = NUM_0TH_IFD_GPS - 1;
        } else {
            tmp = NUM_0TH_IFD_GPS;
        }
        memcpy(pCur, &tmp, NUM_SIZE);
        pCur += NUM_SIZE;

        LongerTagOffest += NUM_SIZE + tmp*IFD_SIZE + OFFSET_SIZE;

        writeExifIfd(&pCur, EXIF_TAG_GPS_VERSION_ID, EXIF_TYPE_BYTE,
                     4, exifInfo->gps_version_id);
        writeExifIfd(&pCur, EXIF_TAG_GPS_LATITUDE_REF, EXIF_TYPE_ASCII,
                     2, exifInfo->gps_latitude_ref);
        writeExifIfd(&pCur, EXIF_TAG_GPS_LATITUDE, EXIF_TYPE_RATIONAL,
                     3, exifInfo->gps_latitude, &LongerTagOffest, pIfdStart);
        writeExifIfd(&pCur, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_TYPE_ASCII,
                     2, exifInfo->gps_longitude_ref);
        writeExifIfd(&pCur, EXIF_TAG_GPS_LONGITUDE, EXIF_TYPE_RATIONAL,
                     3, exifInfo->gps_longitude, &LongerTagOffest, pIfdStart);
        writeExifIfd(&pCur, EXIF_TAG_GPS_ALTITUDE_REF, EXIF_TYPE_BYTE,
                     1, exifInfo->gps_altitude_ref);
        writeExifIfd(&pCur, EXIF_TAG_GPS_ALTITUDE, EXIF_TYPE_RATIONAL,
                     1, &exifInfo->gps_altitude, &LongerTagOffest, pIfdStart);
        writeExifIfd(&pCur, EXIF_TAG_GPS_TIMESTAMP, EXIF_TYPE_RATIONAL,
                     3, exifInfo->gps_timestamp, &LongerTagOffest, pIfdStart);
        tmp = strlen((char*)exifInfo->gps_processing_method);
        if (tmp > 0) {
            if (tmp > 100) {
                tmp = 100;
            }
            unsigned char tmp_buf[100+sizeof(ExifAsciiPrefix)];
            memcpy(tmp_buf, ExifAsciiPrefix, sizeof(ExifAsciiPrefix));
            memcpy(&tmp_buf[sizeof(ExifAsciiPrefix)], exifInfo->gps_processing_method, tmp);
            writeExifIfd(&pCur, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_TYPE_UNDEFINED,
                         tmp+sizeof(ExifAsciiPrefix), tmp_buf, &LongerTagOffest, pIfdStart);
        }
        writeExifIfd(&pCur, EXIF_TAG_GPS_DATESTAMP, EXIF_TYPE_ASCII,
                     11, exifInfo->gps_datestamp, &LongerTagOffest, pIfdStart);
        tmp = 0;
        memcpy(pCur, &tmp, OFFSET_SIZE); // next IFD offset
        pCur += OFFSET_SIZE;
    }

    //2 1th IFD TIFF Tags
    int iThumbFd = 0;
    char *thumbBuf = NULL;
    unsigned int thumbSize = 0;
    int thumbBufSize = 0;
    int ret = ERROR_NONE;

    if (useMainbufForThumb) {
        if (m_jpegMain) {
            ret = m_jpegMain->getOutBuf((int *)&iThumbFd, (int *)&thumbBufSize);
            if (ret != ERROR_NONE) {
                iThumbFd = -1;
            }
            thumbSize = (unsigned int)m_jpegMain->getJpegSize();
            thumbBuf = m_stMainOutBuf.pcBuf[0];
        }
    } else {
        if (m_jpegThumb) {
            ret = m_jpegThumb->getOutBuf((int *)&iThumbFd, (int *)&thumbBufSize);
            if (ret != ERROR_NONE) {
                iThumbFd = -1;
            }
            thumbSize = (unsigned int)m_jpegThumb->getJpegSize();
            thumbBuf = m_stThumbOutBuf.pcBuf[0];
        }
    }

    if (exifInfo->enableThumb && (thumbBuf != NULL) && (thumbSize != 0)) {
        exifSizeExceptThumb = tmp = LongerTagOffest;
        memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE);  // NEXT IFD offset skipped on 0th IFD

        pCur = pIfdStart + LongerTagOffest;

        tmp = NUM_1TH_IFD_TIFF;
        memcpy(pCur, &tmp, NUM_SIZE);
        pCur += NUM_SIZE;

        LongerTagOffest += NUM_SIZE + NUM_1TH_IFD_TIFF*IFD_SIZE + OFFSET_SIZE;

        writeExifIfd(&pCur, EXIF_TAG_IMAGE_WIDTH, EXIF_TYPE_LONG,
                     1, exifInfo->widthThumb);
        writeExifIfd(&pCur, EXIF_TAG_IMAGE_HEIGHT, EXIF_TYPE_LONG,
                     1, exifInfo->heightThumb);
        writeExifIfd(&pCur, EXIF_TAG_COMPRESSION_SCHEME, EXIF_TYPE_SHORT,
                     1, exifInfo->compression_scheme);
        writeExifIfd(&pCur, EXIF_TAG_ORIENTATION, EXIF_TYPE_SHORT,
                     1, exifInfo->orientation);
        writeExifIfd(&pCur, EXIF_TAG_X_RESOLUTION, EXIF_TYPE_RATIONAL,
                     1, &exifInfo->x_resolution, &LongerTagOffest, pIfdStart);
        writeExifIfd(&pCur, EXIF_TAG_Y_RESOLUTION, EXIF_TYPE_RATIONAL,
                     1, &exifInfo->y_resolution, &LongerTagOffest, pIfdStart);
        writeExifIfd(&pCur, EXIF_TAG_RESOLUTION_UNIT, EXIF_TYPE_SHORT,
                     1, exifInfo->resolution_unit);
        writeExifIfd(&pCur, EXIF_TAG_JPEG_INTERCHANGE_FORMAT, EXIF_TYPE_LONG,
                     1, LongerTagOffest);
        writeExifIfd(&pCur, EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LEN, EXIF_TYPE_LONG,
                     1, thumbSize);

        tmp = 0;
        memcpy(pCur, &tmp, OFFSET_SIZE); // next IFD offset
        pCur += OFFSET_SIZE;

        memcpy(pIfdStart + LongerTagOffest,
               thumbBuf, thumbSize);
        LongerTagOffest += thumbSize;
        if (LongerTagOffest > EXIF_LIMIT_SIZE) {
            LongerTagOffest = exifSizeExceptThumb;
            tmp = 0;
            memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE);  // NEXT IFD offset skipped on 0th IFD
        }
    } else {
        tmp = 0;
        memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE);  // NEXT IFD offset skipped on 0th IFD
    }

    unsigned char App1Marker[2] = { 0xff, 0xe1 };
    memcpy(pApp1Start, App1Marker, 2);
    pApp1Start += 2;

    *size = 10 + LongerTagOffest;
    tmp = *size - 2;    // APP1 Maker isn't counted
    unsigned char size_mm[2] = {(tmp >> 8) & 0xFF, tmp & 0xFF};
    memcpy(pApp1Start, size_mm, 2);

    return ERROR_NONE;
}

/*
 * private member functions
*/
inline void ExynosJpegEncoderForCamera::writeExifIfd(unsigned char **pCur,
                                             unsigned short tag,
                                             unsigned short type,
                                             unsigned int count,
                                             unsigned int value)
{
    memcpy(*pCur, &tag, 2);
    *pCur += 2;
    memcpy(*pCur, &type, 2);
    *pCur += 2;
    memcpy(*pCur, &count, 4);
    *pCur += 4;
    memcpy(*pCur, &value, 4);
    *pCur += 4;
}

inline void ExynosJpegEncoderForCamera::writeExifIfd(unsigned char **pCur,
                                             unsigned short tag,
                                             unsigned short type,
                                             unsigned int count,
                                             unsigned char *pValue)
{
    char buf[4] = { 0,};

    memcpy(buf, pValue, count);
    memcpy(*pCur, &tag, 2);
    *pCur += 2;
    memcpy(*pCur, &type, 2);
    *pCur += 2;
    memcpy(*pCur, &count, 4);
    *pCur += 4;
    memcpy(*pCur, buf, 4);
    *pCur += 4;
}

inline void ExynosJpegEncoderForCamera::writeExifIfd(unsigned char **pCur,
                                             unsigned short tag,
                                             unsigned short type,
                                             unsigned int count,
                                             unsigned char *pValue,
                                             unsigned int *offset,
                                             unsigned char *start)
{
    memcpy(*pCur, &tag, 2);
    *pCur += 2;
    memcpy(*pCur, &type, 2);
    *pCur += 2;
    memcpy(*pCur, &count, 4);
    *pCur += 4;
    memcpy(*pCur, offset, 4);
    *pCur += 4;
    memcpy(start + *offset, pValue, count);
    *offset += count;
}

inline void ExynosJpegEncoderForCamera::writeExifIfd(unsigned char **pCur,
                                             unsigned short tag,
                                             unsigned short type,
                                             unsigned int count,
                                             rational_t *pValue,
                                             unsigned int *offset,
                                             unsigned char *start)
{
    memcpy(*pCur, &tag, 2);
    *pCur += 2;
    memcpy(*pCur, &type, 2);
    *pCur += 2;
    memcpy(*pCur, &count, 4);
    *pCur += 4;
    memcpy(*pCur, offset, 4);
    *pCur += 4;
    memcpy(start + *offset, pValue, 8 * count);
    *offset += 8 * count;
}

int ExynosJpegEncoderForCamera::scaleDownYuv422(char **srcBuf, unsigned int srcW, unsigned int srcH,  char **dstBuf, unsigned int dstW, unsigned int dstH)
{
    int step_x, step_y;
    int src_y_start_pos, dst_pos, src_pos;
    char *src_buf = srcBuf[0];
    char *dst_buf = dstBuf[0];

    if (dstW & 0x01 || dstH & 0x01) {
        return ERROR_INVALID_SCALING_WIDTH_HEIGHT;
    }

    step_x = srcW / dstW;
    step_y = srcH / dstH;

    unsigned int srcWStride = srcW * 2;
    unsigned int stepXStride = step_x * 2;

    dst_pos = 0;
    for (unsigned int y = 0; y < dstH; y++) {
        src_y_start_pos = srcWStride * step_y * y;

        for (unsigned int x = 0; x < dstW; x += 2) {
            src_pos = src_y_start_pos + (stepXStride * x);

            dst_buf[dst_pos++] = src_buf[src_pos    ];
            dst_buf[dst_pos++] = src_buf[src_pos + 1];
            dst_buf[dst_pos++] = src_buf[src_pos + 2];
            dst_buf[dst_pos++] = src_buf[src_pos + 3];
        }
    }

    return ERROR_NONE;
}

int ExynosJpegEncoderForCamera::scaleDownYuv422_2p(char **srcBuf, unsigned int srcW, unsigned int srcH, char **dstBuf, unsigned int dstW, unsigned int dstH)
{
    int32_t step_x, step_y;
    int32_t src_y_start_pos, dst_pos, src_pos;
    int32_t src_Y_offset;
    char *src_buf;
    char *dst_buf;

    if (dstW % 2 != 0 || dstH % 2 != 0) {
        return ERROR_INVALID_SCALING_WIDTH_HEIGHT;
    }

    step_x = srcW / dstW;
    step_y = srcH / dstH;

    // Y scale down
    src_buf = srcBuf[0];
    dst_buf = dstBuf[0];
    dst_pos = 0;
    for (uint32_t y = 0; y < dstH; y++) {
        src_y_start_pos = y * step_y * srcW;

        for (uint32_t x = 0; x < dstW; x++) {
            src_pos = src_y_start_pos + (x * step_x);

            dst_buf[dst_pos++] = src_buf[src_pos];
        }
    }

    // UV scale down
    for (uint32_t i = 0; i < dstH; i++) {
        src_y_start_pos = i * step_y * srcW + (srcW*srcH);

        for (uint32_t j = 0; j < dstW; j += 2) {
            src_pos = src_y_start_pos + (j * step_x);

            dst_buf[dst_pos++] = src_buf[src_pos    ];
            dst_buf[dst_pos++] = src_buf[src_pos + 1];
        }
    }

    return ERROR_NONE;
}

// thumbnail
int ExynosJpegEncoderForCamera::setThumbnailSize(int w, int h)
{
    if (m_flagCreate == false) {
        return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
    }

    if (w < 0 || MAX_JPG_WIDTH < w) {
        return false;
    }

    if (h < 0 || MAX_JPG_HEIGHT < h) {
        return false;
    }

    m_thumbnailW = w;
    m_thumbnailH = h;
    return ERROR_NONE;
}

int ExynosJpegEncoderForCamera::setThumbnailQuality(int quality)
{
    if (m_flagCreate == false) {
        return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
    }

    if (quality < 1 || 100 < quality) {
        return false;
    }

    m_thumbnailQuality = quality;
    return ERROR_NONE;
}

int ExynosJpegEncoderForCamera::encodeThumbnail(unsigned int *size, bool useMain)
{
    int ret = ERROR_NONE;

    if (m_flagCreate == false) {
        return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
    }

    // create jpeg thumbnail class
    if (m_jpegThumb == NULL) {
        m_jpegThumb = new ExynosJpegEncoder;

        if (m_jpegThumb == NULL) {
            JPEG_ERROR_LOG("ERR(%s):Cannot open a jpeg device file\n", __func__);
            return ERROR_CANNOT_CREATE_SEC_THUMB;
        }
    }

    ret = m_jpegThumb->create();
    if (ret) {
        JPEG_ERROR_LOG("ERR(%s):Fail create\n", __func__);
        return ret;
    }

        ret = m_jpegThumb->setCache(JPEG_CACHE_ON);
    if (ret) {
        JPEG_ERROR_LOG("ERR(%s):Fail cache set\n", __func__);
        return ret;
    }

    void *pConfig = m_jpegMain->getJpegConfig();
    if (pConfig == NULL) {
        JPEG_ERROR_LOG("ERR(%s):Fail getJpegConfig\n", __func__);
        return ERROR_BUFFR_IS_NULL;
    }

    ret = m_jpegThumb->setJpegConfig(pConfig);
    if (ret) {
        JPEG_ERROR_LOG("ERR(%s):Fail setJpegConfig\n", __func__);
        return ret;
    }

    /* TODO: Currently we fix the thumbnail quality */
    ret = m_jpegThumb->setQuality(JPEG_THUMBNAIL_QUALITY);
    if (ret) {
        JPEG_ERROR_LOG("ERR(%s):Fail setQuality\n", __func__);
        return ret;
    }

    ret = m_jpegThumb->setSize(m_thumbnailW, m_thumbnailH);
    if (ret) {
        JPEG_ERROR_LOG("ERR(%s):Fail setSize\n", __func__);
        return ret;
    }

    freeJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM);
    freeJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM);

    if (m_jpegThumb->setColorBufSize(m_stThumbInBuf.iSize, MAX_IMAGE_PLANE_NUM) != ERROR_NONE) {
        return ERROR_INVALID_COLOR_FORMAT;
    }
    m_stThumbOutBuf.iSize[0] = sizeof(char)*m_thumbnailW*m_thumbnailH*THUMBNAIL_IMAGE_PIXEL_SIZE;


    if (allocJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM) != ERROR_NONE) {
        return ERROR_MEM_ALLOC_FAIL;
    }

    if (allocJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM) != ERROR_NONE) {
        return ERROR_MEM_ALLOC_FAIL;
    }

    ret = m_jpegThumb->setInBuf(m_stThumbInBuf.ionBuffer, m_stThumbInBuf.iSize);
    if (ret) {
        JPEG_ERROR_LOG("ERR(%s):Fail setInBuf\n", __func__);
        return ret;
    }

    ret = m_jpegThumb->setOutBuf(m_stThumbOutBuf.ionBuffer[0], m_stThumbOutBuf.iSize[0]);
    if (ret) {
        JPEG_ERROR_LOG("ERR(%s):Fail setOutBuf\n", __func__);
        return ret;
    }

    ret = m_jpegThumb->updateConfig();
    if (ret) {
        JPEG_ERROR_LOG("update config failed\n");
        return ret;
    }

    if (useMain) {
        int iTempWidth=0;
        int iTempHeight=0;
        int iTempColorformat = 0;

        iTempColorformat = m_jpegMain->getColorFormat();

        ret = m_jpegMain->getSize(&iTempWidth, &iTempHeight);
        if (ret) {
            JPEG_ERROR_LOG("ERR(%s):Fail getSize\n", __func__);
            return ret;
        }

        switch (iTempColorformat) {
        case V4L2_PIX_FMT_YUYV:
            ret = scaleDownYuv422(m_stMainInBuf.pcBuf,
                              iTempWidth,
                              iTempHeight,
                              m_stThumbInBuf.pcBuf,
                              m_thumbnailW,
                              m_thumbnailH);
            break;
        case V4L2_PIX_FMT_NV16:
            ret = scaleDownYuv422_2p(m_stMainInBuf.pcBuf,
                              iTempWidth,
                              iTempHeight,
                              m_stThumbInBuf.pcBuf,
                              m_thumbnailW,
                              m_thumbnailH);
            break;
        default:
            return ERROR_INVALID_COLOR_FORMAT;
            break;
        }

        if (ret) {
            JPEG_ERROR_LOG("%s::scaleDown(%d, %d, %d, %d) fail", __func__, iTempWidth, iTempHeight, m_thumbnailW, m_thumbnailH);
            return ret;
        }
    }
    else {
        return ERROR_IMPLEMENT_NOT_YET;
    }

    int iOutSizeThumb;

    ret = m_jpegThumb->encode();
    if (ret) {
        JPEG_ERROR_LOG("encode failed\n");
        return ret;
    }

    iOutSizeThumb = m_jpegThumb->getJpegSize();
    if (iOutSizeThumb<=0) {
        JPEG_ERROR_LOG("jpeg size is too small\n");
        return ERROR_THUMB_JPEG_SIZE_TOO_SMALL;
    }

    *size = (unsigned int)iOutSizeThumb;

    return ERROR_NONE;

}

int ExynosJpegEncoderForCamera::createIonClient(ion_client ionClient)
{
    if (ionClient == 0) {
        ionClient = ion_client_create();
        if (ionClient < 0) {
            JPEG_ERROR_LOG("[%s]src ion client create failed, value = %d\n", __func__, ionClient);
            return 0;
        }
    }

    return ionClient;
}

int ExynosJpegEncoderForCamera::deleteIonClient(ion_client ionClient)
{
    if (ionClient != 0) {
        if (ionClient > 0) {
            ion_client_destroy(ionClient);
        }
        ionClient = 0;
    }

    return ionClient;
}

int ExynosJpegEncoderForCamera::allocJpegMemory(struct stJpegMem *pstMem, int iMemoryNum)
{
    int ret = ERROR_NONE;
    int i = 0;

    if (pstMem->ionClient == 0) {
        JPEG_ERROR_LOG("[%s] i = %d , ionClient is zero (%d)\n", __func__, i, pstMem->ionClient);
        return ERROR_BUFFR_IS_NULL;
    }

    for (i=0;i<iMemoryNum;i++) {
        if (pstMem->iSize[i] == 0) {
            break;
        }

        pstMem->ionBuffer[i] = ion_alloc(pstMem->ionClient, \
                                                        pstMem->iSize[i], 0, ION_HEAP_SYSTEM_MASK, 0);
        if ((pstMem->ionBuffer[i] == -1) ||(pstMem->ionBuffer[i] == 0)) {
            JPEG_ERROR_LOG("[%s]ion_alloc(%d) failed\n", __func__, pstMem->iSize[i]);
            pstMem->ionBuffer[i] = -1;
            freeJpegMemory(pstMem, iMemoryNum);
            return ERROR_MEM_ALLOC_FAIL;
        }

        pstMem->pcBuf[i] = (char *)ion_map(pstMem->ionBuffer[i], \
                                                            pstMem->iSize[i], 0);
        if ((pstMem->pcBuf[i] == (char *)MAP_FAILED) || (pstMem->pcBuf[i] == NULL)) {
            JPEG_ERROR_LOG("[%s]src ion map failed(%d)\n", __func__, pstMem->iSize[i]);
            pstMem->pcBuf[i] = (char *)MAP_FAILED;
            freeJpegMemory(pstMem, iMemoryNum);
            return ERROR_MEM_ALLOC_FAIL;
        }
    }

    return ERROR_NONE;
}

void ExynosJpegEncoderForCamera::freeJpegMemory(struct stJpegMem *pstMem, int iMemoryNum)
{
    int i =0 ;
    if (pstMem->ionClient == 0) {
        return;
    }


    for (i=0;i<iMemoryNum;i++) {
        if (pstMem->ionBuffer[i] != -1) {
            if (pstMem->pcBuf[i] != (char *)MAP_FAILED) {
                ion_unmap(pstMem->pcBuf[i], pstMem->iSize[i]);
            }
            ion_free(pstMem->ionBuffer[i]);
        }
        pstMem->ionBuffer[i] = -1;
        pstMem->pcBuf[i] = (char *)MAP_FAILED;
        pstMem->iSize[i] = 0;
    }
}

void ExynosJpegEncoderForCamera::initJpegMemory(struct stJpegMem *pstMem, int iMemoryNum)
{
    int i =0 ;
    for (i=0;i<iMemoryNum;i++) {
        pstMem->pcBuf[i] = (char *)MAP_FAILED;
        pstMem->ionBuffer[i] = -1;
        pstMem->iSize[i] = 0;
    }
    pstMem->ionClient = 0;
}