Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e00cab70 authored by James Dong's avatar James Dong
Browse files

Application-managed callback buffer support for raw image

bug - 3292153

Change-Id: I9789f7c5cde3a3889d7375e881181e9152d95fc2
parent 16dc3073
Loading
Loading
Loading
Loading
+90 −8
Original line number Diff line number Diff line
@@ -518,13 +518,76 @@ public class Camera {
     * {@link #setPreviewCallbackWithBuffer(PreviewCallback)} is used. When
     * {@link #setPreviewCallback(PreviewCallback)} or
     * {@link #setOneShotPreviewCallback(PreviewCallback)} are used, buffers
     * are automatically allocated.
     * are automatically allocated. When a supplied buffer is too small to
     * hold the preview frame data, preview callback will return null and
     * the buffer will be removed from the buffer queue.
     *
     * @param callbackBuffer the buffer to add to the queue.
     *     The size should be width * height * bits_per_pixel / 8.
     * @see #setPreviewCallbackWithBuffer(PreviewCallback)
     */
    public native final void addCallbackBuffer(byte[] callbackBuffer);
    public final void addCallbackBuffer(byte[] callbackBuffer)
    {
        _addCallbackBuffer(callbackBuffer, CAMERA_MSG_PREVIEW_FRAME);
    }

    /**
     * Adds a pre-allocated buffer to the raw image callback buffer queue.
     * Applications can add one or more buffers to the queue. When a raw image
     * frame arrives and there is still at least one available buffer, the
     * buffer will be used to hold the raw image data and removed from the
     * queue. Then raw image callback is invoked with the buffer. If a raw
     * image frame arrives but there is no buffer left, the frame is
     * discarded. Applications should add buffers back when they finish
     * processing the data in them by calling this method again in order
     * to avoid running out of raw image callback buffers.
     *
     * <p>The size of the buffer is determined by multiplying the raw image
     * width, height, and bytes per pixel. The width and height can be
     * read from {@link Camera.Parameters#getPictureSize()}. Bytes per pixel
     * can be computed from
     * {@link android.graphics.ImageFormat#getBitsPerPixel(int)} / 8,
     * using the image format from {@link Camera.Parameters#getPreviewFormat()}.
     *
     * <p>This method is only necessary when the PictureCallbck for raw image
     * is used while calling {@link #takePicture(Camera.ShutterCallback,
     * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}.
     *
     * Please note that by calling this method, the mode for application-managed
     * callback buffers is triggered. If this method has never been called,
     * null will be returned by the raw image callback since there is
     * no image callback buffer available. Furthermore, When a supplied buffer
     * is too small to hold the raw image data, raw image callback will return
     * null and the buffer will be removed from the buffer queue.
     *
     * @param callbackBuffer the buffer to add to the raw image callback buffer
     *     queue. The size should be width * height * (bits per pixel) / 8. An
     *     null callbackBuffer will be ignored and won't be added to the queue.
     *
     * @see #takePicture(Camera.ShutterCallback,
     * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}.
     *
     * {@hide}
     */
    public final void addRawImageCallbackBuffer(byte[] callbackBuffer)
    {
        addCallbackBuffer(callbackBuffer, CAMERA_MSG_RAW_IMAGE);
    }

    private final void addCallbackBuffer(byte[] callbackBuffer, int msgType)
    {
        // CAMERA_MSG_VIDEO_FRAME may be allowed in the future.
        if (msgType != CAMERA_MSG_PREVIEW_FRAME &&
            msgType != CAMERA_MSG_RAW_IMAGE) {
            throw new IllegalArgumentException(
                            "Unsupported message type: " + msgType);
        }

        _addCallbackBuffer(callbackBuffer, msgType);
    }

    private native final void _addCallbackBuffer(
                                byte[] callbackBuffer, int msgType);

    private class EventHandler extends Handler
    {
@@ -735,7 +798,7 @@ public class Camera {
            PictureCallback jpeg) {
        takePicture(shutter, raw, null, jpeg);
    }
    private native final void native_takePicture();
    private native final void native_takePicture(int msgType);

    /**
     * Triggers an asynchronous image capture. The camera service will initiate
@@ -743,7 +806,8 @@ public class Camera {
     * The shutter callback occurs after the image is captured. This can be used
     * to trigger a sound to let the user know that image has been captured. The
     * raw callback occurs when the raw image data is available (NOTE: the data
     * may be null if the hardware does not have enough memory to make a copy).
     * will be null if there is no raw image callback buffer available or the
     * raw image callback buffer is not large enough to hold the raw image).
     * The postview callback occurs when a scaled, fully processed postview
     * image is available (NOTE: not all hardware supports this). The jpeg
     * callback occurs when the compressed image is available. If the
@@ -762,6 +826,8 @@ public class Camera {
     * @param raw       the callback for raw (uncompressed) image data, or null
     * @param postview  callback with postview image data, may be null
     * @param jpeg      the callback for JPEG image data, or null
     *
     * @see #addRawImageCallbackBuffer(byte[])
     */
    public final void takePicture(ShutterCallback shutter, PictureCallback raw,
            PictureCallback postview, PictureCallback jpeg) {
@@ -769,7 +835,23 @@ public class Camera {
        mRawImageCallback = raw;
        mPostviewCallback = postview;
        mJpegCallback = jpeg;
        native_takePicture();

        // If callback is not set, do not send me callbacks.
        int msgType = 0;
        if (mShutterCallback != null) {
            msgType |= CAMERA_MSG_SHUTTER;
        }
        if (mRawImageCallback != null) {
            msgType |= CAMERA_MSG_RAW_IMAGE;
        }
        if (mPostviewCallback != null) {
            msgType |= CAMERA_MSG_POSTVIEW_FRAME;
        }
        if (mJpegCallback != null) {
            msgType |= CAMERA_MSG_COMPRESSED_IMAGE;
        }

        native_takePicture(msgType);
    }

    /**
+173 −67
Original line number Diff line number Diff line
@@ -53,25 +53,48 @@ public:
    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
    void addCallbackBuffer(JNIEnv *env, jbyteArray cbb);
    void addCallbackBuffer(JNIEnv *env, jbyteArray cbb, int msgType);
    void setCallbackMode(JNIEnv *env, bool installed, bool manualMode);
    sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
    bool isRawImageCallbackBufferAvailable() const;
    void release();

private:
    void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
    void clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers);
    void clearCallbackBuffers_l(JNIEnv *env);
    jbyteArray getCallbackBuffer(JNIEnv *env, Vector<jbyteArray> *buffers, size_t bufferSize);

    jobject     mCameraJObjectWeak;     // weak reference to java object
    jclass      mCameraJClass;          // strong reference to java class
    sp<Camera>  mCamera;                // strong reference to native object
    Mutex       mLock;

    /*
     * Global reference application-managed raw image buffer queue.
     *
     * Manual-only mode is supported for raw image callbacks, which is
     * set whenever method addCallbackBuffer() with msgType =
     * CAMERA_MSG_RAW_IMAGE is called; otherwise, null is returned
     * with raw image callbacks.
     */
    Vector<jbyteArray> mRawImageCallbackBuffers;

    /*
     * Application-managed preview buffer queue and the flags
     * associated with the usage of the preview buffer callback.
     */
    Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[]
    bool mManualBufferMode;              // Whether to use application managed buffers.
    bool mManualCameraCallbackSet;       // Whether the callback has been set, used to reduce unnecessary calls to set the callback.
    bool mManualCameraCallbackSet;       // Whether the callback has been set, used to
                                         // reduce unnecessary calls to set the callback.
};

bool JNICameraContext::isRawImageCallbackBufferAvailable() const
{
    return !mRawImageCallbackBuffers.isEmpty();
}

sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
    sp<Camera> camera;
@@ -128,10 +151,48 @@ void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2)
        return;
    }
    JNIEnv *env = AndroidRuntime::getJNIEnv();

    /*
     * If the notification or msgType is CAMERA_MSG_RAW_IMAGE_NOTIFY, change it
     * to CAMERA_MSG_RAW_IMAGE since CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed
     * to the Java app.
     */
    if (msgType == CAMERA_MSG_RAW_IMAGE_NOTIFY) {
        msgType = CAMERA_MSG_RAW_IMAGE;
    }

    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
            mCameraJObjectWeak, msgType, ext1, ext2, NULL);
}

jbyteArray JNICameraContext::getCallbackBuffer(
        JNIEnv* env, Vector<jbyteArray>* buffers, size_t bufferSize)
{
    jbyteArray obj = NULL;

    // Vector access should be protected by lock in postData()
    if (!buffers->isEmpty()) {
        LOGV("Using callback buffer from queue of length %d", buffers->size());
        jbyteArray globalBuffer = buffers->itemAt(0);
        buffers->removeAt(0);

        obj = (jbyteArray)env->NewLocalRef(globalBuffer);
        env->DeleteGlobalRef(globalBuffer);

        if (obj != NULL) {
            jsize bufferLength = env->GetArrayLength(obj);
            if ((int)bufferLength < (int)bufferSize) {
                LOGE("Callback buffer was too small! Expected %d bytes, but got %d bytes!",
                    bufferSize, bufferLength);
                env->DeleteLocalRef(obj);
                return NULL;
            }
        }
    }

    return obj;
}

void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
{
    jbyteArray obj = NULL;
@@ -141,7 +202,7 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int
        ssize_t offset;
        size_t size;
        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
        LOGV("postData: off=%d, size=%d", offset, size);
        LOGV("copyAndPost: off=%ld, size=%d", offset, size);
        uint8_t *heapBase = (uint8_t*)heap->base();

        if (heapBase != NULL) {
@@ -151,25 +212,9 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int
                LOGV("Allocating callback buffer");
                obj = env->NewByteArray(size);
            } else {
                // Vector access should be protected by lock in postData()
                if(!mCallbackBuffers.isEmpty()) {
                    LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size());
                    jbyteArray globalBuffer = mCallbackBuffers.itemAt(0);
                    mCallbackBuffers.removeAt(0);

                    obj = (jbyteArray)env->NewLocalRef(globalBuffer);
                    env->DeleteGlobalRef(globalBuffer);

                    if (obj != NULL) {
                        jsize bufferLength = env->GetArrayLength(obj);
                        if ((int)bufferLength < (int)size) {
                            LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!",
                                 size, bufferLength);
                            env->DeleteLocalRef(obj);
                            return;
                        }
                    }
                }
                switch (msgType) {
                    case CAMERA_MSG_PREVIEW_FRAME: {
                        obj = getCallbackBuffer(env, &mCallbackBuffers, size);

                        if (mCallbackBuffers.isEmpty()) {
                            LOGV("Out of buffers, clearing callback!");
@@ -180,6 +225,18 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int
                                return;
                            }
                        }
                        break;
                    }
                    case CAMERA_MSG_RAW_IMAGE: {
                        obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size);
                        break;
                    }
                    default: {
                        jniThrowException(env,
                            "java/lang/RuntimeException", "Unsupported message type");
                        return;
                    }
                }
            }

            if (obj == NULL) {
@@ -216,14 +273,20 @@ void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr)
        case CAMERA_MSG_VIDEO_FRAME:
            // should never happen
            break;
    // don't return raw data to Java

        // For backward-compatibility purpose, if there is no callback
        // buffer for raw image, the callback returns null.
        case CAMERA_MSG_RAW_IMAGE:
            LOGV("rawCallback");
            if (mRawImageCallbackBuffers.isEmpty()) {
                env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
                        mCameraJObjectWeak, msgType, 0, 0, NULL);
            } else {
                copyAndPost(env, dataPtr, msgType);
            }
            break;

        default:
        // TODO: Change to LOGV
            LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
            copyAndPost(env, dataPtr, msgType);
            break;
