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 "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
JpegCompressor()32 JpegCompressor::JpegCompressor():
33 Thread(false),
34 mIsBusy(false),
35 mSynchronous(false),
36 mBuffers(NULL),
37 mListener(NULL) {
38 }
39
~JpegCompressor()40 JpegCompressor::~JpegCompressor() {
41 Mutex::Autolock lock(mMutex);
42 }
43
reserve()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
start(Buffers * buffers,JpegListener * listener,CameraMetadata * settings)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
compressSynchronous(Buffers * buffers)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
cancel()109 status_t JpegCompressor::cancel() {
110 requestExitAndWait();
111 return OK;
112 }
113
readyToRun()114 status_t JpegCompressor::readyToRun() {
115 return OK;
116 }
117
threadLoop()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
compress()131 status_t JpegCompressor::compress() {
132 // Find source and target buffers. Assumes only one buffer matches
133 // each condition!
134 bool foundJpeg = false, 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
isBusy()200 bool JpegCompressor::isBusy() {
201 Mutex::Autolock busyLock(mBusyMutex);
202 return mIsBusy;
203 }
204
isStreamInUse(uint32_t id)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
waitForDone(nsecs_t timeout)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
cleanUp()225 void JpegCompressor::cleanUp() {
226 status_t res;
227 Mutex::Autolock lock(mBusyMutex);
228
229 if (mFoundAux) {
230 if (mAuxBuffer.streamId == 0) {
231 delete[] mAuxBuffer.img;
232 } else if (!mSynchronous) {
233 mListener->onJpegInputDone(mAuxBuffer);
234 }
235 }
236 if (!mSynchronous) {
237 delete mBuffers;
238 }
239
240 mBuffers = NULL;
241
242 mIsBusy = false;
243 mDone.signal();
244 }
245
~JpegListener()246 JpegCompressor::JpegListener::~JpegListener() {
247 }
248
249 } // namespace android
250