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