@@ -251,7 +314,7 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM

    if (!installed) {
        mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
        clearCallbackBuffers_l(env);
        clearCallbackBuffers_l(env, &mCallbackBuffers);
    } else if (mManualBufferMode) {
        if (!mCallbackBuffers.isEmpty()) {
            mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
@@ -259,25 +322,45 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM
        }
    } else {
        mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_BARCODE_SCANNER);
        clearCallbackBuffers_l(env);
        clearCallbackBuffers_l(env, &mCallbackBuffers);
    }
}

void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb)
void JNICameraContext::addCallbackBuffer(
        JNIEnv *env, jbyteArray cbb, int msgType)
{
    LOGV("addCallbackBuffer: 0x%x", msgType);
    if (cbb != NULL) {
        Mutex::Autolock _l(mLock);
        switch (msgType) {
            case CAMERA_MSG_PREVIEW_FRAME: {
                jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb);
        mCallbackBuffers.push(cbb);
                mCallbackBuffers.push(callbackBuffer);

        LOGV("Adding callback buffer to queue, %d total", mCallbackBuffers.size());
                LOGV("Adding callback buffer to queue, %d total",
                        mCallbackBuffers.size());

        // We want to make sure the camera knows we're ready for the next frame.
        // This may have come unset had we not had a callbackbuffer ready for it last time.
                // We want to make sure the camera knows we're ready for the
                // next frame. This may have come unset had we not had a
                // callbackbuffer ready for it last time.
                if (mManualBufferMode && !mManualCameraCallbackSet) {
                    mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
                    mManualCameraCallbackSet = true;
                }
                break;
            }
            case CAMERA_MSG_RAW_IMAGE: {
                jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb);
                mRawImageCallbackBuffers.push(callbackBuffer);
                break;
            }
            default: {
                jniThrowException(env,
                        "java/lang/IllegalArgumentException",
                        "Unsupported message type");
                return;
            }
        }
    } else {
       LOGE("Null byte array!");
    }
