Home | History | Annotate | Download | only in fake-pipeline2
      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 "EmulatedCamera2_JpegCompressor"
     19 
     20 #include <log/log.h>
     21 
     22 #include "gralloc_cb.h"
     23 #include "JpegCompressor.h"
     24 #include "../EmulatedFakeCamera2.h"
     25 #include "../EmulatedFakeCamera3.h"
     26 #include "../Exif.h"
     27 #include "../Thumbnail.h"
     28 #include "hardware/camera3.h"
     29 
     30 namespace android {
     31 
     32 JpegCompressor::JpegCompressor():
     33         Thread(false),
     34         mIsBusy(false),
     35         mSynchronous(false),
     36         mBuffers(NULL),
     37         mListener(NULL) {
     38 }
     39 
     40 JpegCompressor::~JpegCompressor() {
     41     Mutex::Autolock lock(mMutex);
     42 }
     43 
     44 status_t JpegCompressor::reserve() {
     45     Mutex::Autolock busyLock(mBusyMutex);
     46     if (mIsBusy) {
     47         ALOGE("%s: Already processing a buffer!", __FUNCTION__);
     48         return INVALID_OPERATION;
     49     }
     50     mIsBusy = true;
     51     return OK;
     52 }
     53 
     54 status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener, CameraMetadata* settings) {
     55     if (listener == NULL) {
     56         ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
     57         return BAD_VALUE;
     58     }
     59     Mutex::Autolock lock(mMutex);
     60     {
     61         Mutex::Autolock busyLock(mBusyMutex);
     62 
     63         if (!mIsBusy) {
     64             ALOGE("Called start without reserve() first!");
     65             return INVALID_OPERATION;
     66         }
     67         mSynchronous = false;
     68         mBuffers = buffers;
     69         mListener = listener;
     70         if (settings) {
     71               mSettings = *settings;
     72         }
     73     }
     74 
     75     status_t res;
     76     res = run("EmulatedFakeCamera2::JpegCompressor");
     77     if (res != OK) {
     78         ALOGE("%s: Unable to start up compression thread: %s (%d)",
     79                 __FUNCTION__, strerror(-res), res);
     80         delete mBuffers;
     81     }
     82     return res;
     83 }
     84 
     85 status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
     86     status_t res;
     87 
     88     Mutex::Autolock lock(mMutex);
     89     {
     90         Mutex::Autolock busyLock(mBusyMutex);
     91 
     92         if (mIsBusy) {
     93             ALOGE("%s: Already processing a buffer!", __FUNCTION__);
     94             return INVALID_OPERATION;
     95         }
     96 
     97         mIsBusy = true;
     98         mSynchronous = true;
     99         mBuffers = buffers;
    100     }
    101 
    102     res = compress();
    103 
    104     cleanUp();
    105 
    106     return res;
    107 }
    108 
    109 status_t JpegCompressor::cancel() {
    110     requestExitAndWait();
    111     return OK;
    112 }
    113 
    114 status_t JpegCompressor::readyToRun() {
    115     return OK;
    116 }
    117 
    118 bool JpegCompressor::threadLoop() {
    119     status_t res;
    120     ALOGV("%s: Starting compression thread", __FUNCTION__);
    121 
    122     res = compress();
    123 
    124     mListener->onJpegDone(mJpegBuffer, res == OK);
    125 
    126     cleanUp();
    127 
    128     return false;
    129 }
    130 
    131 status_t JpegCompressor::compress() {
    132     // Find source and target buffers. Assumes only one buffer matches
    133     // each condition!
    134     bool mFoundAux = false;
    135     int thumbWidth = 0, thumbHeight = 0;
    136     unsigned char thumbJpegQuality = 90;
    137     unsigned char jpegQuality = 90;
    138     camera_metadata_entry_t entry;
    139 
    140     for (size_t i = 0; i < mBuffers->size(); i++) {
    141         const StreamBuffer &b = (*mBuffers)[i];
    142         if (b.format == HAL_PIXEL_FORMAT_BLOB) {
    143             mJpegBuffer = b;
    144             mFoundJpeg = true;
    145         } else if (b.streamId <= 0) {
    146             mAuxBuffer = b;
    147             mFoundAux = true;
    148         }
    149         if (mFoundJpeg && mFoundAux) break;
    150     }
    151     if (!mFoundJpeg || !mFoundAux) {
    152         ALOGE("%s: Unable to find buffers for JPEG source/destination",
    153                 __FUNCTION__);
    154         return BAD_VALUE;
    155     }
    156 
    157     // Create EXIF data and compress thumbnail
    158     ExifData* exifData = createExifData(mSettings, mAuxBuffer.width, mAuxBuffer.height);
    159     entry = mSettings.find(ANDROID_JPEG_THUMBNAIL_SIZE);
    160     if (entry.count > 0) {
    161         thumbWidth = entry.data.i32[0];
    162         thumbHeight = entry.data.i32[1];
    163     }
    164     entry = mSettings.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
    165     if (entry.count > 0) {
    166         thumbJpegQuality = entry.data.u8[0];
    167     }
    168     if (thumbWidth > 0 && thumbHeight > 0) {
    169         createThumbnail(static_cast<const unsigned char*>(mAuxBuffer.img),
    170                         mAuxBuffer.width, mAuxBuffer.height,
    171                         thumbWidth, thumbHeight,
    172                         thumbJpegQuality, exifData);
    173     }
    174 
    175     // Compress the image
    176     entry = mSettings.find(ANDROID_JPEG_QUALITY);
    177     if (entry.count > 0) {
    178         jpegQuality = entry.data.u8[0];
    179     }
    180     NV21JpegCompressor nV21JpegCompressor;
    181     nV21JpegCompressor.compressRawImage((void*)mAuxBuffer.img,
    182                                          mAuxBuffer.width,
    183                                          mAuxBuffer.height,
    184                                          jpegQuality, exifData);
    185     nV21JpegCompressor.getCompressedImage((void*)mJpegBuffer.img);
    186 
    187     // Refer to /hardware/libhardware/include/hardware/camera3.h
    188     // Transport header for compressed JPEG buffers in output streams.
    189     camera3_jpeg_blob_t jpeg_blob;
    190     cb_handle_t *cb = (cb_handle_t *)(*mJpegBuffer.buffer);
    191     jpeg_blob.jpeg_blob_id = CAMERA3_JPEG_BLOB_ID;
    192     jpeg_blob.jpeg_size = nV21JpegCompressor.getCompressedSize();
    193     memcpy(mJpegBuffer.img + cb->width - sizeof(camera3_jpeg_blob_t),
    194            &jpeg_blob, sizeof(camera3_jpeg_blob_t));
    195 
    196     freeExifData(exifData);
    197     return OK;
    198 }
    199 
    200 bool JpegCompressor::isBusy() {
    201     Mutex::Autolock busyLock(mBusyMutex);
    202     return mIsBusy;
    203 }
    204 
    205 bool JpegCompressor::isStreamInUse(uint32_t id) {
    206     Mutex::Autolock lock(mBusyMutex);
    207 
    208     if (mBuffers && mIsBusy) {
    209         for (size_t i = 0; i < mBuffers->size(); i++) {
    210             if ( (*mBuffers)[i].streamId == (int)id ) return true;
    211         }
    212     }
    213     return false;
    214 }
    215 
    216 bool JpegCompressor::waitForDone(nsecs_t timeout) {
    217     Mutex::Autolock lock(mBusyMutex);
    218     while (mIsBusy) {
    219         status_t res = mDone.waitRelative(mBusyMutex, timeout);
    220         if (res != OK) return false;
    221     }
    222     return true;
    223 }
    224 
    225 void JpegCompressor::cleanUp() {
    226     Mutex::Autolock lock(mBusyMutex);
    227 
    228     if (mFoundAux) {
    229         if (mAuxBuffer.streamId == 0) {
    230             delete[] mAuxBuffer.img;
    231         } else if (!mSynchronous) {
    232             mListener->onJpegInputDone(mAuxBuffer);
    233         }
    234     }
    235     if (!mSynchronous) {
    236         delete mBuffers;
    237     }
    238 
    239     mBuffers = NULL;
    240 
    241     mIsBusy = false;
    242     mDone.signal();
    243 }
    244 
    245 JpegCompressor::JpegListener::~JpegListener() {
    246 }
    247 
    248 } // namespace android
    249