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