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
~JpegCompressor()35 JpegCompressor::~JpegCompressor() { Mutex::Autolock lock(mMutex); }
36
reserve()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
start(Buffers * buffers,JpegListener * listener)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
compressSynchronous(Buffers * buffers)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
cancel()100 status_t JpegCompressor::cancel() {
101 requestExitAndWait();
102 return OK;
103 }
104
readyToRun()105 status_t JpegCompressor::readyToRun() { return OK; }
106
threadLoop()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
compress()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
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