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