diff options
Diffstat (limited to 'guest/hals/camera/fake-pipeline2/JpegCompressor.cpp')
-rw-r--r-- | guest/hals/camera/fake-pipeline2/JpegCompressor.cpp | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/guest/hals/camera/fake-pipeline2/JpegCompressor.cpp b/guest/hals/camera/fake-pipeline2/JpegCompressor.cpp new file mode 100644 index 000000000..22c5d3ae3 --- /dev/null +++ b/guest/hals/camera/fake-pipeline2/JpegCompressor.cpp @@ -0,0 +1,286 @@ +/* + * 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 "EmulatedCamera2_JpegCompressor" + +#include <utils/Log.h> + +#include "../EmulatedFakeCamera2.h" +#include "../EmulatedFakeCamera3.h" +#include "JpegCompressor.h" + +namespace android { + +JpegCompressor::JpegCompressor() + : Thread(false), + mIsBusy(false), + mSynchronous(false), + mBuffers(NULL), + mListener(NULL), + mFoundAux(false) {} + +JpegCompressor::~JpegCompressor() { Mutex::Autolock lock(mMutex); } + +status_t JpegCompressor::reserve() { + Mutex::Autolock busyLock(mBusyMutex); + if (mIsBusy) { + ALOGE("%s: Already processing a buffer!", __FUNCTION__); + return INVALID_OPERATION; + } + mIsBusy = true; + return OK; +} + +status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener) { + if (listener == NULL) { + ALOGE("%s: NULL listener not allowed!", __FUNCTION__); + return BAD_VALUE; + } + ALOGV("%s: Starting JPEG compression thread", __FUNCTION__); + Mutex::Autolock lock(mMutex); + { + Mutex::Autolock busyLock(mBusyMutex); + + if (!mIsBusy) { + ALOGE("Called start without reserve() first!"); + return INVALID_OPERATION; + } + mSynchronous = false; + mBuffers = buffers; + mListener = listener; + } + + status_t res; + res = run("EmulatedFakeCamera2::JpegCompressor"); + if (res != OK) { + ALOGE("%s: Unable to start up compression thread: %s (%d)", __FUNCTION__, + strerror(-res), res); + delete mBuffers; + } + return res; +} + +status_t JpegCompressor::compressSynchronous(Buffers *buffers) { + status_t res; + + Mutex::Autolock lock(mMutex); + { + Mutex::Autolock busyLock(mBusyMutex); + + if (mIsBusy) { + ALOGE("%s: Already processing a buffer!", __FUNCTION__); + return INVALID_OPERATION; + } + + mIsBusy = true; + mSynchronous = true; + mBuffers = buffers; + } + + res = compress(); + + cleanUp(); + + return res; +} + +status_t JpegCompressor::cancel() { + requestExitAndWait(); + return OK; +} + +status_t JpegCompressor::readyToRun() { return OK; } + +bool JpegCompressor::threadLoop() { + status_t res; + ALOGV("%s: Starting compression thread", __FUNCTION__); + + res = compress(); + + mListener->onJpegDone(mJpegBuffer, res == OK); + + cleanUp(); + + return false; +} + +status_t JpegCompressor::compress() { + // Find source and target buffers. Assumes only one buffer matches + // each condition! + ALOGV("%s: Compressing start", __FUNCTION__); + for (size_t i = 0; i < mBuffers->size(); i++) { + const StreamBuffer &b = (*mBuffers)[i]; + if (b.format == HAL_PIXEL_FORMAT_BLOB) { + mJpegBuffer = b; + mFoundJpeg = true; + } else if (b.streamId <= 0) { + mAuxBuffer = b; + mFoundAux = true; + } + if (mFoundJpeg && mFoundAux) break; + } + if (!mFoundJpeg || !mFoundAux) { + ALOGE("%s: Unable to find buffers for JPEG source/destination", + __FUNCTION__); + return BAD_VALUE; + } + + // 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 NO_INIT; + + // 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 = 3; + mCInfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&mCInfo); + if (checkError("Error configuring defaults")) return NO_INIT; + + // Do compression + + jpeg_start_compress(&mCInfo, TRUE); + if (checkError("Error starting compression")) return NO_INIT; + + 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.img + (i + mCInfo.next_scanline) * rowStride); + } + jpeg_write_scanlines(&mCInfo, chunk, kChunkSize); + if (checkError("Error while compressing")) return NO_INIT; + if (exitPending()) { + ALOGV("%s: Cancel called, exiting early", __FUNCTION__); + return TIMED_OUT; + } + } + + jpeg_finish_compress(&mCInfo); + if (checkError("Error while finishing compression")) return NO_INIT; + + // All done + ALOGV("%s: Compressing done", __FUNCTION__); + + return OK; +} + +bool JpegCompressor::isBusy() { + Mutex::Autolock busyLock(mBusyMutex); + return mIsBusy; +} + +bool JpegCompressor::isStreamInUse(uint32_t id) { + Mutex::Autolock lock(mBusyMutex); + + if (mBuffers && 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) { + Mutex::Autolock lock(mBusyMutex); + while (mIsBusy) { + status_t res = mDone.waitRelative(mBusyMutex, timeout); + if (res != OK) return false; + } + return true; +} + +bool JpegCompressor::checkError(const char *msg) { + if (mJpegErrorInfo) { + char errBuffer[JMSG_LENGTH_MAX]; + mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer); + ALOGE("%s: %s: %s", __FUNCTION__, msg, errBuffer); + mJpegErrorInfo = NULL; + return true; + } + return false; +} + +void JpegCompressor::cleanUp() { + jpeg_destroy_compress(&mCInfo); + Mutex::Autolock lock(mBusyMutex); + + if (mFoundAux) { + if (mAuxBuffer.streamId == 0) { + delete[] mAuxBuffer.img; + } else if (!mSynchronous) { + mListener->onJpegInputDone(mAuxBuffer); + } + } + if (!mSynchronous) { + delete mBuffers; + } + + mBuffers = NULL; + + mIsBusy = false; + mDone.signal(); +} + +void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) { + JpegError *error = static_cast<JpegError *>(cinfo->err); + error->parent->mJpegErrorInfo = cinfo; +} + +void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) { + JpegDestination *dest = static_cast<JpegDestination *>(cinfo->dest); + ALOGV("%s: Setting destination to %p, size %zu", __FUNCTION__, + dest->parent->mJpegBuffer.img, kMaxJpegSize); + dest->next_output_byte = (JOCTET *)(dest->parent->mJpegBuffer.img); + dest->free_in_buffer = kMaxJpegSize; +} + +boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr /*cinfo*/) { + ALOGE("%s: JPEG destination buffer overflow!", __FUNCTION__); + return true; +} + +void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) { + ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer", __FUNCTION__, + cinfo->dest->free_in_buffer); +} + +JpegCompressor::JpegListener::~JpegListener() {} + +} // namespace android |