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.
This commit is contained in:
@@ -81,6 +81,7 @@ public class Camera {
|
|||||||
private ZoomCallback mZoomCallback;
|
private ZoomCallback mZoomCallback;
|
||||||
private ErrorCallback mErrorCallback;
|
private ErrorCallback mErrorCallback;
|
||||||
private boolean mOneShot;
|
private boolean mOneShot;
|
||||||
|
private boolean mWithBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new Camera object.
|
* Returns a new Camera object.
|
||||||
@@ -229,9 +230,10 @@ public class Camera {
|
|||||||
public final void setPreviewCallback(PreviewCallback cb) {
|
public final void setPreviewCallback(PreviewCallback cb) {
|
||||||
mPreviewCallback = cb;
|
mPreviewCallback = cb;
|
||||||
mOneShot = false;
|
mOneShot = false;
|
||||||
|
mWithBuffer = false;
|
||||||
// Always use one-shot mode. We fake camera preview mode by
|
// Always use one-shot mode. We fake camera preview mode by
|
||||||
// doing one-shot preview continuously.
|
// doing one-shot preview continuously.
|
||||||
setHasPreviewCallback(cb != null, true);
|
setHasPreviewCallback(cb != null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,14 +243,48 @@ public class Camera {
|
|||||||
* @param cb A callback object that receives a copy of the preview frame.
|
* @param cb A callback object that receives a copy of the preview frame.
|
||||||
*/
|
*/
|
||||||
public final void setOneShotPreviewCallback(PreviewCallback cb) {
|
public final void setOneShotPreviewCallback(PreviewCallback cb) {
|
||||||
if (cb != null) {
|
mPreviewCallback = cb;
|
||||||
mPreviewCallback = cb;
|
mOneShot = true;
|
||||||
mOneShot = true;
|
mWithBuffer = false;
|
||||||
setHasPreviewCallback(true, true);
|
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
|
private class EventHandler extends Handler
|
||||||
{
|
{
|
||||||
@@ -288,11 +324,11 @@ public class Camera {
|
|||||||
// in case the app calls setPreviewCallback from
|
// in case the app calls setPreviewCallback from
|
||||||
// the callback function
|
// the callback function
|
||||||
mPreviewCallback = null;
|
mPreviewCallback = null;
|
||||||
} else {
|
} else if (!mWithBuffer) {
|
||||||
// We're faking the camera preview mode to prevent
|
// We're faking the camera preview mode to prevent
|
||||||
// the app from being flooded with preview frames.
|
// the app from being flooded with preview frames.
|
||||||
// Set to oneshot mode again.
|
// Set to oneshot mode again.
|
||||||
setHasPreviewCallback(true, true);
|
setHasPreviewCallback(true, false);
|
||||||
}
|
}
|
||||||
cb.onPreviewFrame((byte[])msg.obj, mCamera);
|
cb.onPreviewFrame((byte[])msg.obj, mCamera);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
#include "JNIHelp.h"
|
#include "JNIHelp.h"
|
||||||
#include "android_runtime/AndroidRuntime.h"
|
#include "android_runtime/AndroidRuntime.h"
|
||||||
|
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
#include <ui/Surface.h>
|
#include <ui/Surface.h>
|
||||||
#include <ui/Camera.h>
|
#include <ui/Camera.h>
|
||||||
#include <binder/IMemory.h>
|
#include <binder/IMemory.h>
|
||||||
@@ -47,16 +49,23 @@ public:
|
|||||||
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
|
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
|
||||||
virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
|
virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
|
||||||
virtual void postDataTimestamp(nsecs_t timestamp, 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; }
|
sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
|
||||||
void release();
|
void release();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
|
void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
|
||||||
|
void clearCallbackBuffers_l(JNIEnv *env);
|
||||||
|
|
||||||
jobject mCameraJObjectWeak; // weak reference to java object
|
jobject mCameraJObjectWeak; // weak reference to java object
|
||||||
jclass mCameraJClass; // strong reference to java class
|
jclass mCameraJClass; // strong reference to java class
|
||||||
sp<Camera> mCamera; // strong reference to native object
|
sp<Camera> mCamera; // strong reference to native object
|
||||||
Mutex mLock;
|
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)
|
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
|
||||||
@@ -81,6 +90,9 @@ JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz,
|
|||||||
mCameraJObjectWeak = env->NewGlobalRef(weak_this);
|
mCameraJObjectWeak = env->NewGlobalRef(weak_this);
|
||||||
mCameraJClass = (jclass)env->NewGlobalRef(clazz);
|
mCameraJClass = (jclass)env->NewGlobalRef(clazz);
|
||||||
mCamera = camera;
|
mCamera = camera;
|
||||||
|
|
||||||
|
mManualBufferMode = false;
|
||||||
|
mManualCameraCallbackSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JNICameraContext::release()
|
void JNICameraContext::release()
|
||||||
@@ -97,6 +109,7 @@ void JNICameraContext::release()
|
|||||||
env->DeleteGlobalRef(mCameraJClass);
|
env->DeleteGlobalRef(mCameraJClass);
|
||||||
mCameraJClass = NULL;
|
mCameraJClass = NULL;
|
||||||
}
|
}
|
||||||
|
clearCallbackBuffers_l(env);
|
||||||
mCamera.clear();
|
mCamera.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +142,42 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int
|
|||||||
|
|
||||||
if (heapBase != NULL) {
|
if (heapBase != NULL) {
|
||||||
const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
|
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) {
|
if (obj == NULL) {
|
||||||
LOGE("Couldn't allocate byte array for JPEG data");
|
LOGE("Couldn't allocate byte array for JPEG data");
|
||||||
env->ExceptionClear();
|
env->ExceptionClear();
|
||||||
@@ -184,6 +232,62 @@ void JNICameraContext::postDataTimestamp(nsecs_t timestamp, int32_t msgType, con
|
|||||||
postData(msgType, dataPtr);
|
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
|
// connect to camera service
|
||||||
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
|
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
|
||||||
{
|
{
|
||||||
@@ -297,8 +401,9 @@ static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz)
|
|||||||
return c->previewEnabled();
|
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
|
// Important: Only install preview_callback if the Java code has called
|
||||||
// setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
|
// setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
|
||||||
// each preview frame for nothing.
|
// each preview frame for nothing.
|
||||||
@@ -306,13 +411,19 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t
|
|||||||
sp<Camera> camera = get_native_camera(env, thiz, &context);
|
sp<Camera> camera = get_native_camera(env, thiz, &context);
|
||||||
if (camera == 0) return;
|
if (camera == 0) return;
|
||||||
|
|
||||||
int callback_flag;
|
// setCallbackMode will take care of setting the context flags and calling
|
||||||
if (installed) {
|
// camera->setPreviewCallbackFlags within a mutex for us.
|
||||||
callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA;
|
context->setCallbackMode(env, installed, manualBuffer);
|
||||||
} else {
|
}
|
||||||
callback_flag = FRAME_CALLBACK_FLAG_NOOP;
|
|
||||||
|
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)
|
static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
|
||||||
@@ -459,6 +570,9 @@ static JNINativeMethod camMethods[] = {
|
|||||||
{ "setHasPreviewCallback",
|
{ "setHasPreviewCallback",
|
||||||
"(ZZ)V",
|
"(ZZ)V",
|
||||||
(void *)android_hardware_Camera_setHasPreviewCallback },
|
(void *)android_hardware_Camera_setHasPreviewCallback },
|
||||||
|
{ "addCallbackBuffer",
|
||||||
|
"([B)V",
|
||||||
|
(void *)android_hardware_Camera_addCallbackBuffer },
|
||||||
{ "native_autoFocus",
|
{ "native_autoFocus",
|
||||||
"()V",
|
"()V",
|
||||||
(void *)android_hardware_Camera_autoFocus },
|
(void *)android_hardware_Camera_autoFocus },
|
||||||
|
|||||||
Reference in New Issue
Block a user