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 <utils/Log.h>
     21 
     22 #include "../EmulatedFakeCamera2.h"
     23 #include "../EmulatedFakeCamera3.h"
     24 #include "JpegCompressor.h"
     25 
     26 namespace android {
     27 
     28 JpegCompressor::JpegCompressor()
     29     : Thread(false),
     30       mIsBusy(false),
     31       mSynchronous(false),
     32       mBuffers(NULL),
     33       mListener(NULL) {}
     34 
     35 JpegCompressor::~JpegCompressor() { Mutex::Autolock lock(mMutex); }
     36 
     37 status_t JpegCompressor::reserve() {
     38   Mutex::Autolock busyLock(mBusyMutex);
     39   if (mIsBusy) {
     40     ALOGE("%s: Already processing a buffer!", __FUNCTION__);
     41     return INVALID_OPERATION;
     42   }
     43   mIsBusy = true;
     44   return OK;
     45 }
     46 
     47 status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener) {
     48   if (listener == NULL) {
     49     ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
     50     return BAD_VALUE;
     51   }
     52   ALOGV("%s: Starting JPEG compression thread", __FUNCTION__);
     53   Mutex::Autolock lock(mMutex);
     54   {
     55     Mutex::Autolock busyLock(mBusyMutex);
     56 
     57     if (!mIsBusy) {
     58       ALOGE("Called start without reserve() first!");
     59       return INVALID_OPERATION;
     60     }
     61     mSynchronous = false;
     62     mBuffers = buffers;
     63     mListener = listener;
     64   }
     65 
     66   status_t res;
     67   res = run("EmulatedFakeCamera2::JpegCompressor");
     68   if (res != OK) {
     69     ALOGE("%s: Unable to start up compression thread: %s (%d)", __FUNCTION__,
     70           strerror(-res), res);
     71     delete mBuffers;
     72   }
     73   return res;
     74 }
     75 
     76 status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
     77   status_t res;
     78 
     79   Mutex::Autolock lock(mMutex);
     80   {
     81     Mutex::Autolock busyLock(mBusyMutex);
     82 
     83     if (mIsBusy) {
     84       ALOGE("%s: Already processing a buffer!", __FUNCTION__);
     85       return INVALID_OPERATION;
     86     }
     87 
     88     mIsBusy = true;
     89     mSynchronous = true;
     90     mBuffers = buffers;
     91   }
     92 
     93   res = compress();
     94 
     95   cleanUp();
     96 
     97   return res;
     98 }
     99 
    100 status_t JpegCompressor::cancel() {
    101   requestExitAndWait();
    102   return OK;
    103 }
    104 
    105 status_t JpegCompressor::readyToRun() { return OK; }
    106 
    107 bool JpegCompressor::threadLoop() {
    108   status_t res;
    109   ALOGV("%s: Starting compression thread", __FUNCTION__);
    110 
    111   res = compress();
    112 
    113   mListener->onJpegDone(mJpegBuffer, res == OK);
    114 
    115   cleanUp();
    116 
    117   return false;
    118 }
    119 
    120 status_t JpegCompressor::compress() {
    121   // Find source and target buffers. Assumes only one buffer matches
    122   // each condition!
    123   ALOGV("%s: Compressing start", __FUNCTION__);
    124   bool mFoundAux = false;
    125   for (size_t i = 0; i < mBuffers->size(); i++) {
    126     const StreamBuffer &b = (*mBuffers)[i];
    127     if (b.format == HAL_PIXEL_FORMAT_BLOB) {
    128       mJpegBuffer = b;
    129       mFoundJpeg = true;
    130     } else if (b.streamId <= 0) {
    131       mAuxBuffer = b;
    132       mFoundAux = true;
    133     }
    134     if (mFoundJpeg && mFoundAux) break;
    135   }
    136   if (!mFoundJpeg || !mFoundAux) {
    137     ALOGE("%s: Unable to find buffers for JPEG source/destination",
    138           __FUNCTION__);
    139     return BAD_VALUE;
    140   }
    141 
    142   // Set up error management
    143 
    144   mJpegErrorInfo = NULL;
    145   JpegError error;
    146   error.parent = this;
    147 
    148   mCInfo.err = jpeg_std_error(&error);
    149   mCInfo.err->error_exit = jpegErrorHandler;
    150 
    151   jpeg_create_compress(&mCInfo);
    152   if (checkError("Error initializing compression")) return NO_INIT;
    153 
    154   // Route compressed data straight to output stream buffer
    155 
    156   JpegDestination jpegDestMgr;
    157   jpegDestMgr.parent = this;
    158   jpegDestMgr.init_destination = jpegInitDestination;
    159   jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer;
    160   jpegDestMgr.term_destination = jpegTermDestination;
    161 
    162   mCInfo.dest = &jpegDestMgr;
    163 
    164   // Set up compression parameters
    165 
    166   mCInfo.image_width = mAuxBuffer.width;
    167   mCInfo.image_height = mAuxBuffer.height;
    168   mCInfo.input_components = 3;
    169   mCInfo.in_color_space = JCS_RGB;
    170 
    171   jpeg_set_defaults(&mCInfo);
    172   if (checkError("Error configuring defaults")) return NO_INIT;
    173 
    174   // Do compression
    175 
    176   jpeg_start_compress(&mCInfo, TRUE);
    177   if (checkError("Error starting compression")) return NO_INIT;
    178 
    179   size_t rowStride = mAuxBuffer.stride * 3;
    180   const size_t kChunkSize = 32;
    181   while (mCInfo.next_scanline < mCInfo.image_height) {
    182     JSAMPROW chunk[kChunkSize];
    183     for (size_t i = 0; i < kChunkSize; i++) {
    184       chunk[i] =
    185           (JSAMPROW)(mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride);
    186     }
    187     jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
    188     if (checkError("Error while compressing")) return NO_INIT;
    189     if (exitPending()) {
    190       ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
    191       return TIMED_OUT;
    192     }
    193   }
    194 
    195   jpeg_finish_compress(&mCInfo);
    196   if (checkError("Error while finishing compression")) return NO_INIT;
    197 
    198   // All done
    199   ALOGV("%s: Compressing done", __FUNCTION__);
    200 
    201   return OK;
    202 }
    203 
    204 bool JpegCompressor::isBusy() {
    205   Mutex::Autolock busyLock(mBusyMutex);
    206   return mIsBusy;
    207 }
    208 
    209 bool JpegCompressor::isStreamInUse(uint32_t id) {
    210   Mutex::Autolock lock(mBusyMutex);
    211 
    212   if (mBuffers && mIsBusy) {
    213     for (size_t i = 0; i < mBuffers->size(); i++) {
    214       if ((*mBuffers)[i].streamId == (int)id) return true;
    215     }
    216   }
    217   return false;
    218 }
    219 
    220 bool JpegCompressor::waitForDone(nsecs_t timeout) {
    221   Mutex::Autolock lock(mBusyMutex);
    222   while (mIsBusy) {
    223     status_t res = mDone.waitRelative(mBusyMutex, timeout);
    224     if (res != OK) return false;
    225   }
    226   return true;
    227 }
    228 
    229 bool JpegCompressor::checkError(const char *msg) {
    230   if (mJpegErrorInfo) {
    231     char errBuffer[JMSG_LENGTH_MAX];
    232     mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
    233     ALOGE("%s: %s: %s", __FUNCTION__, msg, errBuffer);
    234     mJpegErrorInfo = NULL;
    235     return true;
    236   }
    237   return false;
    238 }
    239 
    240 void JpegCompressor::cleanUp() {
    241   jpeg_destroy_compress(&mCInfo);
    242   Mutex::Autolock lock(mBusyMutex);
    243 
    244   if (mFoundAux) {
    245     if (mAuxBuffer.streamId == 0) {
    246       delete[] mAuxBuffer.img;
    247     } else if (!mSynchronous) {
    248       mListener->onJpegInputDone(mAuxBuffer);
    249     }
    250   }
    251   if (!mSynchronous) {
    252     delete mBuffers;
    253   }
    254 
    255   mBuffers = NULL;
    256 
    257   mIsBusy = false;
    258   mDone.signal();
    259 }
    260 
    261 void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) {
    262   JpegError *error = static_cast<JpegError *>(cinfo->err);
    263   error->parent->mJpegErrorInfo = cinfo;
    264 }
    265 
    266 void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) {
    267   JpegDestination *dest = static_cast<JpegDestination *>(cinfo->dest);
    268   ALOGV("%s: Setting destination to %p, size %zu", __FUNCTION__,
    269         dest->parent->mJpegBuffer.img, kMaxJpegSize);
    270   dest->next_output_byte = (JOCTET *)(dest->parent->mJpegBuffer.img);
    271   dest->free_in_buffer = kMaxJpegSize;
    272 }
    273 
    274 boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr /*cinfo*/) {
    275   ALOGE("%s: JPEG destination buffer overflow!", __FUNCTION__);
    276   return true;
    277 }
    278 
    279 void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) {
    280   ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer", __FUNCTION__,
    281         cinfo->dest->free_in_buffer);
    282 }
    283 
    284 JpegCompressor::JpegListener::~JpegListener() {}
    285 
    286 }  // namespace android
    287