1 /* 2 * Copyright (C) 2014 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.content.Context; 21 import android.graphics.ImageFormat; 22 import android.graphics.Matrix; 23 import android.graphics.Rect; 24 import android.graphics.RectF; 25 import android.graphics.SurfaceTexture; 26 import android.hardware.camera2.CameraAccessException; 27 import android.hardware.camera2.CameraCaptureSession; 28 import android.hardware.camera2.CameraCharacteristics; 29 import android.hardware.camera2.CameraDevice; 30 import android.hardware.camera2.CameraManager; 31 import android.hardware.camera2.CaptureFailure; 32 import android.hardware.camera2.CaptureRequest; 33 import android.hardware.camera2.CaptureResult; 34 import android.hardware.camera2.TotalCaptureResult; 35 import android.hardware.camera2.params.MeteringRectangle; 36 import android.media.Image; 37 import android.media.ImageReader; 38 import android.media.MediaActionSound; 39 import android.os.Build; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.Looper; 43 import android.os.Message; 44 import android.view.Surface; 45 46 import com.android.ex.camera2.portability.debug.Log; 47 import com.android.ex.camera2.utils.Camera2RequestSettingsSet; 48 49 import java.nio.ByteBuffer; 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 import java.util.HashSet; 53 import java.util.List; 54 import java.util.Set; 55 56 /** 57 * A class to implement {@link CameraAgent} of the Android camera2 framework. 58 */ 59 class AndroidCamera2AgentImpl extends CameraAgent { 60 private static final Log.Tag TAG = new Log.Tag("AndCam2AgntImp"); 61 62 private final Camera2Handler mCameraHandler; 63 private final HandlerThread mCameraHandlerThread; 64 private final CameraStateHolder mCameraState; 65 private final DispatchThread mDispatchThread; 66 private final CameraManager mCameraManager; 67 private final MediaActionSound mNoisemaker; 68 private CameraExceptionHandler mExceptionHandler; 69 70 /** 71 * Number of camera devices. The length of {@code mCameraDevices} does not reveal this 72 * information because that list may contain since-invalidated indices. 73 */ 74 private int mNumCameraDevices; 75 76 /** 77 * Transformation between integral camera indices and the {@link java.lang.String} indices used 78 * by the underlying API. Note that devices may disappear because they've been disconnected or 79 * have otherwise gone offline. Because we need to keep the meanings of whatever indices we 80 * expose stable, we cannot simply remove them in such a case; instead, we insert {@code null}s 81 * to invalidate any such indices. Whenever new devices appear, they are appended to the end of 82 * the list, and thereby assigned the lowest index that has never yet been used. 83 */ 84 private final List<String> mCameraDevices; 85 86 AndroidCamera2AgentImpl(Context context) { 87 mCameraHandlerThread = new HandlerThread("Camera2 Handler Thread"); 88 mCameraHandlerThread.start(); 89 mCameraHandler = new Camera2Handler(mCameraHandlerThread.getLooper()); 90 mExceptionHandler = new CameraExceptionHandler(mCameraHandler); 91 mCameraState = new AndroidCamera2StateHolder(); 92 mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread); 93 mDispatchThread.start(); 94 mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 95 mNoisemaker = new MediaActionSound(); 96 mNoisemaker.load(MediaActionSound.SHUTTER_CLICK); 97 98 mNumCameraDevices = 0; 99 mCameraDevices = new ArrayList<String>(); 100 updateCameraDevices(); 101 } 102 103 /** 104 * Updates the camera device index assignments stored in {@link mCameraDevices}, without 105 * reappropriating any currently-assigned index. 106 * @return Whether the operation was successful 107 */ 108 private boolean updateCameraDevices() { 109 try { 110 String[] currentCameraDevices = mCameraManager.getCameraIdList(); 111 Set<String> currentSet = new HashSet<String>(Arrays.asList(currentCameraDevices)); 112 113 // Invalidate the indices assigned to any camera devices that are no longer present 114 for (int index = 0; index < mCameraDevices.size(); ++index) { 115 if (!currentSet.contains(mCameraDevices.get(index))) { 116 mCameraDevices.set(index, null); 117 --mNumCameraDevices; 118 } 119 } 120 121 // Assign fresh indices to any new camera devices 122 currentSet.removeAll(mCameraDevices); // The devices we didn't know about 123 for (String device : currentCameraDevices) { 124 if (currentSet.contains(device)) { 125 mCameraDevices.add(device); 126 ++mNumCameraDevices; 127 } 128 } 129 130 return true; 131 } catch (CameraAccessException ex) { 132 Log.e(TAG, "Could not get device listing from camera subsystem", ex); 133 return false; 134 } 135 } 136 137 // TODO: Implement 138 @Override 139 public void recycle() {} 140 141 // TODO: Some indices may now be invalid; ensure everyone can handle that and update the docs 142 @Override 143 public CameraDeviceInfo getCameraDeviceInfo() { 144 updateCameraDevices(); 145 return new AndroidCamera2DeviceInfo(mCameraManager, mCameraDevices.toArray(new String[0]), 146 mNumCameraDevices); 147 } 148 149 @Override 150 protected Handler getCameraHandler() { 151 return mCameraHandler; 152 } 153 154 @Override 155 protected DispatchThread getDispatchThread() { 156 return mDispatchThread; 157 } 158 159 @Override 160 protected CameraStateHolder getCameraState() { 161 return mCameraState; 162 } 163 164 @Override 165 protected CameraExceptionHandler getCameraExceptionHandler() { 166 return mExceptionHandler; 167 } 168 169 @Override 170 public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) { 171 mExceptionHandler = exceptionHandler; 172 } 173 174 private static abstract class CaptureAvailableListener 175 extends CameraCaptureSession.CaptureCallback 176 implements ImageReader.OnImageAvailableListener {}; 177 178 private class Camera2Handler extends HistoryHandler { 179 // Caller-provided when leaving CAMERA_UNOPENED state: 180 private CameraOpenCallback mOpenCallback; 181 private int mCameraIndex; 182 private String mCameraId; 183 private int mCancelAfPending = 0; 184 185 // Available in CAMERA_UNCONFIGURED state and above: 186 private CameraDevice mCamera; 187 private AndroidCamera2ProxyImpl mCameraProxy; 188 private Camera2RequestSettingsSet mPersistentSettings; 189 private Rect mActiveArray; 190 private boolean mLegacyDevice; 191 192 // Available in CAMERA_CONFIGURED state and above: 193 private Size mPreviewSize; 194 private Size mPhotoSize; 195 196 // Available in PREVIEW_READY state and above: 197 private SurfaceTexture mPreviewTexture; 198 private Surface mPreviewSurface; 199 private CameraCaptureSession mSession; 200 private ImageReader mCaptureReader; 201 202 // Available from the beginning of PREVIEW_ACTIVE until the first preview frame arrives: 203 private CameraStartPreviewCallback mOneshotPreviewingCallback; 204 205 // Available in FOCUS_LOCKED between AF trigger receipt and whenever the lens stops moving: 206 private CameraAFCallback mOneshotAfCallback; 207 208 // Available when taking picture between AE trigger receipt and autoexposure convergence 209 private CaptureAvailableListener mOneshotCaptureCallback; 210 211 // Available whenever setAutoFocusMoveCallback() was last invoked with a non-null argument: 212 private CameraAFMoveCallback mPassiveAfCallback; 213 214 // Gets reset on every state change 215 private int mCurrentAeState = CaptureResult.CONTROL_AE_STATE_INACTIVE; 216 217 Camera2Handler(Looper looper) { 218 super(looper); 219 } 220 221 @Override 222 public void handleMessage(final Message msg) { 223 super.handleMessage(msg); 224 Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'"); 225 int cameraAction = msg.what; 226 try { 227 switch (cameraAction) { 228 case CameraActions.OPEN_CAMERA: 229 case CameraActions.RECONNECT: { 230 CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj; 231 int cameraIndex = msg.arg1; 232 233 if (mCameraState.getState() > AndroidCamera2StateHolder.CAMERA_UNOPENED) { 234 openCallback.onDeviceOpenedAlready(cameraIndex, 235 generateHistoryString(cameraIndex)); 236 break; 237 } 238 239 mOpenCallback = openCallback; 240 mCameraIndex = cameraIndex; 241 mCameraId = mCameraDevices.get(mCameraIndex); 242 Log.i(TAG, String.format("Opening camera index %d (id %s) with camera2 API", 243 cameraIndex, mCameraId)); 244 245 if (mCameraId == null) { 246 mOpenCallback.onCameraDisabled(msg.arg1); 247 break; 248 } 249 mCameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, this); 250 251 break; 252 } 253 254 case CameraActions.RELEASE: { 255 if (mCameraState.getState() == AndroidCamera2StateHolder.CAMERA_UNOPENED) { 256 Log.w(TAG, "Ignoring release at inappropriate time"); 257 break; 258 } 259 260 if (mSession != null) { 261 closePreviewSession(); 262 mSession = null; 263 } 264 if (mCamera != null) { 265 mCamera.close(); 266 mCamera = null; 267 } 268 mCameraProxy = null; 269 mPersistentSettings = null; 270 mActiveArray = null; 271 if (mPreviewSurface != null) { 272 mPreviewSurface.release(); 273 mPreviewSurface = null; 274 } 275 mPreviewTexture = null; 276 if (mCaptureReader != null) { 277 mCaptureReader.close(); 278 mCaptureReader = null; 279 } 280 mPreviewSize = null; 281 mPhotoSize = null; 282 mCameraIndex = 0; 283 mCameraId = null; 284 changeState(AndroidCamera2StateHolder.CAMERA_UNOPENED); 285 break; 286 } 287 288 /*case CameraActions.UNLOCK: { 289 break; 290 } 291 292 case CameraActions.LOCK: { 293 break; 294 }*/ 295 296 case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: { 297 setPreviewTexture((SurfaceTexture) msg.obj); 298 break; 299 } 300 301 case CameraActions.START_PREVIEW_ASYNC: { 302 if (mCameraState.getState() != 303 AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) { 304 // TODO: Provide better feedback here? 305 Log.w(TAG, "Refusing to start preview at inappropriate time"); 306 break; 307 } 308 309 mOneshotPreviewingCallback = (CameraStartPreviewCallback) msg.obj; 310 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE); 311 try { 312 mSession.setRepeatingRequest( 313 mPersistentSettings.createRequest(mCamera, 314 CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface), 315 /*listener*/mCameraResultStateCallback, /*handler*/this); 316 } catch(CameraAccessException ex) { 317 Log.w(TAG, "Unable to start preview", ex); 318 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); 319 } 320 break; 321 } 322 323 // FIXME: We need to tear down the CameraCaptureSession here 324 // (and unlock the CameraSettings object from our 325 // CameraProxy) so that the preview/photo sizes can be 326 // changed again while no preview is running. 327 case CameraActions.STOP_PREVIEW: { 328 if (mCameraState.getState() < 329 AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 330 Log.w(TAG, "Refusing to stop preview at inappropriate time"); 331 break; 332 } 333 334 mSession.stopRepeating(); 335 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); 336 break; 337 } 338 339 /*case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: { 340 break; 341 } 342 343 case CameraActions.ADD_CALLBACK_BUFFER: { 344 break; 345 } 346 347 case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: { 348 break; 349 } 350 351 case CameraActions.SET_PREVIEW_CALLBACK: { 352 break; 353 } 354 355 case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: { 356 break; 357 } 358 359 case CameraActions.SET_PARAMETERS: { 360 break; 361 } 362 363 case CameraActions.GET_PARAMETERS: { 364 break; 365 } 366 367 case CameraActions.REFRESH_PARAMETERS: { 368 break; 369 }*/ 370 371 case CameraActions.APPLY_SETTINGS: { 372 AndroidCamera2Settings settings = (AndroidCamera2Settings) msg.obj; 373 applyToRequest(settings); 374 break; 375 } 376 377 case CameraActions.AUTO_FOCUS: { 378 if (mCancelAfPending > 0) { 379 Log.v(TAG, "handleMessage - Ignored AUTO_FOCUS because there was " 380 + mCancelAfPending + " pending CANCEL_AUTO_FOCUS messages"); 381 break; // ignore AF because a CANCEL_AF is queued after this 382 } 383 // We only support locking the focus while a preview is being displayed. 384 // However, it can be requested multiple times in succession; the effect of 385 // the subsequent invocations is determined by the focus mode defined in the 386 // provided CameraSettings object. In passive (CONTINUOUS_*) mode, the 387 // duplicate requests are no-ops and leave the lens locked at its current 388 // position, but in active (AUTO) mode, they perform another scan and lock 389 // once that is finished. In any manual focus mode, this call is a no-op, 390 // and most notably, this is the only case where the callback isn't invoked. 391 if (mCameraState.getState() < 392 AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 393 Log.w(TAG, "Ignoring attempt to autofocus without preview"); 394 break; 395 } 396 397 // The earliest we can reliably tell whether the autofocus has locked in 398 // response to our latest request is when our one-time capture progresses. 399 // However, it will probably take longer than that, so once that happens, 400 // just start checking the repeating preview requests as they complete. 401 final CameraAFCallback callback = (CameraAFCallback) msg.obj; 402 CameraCaptureSession.CaptureCallback deferredCallbackSetter = 403 new CameraCaptureSession.CaptureCallback() { 404 private boolean mAlreadyDispatched = false; 405 406 @Override 407 public void onCaptureProgressed(CameraCaptureSession session, 408 CaptureRequest request, 409 CaptureResult result) { 410 checkAfState(result); 411 } 412 413 @Override 414 public void onCaptureCompleted(CameraCaptureSession session, 415 CaptureRequest request, 416 TotalCaptureResult result) { 417 checkAfState(result); 418 } 419 420 private void checkAfState(CaptureResult result) { 421 if (result.get(CaptureResult.CONTROL_AF_STATE) != null && 422 !mAlreadyDispatched) { 423 // Now our mCameraResultStateCallback will invoke the callback 424 // the first time it finds the focus motor to be locked. 425 mAlreadyDispatched = true; 426 mOneshotAfCallback = callback; 427 // This is an optimization: check the AF state of this frame 428 // instead of simply waiting for the next. 429 mCameraResultStateCallback.monitorControlStates(result); 430 } 431 } 432 433 @Override 434 public void onCaptureFailed(CameraCaptureSession session, 435 CaptureRequest request, 436 CaptureFailure failure) { 437 Log.e(TAG, "Focusing failed with reason " + failure.getReason()); 438 callback.onAutoFocus(false, mCameraProxy); 439 }}; 440 441 // Send a one-time capture to trigger the camera driver to lock focus. 442 changeState(AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 443 Camera2RequestSettingsSet trigger = 444 new Camera2RequestSettingsSet(mPersistentSettings); 445 trigger.set(CaptureRequest.CONTROL_AF_TRIGGER, 446 CaptureRequest.CONTROL_AF_TRIGGER_START); 447 try { 448 mSession.capture( 449 trigger.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW, 450 mPreviewSurface), 451 /*listener*/deferredCallbackSetter, /*handler*/ this); 452 } catch(CameraAccessException ex) { 453 Log.e(TAG, "Unable to lock autofocus", ex); 454 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE); 455 } 456 break; 457 } 458 459 case CameraActions.CANCEL_AUTO_FOCUS: { 460 // Ignore all AFs that were already queued until we see 461 // a CANCEL_AUTO_FOCUS_FINISH 462 mCancelAfPending++; 463 // Why would you want to unlock the lens if it isn't already locked? 464 if (mCameraState.getState() < 465 AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 466 Log.w(TAG, "Ignoring attempt to release focus lock without preview"); 467 break; 468 } 469 470 // Send a one-time capture to trigger the camera driver to resume scanning. 471 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE); 472 Camera2RequestSettingsSet cancel = 473 new Camera2RequestSettingsSet(mPersistentSettings); 474 cancel.set(CaptureRequest.CONTROL_AF_TRIGGER, 475 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); 476 try { 477 mSession.capture( 478 cancel.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW, 479 mPreviewSurface), 480 /*listener*/null, /*handler*/this); 481 } catch(CameraAccessException ex) { 482 Log.e(TAG, "Unable to cancel autofocus", ex); 483 changeState(AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 484 } 485 break; 486 } 487 488 case CameraActions.CANCEL_AUTO_FOCUS_FINISH: { 489 // Stop ignoring AUTO_FOCUS messages unless there are additional 490 // CANCEL_AUTO_FOCUSes that were added 491 mCancelAfPending--; 492 break; 493 } 494 495 case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: { 496 mPassiveAfCallback = (CameraAFMoveCallback) msg.obj; 497 break; 498 } 499 500 /*case CameraActions.SET_ZOOM_CHANGE_LISTENER: { 501 break; 502 } 503 504 case CameraActions.SET_FACE_DETECTION_LISTENER: { 505 break; 506 } 507 508 case CameraActions.START_FACE_DETECTION: { 509 break; 510 } 511 512 case CameraActions.STOP_FACE_DETECTION: { 513 break; 514 } 515 516 case CameraActions.SET_ERROR_CALLBACK: { 517 break; 518 } 519 520 case CameraActions.ENABLE_SHUTTER_SOUND: { 521 break; 522 }*/ 523 524 case CameraActions.SET_DISPLAY_ORIENTATION: { 525 // Only set the JPEG capture orientation if requested to do so; otherwise, 526 // capture in the sensor's physical orientation. (e.g., JPEG rotation is 527 // necessary in auto-rotate mode. 528 mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg2 > 0 ? 529 mCameraProxy.getCharacteristics().getJpegOrientation(msg.arg1) : 0); 530 break; 531 } 532 533 case CameraActions.SET_JPEG_ORIENTATION: { 534 mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg1); 535 break; 536 } 537 538 case CameraActions.CAPTURE_PHOTO: { 539 if (mCameraState.getState() < 540 AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 541 Log.e(TAG, "Photos may only be taken when a preview is active"); 542 break; 543 } 544 if (mCameraState.getState() != 545 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED) { 546 Log.w(TAG, "Taking a (likely blurry) photo without the lens locked"); 547 } 548 549 final CaptureAvailableListener listener = 550 (CaptureAvailableListener) msg.obj; 551 if (mLegacyDevice || 552 (mCurrentAeState == CaptureResult.CONTROL_AE_STATE_CONVERGED && 553 !mPersistentSettings.matches(CaptureRequest.CONTROL_AE_MODE, 554 CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH) && 555 !mPersistentSettings.matches(CaptureRequest.FLASH_MODE, 556 CaptureRequest.FLASH_MODE_SINGLE))) 557 { 558 // Legacy devices don't support the precapture state keys and instead 559 // perform autoexposure convergence automatically upon capture. 560 561 // On other devices, as long as it has already converged, it determined 562 // that flash was not required, and we're not going to invalidate the 563 // current exposure levels by forcing the force on, we can save 564 // significant capture time by not forcing a recalculation. 565 Log.i(TAG, "Skipping pre-capture autoexposure convergence"); 566 mCaptureReader.setOnImageAvailableListener(listener, /*handler*/this); 567 try { 568 mSession.capture( 569 mPersistentSettings.createRequest(mCamera, 570 CameraDevice.TEMPLATE_STILL_CAPTURE, 571 mCaptureReader.getSurface()), 572 listener, /*handler*/this); 573 } catch (CameraAccessException ex) { 574 Log.e(TAG, "Unable to initiate immediate capture", ex); 575 } 576 } else { 577 // We need to let AE converge before capturing. Once our one-time 578 // trigger capture has made it into the pipeline, we'll start checking 579 // for the completion of that convergence, capturing when that happens. 580 Log.i(TAG, "Forcing pre-capture autoexposure convergence"); 581 CameraCaptureSession.CaptureCallback deferredCallbackSetter = 582 new CameraCaptureSession.CaptureCallback() { 583 private boolean mAlreadyDispatched = false; 584 585 @Override 586 public void onCaptureProgressed(CameraCaptureSession session, 587 CaptureRequest request, 588 CaptureResult result) { 589 checkAeState(result); 590 } 591 592 @Override 593 public void onCaptureCompleted(CameraCaptureSession session, 594 CaptureRequest request, 595 TotalCaptureResult result) { 596 checkAeState(result); 597 } 598 599 private void checkAeState(CaptureResult result) { 600 if (result.get(CaptureResult.CONTROL_AE_STATE) != null && 601 !mAlreadyDispatched) { 602 // Now our mCameraResultStateCallback will invoke the 603 // callback once the autoexposure routine has converged. 604 mAlreadyDispatched = true; 605 mOneshotCaptureCallback = listener; 606 // This is an optimization: check the AE state of this frame 607 // instead of simply waiting for the next. 608 mCameraResultStateCallback.monitorControlStates(result); 609 } 610 } 611 612 @Override 613 public void onCaptureFailed(CameraCaptureSession session, 614 CaptureRequest request, 615 CaptureFailure failure) { 616 Log.e(TAG, "Autoexposure and capture failed with reason " + 617 failure.getReason()); 618 // TODO: Make an error callback? 619 }}; 620 621 // Set a one-time capture to trigger the camera driver's autoexposure: 622 Camera2RequestSettingsSet expose = 623 new Camera2RequestSettingsSet(mPersistentSettings); 624 expose.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 625 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 626 try { 627 mSession.capture( 628 expose.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW, 629 mPreviewSurface), 630 /*listener*/deferredCallbackSetter, /*handler*/this); 631 } catch (CameraAccessException ex) { 632 Log.e(TAG, "Unable to run autoexposure and perform capture", ex); 633 } 634 } 635 break; 636 } 637 638 default: { 639 // TODO: Rephrase once everything has been implemented 640 throw new RuntimeException("Unimplemented CameraProxy message=" + msg.what); 641 } 642 } 643 } catch (final Exception ex) { 644 if (cameraAction != CameraActions.RELEASE && mCamera != null) { 645 // TODO: Handle this better 646 mCamera.close(); 647 mCamera = null; 648 } else if (mCamera == null) { 649 if (cameraAction == CameraActions.OPEN_CAMERA) { 650 if (mOpenCallback != null) { 651 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 652 generateHistoryString(mCameraIndex)); 653 } 654 } else { 655 Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null"); 656 } 657 return; 658 } 659 660 if (ex instanceof RuntimeException) { 661 String commandHistory = generateHistoryString(Integer.parseInt(mCameraId)); 662 mExceptionHandler.onCameraException((RuntimeException) ex, commandHistory, 663 cameraAction, mCameraState.getState()); 664 } 665 } finally { 666 WaitDoneBundle.unblockSyncWaiters(msg); 667 } 668 } 669 670 public CameraSettings buildSettings(AndroidCamera2Capabilities caps) { 671 try { 672 return new AndroidCamera2Settings(mCamera, CameraDevice.TEMPLATE_PREVIEW, 673 mActiveArray, mPreviewSize, mPhotoSize); 674 } catch (CameraAccessException ex) { 675 Log.e(TAG, "Unable to query camera device to build settings representation"); 676 return null; 677 } 678 } 679 680 /** 681 * Simply propagates settings from provided {@link CameraSettings} 682 * object to our {@link CaptureRequest.Builder} for use in captures. 683 * <p>Most conversions to match the API 2 formats are performed by 684 * {@link AndroidCamera2Capabilities.IntegralStringifier}; otherwise 685 * any final adjustments are done here before updating the builder.</p> 686 * 687 * @param settings The new/updated settings 688 */ 689 private void applyToRequest(AndroidCamera2Settings settings) { 690 // TODO: If invoked when in PREVIEW_READY state, a new preview size will not take effect 691 692 mPersistentSettings.union(settings.getRequestSettings()); 693 mPreviewSize = settings.getCurrentPreviewSize(); 694 mPhotoSize = settings.getCurrentPhotoSize(); 695 696 if (mCameraState.getState() >= AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 697 // If we're already previewing, reflect most settings immediately 698 try { 699 mSession.setRepeatingRequest( 700 mPersistentSettings.createRequest(mCamera, 701 CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface), 702 /*listener*/mCameraResultStateCallback, /*handler*/this); 703 } catch (CameraAccessException ex) { 704 Log.e(TAG, "Failed to apply updated request settings", ex); 705 } 706 } else if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) { 707 // If we're already ready to preview, this doesn't regress our state 708 changeState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); 709 } 710 } 711 712 private void setPreviewTexture(SurfaceTexture surfaceTexture) { 713 // TODO: Must be called after providing a .*Settings populated with sizes 714 // TODO: We don't technically offer a selection of sizes tailored to SurfaceTextures! 715 716 // TODO: Handle this error condition with a callback or exception 717 if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_CONFIGURED) { 718 Log.w(TAG, "Ignoring texture setting at inappropriate time"); 719 return; 720 } 721 722 // Avoid initializing another capture session unless we absolutely have to 723 if (surfaceTexture == mPreviewTexture) { 724 Log.i(TAG, "Optimizing out redundant preview texture setting"); 725 return; 726 } 727 728 if (mSession != null) { 729 closePreviewSession(); 730 } 731 732 mPreviewTexture = surfaceTexture; 733 surfaceTexture.setDefaultBufferSize(mPreviewSize.width(), mPreviewSize.height()); 734 735 if (mPreviewSurface != null) { 736 mPreviewSurface.release(); 737 } 738 mPreviewSurface = new Surface(surfaceTexture); 739 740 if (mCaptureReader != null) { 741 mCaptureReader.close(); 742 } 743 mCaptureReader = ImageReader.newInstance( 744 mPhotoSize.width(), mPhotoSize.height(), ImageFormat.JPEG, 1); 745 746 try { 747 mCamera.createCaptureSession( 748 Arrays.asList(mPreviewSurface, mCaptureReader.getSurface()), 749 mCameraPreviewStateCallback, this); 750 } catch (CameraAccessException ex) { 751 Log.e(TAG, "Failed to create camera capture session", ex); 752 } 753 } 754 755 private void closePreviewSession() { 756 try { 757 mSession.abortCaptures(); 758 mSession = null; 759 } catch (CameraAccessException ex) { 760 Log.e(TAG, "Failed to close existing camera capture session", ex); 761 } 762 changeState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); 763 } 764 765 private void changeState(int newState) { 766 if (mCameraState.getState() != newState) { 767 mCameraState.setState(newState); 768 if (newState < AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 769 mCurrentAeState = CaptureResult.CONTROL_AE_STATE_INACTIVE; 770 mCameraResultStateCallback.resetState(); 771 } 772 } 773 } 774 775 // This callback monitors our connection to and disconnection from camera devices. 776 private CameraDevice.StateCallback mCameraDeviceStateCallback = 777 new CameraDevice.StateCallback() { 778 @Override 779 public void onOpened(CameraDevice camera) { 780 mCamera = camera; 781 if (mOpenCallback != null) { 782 try { 783 CameraCharacteristics props = 784 mCameraManager.getCameraCharacteristics(mCameraId); 785 CameraDeviceInfo.Characteristics characteristics = 786 getCameraDeviceInfo().getCharacteristics(mCameraIndex); 787 mCameraProxy = new AndroidCamera2ProxyImpl(AndroidCamera2AgentImpl.this, 788 mCameraIndex, mCamera, characteristics, props); 789 mPersistentSettings = new Camera2RequestSettingsSet(); 790 mActiveArray = 791 props.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 792 mLegacyDevice = 793 props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == 794 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; 795 changeState(AndroidCamera2StateHolder.CAMERA_UNCONFIGURED); 796 mOpenCallback.onCameraOpened(mCameraProxy); 797 } catch (CameraAccessException ex) { 798 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 799 generateHistoryString(mCameraIndex)); 800 } 801 } 802 } 803 804 @Override 805 public void onDisconnected(CameraDevice camera) { 806 Log.w(TAG, "Camera device '" + mCameraIndex + "' was disconnected"); 807 } 808 809 @Override 810 public void onError(CameraDevice camera, int error) { 811 Log.e(TAG, "Camera device '" + mCameraIndex + "' encountered error code '" + 812 error + '\''); 813 if (mOpenCallback != null) { 814 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 815 generateHistoryString(mCameraIndex)); 816 } 817 }}; 818 819 // This callback monitors our camera session (i.e. our transition into and out of preview). 820 private CameraCaptureSession.StateCallback mCameraPreviewStateCallback = 821 new CameraCaptureSession.StateCallback() { 822 @Override 823 public void onConfigured(CameraCaptureSession session) { 824 mSession = session; 825 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); 826 } 827 828 @Override 829 public void onConfigureFailed(CameraCaptureSession session) { 830 // TODO: Invoke a callback 831 Log.e(TAG, "Failed to configure the camera for capture"); 832 } 833 834 @Override 835 public void onActive(CameraCaptureSession session) { 836 if (mOneshotPreviewingCallback != null) { 837 // The session is up and processing preview requests. Inform the caller. 838 mOneshotPreviewingCallback.onPreviewStarted(); 839 mOneshotPreviewingCallback = null; 840 } 841 }}; 842 843 private abstract class CameraResultStateCallback 844 extends CameraCaptureSession.CaptureCallback { 845 public abstract void monitorControlStates(CaptureResult result); 846 847 public abstract void resetState(); 848 } 849 850 // This callback monitors requested captures and notifies any relevant callbacks. 851 private CameraResultStateCallback mCameraResultStateCallback = 852 new CameraResultStateCallback() { 853 private int mLastAfState = -1; 854 private long mLastAfFrameNumber = -1; 855 private long mLastAeFrameNumber = -1; 856 857 @Override 858 public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, 859 CaptureResult result) { 860 monitorControlStates(result); 861 } 862 863 @Override 864 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, 865 TotalCaptureResult result) { 866 monitorControlStates(result); 867 } 868 869 @Override 870 public void monitorControlStates(CaptureResult result) { 871 Integer afStateMaybe = result.get(CaptureResult.CONTROL_AF_STATE); 872 if (afStateMaybe != null) { 873 int afState = afStateMaybe; 874 // Since we handle both partial and total results for multiple frames here, we 875 // might get the final callbacks for an earlier frame after receiving one or 876 // more that correspond to the next one. To prevent our data from oscillating, 877 // we never consider AF states that are older than the last one we've seen. 878 if (result.getFrameNumber() > mLastAfFrameNumber) { 879 boolean afStateChanged = afState != mLastAfState; 880 mLastAfState = afState; 881 mLastAfFrameNumber = result.getFrameNumber(); 882 883 switch (afState) { 884 case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN: 885 case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: 886 case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: { 887 if (afStateChanged && mPassiveAfCallback != null) { 888 // A CameraAFMoveCallback is attached. If we just started to 889 // scan, the motor is moving; otherwise, it has settled. 890 mPassiveAfCallback.onAutoFocusMoving( 891 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN, 892 mCameraProxy); 893 } 894 break; 895 } 896 897 case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: 898 case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: { 899 // This check must be made regardless of whether the focus state has 900 // changed recently to avoid infinite waiting during autoFocus() 901 // when the algorithm has already either converged or failed to. 902 if (mOneshotAfCallback != null) { 903 // A call to autoFocus() was just made to request a focus lock. 904 // Notify the caller that the lens is now indefinitely fixed, 905 // and report whether the image we're stuck with is in focus. 906 mOneshotAfCallback.onAutoFocus( 907 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED, 908 mCameraProxy); 909 mOneshotAfCallback = null; 910 } 911 break; 912 } 913 } 914 } 915 } 916 917 Integer aeStateMaybe = result.get(CaptureResult.CONTROL_AE_STATE); 918 if (aeStateMaybe != null) { 919 int aeState = aeStateMaybe; 920 // Since we handle both partial and total results for multiple frames here, we 921 // might get the final callbacks for an earlier frame after receiving one or 922 // more that correspond to the next one. To prevent our data from oscillating, 923 // we never consider AE states that are older than the last one we've seen. 924 if (result.getFrameNumber() > mLastAeFrameNumber) { 925 mCurrentAeState = aeStateMaybe; 926 mLastAeFrameNumber = result.getFrameNumber(); 927 928 switch (aeState) { 929 case CaptureResult.CONTROL_AE_STATE_CONVERGED: 930 case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED: 931 case CaptureResult.CONTROL_AE_STATE_LOCKED: { 932 // This check must be made regardless of whether the exposure state 933 // has changed recently to avoid infinite waiting during 934 // takePicture() when the algorithm has already converged. 935 if (mOneshotCaptureCallback != null) { 936 // A call to takePicture() was just made, and autoexposure 937 // converged so it's time to initiate the capture! 938 mCaptureReader.setOnImageAvailableListener( 939 /*listener*/mOneshotCaptureCallback, 940 /*handler*/Camera2Handler.this); 941 try { 942 mSession.capture( 943 mPersistentSettings.createRequest(mCamera, 944 CameraDevice.TEMPLATE_STILL_CAPTURE, 945 mCaptureReader.getSurface()), 946 /*callback*/mOneshotCaptureCallback, 947 /*handler*/Camera2Handler.this); 948 } catch (CameraAccessException ex) { 949 Log.e(TAG, "Unable to initiate capture", ex); 950 } finally { 951 mOneshotCaptureCallback = null; 952 } 953 } 954 break; 955 } 956 } 957 } 958 } 959 } 960 961 @Override 962 public void resetState() { 963 mLastAfState = -1; 964 mLastAfFrameNumber = -1; 965 mLastAeFrameNumber = -1; 966 } 967 968 @Override 969 public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, 970 CaptureFailure failure) { 971 Log.e(TAG, "Capture attempt failed with reason " + failure.getReason()); 972 }}; 973 } 974 975 private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy { 976 private final AndroidCamera2AgentImpl mCameraAgent; 977 private final int mCameraIndex; 978 private final CameraDevice mCamera; 979 private final CameraDeviceInfo.Characteristics mCharacteristics; 980 private final AndroidCamera2Capabilities mCapabilities; 981 private CameraSettings mLastSettings; 982 private boolean mShutterSoundEnabled; 983 984 public AndroidCamera2ProxyImpl( 985 AndroidCamera2AgentImpl agent, 986 int cameraIndex, 987 CameraDevice camera, 988 CameraDeviceInfo.Characteristics characteristics, 989 CameraCharacteristics properties) { 990 mCameraAgent = agent; 991 mCameraIndex = cameraIndex; 992 mCamera = camera; 993 mCharacteristics = characteristics; 994 mCapabilities = new AndroidCamera2Capabilities(properties); 995 mLastSettings = null; 996 mShutterSoundEnabled = true; 997 } 998 999 // TODO: Implement 1000 @Override 1001 public android.hardware.Camera getCamera() { return null; } 1002 1003 @Override 1004 public int getCameraId() { 1005 return mCameraIndex; 1006 } 1007 1008 @Override 1009 public CameraDeviceInfo.Characteristics getCharacteristics() { 1010 return mCharacteristics; 1011 } 1012 1013 @Override 1014 public CameraCapabilities getCapabilities() { 1015 return mCapabilities; 1016 } 1017 1018 public CameraAgent getAgent() { 1019 return mCameraAgent; 1020 } 1021 1022 private AndroidCamera2Capabilities getSpecializedCapabilities() { 1023 return mCapabilities; 1024 } 1025 1026 // FIXME: Unlock the sizes in stopPreview(), as per the corresponding 1027 // explanation on the STOP_PREVIEW case in the handler. 1028 @Override 1029 public void setPreviewTexture(SurfaceTexture surfaceTexture) { 1030 // Once the Surface has been selected, we configure the session and 1031 // are no longer able to change the sizes. 1032 getSettings().setSizesLocked(true); 1033 super.setPreviewTexture(surfaceTexture); 1034 } 1035 1036 // FIXME: Unlock the sizes in stopPreview(), as per the corresponding 1037 // explanation on the STOP_PREVIEW case in the handler. 1038 @Override 1039 public void setPreviewTextureSync(SurfaceTexture surfaceTexture) { 1040 // Once the Surface has been selected, we configure the session and 1041 // are no longer able to change the sizes. 1042 getSettings().setSizesLocked(true); 1043 super.setPreviewTexture(surfaceTexture); 1044 } 1045 1046 // TODO: Implement 1047 @Override 1048 public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {} 1049 1050 // TODO: Implement 1051 @Override 1052 public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {} 1053 1054 // TODO: Implement 1055 @Override 1056 public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb) 1057 {} 1058 1059 // TODO: Implement 1060 public void addCallbackBuffer(final byte[] callbackBuffer) {} 1061 1062 @Override 1063 public void autoFocus(final Handler handler, final CameraAFCallback cb) { 1064 try { 1065 mDispatchThread.runJob(new Runnable() { 1066 @Override 1067 public void run() { 1068 CameraAFCallback cbForward = null; 1069 if (cb != null) { 1070 cbForward = new CameraAFCallback() { 1071 @Override 1072 public void onAutoFocus(final boolean focused, 1073 final CameraProxy camera) { 1074 handler.post(new Runnable() { 1075 @Override 1076 public void run() { 1077 cb.onAutoFocus(focused, camera); 1078 } 1079 }); 1080 } 1081 }; 1082 } 1083 1084 mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE | 1085 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 1086 mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward) 1087 .sendToTarget(); 1088 } 1089 }); 1090 } catch (RuntimeException ex) { 1091 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex); 1092 } 1093 } 1094 1095 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1096 @Override 1097 public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) { 1098 try { 1099 mDispatchThread.runJob(new Runnable() { 1100 @Override 1101 public void run() { 1102 CameraAFMoveCallback cbForward = null; 1103 if (cb != null) { 1104 cbForward = new CameraAFMoveCallback() { 1105 @Override 1106 public void onAutoFocusMoving(final boolean moving, 1107 final CameraProxy camera) { 1108 handler.post(new Runnable() { 1109 @Override 1110 public void run() { 1111 cb.onAutoFocusMoving(moving, camera); 1112 } 1113 }); 1114 } 1115 }; 1116 } 1117 1118 mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK, 1119 cbForward).sendToTarget(); 1120 } 1121 }); 1122 } catch (RuntimeException ex) { 1123 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex); 1124 } 1125 } 1126 1127 @Override 1128 public void takePicture(final Handler handler, 1129 final CameraShutterCallback shutter, 1130 CameraPictureCallback raw, 1131 CameraPictureCallback postview, 1132 final CameraPictureCallback jpeg) { 1133 // TODO: We never call raw or postview 1134 final CaptureAvailableListener picListener = 1135 new CaptureAvailableListener() { 1136 @Override 1137 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 1138 long timestamp, long frameNumber) { 1139 if (shutter != null) { 1140 handler.post(new Runnable() { 1141 @Override 1142 public void run() { 1143 if (mShutterSoundEnabled) { 1144 mNoisemaker.play(MediaActionSound.SHUTTER_CLICK); 1145 } 1146 shutter.onShutter(AndroidCamera2ProxyImpl.this); 1147 }}); 1148 } 1149 } 1150 1151 @Override 1152 public void onImageAvailable(ImageReader reader) { 1153 try (Image image = reader.acquireNextImage()) { 1154 if (jpeg != null) { 1155 ByteBuffer buffer = image.getPlanes()[0].getBuffer(); 1156 final byte[] pixels = new byte[buffer.remaining()]; 1157 buffer.get(pixels); 1158 handler.post(new Runnable() { 1159 @Override 1160 public void run() { 1161 jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this); 1162 }}); 1163 } 1164 } 1165 }}; 1166 try { 1167 mDispatchThread.runJob(new Runnable() { 1168 @Override 1169 public void run() { 1170 // Wait until PREVIEW_ACTIVE or better 1171 mCameraState.waitForStates( 1172 ~(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE - 1)); 1173 mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener) 1174 .sendToTarget(); 1175 } 1176 }); 1177 } catch (RuntimeException ex) { 1178 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex); 1179 } 1180 } 1181 1182 // TODO: Implement 1183 @Override 1184 public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {} 1185 1186 // TODO: Implement 1187 @Override 1188 public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback) 1189 {} 1190 1191 // TODO: Remove this method override once we handle this message 1192 @Override 1193 public void startFaceDetection() {} 1194 1195 // TODO: Remove this method override once we handle this message 1196 @Override 1197 public void stopFaceDetection() {} 1198 1199 // TODO: Implement 1200 @Override 1201 public void setParameters(android.hardware.Camera.Parameters params) {} 1202 1203 // TODO: Implement 1204 @Override 1205 public android.hardware.Camera.Parameters getParameters() { return null; } 1206 1207 @Override 1208 public CameraSettings getSettings() { 1209 if (mLastSettings == null) { 1210 mLastSettings = mCameraHandler.buildSettings(mCapabilities); 1211 } 1212 return mLastSettings; 1213 } 1214 1215 @Override 1216 public boolean applySettings(CameraSettings settings) { 1217 if (settings == null) { 1218 Log.w(TAG, "null parameters in applySettings()"); 1219 return false; 1220 } 1221 if (!(settings instanceof AndroidCamera2Settings)) { 1222 Log.e(TAG, "Provided settings not compatible with the backing framework API"); 1223 return false; 1224 } 1225 1226 // Wait for any state that isn't OPENED 1227 if (applySettingsHelper(settings, ~AndroidCamera2StateHolder.CAMERA_UNOPENED)) { 1228 mLastSettings = settings; 1229 return true; 1230 } 1231 return false; 1232 } 1233 1234 @Override 1235 public void enableShutterSound(boolean enable) { 1236 mShutterSoundEnabled = enable; 1237 } 1238 1239 // TODO: Implement 1240 @Override 1241 public String dumpDeviceSettings() { return null; } 1242 1243 @Override 1244 public Handler getCameraHandler() { 1245 return AndroidCamera2AgentImpl.this.getCameraHandler(); 1246 } 1247 1248 @Override 1249 public DispatchThread getDispatchThread() { 1250 return AndroidCamera2AgentImpl.this.getDispatchThread(); 1251 } 1252 1253 @Override 1254 public CameraStateHolder getCameraState() { 1255 return mCameraState; 1256 } 1257 } 1258 1259 /** A linear state machine: each state entails all the states below it. */ 1260 private static class AndroidCamera2StateHolder extends CameraStateHolder { 1261 // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() -> 1262 // autoFocus() -> takePicture() 1263 // States are mutually exclusive, but must be separate bits so that they can be used with 1264 // the StateHolder#waitForStates() and StateHolder#waitToAvoidStates() methods. 1265 // Do not set the state to be a combination of these values! 1266 /* Camera states */ 1267 /** No camera device is opened. */ 1268 public static final int CAMERA_UNOPENED = 1 << 0; 1269 /** A camera is opened, but no settings have been provided. */ 1270 public static final int CAMERA_UNCONFIGURED = 1 << 1; 1271 /** The open camera has been configured by providing it with settings. */ 1272 public static final int CAMERA_CONFIGURED = 1 << 2; 1273 /** A capture session is ready to stream a preview, but still has no repeating request. */ 1274 public static final int CAMERA_PREVIEW_READY = 1 << 3; 1275 /** A preview is currently being streamed. */ 1276 public static final int CAMERA_PREVIEW_ACTIVE = 1 << 4; 1277 /** The lens is locked on a particular region. */ 1278 public static final int CAMERA_FOCUS_LOCKED = 1 << 5; 1279 1280 public AndroidCamera2StateHolder() { 1281 this(CAMERA_UNOPENED); 1282 } 1283 1284 public AndroidCamera2StateHolder(int state) { 1285 super(state); 1286 } 1287 } 1288 1289 private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo { 1290 private final CameraManager mCameraManager; 1291 private final String[] mCameraIds; 1292 private final int mNumberOfCameras; 1293 private final int mFirstBackCameraId; 1294 private final int mFirstFrontCameraId; 1295 1296 public AndroidCamera2DeviceInfo(CameraManager cameraManager, 1297 String[] cameraIds, int numberOfCameras) { 1298 mCameraManager = cameraManager; 1299 mCameraIds = cameraIds; 1300 mNumberOfCameras = numberOfCameras; 1301 1302 int firstBackId = NO_DEVICE; 1303 int firstFrontId = NO_DEVICE; 1304 for (int id = 0; id < cameraIds.length; ++id) { 1305 try { 1306 int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id]) 1307 .get(CameraCharacteristics.LENS_FACING); 1308 if (firstBackId == NO_DEVICE && 1309 lensDirection == CameraCharacteristics.LENS_FACING_BACK) { 1310 firstBackId = id; 1311 } 1312 if (firstFrontId == NO_DEVICE && 1313 lensDirection == CameraCharacteristics.LENS_FACING_FRONT) { 1314 firstFrontId = id; 1315 } 1316 } catch (CameraAccessException ex) { 1317 Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex); 1318 } 1319 } 1320 mFirstBackCameraId = firstBackId; 1321 mFirstFrontCameraId = firstFrontId; 1322 } 1323 1324 @Override 1325 public Characteristics getCharacteristics(int cameraId) { 1326 String actualId = mCameraIds[cameraId]; 1327 try { 1328 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId); 1329 return new AndroidCharacteristics2(info); 1330 } catch (CameraAccessException ex) { 1331 return null; 1332 } 1333 } 1334 1335 @Override 1336 public int getNumberOfCameras() { 1337 return mNumberOfCameras; 1338 } 1339 1340 @Override 1341 public int getFirstBackCameraId() { 1342 return mFirstBackCameraId; 1343 } 1344 1345 @Override 1346 public int getFirstFrontCameraId() { 1347 return mFirstFrontCameraId; 1348 } 1349 1350 private static class AndroidCharacteristics2 extends Characteristics { 1351 private CameraCharacteristics mCameraInfo; 1352 1353 AndroidCharacteristics2(CameraCharacteristics cameraInfo) { 1354 mCameraInfo = cameraInfo; 1355 } 1356 1357 @Override 1358 public boolean isFacingBack() { 1359 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1360 .equals(CameraCharacteristics.LENS_FACING_BACK); 1361 } 1362 1363 @Override 1364 public boolean isFacingFront() { 1365 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1366 .equals(CameraCharacteristics.LENS_FACING_FRONT); 1367 } 1368 1369 @Override 1370 public int getSensorOrientation() { 1371 return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION); 1372 } 1373 1374 @Override 1375 public Matrix getPreviewTransform(int currentDisplayOrientation, 1376 RectF surfaceDimensions, 1377 RectF desiredBounds) { 1378 if (!orientationIsValid(currentDisplayOrientation)) { 1379 return new Matrix(); 1380 } 1381 1382 // The system transparently transforms the image to fill the surface 1383 // when the device is in its natural orientation. We rotate the 1384 // coordinates of the rectangle's corners to be relative to the 1385 // original image, instead of to the current screen orientation. 1386 float[] surfacePolygon = rotate(convertRectToPoly(surfaceDimensions), 1387 2 * currentDisplayOrientation / 90); 1388 float[] desiredPolygon = convertRectToPoly(desiredBounds); 1389 1390 Matrix transform = new Matrix(); 1391 // Use polygons instead of rectangles so that rotation will be 1392 // calculated, since that is not done by the new camera API. 1393 transform.setPolyToPoly(surfacePolygon, 0, desiredPolygon, 0, 4); 1394 return transform; 1395 } 1396 1397 @Override 1398 public boolean canDisableShutterSound() { 1399 return true; 1400 } 1401 1402 private static float[] convertRectToPoly(RectF rf) { 1403 return new float[] {rf.left, rf.top, rf.right, rf.top, 1404 rf.right, rf.bottom, rf.left, rf.bottom}; 1405 } 1406 1407 private static float[] rotate(float[] arr, int times) { 1408 if (times < 0) { 1409 times = times % arr.length + arr.length; 1410 } 1411 1412 float[] res = new float[arr.length]; 1413 for (int offset = 0; offset < arr.length; ++offset) { 1414 res[offset] = arr[(times + offset) % arr.length]; 1415 } 1416 return res; 1417 } 1418 } 1419 } 1420 } 1421