• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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