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