aboutsummaryrefslogtreecommitdiff
path: root/guest/hals/camera/fake-pipeline2/JpegCompressor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'guest/hals/camera/fake-pipeline2/JpegCompressor.cpp')
-rw-r--r--guest/hals/camera/fake-pipeline2/JpegCompressor.cpp286
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