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