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 "Camera2-JpegCompressor"
19 
20 #include <utils/Log.h>
21 #include <ui/GraphicBufferMapper.h>
22 
23 #include "JpegCompressor.h"
24 
25 namespace android {
26 namespace camera2 {
27 
JpegCompressor()28 JpegCompressor::JpegCompressor():
29         Thread(false),
30         mIsBusy(false),
31         mCaptureTime(0) {
32 }
33 
~JpegCompressor()34 JpegCompressor::~JpegCompressor() {
35     ALOGV("%s", __FUNCTION__);
36     Mutex::Autolock lock(mMutex);
37 }
38 
start(const Vector<CpuConsumer::LockedBuffer * > & buffers,nsecs_t captureTime)39 status_t JpegCompressor::start(const Vector<CpuConsumer::LockedBuffer*>& buffers,
40         nsecs_t captureTime) {
41     ALOGV("%s", __FUNCTION__);
42     Mutex::Autolock busyLock(mBusyMutex);
43 
44     if (mIsBusy) {
45         ALOGE("%s: Already processing a buffer!", __FUNCTION__);
46         return INVALID_OPERATION;
47     }
48 
49     mIsBusy = true;
50 
51     mBuffers = buffers;
52     mCaptureTime = captureTime;
53 
54     status_t res;
55     res = run("JpegCompressor");
56     if (res != OK) {
57         ALOGE("%s: Unable to start up compression thread: %s (%d)",
58                 __FUNCTION__, strerror(-res), res);
59         //delete mBuffers;  // necessary?
60     }
61     return res;
62 }
63 
cancel()64 status_t JpegCompressor::cancel() {
65     ALOGV("%s", __FUNCTION__);
66     requestExitAndWait();
67     return OK;
68 }
69 
readyToRun()70 status_t JpegCompressor::readyToRun() {
71     ALOGV("%s", __FUNCTION__);
72     return OK;
73 }
74 
threadLoop()75 bool JpegCompressor::threadLoop() {
76     ALOGV("%s", __FUNCTION__);
77 
78     mAuxBuffer = mBuffers[0];    // input
79     mJpegBuffer = mBuffers[1];    // output
80 
81     // Set up error management
82     mJpegErrorInfo = NULL;
83     JpegError error;
84     error.parent = this;
85 
86     mCInfo.err = jpeg_std_error(&error);
87     mCInfo.err->error_exit = jpegErrorHandler;
88 
89     jpeg_create_compress(&mCInfo);
90     if (checkError("Error initializing compression")) return false;
91 
92     // Route compressed data straight to output stream buffer
93     JpegDestination jpegDestMgr;
94     jpegDestMgr.parent = this;
95     jpegDestMgr.init_destination = jpegInitDestination;
96     jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer;
97     jpegDestMgr.term_destination = jpegTermDestination;
98 
99     mCInfo.dest = &jpegDestMgr;
100 
101     // Set up compression parameters
102     mCInfo.image_width = mAuxBuffer->width;
103     mCInfo.image_height = mAuxBuffer->height;
104     mCInfo.input_components = 1; // 3;
105     mCInfo.in_color_space = JCS_GRAYSCALE; // JCS_RGB
106 
107     ALOGV("%s: image_width = %d, image_height = %d", __FUNCTION__, mCInfo.image_width, mCInfo.image_height);
108 
109     jpeg_set_defaults(&mCInfo);
110     if (checkError("Error configuring defaults")) return false;
111 
112     // Do compression
113     jpeg_start_compress(&mCInfo, TRUE);
114     if (checkError("Error starting compression")) return false;
115 
116     size_t rowStride = mAuxBuffer->stride;// * 3;
117     const size_t kChunkSize = 32;
118     while (mCInfo.next_scanline < mCInfo.image_height) {
119         JSAMPROW chunk[kChunkSize];
120         for (size_t i = 0 ; i < kChunkSize; i++) {
121             chunk[i] = (JSAMPROW)
122                     (mAuxBuffer->data + (i + mCInfo.next_scanline) * rowStride);
123         }
124         jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
125         if (checkError("Error while compressing")) return false;
126         if (exitPending()) {
127             ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
128             cleanUp();
129             return false;
130         }
131     }
132 
133     jpeg_finish_compress(&mCInfo);
134     if (checkError("Error while finishing compression")) return false;
135 
136     cleanUp();
137     return false;
138 }
139 
isBusy()140 bool JpegCompressor::isBusy() {
141     ALOGV("%s", __FUNCTION__);
142     Mutex::Autolock busyLock(mBusyMutex);
143     return mIsBusy;
144 }
145 
146 // old function -- TODO: update for new buffer type
isStreamInUse(uint32_t)147 bool JpegCompressor::isStreamInUse(uint32_t /*id*/) {
148     ALOGV("%s", __FUNCTION__);
149     Mutex::Autolock lock(mBusyMutex);
150 
151     if (mBuffers.size() && mIsBusy) {
152         for (size_t i = 0; i < mBuffers.size(); i++) {
153 //            if ( mBuffers[i].streamId == (int)id ) return true;
154         }
155     }
156     return false;
157 }
158 
waitForDone(nsecs_t timeout)159 bool JpegCompressor::waitForDone(nsecs_t timeout) {
160     ALOGV("%s", __FUNCTION__);
161     Mutex::Autolock lock(mBusyMutex);
162     status_t res = OK;
163     if (mIsBusy) {
164         res = mDone.waitRelative(mBusyMutex, timeout);
165     }
166     return (res == OK);
167 }
168 
checkError(const char * msg)169 bool JpegCompressor::checkError(const char *msg) {
170     ALOGV("%s", __FUNCTION__);
171     if (mJpegErrorInfo) {
172         char errBuffer[JMSG_LENGTH_MAX];
173         mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
174         ALOGE("%s: %s: %s",
175                 __FUNCTION__, msg, errBuffer);
176         cleanUp();
177         mJpegErrorInfo = NULL;
178         return true;
179     }
180     return false;
181 }
182 
cleanUp()183 void JpegCompressor::cleanUp() {
184     ALOGV("%s", __FUNCTION__);
185     jpeg_destroy_compress(&mCInfo);
186     Mutex::Autolock lock(mBusyMutex);
187     mIsBusy = false;
188     mDone.signal();
189 }
190 
jpegErrorHandler(j_common_ptr cinfo)191 void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) {
192     ALOGV("%s", __FUNCTION__);
193     JpegError *error = static_cast<JpegError*>(cinfo->err);
194     error->parent->mJpegErrorInfo = cinfo;
195 }
196 
jpegInitDestination(j_compress_ptr cinfo)197 void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) {
198     ALOGV("%s", __FUNCTION__);
199     JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest);
200     ALOGV("%s: Setting destination to %p, size %zu",
201             __FUNCTION__, dest->parent->mJpegBuffer->data, kMaxJpegSize);
202     dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer->data);
203     dest->free_in_buffer = kMaxJpegSize;
204 }
205 
jpegEmptyOutputBuffer(j_compress_ptr)206 boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr /*cinfo*/) {
207     ALOGV("%s", __FUNCTION__);
208     ALOGE("%s: JPEG destination buffer overflow!",
209             __FUNCTION__);
210     return true;
211 }
212 
jpegTermDestination(j_compress_ptr cinfo)213 void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) {
214     (void) cinfo; // TODO: clean up
215     ALOGV("%s", __FUNCTION__);
216     ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer",
217             __FUNCTION__, cinfo->dest->free_in_buffer);
218 }
219 
220 }; // namespace camera2
221 }; // namespace android
222