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(Vector<CpuConsumer::LockedBuffer * > buffers,nsecs_t captureTime)39 status_t JpegCompressor::start(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