OSDN Git Service

Camera2: Add a burst mode skeleton.
authorJames Painter <jpainter@google.com>
Thu, 6 Sep 2012 01:02:32 +0000 (18:02 -0700)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Fri, 7 Sep 2012 23:28:00 +0000 (16:28 -0700)
Bug: 6243944
Change-Id: I7f496ca1051571c68fdd99a6f85bf6a908a4e29a

12 files changed:
camera/CameraParameters.cpp
include/camera/CameraParameters.h
services/camera/libcameraservice/Android.mk
services/camera/libcameraservice/Camera2Client.cpp
services/camera/libcameraservice/camera2/BurstCapture.cpp [new file with mode: 0644]
services/camera/libcameraservice/camera2/BurstCapture.h [new file with mode: 0644]
services/camera/libcameraservice/camera2/CaptureSequencer.cpp
services/camera/libcameraservice/camera2/CaptureSequencer.h
services/camera/libcameraservice/camera2/JpegCompressor.cpp [new file with mode: 0644]
services/camera/libcameraservice/camera2/JpegCompressor.h [new file with mode: 0644]
services/camera/libcameraservice/camera2/Parameters.cpp
services/camera/libcameraservice/camera2/Parameters.h

index 872512a..a657fe3 100644 (file)
@@ -90,6 +90,7 @@ const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
 const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
 const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization";
 const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported";
+const char CameraParameters::KEY_LIGHTFX[] = "light-fx";
 
 const char CameraParameters::TRUE[] = "true";
 const char CameraParameters::FALSE[] = "false";
@@ -166,6 +167,10 @@ const char CameraParameters::FOCUS_MODE_EDOF[] = "edof";
 const char CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO[] = "continuous-video";
 const char CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE[] = "continuous-picture";
 
+// Values for light fx settings
+const char CameraParameters::LIGHTFX_LOWLIGHT[] = "low-light";
+const char CameraParameters::LIGHTFX_HDR[] = "high-dynamic-range";
+
 CameraParameters::CameraParameters()
                 : mMap()
 {
index 4d5aa36..8668958 100644 (file)
@@ -298,7 +298,7 @@ public:
     // Example value: "42.5". Read only.
     static const char KEY_VERTICAL_VIEW_ANGLE[];
     // Exposure compensation index. 0 means exposure is not adjusted.
-    // Example value: "0" or "5". Read/write.
+    // Example value: "-5" or "5". Read/write.
     static const char KEY_EXPOSURE_COMPENSATION[];
     // The maximum exposure compensation index (>=0).
     // Example value: "6". Read only.
@@ -307,7 +307,7 @@ public:
     // Example value: "-6". Read only.
     static const char KEY_MIN_EXPOSURE_COMPENSATION[];
     // The exposure compensation step. Exposure compensation index multiply by
-    // step eqals to EV. Ex: if exposure compensation index is 6 and step is
+    // step eqals to EV. Ex: if exposure compensation index is -6 and step is
     // 0.3333, EV is -2.
     // Example value: "0.333333333" or "0.5". Read only.
     static const char KEY_EXPOSURE_COMPENSATION_STEP[];
@@ -525,6 +525,10 @@ public:
     // stream and record stabilized videos.
     static const char KEY_VIDEO_STABILIZATION_SUPPORTED[];
 
+    // Supported modes for special effects with light.
+    // Example values: "lowlight,hdr".
+    static const char KEY_LIGHTFX[];
+
     // Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
     static const char TRUE[];
     static const char FALSE[];
@@ -660,6 +664,12 @@ public:
     // other modes.
     static const char FOCUS_MODE_CONTINUOUS_PICTURE[];
 
+    // Values for light special effects
+    // Low-light enhancement mode
+    static const char LIGHTFX_LOWLIGHT[];
+    // High-dynamic range mode
+    static const char LIGHTFX_HDR[];
+
 private:
     DefaultKeyedVector<String8,String8>    mMap;
 };
index e27a065..c7927fe 100644 (file)
@@ -17,7 +17,9 @@ LOCAL_SRC_FILES:=               \
     camera2/JpegProcessor.cpp \
     camera2/CallbackProcessor.cpp \
     camera2/ZslProcessor.cpp \
-    camera2/CaptureSequencer.cpp \
+    camera2/BurstCapture.cpp \
+    camera2/JpegCompressor.cpp \
+    camera2/CaptureSequencer.cpp
 
 LOCAL_SHARED_LIBRARIES:= \
     libui \
@@ -30,10 +32,12 @@ LOCAL_SHARED_LIBRARIES:= \
     libgui \
     libhardware \
     libsync \
