AndroidCameraAgentImpl.java revision 4f425ba476d62b4be7078f2084af37cf306b31c6
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.ex.camera2.portability; 18 19import android.annotation.TargetApi; 20import android.graphics.SurfaceTexture; 21import android.hardware.Camera; 22import android.hardware.Camera.AutoFocusCallback; 23import android.hardware.Camera.AutoFocusMoveCallback; 24import android.hardware.Camera.ErrorCallback; 25import android.hardware.Camera.FaceDetectionListener; 26import android.hardware.Camera.OnZoomChangeListener; 27import android.hardware.Camera.Parameters; 28import android.hardware.Camera.PictureCallback; 29import android.hardware.Camera.PreviewCallback; 30import android.hardware.Camera.ShutterCallback; 31import android.os.Build; 32import android.os.Handler; 33import android.os.HandlerThread; 34import android.os.Looper; 35import android.os.Message; 36import android.view.SurfaceHolder; 37 38import com.android.ex.camera2.portability.debug.Log; 39 40import java.io.IOException; 41 42/** 43 * A class to implement {@link CameraAgent} of the Android camera framework. 44 */ 45class AndroidCameraAgentImpl implements CameraAgent { 46 private static final Log.Tag TAG = new Log.Tag("AndroidCamMgrImpl"); 47 48 private Parameters mParameters; 49 private boolean mParametersIsDirty; 50 private AndroidCameraCapabilities mCapabilities; 51 52 private final CameraHandler mCameraHandler; 53 private final HandlerThread mCameraHandlerThread; 54 private final CameraStateHolder mCameraState; 55 private final DispatchThread mDispatchThread; 56 57 private Handler mCameraExceptionCallbackHandler; 58 private CameraExceptionCallback mCameraExceptionCallback = 59 new CameraExceptionCallback() { 60 @Override 61 public void onCameraException(RuntimeException e) { 62 throw e; 63 } 64 }; 65 66 AndroidCameraAgentImpl() { 67 mCameraHandlerThread = new HandlerThread("Camera Handler Thread"); 68 mCameraHandlerThread.start(); 69 mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper()); 70 mCameraExceptionCallbackHandler = mCameraHandler; 71 mCameraState = new CameraStateHolder(); 72 mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread); 73 mDispatchThread.start(); 74 } 75 76 @Override 77 public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback, 78 Handler handler) { 79 synchronized (mCameraExceptionCallback) { 80 mCameraExceptionCallback = callback; 81 mCameraExceptionCallbackHandler = handler; 82 } 83 } 84 85 @Override 86 public void recycle() { 87 closeCamera(null, true); 88 mDispatchThread.end(); 89 } 90 91 @Override 92 public CameraDeviceInfo getCameraDeviceInfo() { 93 return AndroidCameraDeviceInfo.create(); 94 } 95 96 private static class AndroidCameraDeviceInfo implements CameraDeviceInfo { 97 private final Camera.CameraInfo[] mCameraInfos; 98 private final int mNumberOfCameras; 99 private final int mFirstBackCameraId; 100 private final int mFirstFrontCameraId; 101 102 private AndroidCameraDeviceInfo(Camera.CameraInfo[] info, int numberOfCameras, 103 int firstBackCameraId, int firstFrontCameraId) { 104 105 mCameraInfos = info; 106 mNumberOfCameras = numberOfCameras; 107 mFirstBackCameraId = firstBackCameraId; 108 mFirstFrontCameraId = firstFrontCameraId; 109 } 110 111 public static AndroidCameraDeviceInfo create() { 112 int numberOfCameras; 113 Camera.CameraInfo[] cameraInfos; 114 try { 115 numberOfCameras = Camera.getNumberOfCameras(); 116 cameraInfos = new Camera.CameraInfo[numberOfCameras]; 117 for (int i = 0; i < numberOfCameras; i++) { 118 cameraInfos[i] = new Camera.CameraInfo(); 119 Camera.getCameraInfo(i, cameraInfos[i]); 120 } 121 } catch (RuntimeException ex) { 122 return null; 123 } 124 125 int firstFront = NO_DEVICE; 126 int firstBack = NO_DEVICE; 127 // Get the first (smallest) back and first front camera id. 128 for (int i = numberOfCameras - 1; i >= 0; i--) { 129 if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_BACK) { 130 firstBack = i; 131 } else { 132 if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 133 firstFront = i; 134 } 135 } 136 } 137 138 return new AndroidCameraDeviceInfo(cameraInfos, numberOfCameras, firstBack, firstFront); 139 } 140 141 @Override 142 public Camera.CameraInfo[] getCameraInfos() { 143 return mCameraInfos; 144 } 145 146 @Override 147 public int getNumberOfCameras() { 148 return mNumberOfCameras; 149 } 150 151 @Override 152 public int getFirstBackCameraId() { 153 return mFirstBackCameraId; 154 } 155 156 @Override 157 public int getFirstFrontCameraId() { 158 return mFirstFrontCameraId; 159 } 160 } 161 162 /** 163 * The handler on which the actual camera operations happen. 164 */ 165 private class CameraHandler extends HistoryHandler { 166 167 // Used to retain a copy of Parameters for setting parameters. 168 private Parameters mParamsToSet; 169 private Camera mCamera; 170 private int mCameraId; 171 172 private class CaptureCallbacks { 173 public final ShutterCallback mShutter; 174 public final PictureCallback mRaw; 175 public final PictureCallback mPostView; 176 public final PictureCallback mJpeg; 177 178 CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView, 179 PictureCallback jpeg) { 180 mShutter = shutter; 181 mRaw = raw; 182 mPostView = postView; 183 mJpeg = jpeg; 184 } 185 } 186 187 CameraHandler(Looper looper) { 188 super(looper); 189 } 190 191 private void startFaceDetection() { 192 mCamera.startFaceDetection(); 193 } 194 195 private void stopFaceDetection() { 196 mCamera.stopFaceDetection(); 197 } 198 199 private void setFaceDetectionListener(FaceDetectionListener listener) { 200 mCamera.setFaceDetectionListener(listener); 201 } 202 203 private void setPreviewTexture(Object surfaceTexture) { 204 try { 205 mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture); 206 } catch (IOException e) { 207 Log.e(TAG, "Could not set preview texture", e); 208 } 209 } 210 211 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 212 private void enableShutterSound(boolean enable) { 213 mCamera.enableShutterSound(enable); 214 } 215 216 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 217 private void setAutoFocusMoveCallback( 218 android.hardware.Camera camera, Object cb) { 219 try { 220 camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb); 221 } catch (RuntimeException ex) { 222 Log.w(TAG, ex.getMessage()); 223 } 224 } 225 226 private void capture(final CaptureCallbacks cb) { 227 try { 228 mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg); 229 } catch (RuntimeException e) { 230 // TODO: output camera state and focus state for debugging. 231 Log.e(TAG, "take picture failed."); 232 throw e; 233 } 234 } 235 236 public void requestTakePicture( 237 final ShutterCallback shutter, 238 final PictureCallback raw, 239 final PictureCallback postView, 240 final PictureCallback jpeg) { 241 final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg); 242 obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget(); 243 } 244 245 /** 246 * This method does not deal with the API level check. Everyone should 247 * check first for supported operations before sending message to this handler. 248 */ 249 @Override 250 public void handleMessage(final Message msg) { 251 super.handleMessage(msg); 252 try { 253 switch (msg.what) { 254 case CameraActions.OPEN_CAMERA: { 255 final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj; 256 final int cameraId = msg.arg1; 257 if (mCameraState.getState() != CameraStateHolder.CAMERA_UNOPENED) { 258 openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId)); 259 break; 260 } 261 262 mCamera = android.hardware.Camera.open(cameraId); 263 if (mCamera != null) { 264 mCameraId = cameraId; 265 mParametersIsDirty = true; 266 267 // Get a instance of Camera.Parameters for later use. 268 mParamsToSet = mCamera.getParameters(); 269 mCapabilities = new AndroidCameraCapabilities(mParamsToSet); 270 271 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 272 if (openCallback != null) { 273 openCallback.onCameraOpened( 274 new AndroidCameraProxyImpl(cameraId, mCamera, 275 mCapabilities)); 276 } 277 } else { 278 if (openCallback != null) { 279 openCallback.onDeviceOpenFailure(cameraId, generateHistoryString(cameraId)); 280 } 281 } 282 break; 283 } 284 285 case CameraActions.RELEASE: { 286 if (mCamera != null) { 287 mCamera.release(); 288 mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED); 289 mCamera = null; 290 } else { 291 Log.w(TAG, "Releasing camera without any camera opened."); 292 } 293 break; 294 } 295 296 case CameraActions.RECONNECT: { 297 final CameraOpenCallbackForward cbForward = 298 (CameraOpenCallbackForward) msg.obj; 299 final int cameraId = msg.arg1; 300 try { 301 mCamera.reconnect(); 302 } catch (IOException ex) { 303 if (cbForward != null) { 304 cbForward.onReconnectionFailure(AndroidCameraAgentImpl.this, 305 generateHistoryString(mCameraId)); 306 } 307 break; 308 } 309 310 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 311 if (cbForward != null) { 312 cbForward.onCameraOpened( 313 new AndroidCameraProxyImpl(cameraId, mCamera, mCapabilities)); 314 } 315 break; 316 } 317 318 case CameraActions.UNLOCK: { 319 mCamera.unlock(); 320 mCameraState.setState(CameraStateHolder.CAMERA_UNLOCKED); 321 break; 322 } 323 324 case CameraActions.LOCK: { 325 mCamera.lock(); 326 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 327 break; 328 } 329 330 case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: { 331 setPreviewTexture(msg.obj); 332 break; 333 } 334 335 case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: { 336 try { 337 mCamera.setPreviewDisplay((SurfaceHolder) msg.obj); 338 } catch (IOException e) { 339 throw new RuntimeException(e); 340 } 341 break; 342 } 343 344 case CameraActions.START_PREVIEW_ASYNC: { 345 final CameraStartPreviewCallbackForward cbForward = 346 (CameraStartPreviewCallbackForward) msg.obj; 347 mCamera.startPreview(); 348 if (cbForward != null) { 349 cbForward.onPreviewStarted(); 350 } 351 break; 352 } 353 354 case CameraActions.STOP_PREVIEW: { 355 mCamera.stopPreview(); 356 break; 357 } 358 359 case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: { 360 mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj); 361 break; 362 } 363 364 case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: { 365 mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj); 366 break; 367 } 368 369 case CameraActions.ADD_CALLBACK_BUFFER: { 370 mCamera.addCallbackBuffer((byte[]) msg.obj); 371 break; 372 } 373 374 case CameraActions.AUTO_FOCUS: { 375 mCameraState.setState(CameraStateHolder.CAMERA_FOCUSING); 376 mCamera.autoFocus((AutoFocusCallback) msg.obj); 377 break; 378 } 379 380 case CameraActions.CANCEL_AUTO_FOCUS: { 381 mCamera.cancelAutoFocus(); 382 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 383 break; 384 } 385 386 case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: { 387 setAutoFocusMoveCallback(mCamera, msg.obj); 388 break; 389 } 390 391 case CameraActions.SET_DISPLAY_ORIENTATION: { 392 mCamera.setDisplayOrientation(msg.arg1); 393 break; 394 } 395 396 case CameraActions.SET_ZOOM_CHANGE_LISTENER: { 397 mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj); 398 break; 399 } 400 401 case CameraActions.SET_FACE_DETECTION_LISTENER: { 402 setFaceDetectionListener((FaceDetectionListener) msg.obj); 403 break; 404 } 405 406 case CameraActions.START_FACE_DETECTION: { 407 startFaceDetection(); 408 break; 409 } 410 411 case CameraActions.STOP_FACE_DETECTION: { 412 stopFaceDetection(); 413 break; 414 } 415 416 case CameraActions.SET_ERROR_CALLBACK: { 417 mCamera.setErrorCallback((ErrorCallback) msg.obj); 418 break; 419 } 420 421 case CameraActions.APPLY_SETTINGS: { 422 mParametersIsDirty = true; 423 CameraSettings settings = (CameraSettings) msg.obj; 424 applyToParameters(settings); 425 mCamera.setParameters(mParamsToSet); 426 break; 427 } 428 429 case CameraActions.SET_PARAMETERS: { 430 mParametersIsDirty = true; 431 mParamsToSet.unflatten((String) msg.obj); 432 mCamera.setParameters(mParamsToSet); 433 break; 434 } 435 436 case CameraActions.GET_PARAMETERS: { 437 if (mParametersIsDirty) { 438 mParameters = mCamera.getParameters(); 439 mParametersIsDirty = false; 440 } 441 break; 442 } 443 444 case CameraActions.SET_PREVIEW_CALLBACK: { 445 mCamera.setPreviewCallback((PreviewCallback) msg.obj); 446 break; 447 } 448 449 case CameraActions.ENABLE_SHUTTER_SOUND: { 450 enableShutterSound((msg.arg1 == 1) ? true : false); 451 break; 452 } 453 454 case CameraActions.REFRESH_PARAMETERS: { 455 mParametersIsDirty = true; 456 break; 457 } 458 459 case CameraActions.CAPTURE_PHOTO: { 460 mCameraState.setState(CameraStateHolder.CAMERA_CAPTURING); 461 capture((CaptureCallbacks) msg.obj); 462 break; 463 } 464 465 default: { 466 throw new RuntimeException("Invalid CameraProxy message=" + msg.what); 467 } 468 } 469 } catch (final RuntimeException e) { 470 if (msg.what != CameraActions.RELEASE && mCamera != null) { 471 try { 472 mCamera.release(); 473 mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED); 474 } catch (Exception ex) { 475 Log.e(TAG, "Fail to release the camera."); 476 } 477 mCamera = null; 478 } else { 479 if (mCamera == null) { 480 if (msg.what == CameraActions.OPEN_CAMERA) { 481 final int cameraId = msg.arg1; 482 if (msg.obj != null) { 483 ((CameraOpenCallback) msg.obj).onDeviceOpenFailure( 484 msg.arg1, generateHistoryString(cameraId)); 485 } 486 } else { 487 Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null."); 488 } 489 return; 490 } 491 } 492 synchronized (mCameraExceptionCallback) { 493 mCameraExceptionCallbackHandler.post(new Runnable() { 494 @Override 495 public void run() { 496 mCameraExceptionCallback.onCameraException(e); 497 } 498 }); 499 } 500 } 501 } 502 503 private void applyToParameters(final CameraSettings settings) { 504 final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier(); 505 Size photoSize = settings.getCurrentPhotoSize(); 506 mParamsToSet.setPictureSize(photoSize.width(), photoSize.height()); 507 Size previewSize = settings.getCurrentPreviewSize(); 508 mParamsToSet.setPreviewSize(previewSize.width(), previewSize.height()); 509 if (settings.getPreviewFrameRate() == -1) { 510 mParamsToSet.setPreviewFpsRange(settings.getPreviewFpsRangeMin(), 511 settings.getPreviewFpsRangeMax()); 512 } else { 513 mParamsToSet.setPreviewFrameRate(settings.getPreviewFrameRate()); 514 } 515 mParamsToSet.setJpegQuality(settings.getPhotoJpegCompressionQuality()); 516 if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) { 517 // Should use settings.getCurrentZoomRatio() instead here. 518 mParamsToSet.setZoom(settings.getCurrentZoomIndex()); 519 } 520 mParamsToSet.setRotation((int) settings.getCurrentPhotoRotationDegrees()); 521 mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex()); 522 if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) { 523 mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked()); 524 } 525 mParamsToSet.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode())); 526 if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) { 527 mParamsToSet.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked()); 528 } 529 if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) { 530 if (settings.getFocusAreas().size() != 0) { 531 mParamsToSet.setFocusAreas(settings.getFocusAreas()); 532 } 533 } 534 if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) { 535 if (settings.getMeteringAreas().size() != 0) { 536 mParamsToSet.setMeteringAreas(settings.getMeteringAreas()); 537 } 538 } 539 if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) { 540 mParamsToSet.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode())); 541 } 542 if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) { 543 if (settings.getCurrentSceneMode() != null) { 544 mParamsToSet 545 .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode())); 546 } 547 } 548 549 CameraSettings.GpsData gpsData = settings.getGpsData(); 550 if (gpsData == null) { 551 mParamsToSet.removeGpsData(); 552 } else { 553 mParamsToSet.setGpsTimestamp(gpsData.timeStamp); 554 if (gpsData.processingMethod != null) { 555 // It's a hack since we always use GPS time stamp but does 556 // not use other fields sometimes. Setting processing 557 // method to null means the other fields should not be used. 558 mParamsToSet.setGpsAltitude(gpsData.altitude); 559 mParamsToSet.setGpsLatitude(gpsData.latitude); 560 mParamsToSet.setGpsLongitude(gpsData.longitude); 561 mParamsToSet.setGpsProcessingMethod(gpsData.processingMethod); 562 } 563 } 564 565 } 566 } 567 568 @Override 569 public void openCamera(final Handler handler, final int cameraId, 570 final CameraOpenCallback callback) { 571 mDispatchThread.runJob(new Runnable() { 572 @Override 573 public void run() { 574 mCameraHandler.obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0, 575 CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget(); 576 } 577 }); 578 } 579 580 @Override 581 public void closeCamera(CameraProxy camera, boolean synced) { 582 if (synced) { 583 final WaitDoneBundle bundle = new WaitDoneBundle(); 584 585 mDispatchThread.runJobSync(new Runnable() { 586 @Override 587 public void run() { 588 mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget(); 589 mCameraHandler.post(bundle.mUnlockRunnable); 590 } 591 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release"); 592 } else { 593 mDispatchThread.runJob(new Runnable() { 594 @Override 595 public void run() { 596 mCameraHandler.removeCallbacksAndMessages(null); 597 mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget(); 598 } 599 }); 600 } 601 } 602 603 /** 604 * A class which implements {@link CameraAgent.CameraProxy} and 605 * camera handler thread. 606 */ 607 private class AndroidCameraProxyImpl implements CameraAgent.CameraProxy { 608 private final int mCameraId; 609 /* TODO: remove this Camera instance. */ 610 private final Camera mCamera; 611 private final AndroidCameraCapabilities mCapabilities; 612 613 private AndroidCameraProxyImpl(int cameraId, Camera camera, 614 AndroidCameraCapabilities capabilities) { 615 mCamera = camera; 616 mCameraId = cameraId; 617 mCapabilities = capabilities; 618 } 619 620 @Override 621 public android.hardware.Camera getCamera() { 622 return mCamera; 623 } 624 625 @Override 626 public int getCameraId() { 627 return mCameraId; 628 } 629 630 @Override 631 public CameraCapabilities getCapabilities() { 632 return new AndroidCameraCapabilities(mCapabilities); 633 } 634 635 @Override 636 public void reconnect(final Handler handler, final CameraOpenCallback cb) { 637 mDispatchThread.runJob(new Runnable() { 638 @Override 639 public void run() { 640 mCameraHandler.obtainMessage(CameraActions.RECONNECT, mCameraId, 0, 641 CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget(); 642 } 643 }); 644 } 645 646 @Override 647 public void unlock() { 648 final WaitDoneBundle bundle = new WaitDoneBundle(); 649 mDispatchThread.runJobSync(new Runnable() { 650 @Override 651 public void run() { 652 mCameraHandler.sendEmptyMessage(CameraActions.UNLOCK); 653 mCameraHandler.post(bundle.mUnlockRunnable); 654 } 655 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock"); 656 } 657 658 @Override 659 public void lock() { 660 mDispatchThread.runJob(new Runnable() { 661 @Override 662 public void run() { 663 mCameraHandler.sendEmptyMessage(CameraActions.LOCK); 664 } 665 }); 666 } 667 668 @Override 669 public void setPreviewTexture(final SurfaceTexture surfaceTexture) { 670 mDispatchThread.runJob(new Runnable() { 671 @Override 672 public void run() { 673 mCameraHandler 674 .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) 675 .sendToTarget(); 676 } 677 }); 678 } 679 680 @Override 681 public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) { 682 final WaitDoneBundle bundle = new WaitDoneBundle(); 683 mDispatchThread.runJobSync(new Runnable() { 684 @Override 685 public void run() { 686 mCameraHandler 687 .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) 688 .sendToTarget(); 689 mCameraHandler.post(bundle.mUnlockRunnable); 690 } 691 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture"); 692 } 693 694 @Override 695 public void setPreviewDisplay(final SurfaceHolder surfaceHolder) { 696 mDispatchThread.runJob(new Runnable() { 697 @Override 698 public void run() { 699 mCameraHandler 700 .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder) 701 .sendToTarget(); 702 } 703 }); 704 } 705 706 @Override 707 public void startPreview() { 708 mDispatchThread.runJob(new Runnable() { 709 @Override 710 public void run() { 711 mCameraHandler 712 .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget(); 713 } 714 }); 715 } 716 717 @Override 718 public void startPreviewWithCallback(final Handler handler, 719 final CameraStartPreviewCallback cb) { 720 mDispatchThread.runJob(new Runnable() { 721 @Override 722 public void run() { 723 mCameraHandler.obtainMessage(CameraActions.START_PREVIEW_ASYNC, 724 CameraStartPreviewCallbackForward.getNewInstance(handler, cb)).sendToTarget(); 725 } 726 }); 727 } 728 729 @Override 730 public void stopPreview() { 731 final WaitDoneBundle bundle = new WaitDoneBundle(); 732 mDispatchThread.runJobSync(new Runnable() { 733 @Override 734 public void run() { 735 mCameraHandler.sendEmptyMessage(CameraActions.STOP_PREVIEW); 736 mCameraHandler.post(bundle.mUnlockRunnable); 737 } 738 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview"); 739 } 740 741 @Override 742 public void setPreviewDataCallback( 743 final Handler handler, final CameraPreviewDataCallback cb) { 744 mDispatchThread.runJob(new Runnable() { 745 @Override 746 public void run() { 747 mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK, 748 PreviewCallbackForward.getNewInstance( 749 handler, AndroidCameraProxyImpl.this, cb)) 750 .sendToTarget(); 751 } 752 }); 753 } 754 @Override 755 public void setOneShotPreviewCallback(final Handler handler, 756 final CameraPreviewDataCallback cb) { 757 mDispatchThread.runJob(new Runnable() { 758 @Override 759 public void run() { 760 mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK, 761 PreviewCallbackForward 762 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb)) 763 .sendToTarget(); 764 } 765 }); 766 } 767 768 @Override 769 public void setPreviewDataCallbackWithBuffer( 770 final Handler handler, final CameraPreviewDataCallback cb) { 771 mDispatchThread.runJob(new Runnable() { 772 @Override 773 public void run() { 774 mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER, 775 PreviewCallbackForward 776 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb)) 777 .sendToTarget(); 778 } 779 }); 780 } 781 782 @Override 783 public void addCallbackBuffer(final byte[] callbackBuffer) { 784 mDispatchThread.runJob(new Runnable() { 785 @Override 786 public void run() { 787 mCameraHandler.obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer) 788 .sendToTarget(); 789 } 790 }); 791 } 792 793 @Override 794 public void autoFocus(final Handler handler, final CameraAFCallback cb) { 795 final AutoFocusCallback afCallback = new AutoFocusCallback() { 796 @Override 797 public void onAutoFocus(final boolean b, Camera camera) { 798 if (mCameraState.getState() != CameraStateHolder.CAMERA_FOCUSING) { 799 Log.w(TAG, "onAutoFocus callback returning when not focusing"); 800 } else { 801 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 802 } 803 handler.post(new Runnable() { 804 @Override 805 public void run() { 806 cb.onAutoFocus(b, AndroidCameraProxyImpl.this); 807 } 808 }); 809 } 810 }; 811 mDispatchThread.runJob(new Runnable() { 812 @Override 813 public void run() { 814 mCameraState.waitForStates(CameraStateHolder.CAMERA_IDLE); 815 mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback) 816 .sendToTarget(); 817 } 818 }); 819 } 820 821 @Override 822 public void cancelAutoFocus() { 823 mDispatchThread.runJob(new Runnable() { 824 @Override 825 public void run() { 826 mCameraHandler.removeMessages(CameraActions.AUTO_FOCUS); 827 mCameraHandler.sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS); 828 } 829 }); 830 } 831 832 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 833 @Override 834 public void setAutoFocusMoveCallback( 835 final Handler handler, final CameraAFMoveCallback cb) { 836 mDispatchThread.runJob(new Runnable() { 837 @Override 838 public void run() { 839 mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK, 840 AFMoveCallbackForward.getNewInstance( 841 handler, AndroidCameraProxyImpl.this, cb)) 842 .sendToTarget(); 843 } 844 }); 845 } 846 847 @Override 848 public void takePicture( 849 final Handler handler, final CameraShutterCallback shutter, 850 final CameraPictureCallback raw, final CameraPictureCallback post, 851 final CameraPictureCallback jpeg) { 852 final PictureCallback jpegCallback = new PictureCallback() { 853 @Override 854 public void onPictureTaken(final byte[] data, Camera camera) { 855 if (mCameraState.getState() != CameraStateHolder.CAMERA_CAPTURING) { 856 Log.w(TAG, "picture callback returning when not capturing"); 857 } else { 858 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 859 } 860 handler.post(new Runnable() { 861 @Override 862 public void run() { 863 jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this); 864 } 865 }); 866 } 867 }; 868 869 mDispatchThread.runJob(new Runnable() { 870 @Override 871 public void run() { 872 mCameraState.waitForStates( 873 CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); 874 mCameraHandler.requestTakePicture(ShutterCallbackForward 875 .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter), 876 PictureCallbackForward 877 .getNewInstance(handler, AndroidCameraProxyImpl.this, raw), 878 PictureCallbackForward 879 .getNewInstance(handler, AndroidCameraProxyImpl.this, post), 880 jpegCallback 881 ); 882 } 883 }); 884 } 885 886 @Override 887 public void setDisplayOrientation(final int degrees) { 888 mDispatchThread.runJob(new Runnable() { 889 @Override 890 public void run() { 891 mCameraHandler.obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 0) 892 .sendToTarget(); 893 } 894 }); 895 } 896 897 @Override 898 public void setZoomChangeListener(final OnZoomChangeListener listener) { 899 mDispatchThread.runJob(new Runnable() { 900 @Override 901 public void run() { 902 mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener) 903 .sendToTarget(); 904 } 905 }); 906 } 907 908 @Override 909 public void setFaceDetectionCallback(final Handler handler, 910 final CameraFaceDetectionCallback cb) { 911 mDispatchThread.runJob(new Runnable() { 912 @Override 913 public void run() { 914 mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER, 915 FaceDetectionCallbackForward 916 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb)) 917 .sendToTarget(); 918 } 919 }); 920 } 921 922 @Override 923 public void startFaceDetection() { 924 mDispatchThread.runJob(new Runnable() { 925 @Override 926 public void run() { 927 mCameraHandler.sendEmptyMessage(CameraActions.START_FACE_DETECTION); 928 } 929 }); 930 } 931 932 @Override 933 public void stopFaceDetection() { 934 mDispatchThread.runJob(new Runnable() { 935 @Override 936 public void run() { 937 mCameraHandler.sendEmptyMessage(CameraActions.STOP_FACE_DETECTION); 938 } 939 }); 940 } 941 942 @Override 943 public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) { 944 mDispatchThread.runJob(new Runnable() { 945 @Override 946 public void run() { 947 mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK, 948 ErrorCallbackForward.getNewInstance( 949 handler, AndroidCameraProxyImpl.this, cb)) 950 .sendToTarget(); 951 } 952 }); 953 } 954 955 @Override 956 public void setParameters(final Parameters params) { 957 if (params == null) { 958 Log.v(TAG, "null parameters in setParameters()"); 959 return; 960 } 961 final String flattenedParameters = params.flatten(); 962 mDispatchThread.runJob(new Runnable() { 963 @Override 964 public void run() { 965 mCameraState.waitForStates( 966 CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); 967 mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters) 968 .sendToTarget(); 969 } 970 }); 971 } 972 973 @Override 974 public Parameters getParameters() { 975 final WaitDoneBundle bundle = new WaitDoneBundle(); 976 mDispatchThread.runJobSync(new Runnable() { 977 @Override 978 public void run() { 979 mCameraHandler.sendEmptyMessage(CameraActions.GET_PARAMETERS); 980 mCameraHandler.post(bundle.mUnlockRunnable); 981 } 982 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters"); 983 return mParameters; 984 } 985 986 @Override 987 public CameraSettings getSettings() { 988 return new AndroidCameraSettings(mCapabilities, getParameters()); 989 } 990 991 @Override 992 public boolean applySettings(final CameraSettings settings) { 993 if (settings == null) { 994 Log.v(TAG, "null parameters in applySettings()"); 995 return false; 996 } 997 if (!mCapabilities.supports(settings)) { 998 return false; 999 } 1000 1001 final CameraSettings copyOfSettings = new CameraSettings(settings); 1002 mDispatchThread.runJob(new Runnable() { 1003 @Override 1004 public void run() { 1005 mCameraState.waitForStates( 1006 CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); 1007 mCameraHandler.obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings) 1008 .sendToTarget(); 1009 } 1010 }); 1011 return true; 1012 } 1013 1014 @Override 1015 public void refreshSettings() { 1016 mDispatchThread.runJob(new Runnable() { 1017 @Override 1018 public void run() { 1019 mCameraHandler.sendEmptyMessage(CameraActions.REFRESH_PARAMETERS); 1020 } 1021 }); 1022 } 1023 1024 @Override 1025 public void enableShutterSound(final boolean enable) { 1026 mDispatchThread.runJob(new Runnable() { 1027 @Override 1028 public void run() { 1029 mCameraHandler 1030 .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0) 1031 .sendToTarget(); 1032 } 1033 }); 1034 } 1035 } 1036 1037 private static class WaitDoneBundle { 1038 public Runnable mUnlockRunnable; 1039 private final Object mWaitLock; 1040 1041 WaitDoneBundle() { 1042 mWaitLock = new Object(); 1043 mUnlockRunnable = new Runnable() { 1044 @Override 1045 public void run() { 1046 synchronized (mWaitLock) { 1047 mWaitLock.notifyAll(); 1048 } 1049 } 1050 }; 1051 } 1052 } 1053 1054 /** 1055 * A helper class to forward AutoFocusCallback to another thread. 1056 */ 1057 private static class AFCallbackForward implements AutoFocusCallback { 1058 private final Handler mHandler; 1059 private final CameraProxy mCamera; 1060 private final CameraAFCallback mCallback; 1061 1062 /** 1063 * Returns a new instance of {@link AFCallbackForward}. 1064 * 1065 * @param handler The handler in which the callback will be invoked in. 1066 * @param camera The {@link CameraProxy} which the callback is from. 1067 * @param cb The callback to be invoked. 1068 * @return The instance of the {@link AFCallbackForward}, 1069 * or null if any parameter is null. 1070 */ 1071 public static AFCallbackForward getNewInstance( 1072 Handler handler, CameraProxy camera, CameraAFCallback cb) { 1073 if (handler == null || camera == null || cb == null) { 1074 return null; 1075 } 1076 return new AFCallbackForward(handler, camera, cb); 1077 } 1078 1079 private AFCallbackForward( 1080 Handler h, CameraProxy camera, CameraAFCallback cb) { 1081 mHandler = h; 1082 mCamera = camera; 1083 mCallback = cb; 1084 } 1085 1086 @Override 1087 public void onAutoFocus(final boolean b, Camera camera) { 1088 mHandler.post(new Runnable() { 1089 @Override 1090 public void run() { 1091 mCallback.onAutoFocus(b, mCamera); 1092 } 1093 }); 1094 } 1095 } 1096 1097 /** 1098 * A helper class to forward ErrorCallback to another thread. 1099 */ 1100 private static class ErrorCallbackForward implements Camera.ErrorCallback { 1101 private final Handler mHandler; 1102 private final CameraProxy mCamera; 1103 private final CameraErrorCallback mCallback; 1104 1105 /** 1106 * Returns a new instance of {@link AFCallbackForward}. 1107 * 1108 * @param handler The handler in which the callback will be invoked in. 1109 * @param camera The {@link CameraProxy} which the callback is from. 1110 * @param cb The callback to be invoked. 1111 * @return The instance of the {@link AFCallbackForward}, 1112 * or null if any parameter is null. 1113 */ 1114 public static ErrorCallbackForward getNewInstance( 1115 Handler handler, CameraProxy camera, CameraErrorCallback cb) { 1116 if (handler == null || camera == null || cb == null) { 1117 return null; 1118 } 1119 return new ErrorCallbackForward(handler, camera, cb); 1120 } 1121 1122 private ErrorCallbackForward( 1123 Handler h, CameraProxy camera, CameraErrorCallback cb) { 1124 mHandler = h; 1125 mCamera = camera; 1126 mCallback = cb; 1127 } 1128 1129 @Override 1130 public void onError(final int error, Camera camera) { 1131 mHandler.post(new Runnable() { 1132 @Override 1133 public void run() { 1134 mCallback.onError(error, mCamera); 1135 } 1136 }); 1137 } 1138 } 1139 1140 /** A helper class to forward AutoFocusMoveCallback to another thread. */ 1141 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1142 private static class AFMoveCallbackForward implements AutoFocusMoveCallback { 1143 private final Handler mHandler; 1144 private final CameraAFMoveCallback mCallback; 1145 private final CameraProxy mCamera; 1146 1147 /** 1148 * Returns a new instance of {@link AFMoveCallbackForward}. 1149 * 1150 * @param handler The handler in which the callback will be invoked in. 1151 * @param camera The {@link CameraProxy} which the callback is from. 1152 * @param cb The callback to be invoked. 1153 * @return The instance of the {@link AFMoveCallbackForward}, 1154 * or null if any parameter is null. 1155 */ 1156 public static AFMoveCallbackForward getNewInstance( 1157 Handler handler, CameraProxy camera, CameraAFMoveCallback cb) { 1158 if (handler == null || camera == null || cb == null) { 1159 return null; 1160 } 1161 return new AFMoveCallbackForward(handler, camera, cb); 1162 } 1163 1164 private AFMoveCallbackForward( 1165 Handler h, CameraProxy camera, CameraAFMoveCallback cb) { 1166 mHandler = h; 1167 mCamera = camera; 1168 mCallback = cb; 1169 } 1170 1171 @Override 1172 public void onAutoFocusMoving( 1173 final boolean moving, android.hardware.Camera camera) { 1174 mHandler.post(new Runnable() { 1175 @Override 1176 public void run() { 1177 mCallback.onAutoFocusMoving(moving, mCamera); 1178 } 1179 }); 1180 } 1181 } 1182 1183 /** 1184 * A helper class to forward ShutterCallback to to another thread. 1185 */ 1186 private static class ShutterCallbackForward implements ShutterCallback { 1187 private final Handler mHandler; 1188 private final CameraShutterCallback mCallback; 1189 private final CameraProxy mCamera; 1190 1191 /** 1192 * Returns a new instance of {@link ShutterCallbackForward}. 1193 * 1194 * @param handler The handler in which the callback will be invoked in. 1195 * @param camera The {@link CameraProxy} which the callback is from. 1196 * @param cb The callback to be invoked. 1197 * @return The instance of the {@link ShutterCallbackForward}, 1198 * or null if any parameter is null. 1199 */ 1200 public static ShutterCallbackForward getNewInstance( 1201 Handler handler, CameraProxy camera, CameraShutterCallback cb) { 1202 if (handler == null || camera == null || cb == null) { 1203 return null; 1204 } 1205 return new ShutterCallbackForward(handler, camera, cb); 1206 } 1207 1208 private ShutterCallbackForward( 1209 Handler h, CameraProxy camera, CameraShutterCallback cb) { 1210 mHandler = h; 1211 mCamera = camera; 1212 mCallback = cb; 1213 } 1214 1215 @Override 1216 public void onShutter() { 1217 mHandler.post(new Runnable() { 1218 @Override 1219 public void run() { 1220 mCallback.onShutter(mCamera); 1221 } 1222 }); 1223 } 1224 } 1225 1226 /** 1227 * A helper class to forward PictureCallback to another thread. 1228 */ 1229 private static class PictureCallbackForward implements PictureCallback { 1230 private final Handler mHandler; 1231 private final CameraPictureCallback mCallback; 1232 private final CameraProxy mCamera; 1233 1234 /** 1235 * Returns a new instance of {@link PictureCallbackForward}. 1236 * 1237 * @param handler The handler in which the callback will be invoked in. 1238 * @param camera The {@link CameraProxy} which the callback is from. 1239 * @param cb The callback to be invoked. 1240 * @return The instance of the {@link PictureCallbackForward}, 1241 * or null if any parameters is null. 1242 */ 1243 public static PictureCallbackForward getNewInstance( 1244 Handler handler, CameraProxy camera, CameraPictureCallback cb) { 1245 if (handler == null || camera == null || cb == null) { 1246 return null; 1247 } 1248 return new PictureCallbackForward(handler, camera, cb); 1249 } 1250 1251 private PictureCallbackForward( 1252 Handler h, CameraProxy camera, CameraPictureCallback cb) { 1253 mHandler = h; 1254 mCamera = camera; 1255 mCallback = cb; 1256 } 1257 1258 @Override 1259 public void onPictureTaken( 1260 final byte[] data, android.hardware.Camera camera) { 1261 mHandler.post(new Runnable() { 1262 @Override 1263 public void run() { 1264 mCallback.onPictureTaken(data, mCamera); 1265 } 1266 }); 1267 } 1268 } 1269 1270 /** 1271 * A helper class to forward PreviewCallback to another thread. 1272 */ 1273 private static class PreviewCallbackForward implements PreviewCallback { 1274 private final Handler mHandler; 1275 private final CameraPreviewDataCallback mCallback; 1276 private final CameraProxy mCamera; 1277 1278 /** 1279 * Returns a new instance of {@link PreviewCallbackForward}. 1280 * 1281 * @param handler The handler in which the callback will be invoked in. 1282 * @param camera The {@link CameraProxy} which the callback is from. 1283 * @param cb The callback to be invoked. 1284 * @return The instance of the {@link PreviewCallbackForward}, 1285 * or null if any parameters is null. 1286 */ 1287 public static PreviewCallbackForward getNewInstance( 1288 Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) { 1289 if (handler == null || camera == null || cb == null) { 1290 return null; 1291 } 1292 return new PreviewCallbackForward(handler, camera, cb); 1293 } 1294 1295 private PreviewCallbackForward( 1296 Handler h, CameraProxy camera, CameraPreviewDataCallback cb) { 1297 mHandler = h; 1298 mCamera = camera; 1299 mCallback = cb; 1300 } 1301 1302 @Override 1303 public void onPreviewFrame( 1304 final byte[] data, android.hardware.Camera camera) { 1305 mHandler.post(new Runnable() { 1306 @Override 1307 public void run() { 1308 mCallback.onPreviewFrame(data, mCamera); 1309 } 1310 }); 1311 } 1312 } 1313 1314 private static class FaceDetectionCallbackForward implements FaceDetectionListener { 1315 private final Handler mHandler; 1316 private final CameraFaceDetectionCallback mCallback; 1317 private final CameraProxy mCamera; 1318 1319 /** 1320 * Returns a new instance of {@link FaceDetectionCallbackForward}. 1321 * 1322 * @param handler The handler in which the callback will be invoked in. 1323 * @param camera The {@link CameraProxy} which the callback is from. 1324 * @param cb The callback to be invoked. 1325 * @return The instance of the {@link FaceDetectionCallbackForward}, 1326 * or null if any parameter is null. 1327 */ 1328 public static FaceDetectionCallbackForward getNewInstance( 1329 Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) { 1330 if (handler == null || camera == null || cb == null) { 1331 return null; 1332 } 1333 return new FaceDetectionCallbackForward(handler, camera, cb); 1334 } 1335 1336 private FaceDetectionCallbackForward( 1337 Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) { 1338 mHandler = h; 1339 mCamera = camera; 1340 mCallback = cb; 1341 } 1342 1343 @Override 1344 public void onFaceDetection( 1345 final Camera.Face[] faces, Camera camera) { 1346 mHandler.post(new Runnable() { 1347 @Override 1348 public void run() { 1349 mCallback.onFaceDetection(faces, mCamera); 1350 } 1351 }); 1352 } 1353 } 1354} 1355