android_hardware_Camera.cpp revision 22f7dfd23490a3de2f21ff96949ba47003aac8f8
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_TAG "Camera-JNI" 19 20#include "jni.h" 21#include "JNIHelp.h" 22#include "android_runtime/AndroidRuntime.h" 23 24#include <ui/Surface.h> 25#include <ui/Camera.h> 26#include <utils/IMemory.h> 27 28using namespace android; 29 30enum CallbackMessageID { 31 kShutterCallback = 0, 32 kRawCallback = 1, 33 kJpegCallback = 2, 34 kPreviewCallback = 3, 35 kAutoFocusCallback = 4, 36 kErrorCallback = 5 37}; 38 39enum CameraError { 40 kCameraErrorUnknown = 1, 41 kCameraErrorMediaServer = 100 42}; 43 44 45struct fields_t { 46 jfieldID context; 47 jfieldID surface; 48 jfieldID listener_context; 49 jmethodID post_event; 50}; 51 52static fields_t fields; 53static Mutex sLock; 54 55struct callback_cookie { 56 jclass camera_class; 57 jobject camera_ref; 58}; 59 60sp<Camera> get_native_camera(JNIEnv *env, jobject thiz) 61{ 62 Mutex::Autolock _l(sLock); 63 sp<Camera> c = reinterpret_cast<Camera*>(env->GetIntField(thiz, fields.context)); 64 if (c == 0) 65 jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); 66 67 return c; 68} 69 70static void err_callback(status_t err, void *cookie) 71{ 72 JNIEnv *env = AndroidRuntime::getJNIEnv(); 73 if (env == NULL) { 74 LOGE("err_callback on dead VM"); 75 return; 76 } 77 callback_cookie *c = (callback_cookie *)cookie; 78 int error; 79 80 switch (err) { 81 case DEAD_OBJECT: 82 error = kCameraErrorMediaServer; 83 break; 84 default: 85 error = kCameraErrorUnknown; 86 break; 87 } 88 LOGV("err_callback: camera_ref=%x, cookie=%x", (int)c->camera_ref, (int)cookie); 89 90 env->CallStaticVoidMethod(c->camera_class, fields.post_event, 91 c->camera_ref, kErrorCallback, error, 0, NULL); 92} 93 94// connect to camera service 95static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) 96{ 97 sp<Camera> c = Camera::connect(); 98 99 if (c == NULL) { 100 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 101 return; 102 } 103 104 // make sure camera hardware is alive 105 if (c->getStatus() != NO_ERROR) { 106 jniThrowException(env, "java/io/IOException", "Camera initialization failed"); 107 return; 108 } 109 110 callback_cookie *cookie = new callback_cookie; 111 jclass clazz = env->GetObjectClass(thiz); 112 if (clazz == NULL) { 113 LOGE("Can't find android/hardware/Camera"); 114 // XXX no idea what to throw here, can this even happen? 115 jniThrowException(env, "java/lang/Exception", NULL); 116 return; 117 } 118 cookie->camera_class = (jclass)env->NewGlobalRef(clazz); 119 120 // We use a weak reference so the Camera object can be garbage collected. 121 // The reference is only used as a proxy for callbacks. 122 cookie->camera_ref = env->NewGlobalRef(weak_this); 123 env->SetIntField(thiz, fields.listener_context, (int)cookie); 124 125 LOGV("native_setup: camera_ref=%x, camera_obj=%x, cookie=%x", (int)cookie->camera_ref, (int)thiz, (int)cookie); 126 127 // save camera object in opaque field 128 env->SetIntField(thiz, fields.context, reinterpret_cast<int>(c.get())); 129 130 c->setErrorCallback(err_callback, cookie); 131 132 // hold a strong reference so the camera doesn't go away while the app is still running 133 c->incStrong(thiz); 134} 135 136// disconnect from camera service 137static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) 138{ 139 Mutex::Autolock _l(sLock); 140 sp<Camera> c = reinterpret_cast<Camera*>(env->GetIntField(thiz, fields.context)); 141 // It's okay to call this when the native camera context is already null. 142 // This handles the case where the user has called release() and the 143 // finalizer is invoked later. 144 if (c != 0) { 145 // Make sure that we do not attempt to deliver an eror callback on a deleted 146 // Java object. 147 c->setErrorCallback(NULL, NULL); 148 c->disconnect(); 149 150 // remove our strong reference created in native setup 151 c->decStrong(thiz); 152 env->SetIntField(thiz, fields.context, 0); 153 154 callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); 155 156 LOGV("release: camera_ref=%x, camera_obj=%x, cookie=%x", (int)cookie->camera_ref, (int)thiz, (int)cookie); 157 158 if (cookie) { 159 env->DeleteGlobalRef(cookie->camera_ref); 160 env->DeleteGlobalRef(cookie->camera_class); 161 delete cookie; 162 env->SetIntField(thiz, fields.listener_context, 0); 163 } 164 } 165} 166 167static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject surface) 168{ 169 sp<Camera> c = get_native_camera(env, thiz); 170 if (c == 0) 171 return; 172 173 sp<Surface> s = (Surface *)env->GetIntField(surface, fields.surface); 174 if (c->setPreviewDisplay(s) != NO_ERROR) { 175 jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); 176 return; 177 } 178} 179 180static void preview_callback(const sp<IMemory>& mem, void *cookie) 181{ 182 JNIEnv *env = AndroidRuntime::getJNIEnv(); 183 if (env == NULL) { 184 LOGE("preview_callback on dead VM"); 185 return; 186 } 187 callback_cookie *c = (callback_cookie *)cookie; 188 int arg1 = 0, arg2 = 0; 189 jobject obj = NULL; 190 191 ssize_t offset; 192 size_t size; 193 sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); 194 195 uint8_t *data = ((uint8_t *)heap->base()) + offset; 196 197 jbyteArray array = env->NewByteArray(size); 198 if (array == NULL) { 199 LOGE("Couldn't allocate byte array for YUV data"); 200 env->ExceptionClear(); 201 return; 202 } 203 204 jbyte *bytes = env->GetByteArrayElements(array, NULL); 205 memcpy(bytes, data, size); 206 env->ReleaseByteArrayElements(array, bytes, 0); 207 208 obj = array; 209 210 env->CallStaticVoidMethod(c->camera_class, fields.post_event, 211 c->camera_ref, kPreviewCallback, arg1, arg2, obj); 212 env->DeleteLocalRef(array); 213} 214 215static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) 216{ 217 sp<Camera> c = get_native_camera(env, thiz); 218 if (c == 0) 219 return; 220 221 if (c->startPreview() != NO_ERROR) { 222 jniThrowException(env, "java/io/IOException", "startPreview failed"); 223 return; 224 } 225} 226 227static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz) 228{ 229 sp<Camera> c = get_native_camera(env, thiz); 230 if (c == 0) 231 return; 232 233 c->stopPreview(); 234} 235 236static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz) 237{ 238 sp<Camera> c = get_native_camera(env, thiz); 239 if (c == 0) 240 return false; 241 242 return c->previewEnabled(); 243} 244 245static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean oneshot) 246{ 247 sp<Camera> c = get_native_camera(env, thiz); 248 if (c == 0) 249 return; 250 251 // Important: Only install preview_callback if the Java code has called 252 // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy 253 // each preview frame for nothing. 254 callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); 255 256 int callback_flag; 257 if (installed) { 258 callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA; 259 } else { 260 callback_flag = FRAME_CALLBACK_FLAG_NOOP; 261 } 262 c->setFrameCallback(installed ? preview_callback : NULL, cookie, callback_flag); 263} 264 265static void autofocus_callback_impl(bool success, void *cookie) 266{ 267 JNIEnv *env = AndroidRuntime::getJNIEnv(); 268 if (env == NULL) { 269 LOGE("autofocus_callback on dead VM"); 270 return; 271 } 272 callback_cookie *c = (callback_cookie *)cookie; 273 env->CallStaticVoidMethod(c->camera_class, fields.post_event, 274 c->camera_ref, kAutoFocusCallback, 275 success, 0, NULL); 276} 277 278 279 280static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) 281{ 282 sp<Camera> c = get_native_camera(env, thiz); 283 if (c == 0) 284 return; 285 callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); 286 c->setAutoFocusCallback(autofocus_callback_impl, cookie); 287 if (c->autoFocus() != NO_ERROR) { 288 jniThrowException(env, "java/io/IOException", "autoFocus failed"); 289 } 290} 291 292static void jpeg_callback(const sp<IMemory>& mem, void *cookie) 293{ 294 JNIEnv *env = AndroidRuntime::getJNIEnv(); 295 if (env == NULL) { 296 LOGE("jpeg`_callback on dead VM"); 297 return; 298 } 299 callback_cookie *c = (callback_cookie *)cookie; 300 int arg1 = 0, arg2 = 0; 301 jobject obj = NULL; 302 303 if (mem == NULL) { 304 env->CallStaticVoidMethod(c->camera_class, fields.post_event, 305 c->camera_ref, kJpegCallback, arg1, arg2, NULL); 306 return; 307 } 308 ssize_t offset; 309 size_t size; 310 sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); 311 LOGV("jpeg_callback: mem off=%d, size=%d", offset, size); 312 313 uint8_t *heap_base = (uint8_t *)heap->base(); 314 if (heap_base == NULL) { 315 LOGE("YUV heap is NULL"); 316 return; 317 } 318 319 uint8_t *data = heap_base + offset; 320 321 jbyteArray array = env->NewByteArray(size); 322 if (array == NULL) { 323 LOGE("Couldn't allocate byte array for JPEG data"); 324 env->ExceptionClear(); 325 return; 326 } 327 328 jbyte *bytes = env->GetByteArrayElements(array, NULL); 329 memcpy(bytes, data, size); 330 env->ReleaseByteArrayElements(array, bytes, 0); 331 332 obj = array; 333 334 env->CallStaticVoidMethod(c->camera_class, fields.post_event, 335 c->camera_ref, kJpegCallback, arg1, arg2, obj); 336 env->DeleteLocalRef(array); 337} 338 339static void shutter_callback_impl(void *cookie) 340{ 341 JNIEnv *env = AndroidRuntime::getJNIEnv(); 342 if (env == NULL) { 343 LOGE("shutter_callback on dead VM"); 344 return; 345 } 346 callback_cookie *c = (callback_cookie *)cookie; 347 env->CallStaticVoidMethod(c->camera_class, fields.post_event, 348 c->camera_ref, kShutterCallback, 0, 0, NULL); 349} 350 351static void raw_callback(const sp<IMemory>& mem __attribute__((unused)), 352 void *cookie) 353{ 354 JNIEnv *env = AndroidRuntime::getJNIEnv(); 355 if (env == NULL) { 356 LOGE("raw_callback on dead VM"); 357 return; 358 } 359 callback_cookie *c = (callback_cookie *)cookie; 360 env->CallStaticVoidMethod(c->camera_class, fields.post_event, 361 c->camera_ref, kRawCallback, 0, 0, NULL); 362} 363 364static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) 365{ 366 sp<Camera> c = get_native_camera(env, thiz); 367 if (c == 0) 368 return; 369 370 callback_cookie *cookie = 371 (callback_cookie *)env->GetIntField(thiz, fields.listener_context); 372 c->setShutterCallback(shutter_callback_impl, cookie); 373 c->setRawCallback(raw_callback, cookie); 374 c->setJpegCallback(jpeg_callback, cookie); 375 if (c->takePicture() != NO_ERROR) { 376 jniThrowException(env, "java/io/IOException", "takePicture failed"); 377 return; 378 } 379 380 return; 381} 382 383static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params) 384{ 385 sp<Camera> c = get_native_camera(env, thiz); 386 if (c == 0) 387 return; 388 389 const jchar* str = env->GetStringCritical(params, 0); 390 String8 params8; 391 if (params) { 392 params8 = String8(str, env->GetStringLength(params)); 393 env->ReleaseStringCritical(params, str); 394 } 395 if (c->setParameters(params8) != NO_ERROR) { 396 jniThrowException(env, "java/lang/IllegalArgumentException", "setParameters failed"); 397 return; 398 } 399} 400 401static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz) 402{ 403 sp<Camera> c = get_native_camera(env, thiz); 404 if (c == 0) 405 return 0; 406 407 return env->NewStringUTF(c->getParameters().string()); 408} 409 410static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz) 411{ 412 sp<Camera> c = get_native_camera(env, thiz); 413 if (c == 0) 414 return; 415 416 if (c->reconnect() != NO_ERROR) { 417 jniThrowException(env, "java/io/IOException", "reconnect failed"); 418 return; 419 } 420} 421 422static jint android_hardware_Camera_lock(JNIEnv *env, jobject thiz) 423{ 424 sp<Camera> c = get_native_camera(env, thiz); 425 if (c == 0) 426 return INVALID_OPERATION; 427 return (jint) c->lock(); 428} 429 430static jint android_hardware_Camera_unlock(JNIEnv *env, jobject thiz) 431{ 432 sp<Camera> c = get_native_camera(env, thiz); 433 if (c == 0) 434 return INVALID_OPERATION; 435 return (jint) c->unlock(); 436} 437 438//------------------------------------------------- 439 440static JNINativeMethod camMethods[] = { 441 { "native_setup", 442 "(Ljava/lang/Object;)V", 443 (void*)android_hardware_Camera_native_setup }, 444 { "native_release", 445 "()V", 446 (void*)android_hardware_Camera_release }, 447 { "setPreviewDisplay", 448 "(Landroid/view/Surface;)V", 449 (void *)android_hardware_Camera_setPreviewDisplay }, 450 { "startPreview", 451 "()V", 452 (void *)android_hardware_Camera_startPreview }, 453 { "stopPreview", 454 "()V", 455 (void *)android_hardware_Camera_stopPreview }, 456 { "previewEnabled", 457 "()Z", 458 (void *)android_hardware_Camera_previewEnabled }, 459 { "setHasPreviewCallback", 460 "(ZZ)V", 461 (void *)android_hardware_Camera_setHasPreviewCallback }, 462 { "native_autoFocus", 463 "()V", 464 (void *)android_hardware_Camera_autoFocus }, 465 { "native_takePicture", 466 "()V", 467 (void *)android_hardware_Camera_takePicture }, 468 { "native_setParameters", 469 "(Ljava/lang/String;)V", 470 (void *)android_hardware_Camera_setParameters }, 471 { "native_getParameters", 472 "()Ljava/lang/String;", 473 (void *)android_hardware_Camera_getParameters }, 474 { "reconnect", 475 "()V", 476 (void*)android_hardware_Camera_reconnect }, 477 { "lock", 478 "()I", 479 (void*)android_hardware_Camera_lock }, 480 { "unlock", 481 "()I", 482 (void*)android_hardware_Camera_unlock }, 483}; 484 485struct field { 486 const char *class_name; 487 const char *field_name; 488 const char *field_type; 489 jfieldID *jfield; 490}; 491 492static int find_fields(JNIEnv *env, field *fields, int count) 493{ 494 for (int i = 0; i < count; i++) { 495 field *f = &fields[i]; 496 jclass clazz = env->FindClass(f->class_name); 497 if (clazz == NULL) { 498 LOGE("Can't find %s", f->class_name); 499 return -1; 500 } 501 502 jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type); 503 if (field == NULL) { 504 LOGE("Can't find %s.%s", f->class_name, f->field_name); 505 return -1; 506 } 507 508 *(f->jfield) = field; 509 } 510 511 return 0; 512} 513 514// Get all the required offsets in java class and register native functions 515int register_android_hardware_Camera(JNIEnv *env) 516{ 517 field fields_to_find[] = { 518 { "android/hardware/Camera", "mNativeContext", "I", &fields.context }, 519 { "android/hardware/Camera", "mListenerContext", "I", &fields.listener_context }, 520 { "android/view/Surface", "mSurface", "I", &fields.surface } 521 }; 522 523 if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) 524 return -1; 525 526 jclass clazz = env->FindClass("android/hardware/Camera"); 527 fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", 528 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 529 if (fields.post_event == NULL) { 530 LOGE("Can't find android/hardware/Camera.postEventFromNative"); 531 return -1; 532 } 533 534 535 // Register native functions 536 return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera", 537 camMethods, NELEM(camMethods)); 538} 539 540