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