-    libcamera_metadata
+    libcamera_metadata \
+    libjpeg
 
 LOCAL_C_INCLUDES += \
-    system/media/camera/include
+    system/media/camera/include \
+    external/jpeg
 
 LOCAL_MODULE:= libcameraservice
 
index 7abb405..5081289 100644 (file)
 #include <gui/SurfaceTextureClient.h>
 #include <gui/Surface.h>
 #include <media/hardware/MetadataBufferType.h>
-
-#include "Camera2Client.h"
+#include "camera2/Parameters.h"
 
 #define ALOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__);
 #define ALOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__);
 
 namespace android {
-
 using namespace camera2;
 
 static int getCallingPid() {
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.cpp b/services/camera/libcameraservice/camera2/BurstCapture.cpp
new file mode 100644 (file)
index 0000000..5020819
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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 "BurstCapture"
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "BurstCapture.h"
+
+#include "JpegCompressor.h"
+#include "../Camera2Client.h"
+
+namespace android {
+namespace camera2 {
+
+BurstCapture::BurstCapture(wp<Camera2Client> client, wp<CaptureSequencer> sequencer):
+    mCaptureStreamId(NO_STREAM),
+    mClient(client),
+    mSequencer(sequencer)
+{
+}
+
+BurstCapture::~BurstCapture() {
+}
+
+status_t BurstCapture::start(Vector<CameraMetadata> &metadatas, int32_t firstCaptureId) {
+    ALOGE("Not completely implemented");
+    return INVALID_OPERATION;
+}
+
+void BurstCapture::onFrameAvailable() {
+    ALOGV("%s", __FUNCTION__);
+    Mutex::Autolock l(mInputMutex);
+    if(!mInputChanged) {
+        mInputChanged = true;
+        mInputSignal.signal();
+    }
+}
+
+bool BurstCapture::threadLoop() {
+    status_t res;
+    {
+        Mutex::Autolock l(mInputMutex);
+        while(!mInputChanged) {
+            res = mInputSignal.waitRelative(mInputMutex, kWaitDuration);
+            if(res == TIMED_OUT) return true;
+        }
+        mInputChanged = false;
+    }
+
+    do {
+        sp<Camera2Client> client = mClient.promote();
+        if(client == 0) return false;
+        ALOGV("%s: Calling processFrameAvailable()", __FUNCTION__);
+        res = processFrameAvailable(client);
+    } while(res == OK);
+
+    return true;
+}
+
+CpuConsumer::LockedBuffer* BurstCapture::jpegEncode(
+    CpuConsumer::LockedBuffer *imgBuffer,
+    int quality)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    CpuConsumer::LockedBuffer *imgEncoded = new CpuConsumer::LockedBuffer;
+    uint8_t *data = new uint8_t[ANDROID_JPEG_MAX_SIZE];
+    imgEncoded->data = data;
+    imgEncoded->width = imgBuffer->width;
+    imgEncoded->height = imgBuffer->height;
+    imgEncoded->stride = imgBuffer->stride;
+
+    Vector<CpuConsumer::LockedBuffer*> buffers;
+    buffers.push_back(imgBuffer);
+    buffers.push_back(imgEncoded);
+
+    sp<JpegCompressor> jpeg = new JpegCompressor();
+    status_t res = jpeg->start(buffers, 1);
+
+    bool success = jpeg->waitForDone(10 * 1e9);
+    if(success) {
+        return buffers[1];
+    }
+    else {
+        ALOGE("%s: JPEG encode timed out", __FUNCTION__);
+        return NULL;  // TODO: maybe change function return value to status_t
+    }
+}
+
+status_t BurstCapture::processFrameAvailable(sp<Camera2Client> &client) {
+    ALOGE("Not implemented");
+    return INVALID_OPERATION;
+}
+
+} // namespace camera2
+} // namespace android
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.h b/services/camera/libcameraservice/camera2/BurstCapture.h
new file mode 100644 (file)
index 0000000..dfc45eb
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
+#define ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
+
+#include "camera2/CameraMetadata.h"
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <gui/CpuConsumer.h>
+#include "Camera2Device.h"
+
+namespace android {
+
+class Camera2Client;
+
+namespace camera2 {
+
+class CaptureSequencer;
+
+class BurstCapture : public virtual Thread,
+                     public virtual CpuConsumer::FrameAvailableListener
+{
+public:
+    BurstCapture(wp<Camera2Client> client, wp<CaptureSequencer> sequencer);
+    virtual ~BurstCapture();
+
+    virtual void onFrameAvailable();
+    virtual status_t start(Vector<CameraMetadata> &metadatas, int32_t firstCaptureId);
+
+protected:
+    Mutex mInputMutex;
+    bool mInputChanged;
+    Condition mInputSignal;
+    int mCaptureStreamId;
+    wp<Camera2Client> mClient;
+    wp<CaptureSequencer> mSequencer;
+
+    // Should only be accessed by processing thread
+    enum {
+        NO_STREAM = -1
+    };
+
+    CpuConsumer::LockedBuffer* jpegEncode(
+        CpuConsumer::LockedBuffer *imgBuffer,
+        int quality);
+
+    virtual status_t processFrameAvailable(sp<Camera2Client> &client);
+
+private:
+    virtual bool threadLoop();
+    static const nsecs_t kWaitDuration = 10000000; // 10 ms
+};
+
+} // namespace camera2
+} // namespace android
+
+#endif
index 1c42cbf..2f8b7db 100644 (file)
@@ -23,6 +23,7 @@
 #include <utils/Vector.h>
 
 #include "CaptureSequencer.h"
+#include "BurstCapture.h"
 #include "../Camera2Device.h"
 #include "../Camera2Client.h"
 #include "Parameters.h"
@@ -44,6 +45,7 @@ CaptureSequencer::CaptureSequencer(wp<Camera2Client> client):
         mTriggerId(0),
         mTimeoutCount(0),
         mCaptureId(Camera2Client::kFirstCaptureRequestId) {
+    ALOGV("%s", __FUNCTION__);
 }
 
 CaptureSequencer::~CaptureSequencer() {
@@ -56,6 +58,7 @@ void CaptureSequencer::setZslProcessor(wp<ZslProcessor> processor) {
 }
 
 status_t CaptureSequencer::startCapture() {
+    ALOGV("%s", __FUNCTION__);
     ATRACE_CALL();
     Mutex::Autolock l(mInputMutex);
     if (mBusy) {
@@ -82,6 +85,7 @@ void CaptureSequencer::notifyAutoExposure(uint8_t newState, int triggerId) {
 
 void CaptureSequencer::onFrameAvailable(int32_t frameId,
         CameraMetadata &frame) {
+    ALOGV("%s: Listener found new frame", __FUNCTION__);
     ATRACE_CALL();
     Mutex::Autolock l(mInputMutex);
     mNewFrameId = frameId;
@@ -94,6 +98,7 @@ void CaptureSequencer::onFrameAvailable(int32_t frameId,
 
 void CaptureSequencer::onCaptureAvailable(nsecs_t timestamp) {
     ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
     Mutex::Autolock l(mInputMutex);
     mCaptureTimestamp = timestamp;
     if (!mNewCaptureReceived) {
@@ -132,6 +137,8 @@ const char* CaptureSequencer::kStateNames[CaptureSequencer::NUM_CAPTURE_STATES+1
     "STANDARD_START",
     "STANDARD_PRECAPTURE",
     "STANDARD_CAPTURING",
+    "BURST_CAPTURE_START",
+    "BURST_CAPTURE_WAIT",
     "DONE",
     "ERROR",
     "UNKNOWN"
@@ -148,6 +155,8 @@ const CaptureSequencer::StateManager
     &CaptureSequencer::manageStandardPrecaptureWait,
     &CaptureSequencer::manageStandardCapture,
     &CaptureSequencer::manageStandardCaptureWait,
+    &CaptureSequencer::manageBurstCaptureStart,
+    &CaptureSequencer::manageBurstCaptureWait,
     &CaptureSequencer::manageDone,
 };
 
@@ -215,6 +224,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageDone(sp<Camera2Client> &c
 
 CaptureSequencer::CaptureState CaptureSequencer::manageStart(
         sp<Camera2Client> &client) {
+    ALOGV("%s", __FUNCTION__);
     status_t res;
     ATRACE_CALL();
     SharedParameters::Lock l(client->getParameters());
@@ -227,7 +237,11 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStart(
         return DONE;
     }
 
-    if (l.mParameters.zslMode &&
+    if(l.mParameters.lightFx != Parameters::LIGHTFX_NONE &&
+            l.mParameters.state == Parameters::STILL_CAPTURE) {
+        nextState = BURST_CAPTURE_START;
+    }
+    else if (l.mParameters.zslMode &&
             l.mParameters.state == Parameters::STILL_CAPTURE) {
         nextState = ZSL_START;
     } else {
@@ -442,6 +456,77 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardCaptureWait(
     return STANDARD_CAPTURE_WAIT;
 }
 
+CaptureSequencer::CaptureState CaptureSequencer::manageBurstCaptureStart(
+        sp<Camera2Client> &client) {
+    ALOGV("%s", __FUNCTION__);
+    status_t res;
+    ATRACE_CALL();
+
+    // check which burst mode is set, create respective burst object
+    {
+        SharedParameters::Lock l(client->getParameters());
+
+        res = updateCaptureRequest(l.mParameters, client);
+        if(res != OK) {
+            return DONE;
+        }
+
+        //
+        // check for burst mode type in mParameters here
+        //
+        mBurstCapture = new BurstCapture(client, this);
+    }
+
+    res = mCaptureRequest.update(ANDROID_REQUEST_ID, &mCaptureId, 1);
+    if (res == OK) {
+        res = mCaptureRequest.sort();
+    }
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to set up still capture request: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return DONE;
+    }
+
+    CameraMetadata captureCopy = mCaptureRequest;
+    if (captureCopy.entryCount() == 0) {
+        ALOGE("%s: Camera %d: Unable to copy capture request for HAL device",
+                __FUNCTION__, client->getCameraId());
+        return DONE;
+    }
+
+    Vector<CameraMetadata> requests;
+    requests.push(mCaptureRequest);
+    res = mBurstCapture->start(requests, mCaptureId);
+    mTimeoutCount = kMaxTimeoutsForCaptureEnd * 10;
+    return BURST_CAPTURE_WAIT;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageBurstCaptureWait(
+        sp<Camera2Client> &client) {
+    status_t res;
+    ATRACE_CALL();
+
+    while (!mNewCaptureReceived) {
+        res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration);
+        if (res == TIMED_OUT) {
+            mTimeoutCount--;
+            break;
+        }
+    }
+
+    if (mTimeoutCount <= 0) {
+        ALOGW("Timed out waiting for burst capture to complete");
+        return DONE;
+    }
+    if (mNewCaptureReceived) {
+        mNewCaptureReceived = false;
+        // TODO: update mCaptureId to last burst's capture ID + 1?
+        return DONE;
+    }
+
+    return BURST_CAPTURE_WAIT;
+}
+
 status_t CaptureSequencer::updateCaptureRequest(const Parameters &params,
         sp<Camera2Client> &client) {
     ATRACE_CALL();
index 0492a43..39ae079 100644 (file)
@@ -33,6 +33,7 @@ class Camera2Client;
 namespace camera2 {
 
 class ZslProcessor;
+class BurstCapture;
 
 /**
  * Manages the still image capture process for
@@ -96,6 +97,7 @@ class CaptureSequencer:
 
     wp<Camera2Client> mClient;
     wp<ZslProcessor> mZslProcessor;
+    sp<BurstCapture> mBurstCapture;
 
     enum CaptureState {
         IDLE,
@@ -107,6 +109,8 @@ class CaptureSequencer:
         STANDARD_PRECAPTURE_WAIT,
         STANDARD_CAPTURE,
         STANDARD_CAPTURE_WAIT,
+        BURST_CAPTURE_START,
+        BURST_CAPTURE_WAIT,
         DONE,
         ERROR,
         NUM_CAPTURE_STATES
@@ -140,6 +144,9 @@ class CaptureSequencer:
     CaptureState manageStandardCapture(sp<Camera2Client> &client);
     CaptureState manageStandardCaptureWait(sp<Camera2Client> &client);
 
+    CaptureState manageBurstCaptureStart(sp<Camera2Client> &client);
+    CaptureState manageBurstCaptureWait(sp<Camera2Client> &client);
+
     CaptureState manageDone(sp<Camera2Client> &client);
 
     // Utility methods
diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.cpp b/services/camera/libcameraservice/camera2/JpegCompressor.cpp
new file mode 100644 (file)
index 0000000..55964b6
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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 "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
diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.h b/services/camera/libcameraservice/camera2/JpegCompressor.h
new file mode 100644 (file)
index 0000000..945b1de
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * This class simulates a hardware JPEG compressor.  It receives image buffers
+ * in RGBA_8888 format, processes them in a worker thread, and then pushes them
+ * out to their destination stream.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_JPEGCOMPRESSOR_H
+#define ANDROID_SERVERS_CAMERA_JPEGCOMPRESSOR_H
+
+#include "utils/Thread.h"
+#include "utils/Mutex.h"
+#include "utils/Timers.h"
+#include "utils/Vector.h"
+//#include "Base.h"
+#include <stdio.h>
+#include <gui/CpuConsumer.h>
+
+extern "C" {
+#include <jpeglib.h>
+}
+
+
+namespace android {
+namespace camera2 {
+
+class JpegCompressor: private Thread, public virtual RefBase {
+  public:
+
+    JpegCompressor();
+    ~JpegCompressor();
+
+    // Start compressing COMPRESSED format buffers; JpegCompressor takes
+    // ownership of the Buffers vector.
+    status_t start(Vector<CpuConsumer::LockedBuffer*> buffers,
+            nsecs_t captureTime);
+
+    status_t cancel();
+
+    bool isBusy();
+    bool isStreamInUse(uint32_t id);
+
+    bool waitForDone(nsecs_t timeout);
+
+    // TODO: Measure this
+    static const size_t kMaxJpegSize = 300000;
+
+  private:
+    Mutex mBusyMutex;
+    Mutex mMutex;
+    bool mIsBusy;
+    Condition mDone;
+    nsecs_t mCaptureTime;
+
+    Vector<CpuConsumer::LockedBuffer*> mBuffers;
+    CpuConsumer::LockedBuffer *mJpegBuffer;
+    CpuConsumer::LockedBuffer *mAuxBuffer;
+    bool mFoundJpeg, mFoundAux;
+
+    jpeg_compress_struct mCInfo;
+
+    struct JpegError : public jpeg_error_mgr {
+        JpegCompressor *parent;
+    };
+    j_common_ptr mJpegErrorInfo;
+
+    struct JpegDestination : public jpeg_destination_mgr {
+        JpegCompressor *parent;
+    };
+
+    static void jpegErrorHandler(j_common_ptr cinfo);
+
+    static void jpegInitDestination(j_compress_ptr cinfo);
+    static boolean jpegEmptyOutputBuffer(j_compress_ptr cinfo);
+    static void jpegTermDestination(j_compress_ptr cinfo);
+
+    bool checkError(const char *msg);
+    void cleanUp();
+
+    /**
+     * Inherited Thread virtual overrides
+     */
+  private:
+    virtual status_t readyToRun();
+    virtual bool threadLoop();
+};
+
+}; // namespace camera2
+}; // namespace android
+
+#endif
index f89d1e3..e5942dc 100644 (file)
@@ -748,6 +748,10 @@ status_t Parameters::initialize(const CameraMetadata *info) {
 
     previewCallbackFlags = 0;
 
+    zslMode = false;
+
+    lightFx = LIGHTFX_NONE;
+
     state = STOPPED;
 
     paramsFlattened = params.flatten();
@@ -1315,6 +1319,10 @@ status_t Parameters::set(const String8& params) {
         ALOGE("%s: Video stabilization not supported", __FUNCTION__);
     }
 
+    // LIGHTFX
+    validatedParams.lightFx = lightFxStringToEnum(
+        newParams.get(CameraParameters::KEY_LIGHTFX));
+
     /** Update internal parameters */
 
     validatedParams.paramsFlattened = params;
@@ -1726,6 +1734,16 @@ Parameters::Parameters::focusMode_t Parameters::focusModeStringToEnum(
         Parameters::FOCUS_MODE_INVALID;
 }
 
+Parameters::Parameters::lightFxMode_t Parameters::lightFxStringToEnum(
+        const char *lightFxMode) {
+    return
+        !strcmp(lightFxMode, CameraParameters::LIGHTFX_LOWLIGHT) ?
+            Parameters::LIGHTFX_LOWLIGHT :
+        !strcmp(lightFxMode, CameraParameters::LIGHTFX_HDR) ?
+            Parameters::LIGHTFX_HDR :
+        Parameters::LIGHTFX_NONE;
+}
+
 status_t Parameters::parseAreas(const char *areasCStr,
         Vector<Parameters::Area> *areas) {
     static const size_t NUM_FIELDS = 5;
index 509cc85..f768605 100644 (file)
@@ -109,6 +109,12 @@ struct Parameters {
     bool recordingHint;
     bool videoStabilization;
 
+    enum lightFxMode_t {
+        LIGHTFX_NONE = 0,
+        LIGHTFX_LOWLIGHT,
+        LIGHTFX_HDR
+    } lightFx;
+
     String8 paramsFlattened;
 
     // These parameters are also part of the camera API-visible state, but not
@@ -198,6 +204,7 @@ struct Parameters {
     static int sceneModeStringToEnum(const char *sceneMode);
     static flashMode_t flashModeStringToEnum(const char *flashMode);
     static focusMode_t focusModeStringToEnum(const char *focusMode);
+    static lightFxMode_t lightFxStringToEnum(const char *lightFxMode);
     static status_t parseAreas(const char *areasCStr,
             Vector<Area> *areas);
     static status_t validateAreas(const Vector<Area> &areas,