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 <log/log.h> 21 22 #include "gralloc_cb.h" 23 #include "JpegCompressor.h" 24 #include "../EmulatedFakeCamera2.h" 25 #include "../EmulatedFakeCamera3.h" 26 #include "../Exif.h" 27 #include "../Thumbnail.h" 28 #include "hardware/camera3.h" 29 30 namespace android { 31 32 JpegCompressor::JpegCompressor(): 33 Thread(false), 34 mIsBusy(false), 35 mSynchronous(false), 36 mBuffers(NULL), 37 mListener(NULL) { 38 } 39 40 JpegCompressor::~JpegCompressor() { 41 Mutex::Autolock lock(mMutex); 42 } 43 44 status_t JpegCompressor::reserve() { 45 Mutex::Autolock busyLock(mBusyMutex); 46 if (mIsBusy) { 47 ALOGE("%s: Already processing a buffer!", __FUNCTION__); 48 return INVALID_OPERATION; 49 } 50 mIsBusy = true; 51 return OK; 52 } 53 54 status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener, CameraMetadata* settings) { 55 if (listener == NULL) { 56 ALOGE("%s: NULL listener not allowed!", __FUNCTION__); 57 return BAD_VALUE; 58 } 59 Mutex::Autolock lock(mMutex); 60 { 61 Mutex::Autolock busyLock(mBusyMutex); 62 63 if (!mIsBusy) { 64 ALOGE("Called start without reserve() first!"); 65 return INVALID_OPERATION; 66 } 67 mSynchronous = false; 68 mBuffers = buffers; 69 mListener = listener; 70 if (settings) { 71 mSettings = *settings; 72 } 73 } 74 75 status_t res; 76 res = run("EmulatedFakeCamera2::JpegCompressor"); 77 if (res != OK) { 78 ALOGE("%s: Unable to start up compression thread: %s (%d)", 79 __FUNCTION__, strerror(-res), res); 80 delete mBuffers; 81 } 82 return res; 83 } 84 85 status_t JpegCompressor::compressSynchronous(Buffers *buffers) { 86 status_t res; 87 88 Mutex::Autolock lock(mMutex); 89 { 90 Mutex::Autolock busyLock(mBusyMutex); 91 92 if (mIsBusy) { 93 ALOGE("%s: Already processing a buffer!", __FUNCTION__); 94 return INVALID_OPERATION; 95 } 96 97 mIsBusy = true; 98 mSynchronous = true; 99 mBuffers = buffers; 100 } 101 102 res = compress(); 103 104 cleanUp(); 105 106 return res; 107 } 108 109 status_t JpegCompressor::cancel() { 110 requestExitAndWait(); 111 return OK; 112 } 113 114 status_t JpegCompressor::readyToRun() { 115 return OK; 116 } 117 118 bool JpegCompressor::threadLoop() { 119 status_t res; 120 ALOGV("%s: Starting compression thread", __FUNCTION__); 121 122 res = compress(); 123 124 mListener->onJpegDone(mJpegBuffer, res == OK); 125 126 cleanUp(); 127 128 return false; 129 } 130 131 status_t JpegCompressor::compress() { 132 // Find source and target buffers. Assumes only one buffer matches 133 // each condition! 134 bool mFoundAux = false; 135 int thumbWidth = 0, thumbHeight = 0; 136 unsigned char thumbJpegQuality = 90; 137 unsigned char jpegQuality = 90; 138 camera_metadata_entry_t entry; 139 140 for (size_t i = 0; i < mBuffers->size(); i++) { 141 const StreamBuffer &b = (*mBuffers)[i]; 142 if (b.format == HAL_PIXEL_FORMAT_BLOB) { 143 mJpegBuffer = b; 144 mFoundJpeg = true; 145 } else if (b.streamId <= 0) { 146 mAuxBuffer = b; 147 mFoundAux = true; 148 } 149 if (mFoundJpeg && mFoundAux) break; 150 } 151 if (!mFoundJpeg || !mFoundAux) { 152 ALOGE("%s: Unable to find buffers for JPEG source/destination", 153 __FUNCTION__); 154 return BAD_VALUE; 155 } 156 157 // Create EXIF data and compress thumbnail 158 ExifData* exifData = createExifData(mSettings, mAuxBuffer.width, mAuxBuffer.height); 159 entry = mSettings.find(ANDROID_JPEG_THUMBNAIL_SIZE); 160 if (entry.count > 0) { 161 thumbWidth = entry.data.i32[0]; 162 thumbHeight = entry.data.i32[1]; 163 } 164 entry = mSettings.find(ANDROID_JPEG_THUMBNAIL_QUALITY); 165 if (entry.count > 0) { 166 thumbJpegQuality = entry.data.u8[0]; 167 } 168 if (thumbWidth > 0 && thumbHeight > 0) { 169 createThumbnail(static_cast<const unsigned char*>(mAuxBuffer.img), 170 mAuxBuffer.width, mAuxBuffer.height, 171 thumbWidth, thumbHeight, 172 thumbJpegQuality, exifData); 173 } 174 175 // Compress the image 176 entry = mSettings.find(ANDROID_JPEG_QUALITY); 177 if (entry.count > 0) { 178 jpegQuality = entry.data.u8[0]; 179 } 180 NV21JpegCompressor nV21JpegCompressor; 181 nV21JpegCompressor.compressRawImage((void*)mAuxBuffer.img, 182 mAuxBuffer.width, 183 mAuxBuffer.height, 184 jpegQuality, exifData); 185 nV21JpegCompressor.getCompressedImage((void*)mJpegBuffer.img); 186 187 // Refer to /hardware/libhardware/include/hardware/camera3.h 188 // Transport header for compressed JPEG buffers in output streams. 189 camera3_jpeg_blob_t jpeg_blob; 190 cb_handle_t *cb = (cb_handle_t *)(*mJpegBuffer.buffer); 191 jpeg_blob.jpeg_blob_id = CAMERA3_JPEG_BLOB_ID; 192 jpeg_blob.jpeg_size = nV21JpegCompressor.getCompressedSize(); 193 memcpy(mJpegBuffer.img + cb->width - sizeof(camera3_jpeg_blob_t), 194 &jpeg_blob, sizeof(camera3_jpeg_blob_t)); 195 196 freeExifData(exifData); 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 while (mIsBusy) { 219 status_t res = mDone.waitRelative(mBusyMutex, timeout); 220 if (res != OK) return false; 221 } 222 return true; 223 } 224 225 void JpegCompressor::cleanUp() { 226 Mutex::Autolock lock(mBusyMutex); 227 228 if (mFoundAux) { 229 if (mAuxBuffer.streamId == 0) { 230 delete[] mAuxBuffer.img; 231 } else if (!mSynchronous) { 232 mListener->onJpegInputDone(mAuxBuffer); 233 } 234 } 235 if (!mSynchronous) { 236 delete mBuffers; 237 } 238 239 mBuffers = NULL; 240 241 mIsBusy = false; 242 mDone.signal(); 243 } 244 245 JpegCompressor::JpegListener::~JpegListener() { 246 } 247 248 } // namespace android 249