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