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