android_hardware_Camera.cpp revision b8a10fe45657f2dcc50cae8a06805f8438a6937e
1/* 2** 3** Copyright 2008, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18//#define LOG_NDEBUG 0 19#define LOG_TAG "Camera-JNI" 20#include <utils/Log.h> 21 22#include "jni.h" 23#include "JNIHelp.h" 24#include "android_runtime/AndroidRuntime.h" 25 26#include <ui/Surface.h> 27#include <ui/Camera.h> 28#include <utils/IMemory.h> 29 30using namespace android; 31 32enum CallbackMessageID { 33 kShutterCallback = 0, 34 kRawCallback = 1, 35 kJpegCallback = 2, 36 kPreviewCallback = 3, 37 kAutoFocusCallback = 4, 38 kErrorCallback = 5 39}; 40 41enum CameraError { 42 kCameraErrorUnknown = 1, 43 kCameraErrorMediaServer = 100 44}; 45 46 47struct fields_t { 48 jfieldID context; 49 jfieldID surface; 50 jmethodID post_event; 51}; 52 53static fields_t fields; 54static Mutex sLock; 55 56// provides persistent context for calls from native code to Java 57class JNICameraContext: public CameraListener 58{ 59public: 60 JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera); 61 ~JNICameraContext() { release(); } 62 virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); 63 virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr); 64 sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; } 65 void release(); 66 67private: 68 void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType); 69 70 jobject mCameraJObjectWeak; // weak reference to java object 71 jclass mCameraJClass; // strong reference to java class 72 sp<Camera> mCamera; // strong reference to native object 73 Mutex mLock; 74}; 75 76sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext) 77{ 78 sp<Camera> camera; 79 Mutex::Autolock _l(sLock); 80 JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context)); 81 if (context != NULL) { 82 camera = context->getCamera(); 83 } 84 LOGV("get_native_camera: context=%p, camera=%p", context, camera.get()); 85 if (camera == 0) { 86 jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); 87 } 88 89 if (pContext != NULL) *pContext = context; 90 return camera; 91} 92 93JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera) 94{ 95 mCameraJObjectWeak = env->NewGlobalRef(weak_this); 96 mCameraJClass = (jclass)env->NewGlobalRef(clazz); 97 mCamera = camera; 98} 99 100void JNICameraContext::release() 101{ 102 LOGV("release"); 103 Mutex::Autolock _l(mLock); 104 JNIEnv *env = AndroidRuntime::getJNIEnv(); 105 106 if (mCameraJObjectWeak != NULL) { 107 env->DeleteGlobalRef(mCameraJObjectWeak); 108 mCameraJObjectWeak = NULL; 109 } 110 if (mCameraJClass != NULL) { 111 env->DeleteGlobalRef(mCameraJClass); 112 mCameraJClass = NULL; 113 } 114 mCamera.clear(); 115} 116 117void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2) 118{ 119 LOGV("notify"); 120 121 // VM pointer will be NULL if object is released 122 Mutex::Autolock _l(mLock); 123 if (mCameraJObjectWeak == NULL) { 124 LOGW("callback on dead camera object"); 125 return; 126 } 127 JNIEnv *env = AndroidRuntime::getJNIEnv(); 128 env->CallStaticVoidMethod(mCameraJClass, fields.post_event, 129 mCameraJObjectWeak, msgType, ext1, ext2); 130} 131 132void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) 133{ 134 jbyteArray obj = NULL; 135 136 // allocate Java byte array and copy data 137 if (dataPtr != NULL) { 138 ssize_t offset; 139 size_t size; 140 sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size); 141 LOGV("postData: off=%d, size=%d", offset, size); 142 uint8_t *heapBase = (uint8_t*)heap->base(); 143 144 if (heapBase != NULL) { 145 uint8_t *data = heapBase + offset; 146 obj = env->NewByteArray(size); 147 if (obj == NULL) { 148 LOGE("Couldn't allocate byte array for JPEG data"); 149 env->ExceptionClear(); 150 } else { 151 jbyte *bytes = env->GetByteArrayElements(obj, NULL); 152 memcpy(bytes, data, size); 153 env->ReleaseByteArrayElements(obj, bytes, 0); 154 155 } 156 } else { 157 LOGE("image heap is NULL"); 158 } 159 } 160 161 // post image data to Java 162 env->CallStaticVoidMethod(mCameraJClass, fields.post_event, 163 mCameraJObjectWeak, msgType, 0, 0, obj); 164 if (obj) { 165 env->DeleteLocalRef(obj); 166 } 167} 168 169void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr) 170{ 171 // VM pointer will be NULL if object is released 172 Mutex::Autolock _l(mLock); 173 JNIEnv *env = AndroidRuntime::getJNIEnv(); 174 175 // return data based on callback type 176 switch(msgType) { 177 case CAMERA_MSG_VIDEO_FRAME: 178 // should never happen 179 break; 180 // don't return raw data to Java 181 case CAMERA_MSG_RAW_IMAGE: 182 LOGV("rawCallback"); 183 env->CallStaticVoidMethod(mCameraJClass, fields.post_event, 184 mCameraJObjectWeak, msgType, 0, 0, NULL); 185 break; 186 default: 187 LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); 188 copyAndPost(env, dataPtr, msgType); 189 break; 190 } 191} 192 193// connect to camera service 194static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) 195{ 196 sp<Camera> camera = Camera::connect(); 197 198 if (camera == NULL) { 199 jniThrowException(env, "java/lang/RuntimeException", 200 "Fail to connect to camera service"); 201 return; 202 } 203 204 // make sure camera hardware is alive 205 if (camera->getStatus() != NO_ERROR) { 206 jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed"); 207 return; 208 } 209 210 jclass clazz = env->GetObjectClass(thiz); 211 if (clazz == NULL) { 212 jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera"); 213 return; 214 } 215 216 // We use a weak reference so the Camera object can be garbage collected. 217 // The reference is only used as a proxy for callbacks. 218 sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera); 219 context->incStrong(thiz); 220 camera->setListener(context); 221 222 // save context in opaque field 223 env->SetIntField(thiz, fields.context, (int)context.get()); 224} 225 226// disconnect from camera service 227// It's okay to call this when the native camera context is already null. 228// This handles the case where the user has called release() and the 229// finalizer is invoked later. 230static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) 231{ 232 JNICameraContext* context = NULL; 233 sp<Camera> camera; 234 { 235 Mutex::Autolock _l(sLock); 236 context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context)); 237 238 // Make sure we do not attempt to callback on a deleted Java object. 239 env->SetIntField(thiz, fields.context, 0); 240 } 241 242 // clean up if release has not been called before 243 if (context != NULL) { 244 camera = context->getCamera(); 245 context->release(); 246 LOGV("native_release: context=%p camera=%p", context, camera.get()); 247 248 // clear callbacks 249 if (camera != NULL) { 250 camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); 251 camera->disconnect(); 252 } 253 254 // remove context to prevent further Java access 255 context->decStrong(thiz); 256 } 257} 258 259static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface) 260{ 261 LOGV("setPreviewDisplay"); 262 sp<Camera> camera = get_native_camera(env, thiz, NULL); 263 if (camera == 0) return; 264 265 sp<Surface> surface = NULL; 266 if (jSurface != NULL) { 267 surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); 268 } 269 if (camera->setPreviewDisplay(surface) != NO_ERROR) { 270 jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); 271 } 272} 273 274static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) 275{ 276 LOGV("startPreview"); 277 sp<Camera> camera = get_native_camera(env, thiz, NULL); 278 if (camera == 0) return; 279 280 if (camera->startPreview() != NO_ERROR) { 281 jniThrowException(env, "java/lang/RuntimeException", "startPreview failed"); 282 return; 283 } 284} 285 286static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz) 287{ 288 LOGV("stopPreview"); 289 sp<Camera> c = get_native_camera(env, thiz, NULL); 290 if (c == 0) return; 291 292 c->stopPreview(); 293} 294 295static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz) 296{ 297 LOGV("previewEnabled"); 298 sp<Camera> c = get_native_camera(env, thiz, NULL); 299 if (c == 0) return false; 300 301 return c->previewEnabled(); 302} 303 304static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean oneshot) 305{ 306 // Important: Only install preview_callback if the Java code has called 307 // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy 308 // each preview frame for nothing. 309 JNICameraContext* context; 310 sp<Camera> camera = get_native_camera(env, thiz, &context); 311 if (camera == 0) return; 312 313 int callback_flag; 314 if (installed) { 315 callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA; 316 } else { 317 callback_flag = FRAME_CALLBACK_FLAG_NOOP; 318 } 319 camera->setPreviewCallbackFlags(callback_flag); 320} 321 322static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) 323{ 324 LOGV("autoFocus"); 325 JNICameraContext* context; 326 sp<Camera> c = get_native_camera(env, thiz, &context); 327 if (c == 0) return; 328 329 if (c->autoFocus() != NO_ERROR) { 330 jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed"); 331 } 332} 333 334static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) 335{ 336 LOGV("takePicture"); 337 JNICameraContext* context; 338 sp<Camera> camera = get_native_camera(env, thiz, &context); 339 if (camera == 0) return; 340 341 if (camera->takePicture() != NO_ERROR) { 342 jniThrowException(env, "java/lang/RuntimeException", "takePicture failed"); 343 return; 344 } 345} 346 347static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params) 348{ 349 LOGV("setParameters"); 350 sp<Camera> camera = get_native_camera(env, thiz, NULL); 351 if (camera == 0) return; 352 353 const jchar* str = env->GetStringCritical(params, 0); 354 String8 params8; 355 if (params) { 356 params8 = String8(str, env->GetStringLength(params)); 357 env->ReleaseStringCritical(params, str); 358 } 359 if (camera->setParameters(params8) != NO_ERROR) { 360 jniThrowException(env, "java/lang/RuntimeException", "setParameters failed"); 361 return; 362 } 363} 364 365static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz) 366{ 367 LOGV("getParameters"); 368 sp<Camera> camera = get_native_camera(env, thiz, NULL); 369 if (camera == 0) return 0; 370 371 return env->NewStringUTF(camera->getParameters().string()); 372} 373 374static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz) 375{ 376 LOGV("reconnect"); 377 sp<Camera> camera = get_native_camera(env, thiz, NULL); 378 if (camera == 0) return; 379 380 if (camera->reconnect() != NO_ERROR) { 381 jniThrowException(env, "java/io/IOException", "reconnect failed"); 382 return; 383 } 384} 385 386static jint android_hardware_Camera_lock(JNIEnv *env, jobject thiz) 387{ 388 LOGV("lock"); 389 sp<Camera> camera = get_native_camera(env, thiz, NULL); 390 if (camera == 0) return INVALID_OPERATION; 391 return (jint) camera->lock(); 392} 393 394static jint android_hardware_Camera_unlock(JNIEnv *env, jobject thiz) 395{ 396 LOGV("unlock"); 397 sp<Camera> camera = get_native_camera(env, thiz, NULL); 398 if (camera == 0) return INVALID_OPERATION; 399 return (jint) camera->unlock(); 400} 401 402//------------------------------------------------- 403 404static JNINativeMethod camMethods[] = { 405 { "native_setup", 406 "(Ljava/lang/Object;)V", 407 (void*)android_hardware_Camera_native_setup }, 408 { "native_release", 409 "()V", 410 (void*)android_hardware_Camera_release }, 411 { "setPreviewDisplay", 412 "(Landroid/view/Surface;)V", 413 (void *)android_hardware_Camera_setPreviewDisplay }, 414 { "startPreview", 415 "()V", 416 (void *)android_hardware_Camera_startPreview }, 417 { "stopPreview", 418 "()V", 419 (void *)android_hardware_Camera_stopPreview }, 420 { "previewEnabled", 421 "()Z", 422 (void *)android_hardware_Camera_previewEnabled }, 423 { "setHasPreviewCallback", 424 "(ZZ)V", 425 (void *)android_hardware_Camera_setHasPreviewCallback }, 426 { "native_autoFocus", 427 "()V", 428 (void *)android_hardware_Camera_autoFocus }, 429 { "native_takePicture", 430 "()V", 431 (void *)android_hardware_Camera_takePicture }, 432 { "native_setParameters", 433 "(Ljava/lang/String;)V", 434 (void *)android_hardware_Camera_setParameters }, 435 { "native_getParameters", 436 "()Ljava/lang/String;", 437 (void *)android_hardware_Camera_getParameters }, 438 { "reconnect", 439 "()V", 440 (void*)android_hardware_Camera_reconnect }, 441 { "lock", 442 "()I", 443 (void*)android_hardware_Camera_lock }, 444 { "unlock", 445 "()I", 446 (void*)android_hardware_Camera_unlock }, 447}; 448 449struct field { 450 const char *class_name; 451 const char *field_name; 452 const char *field_type; 453 jfieldID *jfield; 454}; 455 456static int find_fields(JNIEnv *env, field *fields, int count) 457{ 458 for (int i = 0; i < count; i++) { 459 field *f = &fields[i]; 460 jclass clazz = env->FindClass(f->class_name); 461 if (clazz == NULL) { 462 LOGE("Can't find %s", f->class_name); 463 return -1; 464 } 465 466 jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type); 467 if (field == NULL) { 468 LOGE("Can't find %s.%s", f->class_name, f->field_name); 469 return -1; 470 } 471 472 *(f->jfield) = field; 473 } 474 475 return 0; 476} 477 478// Get all the required offsets in java class and register native functions 479int register_android_hardware_Camera(JNIEnv *env) 480{ 481 field fields_to_find[] = { 482 { "android/hardware/Camera", "mNativeContext", "I", &fields.context }, 483 { "android/view/Surface", "mSurface", "I", &fields.surface } 484 }; 485 486 if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) 487 return -1; 488 489 jclass clazz = env->FindClass("android/hardware/Camera"); 490 fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", 491 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 492 if (fields.post_event == NULL) { 493 LOGE("Can't find android/hardware/Camera.postEventFromNative"); 494 return -1; 495 } 496 497 498 // Register native functions 499 return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera", 500 camMethods, NELEM(camMethods)); 501} 502 503