diff options
author | James Dong <jdong@google.com> | 2011-02-17 16:38:06 -0800 |
---|---|---|
committer | James Dong <jdong@google.com> | 2011-02-22 20:48:15 -0800 |
commit | e00cab707dcaf6f05adb5ccb9c80fdf25c483427 (patch) | |
tree | 1e69fd3c108e675a5a68b598f31882df58b602fb /core/jni | |
parent | 16dc3073a2f06a77d9873e536f633a4adf575fce (diff) | |
download | frameworks_base-e00cab707dcaf6f05adb5ccb9c80fdf25c483427.zip frameworks_base-e00cab707dcaf6f05adb5ccb9c80fdf25c483427.tar.gz frameworks_base-e00cab707dcaf6f05adb5ccb9c80fdf25c483427.tar.bz2 |
Application-managed callback buffer support for raw image
bug - 3292153
Change-Id: I9789f7c5cde3a3889d7375e881181e9152d95fc2
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/android_hardware_Camera.cpp | 240 |
1 files changed, 173 insertions, 67 deletions
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 9f70509..bfbfd37 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -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,32 +212,28 @@ 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!"); + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); + mManualCameraCallbackSet = false; + + if (obj == NULL) { + return; + } } + break; } - } - - if(mCallbackBuffers.isEmpty()) { - LOGV("Out of buffers, clearing callback!"); - mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); - mManualCameraCallbackSet = false; - - if (obj == NULL) { + case CAMERA_MSG_RAW_IMAGE: { + obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size); + break; + } + default: { + jniThrowException(env, + "java/lang/RuntimeException", "Unsupported message type"); return; } } @@ -212,21 +269,27 @@ void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr) } // return data based on callback type - switch(msgType) { - case CAMERA_MSG_VIDEO_FRAME: - // should never happen - break; - // don't return raw data to Java - case CAMERA_MSG_RAW_IMAGE: - LOGV("rawCallback"); - env->CallStaticVoidMethod(mCameraJClass, fields.post_event, - mCameraJObjectWeak, msgType, 0, 0, NULL); - break; - default: - // TODO: Change to LOGV - LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); - copyAndPost(env, dataPtr, msgType); - break; + switch (msgType) { + case CAMERA_MSG_VIDEO_FRAME: + // should never happen + break; + + // 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: + 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,24 +322,44 @@ 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); - jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); - mCallbackBuffers.push(cbb); - - 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. - if (mManualBufferMode && !mManualCameraCallbackSet) { - mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA); - mManualCameraCallbackSet = true; + switch (msgType) { + case CAMERA_MSG_PREVIEW_FRAME: { + jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); + mCallbackBuffers.push(callbackBuffer); + + 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. + 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", |