diff options
author | Eino-Ville Talvala <etalvala@google.com> | 2013-03-22 09:10:39 -0700 |
---|---|---|
committer | Eino-Ville Talvala <etalvala@google.com> | 2013-04-08 16:09:38 -0700 |
commit | 03a28bba9dcf5276cdf64408a648e1dddd2945ba (patch) | |
tree | 834aba89639c8c3f2672b81f775ef7715e4bfd36 | |
parent | 2de81ad1fbadb0b2dd26830f6bb97c19c783969f (diff) | |
download | android_device_generic_goldfish-03a28bba9dcf5276cdf64408a648e1dddd2945ba.tar.gz android_device_generic_goldfish-03a28bba9dcf5276cdf64408a648e1dddd2945ba.tar.bz2 android_device_generic_goldfish-03a28bba9dcf5276cdf64408a648e1dddd2945ba.zip |
Camera3: Support for still capture
- Basic 3A routine with AE precapture support
- JPEG compression
Change-Id: I329bb6d2c5315e13ed657b096b8bb573c14a3738
-rw-r--r-- | camera/EmulatedFakeCamera3.cpp | 325 | ||||
-rw-r--r-- | camera/EmulatedFakeCamera3.h | 95 | ||||
-rw-r--r-- | camera/fake-pipeline2/JpegCompressor.cpp | 93 | ||||
-rw-r--r-- | camera/fake-pipeline2/JpegCompressor.h | 6 |
4 files changed, 471 insertions, 48 deletions
diff --git a/camera/EmulatedFakeCamera3.cpp b/camera/EmulatedFakeCamera3.cpp index 8fd4df0..43ffc44 100644 --- a/camera/EmulatedFakeCamera3.cpp +++ b/camera/EmulatedFakeCamera3.cpp @@ -31,6 +31,7 @@ #include "gralloc_cb.h" #include "fake-pipeline2/Sensor.h" #include "fake-pipeline2/JpegCompressor.h" +#include <cmath> namespace android { @@ -89,6 +90,21 @@ const uint64_t EmulatedFakeCamera3::kAvailableJpegMinDurations[1] = { }; /** + * 3A constants + */ + +// Default exposure and gain targets for different scenarios +const nsecs_t EmulatedFakeCamera3::kNormalExposureTime = 10 * MSEC; +const nsecs_t EmulatedFakeCamera3::kFacePriorityExposureTime = 30 * MSEC; +const int EmulatedFakeCamera3::kNormalSensitivity = 100; +const int EmulatedFakeCamera3::kFacePrioritySensitivity = 400; +const float EmulatedFakeCamera3::kExposureTrackRate = 0.1; +const int EmulatedFakeCamera3::kPrecaptureMinFrames = 10; +const int EmulatedFakeCamera3::kStableAeMaxFrames = 100; +const float EmulatedFakeCamera3::kExposureWanderMin = -2; +const float EmulatedFakeCamera3::kExposureWanderMax = 1; + +/** * Camera device lifecycle methods */ @@ -147,10 +163,26 @@ status_t EmulatedFakeCamera3::connectCamera(hw_device_t** device) { if (res != NO_ERROR) return res; mReadoutThread = new ReadoutThread(this); + mJpegCompressor = new JpegCompressor(NULL); res = mReadoutThread->run("EmuCam3::readoutThread"); if (res != NO_ERROR) return res; + // Initialize fake 3A + + mControlMode = ANDROID_CONTROL_MODE_AUTO; + mFacePriority = false; + mAeMode = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH; + mAfMode = ANDROID_CONTROL_AF_MODE_AUTO; + mAwbMode = ANDROID_CONTROL_AWB_MODE_AUTO; + mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE; + mAfState = ANDROID_CONTROL_AF_STATE_INACTIVE; + mAwbState = ANDROID_CONTROL_AWB_STATE_INACTIVE; + mAfTriggerId = 0; + mAeTriggerId = 0; + mAeCurrentExposureTime = kNormalExposureTime; + mAeCurrentSensitivity = kNormalSensitivity; + return EmulatedCamera3::connectCamera(device); } @@ -754,7 +786,10 @@ status_t EmulatedFakeCamera3::processCaptureRequest( settings = request->settings; } - // TODO: Apply 3A overrides + res = process3A(settings); + if (res != OK) { + return res; + } // TODO: Handle reprocessing @@ -783,12 +818,12 @@ status_t EmulatedFakeCamera3::processCaptureRequest( const cb_handle_t *privBuffer = static_cast<const cb_handle_t*>(*srcBuf.buffer); StreamBuffer destBuf; - destBuf.streamId = 0; - destBuf.width = srcBuf.stream->width; - destBuf.height = srcBuf.stream->height; - destBuf.format = privBuffer->format; // Use real private format - destBuf.stride = srcBuf.stream->width; // TODO: query from gralloc - destBuf.buffer = srcBuf.buffer; + destBuf.streamId = kGenericStreamId; + destBuf.width = srcBuf.stream->width; + destBuf.height = srcBuf.stream->height; + destBuf.format = privBuffer->format; // Use real private format + destBuf.stride = srcBuf.stream->width; // TODO: query from gralloc + destBuf.buffer = srcBuf.buffer; // Wait on fence sp<Fence> bufferAcquireFence = new Fence(srcBuf.acquire_fence); @@ -1198,6 +1233,268 @@ status_t EmulatedFakeCamera3::constructStaticInfo() { return OK; } +status_t EmulatedFakeCamera3::process3A(CameraMetadata &settings) { + /** + * Extract top-level 3A controls + */ + status_t res; + + bool facePriority = false; + + camera_metadata_entry e; + + e = settings.find(ANDROID_CONTROL_MODE); + if (e.count == 0) { + ALOGE("%s: No control mode entry!", __FUNCTION__); + return BAD_VALUE; + } + uint8_t controlMode = e.data.u8[0]; + + e = settings.find(ANDROID_CONTROL_SCENE_MODE); + if (e.count == 0) { + ALOGE("%s: No scene mode entry!", __FUNCTION__); + return BAD_VALUE; + } + uint8_t sceneMode = e.data.u8[0]; + + if (controlMode == ANDROID_CONTROL_MODE_OFF) { + mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE; + mAfState = ANDROID_CONTROL_AF_STATE_INACTIVE; + mAwbState = ANDROID_CONTROL_AWB_STATE_INACTIVE; + update3A(settings); + return OK; + } else if (controlMode == ANDROID_CONTROL_MODE_USE_SCENE_MODE) { + switch(sceneMode) { + case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY: + mFacePriority = true; + break; + default: + ALOGE("%s: Emulator doesn't support scene mode %d", + __FUNCTION__, sceneMode); + return BAD_VALUE; + } + } else { + mFacePriority = false; + } + + // controlMode == AUTO or sceneMode = FACE_PRIORITY + // Process individual 3A controls + + res = doFakeAE(settings); + if (res != OK) return res; + + res = doFakeAF(settings); + if (res != OK) return res; + + res = doFakeAWB(settings); + if (res != OK) return res; + + update3A(settings); + return OK; +} + +status_t EmulatedFakeCamera3::doFakeAE(CameraMetadata &settings) { + camera_metadata_entry e; + + e = settings.find(ANDROID_CONTROL_AE_MODE); + if (e.count == 0) { + ALOGE("%s: No AE mode entry!", __FUNCTION__); + return BAD_VALUE; + } + uint8_t aeMode = e.data.u8[0]; + + switch (aeMode) { + case ANDROID_CONTROL_AE_MODE_OFF: + // AE is OFF + mAeState = ANDROID_CONTROL_AE_STATE_INACTIVE; + return OK; + case ANDROID_CONTROL_AE_MODE_ON: + // OK for AUTO modes + break; + default: + ALOGE("%s: Emulator doesn't support AE mode %d", + __FUNCTION__, aeMode); + return BAD_VALUE; + } + + e = settings.find(ANDROID_CONTROL_AE_LOCK); + if (e.count == 0) { + ALOGE("%s: No AE lock entry!", __FUNCTION__); + return BAD_VALUE; + } + bool aeLocked = (e.data.u8[0] == ANDROID_CONTROL_AE_LOCK_ON); + + e = settings.find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER); + bool precaptureTrigger = false; + if (e.count != 0) { + precaptureTrigger = + (e.data.u8[0] == ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START); + } + + if (precaptureTrigger || mAeState == ANDROID_CONTROL_AE_STATE_PRECAPTURE) { + // Run precapture sequence + if (mAeState != ANDROID_CONTROL_AE_STATE_PRECAPTURE) { + mAeCounter = 0; + mAeTriggerId++; + } + + if (mFacePriority) { + mAeTargetExposureTime = kFacePriorityExposureTime; + } else { + mAeTargetExposureTime = kNormalExposureTime; + } + + if (mAeCounter > kPrecaptureMinFrames && + (mAeTargetExposureTime - mAeCurrentExposureTime) < + mAeTargetExposureTime / 10) { + // Done with precapture + mAeCounter = 0; + mAeState = aeLocked ? ANDROID_CONTROL_AE_STATE_LOCKED : + ANDROID_CONTROL_AE_STATE_CONVERGED; + } else { + // Converge some more + mAeCurrentExposureTime += + (mAeTargetExposureTime - mAeCurrentExposureTime) * + kExposureTrackRate; + mAeCounter++; + mAeState = ANDROID_CONTROL_AE_STATE_PRECAPTURE; + } + + } else if (!aeLocked) { + // Run standard occasional AE scan + switch (mAeState) { + case ANDROID_CONTROL_AE_STATE_CONVERGED: + case ANDROID_CONTROL_AE_STATE_INACTIVE: + mAeCounter++; + if (mAeCounter > kStableAeMaxFrames) { + mAeTargetExposureTime = + mFacePriority ? kFacePriorityExposureTime : + kNormalExposureTime; + float exposureStep = ((double)rand() / RAND_MAX) * + (kExposureWanderMax - kExposureWanderMin) + + kExposureWanderMin; + mAeTargetExposureTime *= std::pow(2, exposureStep); + mAeState = ANDROID_CONTROL_AE_STATE_SEARCHING; + } + break; + case ANDROID_CONTROL_AE_STATE_SEARCHING: + mAeCurrentExposureTime += + (mAeTargetExposureTime - mAeCurrentExposureTime) * + kExposureTrackRate; + if (abs(mAeTargetExposureTime - mAeCurrentExposureTime) < + mAeTargetExposureTime / 10) { + // Close enough + mAeState = ANDROID_CONTROL_AE_STATE_CONVERGED; + mAeCounter = 0; + } + break; + case ANDROID_CONTROL_AE_STATE_LOCKED: + mAeState = ANDROID_CONTROL_AE_STATE_CONVERGED; + mAeCounter = 0; + break; + default: + ALOGE("%s: Emulator in unexpected AE state %d", + __FUNCTION__, mAeState); + return INVALID_OPERATION; + } + } else { + // AE is locked + mAeState = ANDROID_CONTROL_AE_STATE_LOCKED; + } + + return OK; +} + +status_t EmulatedFakeCamera3::doFakeAF(CameraMetadata &settings) { + camera_metadata_entry e; + + e = settings.find(ANDROID_CONTROL_AF_MODE); + if (e.count == 0) { + ALOGE("%s: No AF mode entry!", __FUNCTION__); + return BAD_VALUE; + } + uint8_t afMode = e.data.u8[0]; + + switch (afMode) { + case ANDROID_CONTROL_AF_MODE_OFF: + mAfState = ANDROID_CONTROL_AF_STATE_INACTIVE; + return OK; + case ANDROID_CONTROL_AF_MODE_AUTO: + case ANDROID_CONTROL_AF_MODE_MACRO: + case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO: + case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE: + if (!mFacingBack) { + ALOGE("%s: Front camera doesn't support AF mode %d", + __FUNCTION__, afMode); + return BAD_VALUE; + } + // OK + break; + default: + ALOGE("%s: Emulator doesn't support AF mode %d", + __FUNCTION__, afMode); + return BAD_VALUE; + } + + return OK; +} + +status_t EmulatedFakeCamera3::doFakeAWB(CameraMetadata &settings) { + camera_metadata_entry e; + + e = settings.find(ANDROID_CONTROL_AWB_MODE); + if (e.count == 0) { + ALOGE("%s: No AWB mode entry!", __FUNCTION__); + return BAD_VALUE; + } + uint8_t awbMode = e.data.u8[0]; + + // TODO: Add white balance simulation + + switch (awbMode) { + case ANDROID_CONTROL_AWB_MODE_OFF: + mAwbState = ANDROID_CONTROL_AWB_STATE_INACTIVE; + return OK; + case ANDROID_CONTROL_AWB_MODE_AUTO: + case ANDROID_CONTROL_AWB_MODE_INCANDESCENT: + case ANDROID_CONTROL_AWB_MODE_FLUORESCENT: + case ANDROID_CONTROL_AWB_MODE_DAYLIGHT: + case ANDROID_CONTROL_AWB_MODE_SHADE: + // OK + break; + default: + ALOGE("%s: Emulator doesn't support AWB mode %d", + __FUNCTION__, awbMode); + return BAD_VALUE; + } + + return OK; +} + + +void EmulatedFakeCamera3::update3A(CameraMetadata &settings) { + if (mAeState != ANDROID_CONTROL_AE_STATE_INACTIVE) { + settings.update(ANDROID_SENSOR_EXPOSURE_TIME, + &mAeCurrentExposureTime, 1); + settings.update(ANDROID_SENSOR_SENSITIVITY, + &mAeCurrentSensitivity, 1); + } + + settings.update(ANDROID_CONTROL_AE_STATE, + &mAeState, 1); + settings.update(ANDROID_CONTROL_AF_STATE, + &mAfState, 1); + settings.update(ANDROID_CONTROL_AWB_STATE, + &mAwbState, 1); + /** + * TODO: Trigger IDs need a think-through + */ + settings.update(ANDROID_CONTROL_AE_PRECAPTURE_ID, + &mAeTriggerId, 1); + settings.update(ANDROID_CONTROL_AF_TRIGGER_ID, + &mAfTriggerId, 1); +} + void EmulatedFakeCamera3::signalReadoutIdle() { Mutex::Autolock l(mLock); // Need to chek isIdle again because waiting on mLock may have allowed @@ -1286,6 +1583,20 @@ bool EmulatedFakeCamera3::ReadoutThread::threadLoop() { mParent->mSensor->waitForNewFrame(kWaitPerLoop, &captureTime); if (!gotFrame) return true; + // Check if we need to JPEG encode a buffer + + for (size_t i = 0; i < mCurrentRequest.buffers->size(); i++) { + if ((*mCurrentRequest.buffers)[i].stream->format == + HAL_PIXEL_FORMAT_BLOB) { + res = mParent->mJpegCompressor-> + compressSynchronous(mCurrentRequest.sensorBuffers); + if (res != OK) { + ALOGE("%s: Error compressing output buffer: %s (%d)", + __FUNCTION__, strerror(-res), res); + } + } + } + // Got everything, construct result camera3_capture_result result; diff --git a/camera/EmulatedFakeCamera3.h b/camera/EmulatedFakeCamera3.h index 74f8496..dd9e6a1 100644 --- a/camera/EmulatedFakeCamera3.h +++ b/camera/EmulatedFakeCamera3.h @@ -33,7 +33,8 @@ namespace android { -/* Encapsulates functionality common to all version 3.0 emulated camera devices +/** + * Encapsulates functionality for a v3 HAL camera which produces synthetic data. * * Note that EmulatedCameraFactory instantiates an object of this class just * once, when EmulatedCameraFactory instance gets constructed. Connection to / @@ -69,7 +70,7 @@ public: virtual status_t getCameraInfo(struct camera_info *info); /**************************************************************************** - * EmualtedCamera3 abstract API implementation + * EmulatedCamera3 abstract API implementation ***************************************************************************/ protected: @@ -98,8 +99,22 @@ protected: private: + /** + * Build the static info metadata buffer for this device + */ status_t constructStaticInfo(); + /** + * Run the fake 3A algorithms as needed. May override/modify settings + * values. + */ + status_t process3A(CameraMetadata &settings); + + status_t doFakeAE(CameraMetadata &settings); + status_t doFakeAF(CameraMetadata &settings); + status_t doFakeAWB(CameraMetadata &settings); + void update3A(CameraMetadata &settings); + /** Signal from readout thread that it doesn't have anything to do */ void signalReadoutIdle(); @@ -112,6 +127,10 @@ private: static const uint32_t kMaxJpegStreamCount = 1; static const uint32_t kMaxReprocessStreamCount = 2; static const uint32_t kMaxBufferCount = 4; + // We need a positive stream ID to distinguish external buffers from + // sensor-generated buffers which use a nonpositive ID. Otherwise, HAL3 has + // no concept of a stream id. + static const uint32_t kGenericStreamId = 1; static const uint32_t kAvailableFormats[]; static const uint32_t kAvailableRawSizes[]; static const uint64_t kAvailableRawMinDurations[]; @@ -131,10 +150,10 @@ private: ***************************************************************************/ /* HAL interface serialization lock. */ - Mutex mLock; + Mutex mLock; /* Facing back (true) or front (false) switch. */ - bool mFacingBack; + bool mFacingBack; /** * Cache for default templates. Once one is requested, the pointer must be @@ -151,18 +170,19 @@ private: }; // Shortcut to the input stream - camera3_stream_t* mInputStream; - - // All streams, including input stream - List<camera3_stream_t*> mStreams; + camera3_stream_t* mInputStream; + typedef List<camera3_stream_t*> StreamList; typedef List<camera3_stream_t*>::iterator StreamIterator; + // All streams, including input stream + StreamList mStreams; + // Cached settings from latest submitted request - CameraMetadata mPrevSettings; + CameraMetadata mPrevSettings; /** Fake hardware interfaces */ - sp<Sensor> mSensor; + sp<Sensor> mSensor; sp<JpegCompressor> mJpegCompressor; /** Processing thread for sending out results */ @@ -179,8 +199,18 @@ private: Buffers *sensorBuffers; }; - void queueCaptureRequest(const Request &r); - bool isIdle(); + /** + * Interface to parent class + */ + + // Place request in the in-flight queue to wait for sensor capture + void queueCaptureRequest(const Request &r); + + // Test if the readout thread is idle (no in-flight requests, not + // currently reading out anything + bool isIdle(); + + // Wait until isIdle is true status_t waitForReadout(); private: @@ -202,8 +232,47 @@ private: Request mCurrentRequest; }; - sp<ReadoutThread> mReadoutThread; + + /** Fake 3A constants */ + + static const nsecs_t kNormalExposureTime; + static const nsecs_t kFacePriorityExposureTime; + static const int kNormalSensitivity; + static const int kFacePrioritySensitivity; + // Rate of converging AE to new target value, as fraction of difference between + // current and target value. + static const float kExposureTrackRate; + // Minimum duration for precapture state. May be longer if slow to converge + // to target exposure + static const int kPrecaptureMinFrames; + // How often to restart AE 'scanning' + static const int kStableAeMaxFrames; + // Maximum stop below 'normal' exposure time that we'll wander to while + // pretending to converge AE. In powers of 2. (-2 == 1/4 as bright) + static const float kExposureWanderMin; + // Maximum stop above 'normal' exposure time that we'll wander to while + // pretending to converge AE. In powers of 2. (2 == 4x as bright) + static const float kExposureWanderMax; + + /** Fake 3A state */ + + uint8_t mControlMode; + bool mFacePriority; + uint8_t mAeState; + uint8_t mAfState; + uint8_t mAwbState; + uint8_t mAeMode; + uint8_t mAfMode; + uint8_t mAwbMode; + int mAfTriggerId; + int mAeTriggerId; + + int mAeCounter; + nsecs_t mAeCurrentExposureTime; + nsecs_t mAeTargetExposureTime; + int mAeCurrentSensitivity; + }; } // namespace android diff --git a/camera/fake-pipeline2/JpegCompressor.cpp b/camera/fake-pipeline2/JpegCompressor.cpp index 20b9634..a193ce1 100644 --- a/camera/fake-pipeline2/JpegCompressor.cpp +++ b/camera/fake-pipeline2/JpegCompressor.cpp @@ -28,6 +28,7 @@ namespace android { JpegCompressor::JpegCompressor(EmulatedFakeCamera2 *parent): Thread(false), mIsBusy(false), + mSynchronous(false), mParent(parent), mBuffers(NULL), mCaptureTime(0) { @@ -49,7 +50,7 @@ status_t JpegCompressor::start(Buffers *buffers, } mIsBusy = true; - + mSynchronous = false; mBuffers = buffers; mCaptureTime = captureTime; } @@ -64,6 +65,30 @@ status_t JpegCompressor::start(Buffers *buffers, 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(); + if (res == OK) { + cleanUp(); + } + return res; +} + status_t JpegCompressor::cancel() { requestExitAndWait(); return OK; @@ -75,8 +100,34 @@ status_t JpegCompressor::readyToRun() { bool JpegCompressor::threadLoop() { Mutex::Autolock lock(mMutex); + status_t res; ALOGV("%s: Starting compression thread", __FUNCTION__); + res = compress(); + + if (res == OK) { + // Write to JPEG output stream + + ALOGV("%s: Compression complete, pushing to stream %d", __FUNCTION__, + mJpegBuffer.streamId); + + GraphicBufferMapper::get().unlock(*(mJpegBuffer.buffer)); + status_t res; + const Stream &s = mParent->getStreamInfo(mJpegBuffer.streamId); + res = s.ops->enqueue_buffer(s.ops, mCaptureTime, mJpegBuffer.buffer); + if (res != OK) { + ALOGE("%s: Error queueing compressed image buffer %p: %s (%d)", + __FUNCTION__, mJpegBuffer.buffer, strerror(-res), res); + mParent->signalError(); + } + + cleanUp(); + } + + return false; +} + +status_t JpegCompressor::compress() { // Find source and target buffers. Assumes only one buffer matches // each condition! @@ -96,7 +147,7 @@ bool JpegCompressor::threadLoop() { ALOGE("%s: Unable to find buffers for JPEG source/destination", __FUNCTION__); cleanUp(); - return false; + return BAD_VALUE; } // Set up error management @@ -109,7 +160,7 @@ bool JpegCompressor::threadLoop() { mCInfo.err->error_exit = jpegErrorHandler; jpeg_create_compress(&mCInfo); - if (checkError("Error initializing compression")) return false; + if (checkError("Error initializing compression")) return NO_INIT; // Route compressed data straight to output stream buffer @@ -129,12 +180,12 @@ bool JpegCompressor::threadLoop() { mCInfo.in_color_space = JCS_RGB; jpeg_set_defaults(&mCInfo); - if (checkError("Error configuring defaults")) return false; + if (checkError("Error configuring defaults")) return NO_INIT; // Do compression jpeg_start_compress(&mCInfo, TRUE); - if (checkError("Error starting compression")) return false; + if (checkError("Error starting compression")) return NO_INIT; size_t rowStride = mAuxBuffer.stride * 3; const size_t kChunkSize = 32; @@ -145,37 +196,20 @@ bool JpegCompressor::threadLoop() { (mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride); } jpeg_write_scanlines(&mCInfo, chunk, kChunkSize); - if (checkError("Error while compressing")) return false; + if (checkError("Error while compressing")) return NO_INIT; if (exitPending()) { ALOGV("%s: Cancel called, exiting early", __FUNCTION__); cleanUp(); - return false; + return TIMED_OUT; } } jpeg_finish_compress(&mCInfo); - if (checkError("Error while finishing compression")) return false; - - // Write to JPEG output stream - - ALOGV("%s: Compression complete, pushing to stream %d", __FUNCTION__, - mJpegBuffer.streamId); - - GraphicBufferMapper::get().unlock(*(mJpegBuffer.buffer)); - status_t res; - const Stream &s = mParent->getStreamInfo(mJpegBuffer.streamId); - res = s.ops->enqueue_buffer(s.ops, mCaptureTime, mJpegBuffer.buffer); - if (res != OK) { - ALOGE("%s: Error queueing compressed image buffer %p: %s (%d)", - __FUNCTION__, mJpegBuffer.buffer, strerror(-res), res); - mParent->signalError(); - } + if (checkError("Error while finishing compression")) return NO_INIT; // All done - cleanUp(); - - return false; + return OK; } bool JpegCompressor::isBusy() { @@ -224,7 +258,7 @@ void JpegCompressor::cleanUp() { if (mFoundAux) { if (mAuxBuffer.streamId == 0) { delete[] mAuxBuffer.img; - } else { + } else if (!mSynchronous) { GraphicBufferMapper::get().unlock(*(mAuxBuffer.buffer)); const ReprocessStream &s = mParent->getReprocessStreamInfo(-mAuxBuffer.streamId); @@ -236,7 +270,10 @@ void JpegCompressor::cleanUp() { } } } - delete mBuffers; + if (!mSynchronous) { + delete mBuffers; + } + mBuffers = NULL; mIsBusy = false; diff --git a/camera/fake-pipeline2/JpegCompressor.h b/camera/fake-pipeline2/JpegCompressor.h index ea2a84f..56e420f 100644 --- a/camera/fake-pipeline2/JpegCompressor.h +++ b/camera/fake-pipeline2/JpegCompressor.h @@ -51,6 +51,9 @@ class JpegCompressor: private Thread, public virtual RefBase { status_t start(Buffers *buffers, nsecs_t captureTime); + // Compress and block until buffer is complete. + status_t compressSynchronous(Buffers *buffers); + status_t cancel(); bool isBusy(); @@ -65,6 +68,7 @@ class JpegCompressor: private Thread, public virtual RefBase { Mutex mBusyMutex; bool mIsBusy; Condition mDone; + bool mSynchronous; Mutex mMutex; @@ -94,6 +98,8 @@ class JpegCompressor: private Thread, public virtual RefBase { static void jpegTermDestination(j_compress_ptr cinfo); bool checkError(const char *msg); + status_t compress(); + void cleanUp(); /** |