@@ -285,10 +368,15 @@ void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb)

void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env)
{
    LOGV("Clearing callback buffers, %d remained", mCallbackBuffers.size());
    while(!mCallbackBuffers.isEmpty()) {
        env->DeleteGlobalRef(mCallbackBuffers.top());
        mCallbackBuffers.pop();
    clearCallbackBuffers_l(env, &mCallbackBuffers);
    clearCallbackBuffers_l(env, &mRawImageCallbackBuffers);
}

void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers) {
    LOGV("Clearing callback buffers, %d remained", buffers->size());
    while (!buffers->isEmpty()) {
        env->DeleteGlobalRef(buffers->top());
        buffers->pop();
    }
}

@@ -458,13 +546,13 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t
    context->setCallbackMode(env, installed, manualBuffer);
}

static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) {
    LOGV("addCallbackBuffer");
static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, int msgType) {
    LOGV("addCallbackBuffer: 0x%x", msgType);

    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));

    if (context != NULL) {
        context->addCallbackBuffer(env, bytes);
        context->addCallbackBuffer(env, bytes, msgType);
    }
}

@@ -492,14 +580,32 @@ static void android_hardware_Camera_cancelAutoFocus(JNIEnv *env, jobject thiz)
    }
}

static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz, int msgType)
{
    LOGV("takePicture");
    JNICameraContext* context;
    sp<Camera> camera = get_native_camera(env, thiz, &context);
    if (camera == 0) return;

    if (camera->takePicture() != NO_ERROR) {
    /*
     * When CAMERA_MSG_RAW_IMAGE is requested, if the raw image callback
     * buffer is available, CAMERA_MSG_RAW_IMAGE is enabled to get the
     * notification _and_ the data; otherwise, CAMERA_MSG_RAW_IMAGE_NOTIFY
     * is enabled to receive the callback notification but no data.
     *
     * Note that CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed to the
     * Java application.
     */
    if (msgType & CAMERA_MSG_RAW_IMAGE) {
        LOGV("Enable raw image callback buffer");
        if (!context->isRawImageCallbackBufferAvailable()) {
            LOGV("Enable raw image notification, since no callback buffer exists");
            msgType &= ~CAMERA_MSG_RAW_IMAGE;
            msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY;
        }
    }

    if (camera->takePicture(msgType) != NO_ERROR) {
        jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
        return;
    }
@@ -638,8 +744,8 @@ static JNINativeMethod camMethods[] = {
  { "setHasPreviewCallback",
    "(ZZ)V",
    (void *)android_hardware_Camera_setHasPreviewCallback },
  { "addCallbackBuffer",
    "([B)V",
  { "_addCallbackBuffer",
    "([BI)V",
    (void *)android_hardware_Camera_addCallbackBuffer },
  { "native_autoFocus",
    "()V",
@@ -648,7 +754,7 @@ static JNINativeMethod camMethods[] = {
    "()V",
    (void *)android_hardware_Camera_cancelAutoFocus },
  { "native_takePicture",
    "()V",
    "(I)V",
    (void *)android_hardware_Camera_takePicture },
  { "native_setParameters",
    "(Ljava/lang/String;)V",
+12 −11
Original line number Diff line number Diff line
@@ -66,16 +66,17 @@ namespace android {

// msgType in notifyCallback and dataCallback functions
enum {
    CAMERA_MSG_ERROR            = 0x001,
    CAMERA_MSG_SHUTTER          = 0x002,
    CAMERA_MSG_FOCUS            = 0x004,
    CAMERA_MSG_ZOOM             = 0x008,
    CAMERA_MSG_PREVIEW_FRAME    = 0x010,
    CAMERA_MSG_VIDEO_FRAME      = 0x020,
    CAMERA_MSG_POSTVIEW_FRAME   = 0x040,
    CAMERA_MSG_RAW_IMAGE        = 0x080,
    CAMERA_MSG_COMPRESSED_IMAGE = 0x100,
    CAMERA_MSG_ALL_MSGS         = 0x1FF
    CAMERA_MSG_ERROR            = 0x0001,
    CAMERA_MSG_SHUTTER          = 0x0002,
    CAMERA_MSG_FOCUS            = 0x0004,
    CAMERA_MSG_ZOOM             = 0x0008,
    CAMERA_MSG_PREVIEW_FRAME    = 0x0010,
    CAMERA_MSG_VIDEO_FRAME      = 0x0020,
    CAMERA_MSG_POSTVIEW_FRAME   = 0x0040,
    CAMERA_MSG_RAW_IMAGE        = 0x0080,
    CAMERA_MSG_COMPRESSED_IMAGE = 0x0100,
    CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x0200,
    CAMERA_MSG_ALL_MSGS         = 0xFFFF
};

// cmdType in sendCommand functions
@@ -207,7 +208,7 @@ public:
            status_t    cancelAutoFocus();

            // take a picture - picture returned from callback
            status_t    takePicture();
            status_t    takePicture(int msgType);

            // set preview/capture parameters - key/value pairs
            status_t    setParameters(const String8& params);
+9 −3
Original line number Diff line number Diff line
@@ -84,8 +84,14 @@ public:
    // cancel auto focus
    virtual status_t        cancelAutoFocus() = 0;

    // take a picture
    virtual status_t        takePicture() = 0;
    /*
     * take a picture.
     * @param msgType the message type an application selectively turn on/off
     * on a photo-by-photo basis. The supported message types are:
     * CAMERA_MSG_SHUTTER, CAMERA_MSG_RAW_IMAGE, CAMERA_MSG_COMPRESSED_IMAGE,
     * and CAMERA_MSG_POSTVIEW_FRAME. Any other message types will be ignored.
     */
    virtual status_t        takePicture(int msgType) = 0;

    // set preview/capture parameters - key/value pairs
    virtual status_t        setParameters(const String8& params) = 0;
+3 −3
Original line number Diff line number Diff line
@@ -301,12 +301,12 @@ status_t Camera::cancelAutoFocus()
}

// take a picture
status_t Camera::takePicture()
status_t Camera::takePicture(int msgType)
{
    LOGV("takePicture");
    LOGV("takePicture: 0x%x", msgType);
    sp <ICamera> c = mCamera;
    if (c == 0) return NO_INIT;
    return c->takePicture();
    return c->takePicture(msgType);
}

// set preview/capture parameters - key/value pairs
Loading