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