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
JpegCompressor()28 JpegCompressor::JpegCompressor()
29 : Thread(false),
30 mIsBusy(false),
31 mSynchronous(false),
32 mBuffers(NULL),
33 mListener(NULL),
34 mFoundAux(false) {}
35
~JpegCompressor()36 JpegCompressor::~JpegCompressor() { Mutex::Autolock lock(mMutex); }
37
reserve()38 status_t JpegCompressor::reserve() {
39 Mutex::Autolock busyLock(mBusyMutex);
40 if (mIsBusy) {
41 ALOGE("%s: Already processing a buffer!", __FUNCTION__);
42 return INVALID_OPERATION;
43 }
44 mIsBusy = true;
45 return OK;
46 }
47
start(Buffers * buffers,JpegListener * listener)48 status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener) {
49 if (listener == NULL) {
50 ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
51 return BAD_VALUE;
52 }
53 ALOGV("%s: Starting JPEG compression thread", __FUNCTION__);
54 Mutex::Autolock lock(mMutex);
55 {
56 Mutex::Autolock busyLock(mBusyMutex);
57
58 if (!mIsBusy) {
59 ALOGE("Called start without reserve() first!");
60 return INVALID_OPERATION;
61 }
62 mSynchronous = false;
63 mBuffers = buffers;
64 mListener = listener;
65 }
66
67 status_t res;
68 res = run("EmulatedFakeCamera2::JpegCompressor");
69 if (res != OK) {
70 ALOGE("%s: Unable to start up compression thread: %s (%d)", __FUNCTION__,
71 strerror(-res), res);
72 delete mBuffers;
73 }
74 return res;
75 }
76
compressSynchronous(Buffers * buffers)77 status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
78 status_t res;
79
80 Mutex::Autolock lock(mMutex);
81 {
82 Mutex::Autolock busyLock(mBusyMutex);
83
84 if (mIsBusy) {
85 ALOGE("%s: Already processing a buffer!", __FUNCTION__);
86 return INVALID_OPERATION;
87 }
88
89 mIsBusy = true;
90 mSynchronous = true;
91 mBuffers = buffers;
92 }
93
94 res = compress();
95
96 cleanUp();
97
98 return res;
99 }
100
cancel()101 status_t JpegCompressor::cancel() {
102 requestExitAndWait();
103 return OK;
104 }
105
readyToRun()106 status_t JpegCompressor::readyToRun() { return OK; }
107
threadLoop()108 bool JpegCompressor::threadLoop() {
109 status_t res;
110 ALOGV("%s: Starting compression thread", __FUNCTION__);
111
112 res = compress();
113
114 mListener->onJpegDone(mJpegBuffer, res == OK);
115
116 cleanUp();
117
118 return false;
119 }
120
compress()121 status_t JpegCompressor::compress() {
122 // Find source and target buffers. Assumes only one buffer matches
123 // each condition!
124 ALOGV("%s: Compressing start", __FUNCTION__);
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
isBusy()204 bool JpegCompressor::isBusy() {
205 Mutex::Autolock busyLock(mBusyMutex);
206 return mIsBusy;
207 }
208
isStreamInUse(uint32_t id)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
waitForDone(nsecs_t timeout)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
checkError(const char * msg)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
cleanUp()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
jpegErrorHandler(j_common_ptr cinfo)261 void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) {
262 JpegError *error = static_cast<JpegError *>(cinfo->err);
263 error->parent->mJpegErrorInfo = cinfo;
264 }
265
jpegInitDestination(j_compress_ptr cinfo)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
jpegEmptyOutputBuffer(j_compress_ptr)274 boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr /*cinfo*/) {
275 ALOGE("%s: JPEG destination buffer overflow!", __FUNCTION__);
276 return true;
277 }
278
jpegTermDestination(j_compress_ptr cinfo)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
~JpegListener()284 JpegCompressor::JpegListener::~JpegListener() {}
285
286 } // namespace android
287