/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "Camera2-JpegCompressor" #include <utils/Log.h> #include <ui/GraphicBufferMapper.h> #include "JpegCompressor.h" namespace android { namespace camera2 { JpegCompressor::JpegCompressor(): Thread(false), mIsBusy(false), mCaptureTime(0) { } JpegCompressor::~JpegCompressor() { ALOGV("%s", __FUNCTION__); Mutex::Autolock lock(mMutex); } status_t JpegCompressor::start(Vector<CpuConsumer::LockedBuffer*> buffers, nsecs_t captureTime) { ALOGV("%s", __FUNCTION__); Mutex::Autolock busyLock(mBusyMutex); if (mIsBusy) { ALOGE("%s: Already processing a buffer!", __FUNCTION__); return INVALID_OPERATION; } mIsBusy = true; mBuffers = buffers; mCaptureTime = captureTime; status_t res; res = run("JpegCompressor"); if (res != OK) { ALOGE("%s: Unable to start up compression thread: %s (%d)", __FUNCTION__, strerror(-res), res); //delete mBuffers; // necessary? } return res; } status_t JpegCompressor::cancel() { ALOGV("%s", __FUNCTION__); requestExitAndWait(); return OK; } status_t JpegCompressor::readyToRun() { ALOGV("%s", __FUNCTION__); return OK; } bool JpegCompressor::threadLoop() { ALOGV("%s", __FUNCTION__); mAuxBuffer = mBuffers[0]; // input mJpegBuffer = mBuffers[1]; // output // Set up error management mJpegErrorInfo = NULL; JpegError error; error.parent = this; mCInfo.err = jpeg_std_error(&error); mCInfo.err->error_exit = jpegErrorHandler; jpeg_create_compress(&mCInfo); if (checkError("Error initializing compression")) return false; // Route compressed data straight to output stream buffer JpegDestination jpegDestMgr; jpegDestMgr.parent = this; jpegDestMgr.init_destination = jpegInitDestination; jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer; jpegDestMgr.term_destination = jpegTermDestination; mCInfo.dest = &jpegDestMgr; // Set up compression parameters mCInfo.image_width = mAuxBuffer->width; mCInfo.image_height = mAuxBuffer->height; mCInfo.input_components = 1; // 3; mCInfo.in_color_space = JCS_GRAYSCALE; // JCS_RGB ALOGV("%s: image_width = %d, image_height = %d", __FUNCTION__, mCInfo.image_width, mCInfo.image_height); jpeg_set_defaults(&mCInfo); if (checkError("Error configuring defaults")) return false; // Do compression jpeg_start_compress(&mCInfo, TRUE); if (checkError("Error starting compression")) return false; size_t rowStride = mAuxBuffer->stride;// * 3; const size_t kChunkSize = 32; while (mCInfo.next_scanline < mCInfo.image_height) { JSAMPROW chunk[kChunkSize]; for (size_t i = 0 ; i < kChunkSize; i++) { chunk[i] = (JSAMPROW) (mAuxBuffer->data + (i + mCInfo.next_scanline) * rowStride); } jpeg_write_scanlines(&mCInfo, chunk, kChunkSize); if (checkError("Error while compressing")) return false; if (exitPending()) { ALOGV("%s: Cancel called, exiting early", __FUNCTION__); cleanUp(); return false; } } jpeg_finish_compress(&mCInfo); if (checkError("Error while finishing compression")) return false; cleanUp(); return false; } bool JpegCompressor::isBusy() { ALOGV("%s", __FUNCTION__); Mutex::Autolock busyLock(mBusyMutex); return mIsBusy; } // old function -- TODO: update for new buffer type bool JpegCompressor::isStreamInUse(uint32_t /*id*/) { ALOGV("%s", __FUNCTION__); Mutex::Autolock lock(mBusyMutex); if (mBuffers.size() && mIsBusy) { for (size_t i = 0; i < mBuffers.size(); i++) { // if ( mBuffers[i].streamId == (int)id ) return true; } } return false; } bool JpegCompressor::waitForDone(nsecs_t timeout) { ALOGV("%s", __FUNCTION__); Mutex::Autolock lock(mBusyMutex); status_t res = OK; if (mIsBusy) { res = mDone.waitRelative(mBusyMutex, timeout); } return (res == OK); } bool JpegCompressor::checkError(const char *msg) { ALOGV("%s", __FUNCTION__); if (mJpegErrorInfo) { char errBuffer[JMSG_LENGTH_MAX]; mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer); ALOGE("%s: %s: %s", __FUNCTION__, msg, errBuffer); cleanUp(); mJpegErrorInfo = NULL; return true; } return false; } void JpegCompressor::cleanUp() { ALOGV("%s", __FUNCTION__); jpeg_destroy_compress(&mCInfo); Mutex::Autolock lock(mBusyMutex); mIsBusy = false; mDone.signal(); } void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) { ALOGV("%s", __FUNCTION__); JpegError *error = static_cast<JpegError*>(cinfo->err); error->parent->mJpegErrorInfo = cinfo; } void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) { ALOGV("%s", __FUNCTION__); JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest); ALOGV("%s: Setting destination to %p, size %d", __FUNCTION__, dest->parent->mJpegBuffer->data, kMaxJpegSize); dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer->data); dest->free_in_buffer = kMaxJpegSize; } boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr /*cinfo*/) { ALOGV("%s", __FUNCTION__); ALOGE("%s: JPEG destination buffer overflow!", __FUNCTION__); return true; } void JpegCompressor::jpegTermDestination(j_compress_ptr /*cinfo*/) { ALOGV("%s", __FUNCTION__); ALOGV("%s: Done writing JPEG data. %d bytes left in buffer", __FUNCTION__, cinfo->dest->free_in_buffer); } }; // namespace camera2 }; // namespace android