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 %d", 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. %d bytes left in buffer", 280 __FUNCTION__, cinfo->dest->free_in_buffer); 281 } 282 283 JpegCompressor::JpegListener::~JpegListener() { 284 } 285 286 } // namespace android 287