• 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 #include <ui/GraphicBufferMapper.h>
22 
23 #include "JpegCompressor.h"
24 #include "../EmulatedFakeCamera2.h"
25 
26 namespace android {
27 
JpegCompressor(EmulatedFakeCamera2 * parent)28 JpegCompressor::JpegCompressor(EmulatedFakeCamera2 *parent):
29         Thread(false),
30         mIsBusy(false),
31         mParent(parent),
32         mBuffers(NULL),
33         mCaptureTime(0) {
34 }
35 
~JpegCompressor()36 JpegCompressor::~JpegCompressor() {
37     Mutex::Autolock lock(mMutex);
38 }
39 
start(Buffers * buffers,nsecs_t captureTime)40 status_t JpegCompressor::start(Buffers *buffers,
41         nsecs_t captureTime) {
42     Mutex::Autolock lock(mMutex);
43     {
44         Mutex::Autolock busyLock(mBusyMutex);
45 
46         if (mIsBusy) {
47             ALOGE("%s: Already processing a buffer!", __FUNCTION__);
48             return INVALID_OPERATION;
49         }
50 
51         mIsBusy = true;
52 
53         mBuffers = buffers;
54         mCaptureTime = captureTime;
55     }
56 
57     status_t res;
58     res = run("EmulatedFakeCamera2::JpegCompressor");
59     if (res != OK) {
60         ALOGE("%s: Unable to start up compression thread: %s (%d)",
61                 __FUNCTION__, strerror(-res), res);
62         delete mBuffers;
63     }
64     return res;
65 }
66 
cancel()67 status_t JpegCompressor::cancel() {
68     requestExitAndWait();
69     return OK;
70 }
71 
readyToRun()72 status_t JpegCompressor::readyToRun() {
73     return OK;
74 }
75 
threadLoop()76 bool JpegCompressor::threadLoop() {
77     Mutex::Autolock lock(mMutex);
78     ALOGV("%s: Starting compression thread", __FUNCTION__);
79 
80     // Find source and target buffers. Assumes only one buffer matches
81     // each condition!
82 
83     bool foundJpeg = false, mFoundAux = false;
84     for (size_t i = 0; i < mBuffers->size(); i++) {
85         const StreamBuffer &b = (*mBuffers)[i];
86         if (b.format == HAL_PIXEL_FORMAT_BLOB) {
87             mJpegBuffer = b;
88             mFoundJpeg = true;
89         } else if (b.streamId <= 0) {
90             mAuxBuffer = b;
91             mFoundAux = true;
92         }
93         if (mFoundJpeg && mFoundAux) break;
94     }
95     if (!mFoundJpeg || !mFoundAux) {
96         ALOGE("%s: Unable to find buffers for JPEG source/destination",
97                 __FUNCTION__);
98         cleanUp();
99         return false;
100     }
101 
102     // Set up error management
103 
104     mJpegErrorInfo = NULL;
105     JpegError error;
106     error.parent = this;
107 
108     mCInfo.err = jpeg_std_error(&error);
109     mCInfo.err->error_exit = jpegErrorHandler;
110 
111     jpeg_create_compress(&mCInfo);
112     if (checkError("Error initializing compression")) return false;
113 
114     // Route compressed data straight to output stream buffer
115 
116     JpegDestination jpegDestMgr;
117     jpegDestMgr.parent = this;
118     jpegDestMgr.init_destination = jpegInitDestination;
119     jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer;
120     jpegDestMgr.term_destination = jpegTermDestination;
121 
122     mCInfo.dest = &jpegDestMgr;
123 
124     // Set up compression parameters
125 
126     mCInfo.image_width = mAuxBuffer.width;
127     mCInfo.image_height = mAuxBuffer.height;
128     mCInfo.input_components = 3;
129     mCInfo.in_color_space = JCS_RGB;
130 
131     jpeg_set_defaults(&mCInfo);
132     if (checkError("Error configuring defaults")) return false;
133 
134     // Do compression
135 
136     jpeg_start_compress(&mCInfo, TRUE);
137     if (checkError("Error starting compression")) return false;
138 
139     size_t rowStride = mAuxBuffer.stride * 3;
140     const size_t kChunkSize = 32;
141     while (mCInfo.next_scanline < mCInfo.image_height) {
142         JSAMPROW chunk[kChunkSize];
143         for (size_t i = 0 ; i < kChunkSize; i++) {
144             chunk[i] = (JSAMPROW)
145                     (mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride);
146         }
147         jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
148         if (checkError("Error while compressing")) return false;
149         if (exitPending()) {
150             ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
151             cleanUp();
152             return false;
153         }
154     }
155 
156     jpeg_finish_compress(&mCInfo);
157     if (checkError("Error while finishing compression")) return false;
158 
159     // Write to JPEG output stream
160 
161     ALOGV("%s: Compression complete, pushing to stream %d", __FUNCTION__,
162           mJpegBuffer.streamId);
163 
164     GraphicBufferMapper::get().unlock(*(mJpegBuffer.buffer));
165     status_t res;
166     const Stream &s = mParent->getStreamInfo(mJpegBuffer.streamId);
167     res = s.ops->enqueue_buffer(s.ops, mCaptureTime, mJpegBuffer.buffer);
168     if (res != OK) {
169         ALOGE("%s: Error queueing compressed image buffer %p: %s (%d)",
170                 __FUNCTION__, mJpegBuffer.buffer, strerror(-res), res);
171         mParent->signalError();
172     }
173 
174     // All done
175 
176     cleanUp();
177 
178     return false;
179 }
180 
isBusy()181 bool JpegCompressor::isBusy() {
182     Mutex::Autolock busyLock(mBusyMutex);
183     return mIsBusy;
184 }
185 
isStreamInUse(uint32_t id)186 bool JpegCompressor::isStreamInUse(uint32_t id) {
187     Mutex::Autolock lock(mBusyMutex);
188 
189     if (mBuffers && mIsBusy) {
190         for (size_t i = 0; i < mBuffers->size(); i++) {
191             if ( (*mBuffers)[i].streamId == (int)id ) return true;
192         }
193     }
194     return false;
195 }
196 
waitForDone(nsecs_t timeout)197 bool JpegCompressor::waitForDone(nsecs_t timeout) {
198     Mutex::Autolock lock(mBusyMutex);
199     status_t res = OK;
200     if (mIsBusy) {
201         res = mDone.waitRelative(mBusyMutex, timeout);
202     }
203     return (res == OK);
204 }
205 
checkError(const char * msg)206 bool JpegCompressor::checkError(const char *msg) {
207     if (mJpegErrorInfo) {
208         char errBuffer[JMSG_LENGTH_MAX];
209         mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
210         ALOGE("%s: %s: %s",
211                 __FUNCTION__, msg, errBuffer);
212         cleanUp();
213         mJpegErrorInfo = NULL;
214         return true;
215     }
216     return false;
217 }
218 
cleanUp()219 void JpegCompressor::cleanUp() {
220     status_t res;
221     jpeg_destroy_compress(&mCInfo);
222     Mutex::Autolock lock(mBusyMutex);
223 
224     if (mFoundAux) {
225         if (mAuxBuffer.streamId == 0) {
226             delete[] mAuxBuffer.img;
227         } else {
228             GraphicBufferMapper::get().unlock(*(mAuxBuffer.buffer));
229             const ReprocessStream &s =
230                     mParent->getReprocessStreamInfo(-mAuxBuffer.streamId);
231             res = s.ops->release_buffer(s.ops, mAuxBuffer.buffer);
232             if (res != OK) {
233                 ALOGE("Error releasing reprocess buffer %p: %s (%d)",
234                         mAuxBuffer.buffer, strerror(-res), res);
235                 mParent->signalError();
236             }
237         }
238     }
239     delete mBuffers;
240     mBuffers = NULL;
241 
242     mIsBusy = false;
243     mDone.signal();
244 }
245 
jpegErrorHandler(j_common_ptr cinfo)246 void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) {
247     JpegError *error = static_cast<JpegError*>(cinfo->err);
248     error->parent->mJpegErrorInfo = cinfo;
249 }
250 
jpegInitDestination(j_compress_ptr cinfo)251 void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) {
252     JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest);
253     ALOGV("%s: Setting destination to %p, size %d",
254             __FUNCTION__, dest->parent->mJpegBuffer.img, kMaxJpegSize);
255     dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer.img);
256     dest->free_in_buffer = kMaxJpegSize;
257 }
258 
jpegEmptyOutputBuffer(j_compress_ptr cinfo)259 boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr cinfo) {
260     ALOGE("%s: JPEG destination buffer overflow!",
261             __FUNCTION__);
262     return true;
263 }
264 
jpegTermDestination(j_compress_ptr cinfo)265 void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) {
266     ALOGV("%s: Done writing JPEG data. %d bytes left in buffer",
267             __FUNCTION__, cinfo->dest->free_in_buffer);
268 }
269 
270 } // namespace android
271