am 78a50aa1: Merge change If3c30fc9 into eclair
Merge commit '78a50aa1db6572ba7d9f9d91c6eb16f993c09f40' into eclair-mr2
* commit '78a50aa1db6572ba7d9f9d91c6eb16f993c09f40':
Patching in hidden API to allow app managed preview frame buffers.
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0c1c7ec..4b733ef 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -81,6 +81,7 @@
private ZoomCallback mZoomCallback;
private ErrorCallback mErrorCallback;
private boolean mOneShot;
+ private boolean mWithBuffer;
/**
* Returns a new Camera object.
@@ -229,9 +230,10 @@
public final void setPreviewCallback(PreviewCallback cb) {
mPreviewCallback = cb;
mOneShot = false;
+ mWithBuffer = false;
// Always use one-shot mode. We fake camera preview mode by
// doing one-shot preview continuously.
- setHasPreviewCallback(cb != null, true);
+ setHasPreviewCallback(cb != null, false);
}
/**
@@ -241,14 +243,48 @@
* @param cb A callback object that receives a copy of the preview frame.
*/
public final void setOneShotPreviewCallback(PreviewCallback cb) {
- if (cb != null) {
- mPreviewCallback = cb;
- mOneShot = true;
- setHasPreviewCallback(true, true);
- }
+ mPreviewCallback = cb;
+ mOneShot = true;
+ mWithBuffer = false;
+ setHasPreviewCallback(cb != null, false);
}
- private native final void setHasPreviewCallback(boolean installed, boolean oneshot);
+ private native final void setHasPreviewCallback(boolean installed, boolean manualBuffer);
+
+ /**
+ * Installs a callback which will get called as long as there are buffers in the
+ * preview buffer queue, which minimizes dynamic allocation of preview buffers.
+ *
+ * Apps must call addCallbackBuffer to explicitly register the buffers to use, or no callbacks
+ * will be received. addCallbackBuffer may be safely called before or after
+ * a call to setPreviewCallbackWithBuffer with a non-null callback parameter.
+ *
+ * The buffer queue will be cleared upon any calls to setOneShotPreviewCallback,
+ * setPreviewCallback, or to this method with a null callback parameter.
+ *
+ * @param cb A callback object that receives a copy of the preview frame. A null value will clear the queue.
+ * @hide
+ */
+ public final void setPreviewCallbackWithBuffer(PreviewCallback cb) {
+ mPreviewCallback = cb;
+ mOneShot = false;
+ mWithBuffer = true;
+ setHasPreviewCallback(cb != null, true);
+ }
+
+ /**
+ * Adds a pre-allocated buffer to the callback buffer queue.
+ * Preview width and height can be determined from getPreviewSize, and bitsPerPixel can be
+ * found from from {@link android.hardware.Camera.Parameters#getPreviewFormat()} and
+ * {@link android.graphics.PixelFormat#getPixelFormatInfo(int, PixelFormat)}
+ *
+ * Alternatively, a buffer from a previous callback may be passed in or used
+ * to determine the size of new preview frame buffers.
+ *
+ * @param callbackBuffer The buffer to register. Size should be width * height * bitsPerPixel / 8.
+ * @hide
+ */
+ public native final void addCallbackBuffer(byte[] callbackBuffer);
private class EventHandler extends Handler
{
@@ -288,11 +324,11 @@
// in case the app calls setPreviewCallback from
// the callback function
mPreviewCallback = null;
- } else {
+ } else if (!mWithBuffer) {
// We're faking the camera preview mode to prevent
// the app from being flooded with preview frames.
// Set to oneshot mode again.
- setHasPreviewCallback(true, true);
+ setHasPreviewCallback(true, false);
}
cb.onPreviewFrame((byte[])msg.obj, mCamera);
}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 5b6bd35..d57e526 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -23,6 +23,8 @@
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
+#include <utils/Vector.h>
+
#include <ui/Surface.h>
#include <ui/Camera.h>
#include <binder/IMemory.h>
@@ -47,16 +49,23 @@
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 setCallbackMode(JNIEnv *env, bool installed, bool manualMode);
sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
void release();
private:
void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
+ void clearCallbackBuffers_l(JNIEnv *env);
jobject mCameraJObjectWeak; // weak reference to java object
jclass mCameraJClass; // strong reference to java class
sp<Camera> mCamera; // strong reference to native object
Mutex mLock;
+
+ 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.
};
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
@@ -81,6 +90,9 @@
mCameraJObjectWeak = env->NewGlobalRef(weak_this);
mCameraJClass = (jclass)env->NewGlobalRef(clazz);
mCamera = camera;
+
+ mManualBufferMode = false;
+ mManualCameraCallbackSet = false;
}
void JNICameraContext::release()
@@ -97,6 +109,7 @@
env->DeleteGlobalRef(mCameraJClass);
mCameraJClass = NULL;
}
+ clearCallbackBuffers_l(env);
mCamera.clear();
}
@@ -129,7 +142,42 @@
if (heapBase != NULL) {
const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
- obj = env->NewByteArray(size);
+
+ if (!mManualBufferMode) {
+ 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;
+ }
+ }
+ }
+
+ if(mCallbackBuffers.isEmpty()) {
+ LOGW("Out of buffers, clearing callback!");
+ mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
+ mManualCameraCallbackSet = false;
+
+ if (obj == NULL) {
+ return;
+ }
+ }
+ }
+
if (obj == NULL) {
LOGE("Couldn't allocate byte array for JPEG data");
env->ExceptionClear();
@@ -184,6 +232,62 @@
postData(msgType, dataPtr);
}
+void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualMode)
+{
+ Mutex::Autolock _l(mLock);
+ mManualBufferMode = manualMode;
+ mManualCameraCallbackSet = false;
+
+ // In order to limit the over usage of binder threads, all non-manual buffer
+ // callbacks use FRAME_CALLBACK_FLAG_BARCODE_SCANNER mode now.
+ //
+ // Continuous callbacks will have the callback re-registered from handleMessage.
+ // Manual buffer mode will operate as fast as possible, relying on the finite supply
+ // of buffers for throttling.
+
+ if (!installed) {
+ mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
+ clearCallbackBuffers_l(env);
+ } else if (mManualBufferMode) {
+ if (!mCallbackBuffers.isEmpty()) {
+ mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
+ mManualCameraCallbackSet = true;
+ }
+ } else {
+ mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_BARCODE_SCANNER);
+ clearCallbackBuffers_l(env);
+ }
+}
+
+void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb)
+{
+ 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;
+ }
+ } else {
+ LOGE("Null byte array!");
+ }
+}
+
+void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env)
+{
+ LOGV("Clearing callback buffers, %d remained", mCallbackBuffers.size());
+ while(!mCallbackBuffers.isEmpty()) {
+ env->DeleteGlobalRef(mCallbackBuffers.top());
+ mCallbackBuffers.pop();
+ }
+}
+
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
@@ -297,8 +401,9 @@
return c->previewEnabled();
}
-static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean oneshot)
+static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean manualBuffer)
{
+ LOGV("setHasPreviewCallback: installed:%d, manualBuffer:%d", (int)installed, (int)manualBuffer);
// Important: Only install preview_callback if the Java code has called
// setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
// each preview frame for nothing.
@@ -306,13 +411,19 @@
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
- int callback_flag;
- if (installed) {
- callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA;
- } else {
- callback_flag = FRAME_CALLBACK_FLAG_NOOP;
+ // setCallbackMode will take care of setting the context flags and calling
+ // camera->setPreviewCallbackFlags within a mutex for us.
+ context->setCallbackMode(env, installed, manualBuffer);
+}
+
+static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) {
+ LOGV("addCallbackBuffer");
+
+ JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
+
+ if (context != NULL) {
+ context->addCallbackBuffer(env, bytes);
}
- camera->setPreviewCallbackFlags(callback_flag);
}
static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
@@ -459,6 +570,9 @@
{ "setHasPreviewCallback",
"(ZZ)V",
(void *)android_hardware_Camera_setHasPreviewCallback },
+ { "addCallbackBuffer",
+ "([B)V",
+ (void *)android_hardware_Camera_addCallbackBuffer },
{ "native_autoFocus",
"()V",
(void *)android_hardware_Camera_autoFocus },