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