AndroidCamera2AgentImpl.java revision 25ee73acd2dbd6f60deef5306994fbf3a7997936
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.CaptureCallback 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, mCameraDeviceStateCallback, 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*/mCameraResultStateCallback, /*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.CaptureCallback deferredCallbackSetter = 383 new CameraCaptureSession.CaptureCallback() { 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 mCameraResultStateCallback 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 mCameraResultStateCallback.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. (e.g., JPEG rotation is 497 // necessary in auto-rotate mode. 498 mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg2 > 0 ? 499 mCameraProxy.getCharacteristics().getJpegOrientation(msg.arg1) : 0); 500 break; 501 } 502 503 case CameraActions.SET_JPEG_ORIENTATION: { 504 mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg1); 505 break; 506 } 507 508 case CameraActions.CAPTURE_PHOTO: { 509 if (mCameraState.getState() < 510 AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 511 Log.e(TAG, "Photos may only be taken when a preview is active"); 512 break; 513 } 514 if (mCameraState.getState() != 515 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED) { 516 Log.w(TAG, "Taking a (likely blurry) photo without the lens locked"); 517 } 518 519 final CaptureAvailableListener listener = 520 (CaptureAvailableListener) msg.obj; 521 if (mLegacyDevice || 522 (mCurrentAeState == CaptureResult.CONTROL_AE_STATE_CONVERGED && 523 !mPersistentSettings.matches(CaptureRequest.CONTROL_AE_MODE, 524 CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH) && 525 !mPersistentSettings.matches(CaptureRequest.FLASH_MODE, 526 CaptureRequest.FLASH_MODE_SINGLE))) 527 { 528 // Legacy devices don't support the precapture state keys and instead 529 // perform autoexposure convergence automatically upon capture. 530 531 // On other devices, as long as it has already converged, it determined 532 // that flash was not required, and we're not going to invalidate the 533 // current exposure levels by forcing the force on, we can save 534 // significant capture time by not forcing a recalculation. 535 Log.i(TAG, "Skipping pre-capture autoexposure convergence"); 536 mCaptureReader.setOnImageAvailableListener(listener, /*handler*/this); 537 try { 538 mSession.capture( 539 mPersistentSettings.createRequest(mCamera, 540 CameraDevice.TEMPLATE_STILL_CAPTURE, 541 mCaptureReader.getSurface()), 542 listener, /*handler*/this); 543 } catch (CameraAccessException ex) { 544 Log.e(TAG, "Unable to initiate immediate capture", ex); 545 } 546 } else { 547 // We need to let AE converge before capturing. Once our one-time 548 // trigger capture has made it into the pipeline, we'll start checking 549 // for the completion of that convergence, capturing when that happens. 550 Log.i(TAG, "Forcing pre-capture autoexposure convergence"); 551 CameraCaptureSession.CaptureCallback deferredCallbackSetter = 552 new CameraCaptureSession.CaptureCallback() { 553 private boolean mAlreadyDispatched = false; 554 555 @Override 556 public void onCaptureProgressed(CameraCaptureSession session, 557 CaptureRequest request, 558 CaptureResult result) { 559 checkAeState(result); 560 } 561 562 @Override 563 public void onCaptureCompleted(CameraCaptureSession session, 564 CaptureRequest request, 565 TotalCaptureResult result) { 566 checkAeState(result); 567 } 568 569 private void checkAeState(CaptureResult result) { 570 if (result.get(CaptureResult.CONTROL_AE_STATE) != null && 571 !mAlreadyDispatched) { 572 // Now our mCameraResultStateCallback will invoke the 573 // callback once the autoexposure routine has converged. 574 mAlreadyDispatched = true; 575 mOneshotCaptureCallback = listener; 576 // This is an optimization: check the AE state of this frame 577 // instead of simply waiting for the next. 578 mCameraResultStateCallback.monitorControlStates(result); 579 } 580 } 581 582 @Override 583 public void onCaptureFailed(CameraCaptureSession session, 584 CaptureRequest request, 585 CaptureFailure failure) { 586 Log.e(TAG, "Autoexposure and capture failed with reason " + 587 failure.getReason()); 588 // TODO: Make an error callback? 589 }}; 590 591 // Set a one-time capture to trigger the camera driver's autoexposure: 592 Camera2RequestSettingsSet expose = 593 new Camera2RequestSettingsSet(mPersistentSettings); 594 expose.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 595 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 596 try { 597 mSession.capture( 598 expose.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW, 599 mPreviewSurface), 600 /*listener*/deferredCallbackSetter, /*handler*/this); 601 } catch (CameraAccessException ex) { 602 Log.e(TAG, "Unable to run autoexposure and perform capture", ex); 603 } 604 } 605 break; 606 } 607 608 default: { 609 // TODO: Rephrase once everything has been implemented 610 throw new RuntimeException("Unimplemented CameraProxy message=" + msg.what); 611 } 612 } 613 } catch (final Exception ex) { 614 if (msg.what != CameraActions.RELEASE && mCamera != null) { 615 // TODO: Handle this better 616 mCamera.close(); 617 mCamera = null; 618 } else if (mCamera == null) { 619 if (msg.what == CameraActions.OPEN_CAMERA) { 620 if (mOpenCallback != null) { 621 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 622 generateHistoryString(mCameraIndex)); 623 } 624 } else { 625 Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null"); 626 } 627 return; 628 } 629 630 if (ex instanceof RuntimeException) { 631 post(new Runnable() { 632 @Override 633 public void run() { 634 sCameraExceptionCallback.onCameraException((RuntimeException) ex); 635 }}); 636 } 637 } 638 } 639 640 public CameraSettings buildSettings(AndroidCamera2Capabilities caps) { 641 try { 642 return new AndroidCamera2Settings(mCamera, CameraDevice.TEMPLATE_PREVIEW, 643 mActiveArray, mPreviewSize, mPhotoSize); 644 } catch (CameraAccessException ex) { 645 Log.e(TAG, "Unable to query camera device to build settings representation"); 646 return null; 647 } 648 } 649 650 /** 651 * Simply propagates settings from provided {@link CameraSettings} 652 * object to our {@link CaptureRequest.Builder} for use in captures. 653 * <p>Most conversions to match the API 2 formats are performed by 654 * {@link AndroidCamera2Capabilities.IntegralStringifier}; otherwise 655 * any final adjustments are done here before updating the builder.</p> 656 * 657 * @param settings The new/updated settings 658 */ 659 private void applyToRequest(AndroidCamera2Settings settings) { 660 // TODO: If invoked when in PREVIEW_READY state, a new preview size will not take effect 661 662 mPersistentSettings.union(settings.getRequestSettings()); 663 mPreviewSize = settings.getCurrentPreviewSize(); 664 mPhotoSize = settings.getCurrentPhotoSize(); 665 666 if (mCameraState.getState() >= AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 667 // If we're already previewing, reflect most settings immediately 668 try { 669 mSession.setRepeatingRequest( 670 mPersistentSettings.createRequest(mCamera, 671 CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface), 672 /*listener*/mCameraResultStateCallback, /*handler*/this); 673 } catch (CameraAccessException ex) { 674 Log.e(TAG, "Failed to apply updated request settings", ex); 675 } 676 } else if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) { 677 // If we're already ready to preview, this doesn't regress our state 678 changeState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); 679 } 680 } 681 682 private void setPreviewTexture(SurfaceTexture surfaceTexture) { 683 // TODO: Must be called after providing a .*Settings populated with sizes 684 // TODO: We don't technically offer a selection of sizes tailored to SurfaceTextures! 685 686 // TODO: Handle this error condition with a callback or exception 687 if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_CONFIGURED) { 688 Log.w(TAG, "Ignoring texture setting at inappropriate time"); 689 return; 690 } 691 692 // Avoid initializing another capture session unless we absolutely have to 693 if (surfaceTexture == mPreviewTexture) { 694 Log.i(TAG, "Optimizing out redundant preview texture setting"); 695 return; 696 } 697 698 if (mSession != null) { 699 closePreviewSession(); 700 } 701 702 mPreviewTexture = surfaceTexture; 703 surfaceTexture.setDefaultBufferSize(mPreviewSize.width(), mPreviewSize.height()); 704 705 if (mPreviewSurface != null) { 706 mPreviewSurface.release(); 707 } 708 mPreviewSurface = new Surface(surfaceTexture); 709 710 if (mCaptureReader != null) { 711 mCaptureReader.close(); 712 } 713 mCaptureReader = ImageReader.newInstance( 714 mPhotoSize.width(), mPhotoSize.height(), ImageFormat.JPEG, 1); 715 716 try { 717 mCamera.createCaptureSession( 718 Arrays.asList(mPreviewSurface, mCaptureReader.getSurface()), 719 mCameraPreviewStateCallback, this); 720 } catch (CameraAccessException ex) { 721 Log.e(TAG, "Failed to create camera capture session", ex); 722 } 723 } 724 725 private void closePreviewSession() { 726 try { 727 mSession.abortCaptures(); 728 mSession = null; 729 } catch (CameraAccessException ex) { 730 Log.e(TAG, "Failed to close existing camera capture session", ex); 731 } 732 changeState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); 733 } 734 735 private void changeState(int newState) { 736 if (mCameraState.getState() != newState) { 737 mCameraState.setState(newState); 738 if (newState < AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { 739 mCurrentAeState = CaptureResult.CONTROL_AE_STATE_INACTIVE; 740 mCameraResultStateCallback.resetState(); 741 } 742 } 743 } 744 745 // This callback monitors our connection to and disconnection from camera devices. 746 private CameraDevice.StateCallback mCameraDeviceStateCallback = 747 new CameraDevice.StateCallback() { 748 @Override 749 public void onOpened(CameraDevice camera) { 750 mCamera = camera; 751 if (mOpenCallback != null) { 752 try { 753 CameraCharacteristics props = 754 mCameraManager.getCameraCharacteristics(mCameraId); 755 mCameraProxy = new AndroidCamera2ProxyImpl(mCameraIndex, mCamera, 756 getCameraDeviceInfo().getCharacteristics(mCameraIndex), props); 757 mPersistentSettings = new Camera2RequestSettingsSet(); 758 mActiveArray = 759 props.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 760 mLegacyDevice = 761 props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == 762 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; 763 changeState(AndroidCamera2StateHolder.CAMERA_UNCONFIGURED); 764 mOpenCallback.onCameraOpened(mCameraProxy); 765 } catch (CameraAccessException ex) { 766 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 767 generateHistoryString(mCameraIndex)); 768 } 769 } 770 } 771 772 @Override 773 public void onDisconnected(CameraDevice camera) { 774 Log.w(TAG, "Camera device '" + mCameraIndex + "' was disconnected"); 775 } 776 777 @Override 778 public void onError(CameraDevice camera, int error) { 779 Log.e(TAG, "Camera device '" + mCameraIndex + "' encountered error code '" + 780 error + '\''); 781 if (mOpenCallback != null) { 782 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 783 generateHistoryString(mCameraIndex)); 784 } 785 }}; 786 787 // This callback monitors our camera session (i.e. our transition into and out of preview). 788 private CameraCaptureSession.StateCallback mCameraPreviewStateCallback = 789 new CameraCaptureSession.StateCallback() { 790 @Override 791 public void onConfigured(CameraCaptureSession session) { 792 mSession = session; 793 changeState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); 794 } 795 796 @Override 797 public void onConfigureFailed(CameraCaptureSession session) { 798 // TODO: Invoke a callback 799 Log.e(TAG, "Failed to configure the camera for capture"); 800 } 801 802 @Override 803 public void onActive(CameraCaptureSession session) { 804 if (mOneshotPreviewingCallback != null) { 805 // The session is up and processing preview requests. Inform the caller. 806 mOneshotPreviewingCallback.onPreviewStarted(); 807 mOneshotPreviewingCallback = null; 808 } 809 }}; 810 811 private abstract class CameraResultStateCallback 812 extends CameraCaptureSession.CaptureCallback { 813 public abstract void monitorControlStates(CaptureResult result); 814 815 public abstract void resetState(); 816 } 817 818 // This callback monitors requested captures and notifies any relevant callbacks. 819 private CameraResultStateCallback mCameraResultStateCallback = 820 new CameraResultStateCallback() { 821 private int mLastAfState = -1; 822 private long mLastAfFrameNumber = -1; 823 private long mLastAeFrameNumber = -1; 824 825 @Override 826 public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, 827 CaptureResult result) { 828 monitorControlStates(result); 829 } 830 831 @Override 832 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, 833 TotalCaptureResult result) { 834 monitorControlStates(result); 835 } 836 837 @Override 838 public void monitorControlStates(CaptureResult result) { 839 Integer afStateMaybe = result.get(CaptureResult.CONTROL_AF_STATE); 840 if (afStateMaybe != null) { 841 int afState = afStateMaybe; 842 // Since we handle both partial and total results for multiple frames here, we 843 // might get the final callbacks for an earlier frame after receiving one or 844 // more that correspond to the next one. To prevent our data from oscillating, 845 // we never consider AF states that are older than the last one we've seen. 846 if (result.getFrameNumber() > mLastAfFrameNumber) { 847 boolean afStateChanged = afState != mLastAfState; 848 mLastAfState = afState; 849 mLastAfFrameNumber = result.getFrameNumber(); 850 851 switch (afState) { 852 case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN: 853 case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: 854 case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: { 855 if (afStateChanged && mPassiveAfCallback != null) { 856 // A CameraAFMoveCallback is attached. If we just started to 857 // scan, the motor is moving; otherwise, it has settled. 858 mPassiveAfCallback.onAutoFocusMoving( 859 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN, 860 mCameraProxy); 861 } 862 break; 863 } 864 865 case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: 866 case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: { 867 // This check must be made regardless of whether the focus state has 868 // changed recently to avoid infinite waiting during autoFocus() 869 // when the algorithm has already either converged or failed to. 870 if (mOneshotAfCallback != null) { 871 // A call to autoFocus() was just made to request a focus lock. 872 // Notify the caller that the lens is now indefinitely fixed, 873 // and report whether the image we're stuck with is in focus. 874 mOneshotAfCallback.onAutoFocus( 875 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED, 876 mCameraProxy); 877 mOneshotAfCallback = null; 878 } 879 break; 880 } 881 } 882 } 883 } 884 885 Integer aeStateMaybe = result.get(CaptureResult.CONTROL_AE_STATE); 886 if (aeStateMaybe != null) { 887 int aeState = aeStateMaybe; 888 // Since we handle both partial and total results for multiple frames here, we 889 // might get the final callbacks for an earlier frame after receiving one or 890 // more that correspond to the next one. To prevent our data from oscillating, 891 // we never consider AE states that are older than the last one we've seen. 892 if (result.getFrameNumber() > mLastAeFrameNumber) { 893 mCurrentAeState = aeStateMaybe; 894 mLastAeFrameNumber = result.getFrameNumber(); 895 896 switch (aeState) { 897 case CaptureResult.CONTROL_AE_STATE_CONVERGED: 898 case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED: 899 case CaptureResult.CONTROL_AE_STATE_LOCKED: { 900 // This check must be made regardless of whether the exposure state 901 // has changed recently to avoid infinite waiting during 902 // takePicture() when the algorithm has already converged. 903 if (mOneshotCaptureCallback != null) { 904 // A call to takePicture() was just made, and autoexposure 905 // converged so it's time to initiate the capture! 906 mCaptureReader.setOnImageAvailableListener( 907 /*listener*/mOneshotCaptureCallback, 908 /*handler*/Camera2Handler.this); 909 try { 910 mSession.capture( 911 mPersistentSettings.createRequest(mCamera, 912 CameraDevice.TEMPLATE_STILL_CAPTURE, 913 mCaptureReader.getSurface()), 914 /*callback*/mOneshotCaptureCallback, 915 /*handler*/Camera2Handler.this); 916 } catch (CameraAccessException ex) { 917 Log.e(TAG, "Unable to initiate capture", ex); 918 } finally { 919 mOneshotCaptureCallback = null; 920 } 921 } 922 break; 923 } 924 } 925 } 926 } 927 } 928 929 @Override 930 public void resetState() { 931 mLastAfState = -1; 932 mLastAfFrameNumber = -1; 933 mLastAeFrameNumber = -1; 934 } 935 936 @Override 937 public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, 938 CaptureFailure failure) { 939 Log.e(TAG, "Capture attempt failed with reason " + failure.getReason()); 940 }}; 941 } 942 943 private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy { 944 private final int mCameraIndex; 945 private final CameraDevice mCamera; 946 private final CameraDeviceInfo.Characteristics mCharacteristics; 947 private final AndroidCamera2Capabilities mCapabilities; 948 private CameraSettings mLastSettings; 949 private boolean mShutterSoundEnabled; 950 951 public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera, 952 CameraDeviceInfo.Characteristics characteristics, 953 CameraCharacteristics properties) { 954 mCameraIndex = cameraIndex; 955 mCamera = camera; 956 mCharacteristics = characteristics; 957 mCapabilities = new AndroidCamera2Capabilities(properties); 958 mLastSettings = null; 959 mShutterSoundEnabled = true; 960 } 961 962 // TODO: Implement 963 @Override 964 public android.hardware.Camera getCamera() { return null; } 965 966 @Override 967 public int getCameraId() { 968 return mCameraIndex; 969 } 970 971 @Override 972 public CameraDeviceInfo.Characteristics getCharacteristics() { 973 return mCharacteristics; 974 } 975 976 @Override 977 public CameraCapabilities getCapabilities() { 978 return mCapabilities; 979 } 980 981 private AndroidCamera2Capabilities getSpecializedCapabilities() { 982 return mCapabilities; 983 } 984 985 // FIXME: Unlock the sizes in stopPreview(), as per the corresponding 986 // explanation on the STOP_PREVIEW case in the handler. 987 @Override 988 public void setPreviewTexture(SurfaceTexture surfaceTexture) { 989 // Once the Surface has been selected, we configure the session and 990 // are no longer able to change the sizes. 991 getSettings().setSizesLocked(true); 992 super.setPreviewTexture(surfaceTexture); 993 } 994 995 // FIXME: Unlock the sizes in stopPreview(), as per the corresponding 996 // explanation on the STOP_PREVIEW case in the handler. 997 @Override 998 public void setPreviewTextureSync(SurfaceTexture surfaceTexture) { 999 // Once the Surface has been selected, we configure the session and 1000 // are no longer able to change the sizes. 1001 getSettings().setSizesLocked(true); 1002 super.setPreviewTexture(surfaceTexture); 1003 } 1004 1005 // TODO: Implement 1006 @Override 1007 public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {} 1008 1009 // TODO: Implement 1010 @Override 1011 public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {} 1012 1013 // TODO: Implement 1014 @Override 1015 public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb) 1016 {} 1017 1018 // TODO: Implement 1019 public void addCallbackBuffer(final byte[] callbackBuffer) {} 1020 1021 @Override 1022 public void autoFocus(final Handler handler, final CameraAFCallback cb) { 1023 mDispatchThread.runJob(new Runnable() { 1024 @Override 1025 public void run() { 1026 CameraAFCallback cbForward = null; 1027 if (cb != null) { 1028 cbForward = new CameraAFCallback() { 1029 @Override 1030 public void onAutoFocus(final boolean focused, 1031 final CameraProxy camera) { 1032 handler.post(new Runnable() { 1033 @Override 1034 public void run() { 1035 cb.onAutoFocus(focused, camera); 1036 }}); 1037 }}; 1038 } 1039 1040 mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE | 1041 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 1042 mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward) 1043 .sendToTarget(); 1044 }}); 1045 } 1046 1047 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1048 @Override 1049 public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) { 1050 mDispatchThread.runJob(new Runnable() { 1051 @Override 1052 public void run() { 1053 CameraAFMoveCallback cbForward = null; 1054 if (cb != null) { 1055 cbForward = new CameraAFMoveCallback() { 1056 @Override 1057 public void onAutoFocusMoving(final boolean moving, 1058 final CameraProxy camera) { 1059 handler.post(new Runnable() { 1060 @Override 1061 public void run() { 1062 cb.onAutoFocusMoving(moving, camera); 1063 }}); 1064 }}; 1065 } 1066 1067 mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK, 1068 cbForward).sendToTarget(); 1069 }}); 1070 } 1071 1072 @Override 1073 public void takePicture(final Handler handler, 1074 final CameraShutterCallback shutter, 1075 CameraPictureCallback raw, 1076 CameraPictureCallback postview, 1077 final CameraPictureCallback jpeg) { 1078 // TODO: We never call raw or postview 1079 final CaptureAvailableListener picListener = 1080 new CaptureAvailableListener() { 1081 @Override 1082 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 1083 long timestamp) { 1084 if (shutter != null) { 1085 handler.post(new Runnable() { 1086 @Override 1087 public void run() { 1088 if (mShutterSoundEnabled) { 1089 mNoisemaker.play(MediaActionSound.SHUTTER_CLICK); 1090 } 1091 shutter.onShutter(AndroidCamera2ProxyImpl.this); 1092 }}); 1093 } 1094 } 1095 1096 @Override 1097 public void onImageAvailable(ImageReader reader) { 1098 try (Image image = reader.acquireNextImage()) { 1099 if (jpeg != null) { 1100 ByteBuffer buffer = image.getPlanes()[0].getBuffer(); 1101 final byte[] pixels = new byte[buffer.remaining()]; 1102 buffer.get(pixels); 1103 handler.post(new Runnable() { 1104 @Override 1105 public void run() { 1106 jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this); 1107 }}); 1108 } 1109 } 1110 }}; 1111 mDispatchThread.runJob(new Runnable() { 1112 @Override 1113 public void run() { 1114 // Wait until PREVIEW_ACTIVE or better 1115 mCameraState.waitForStates( 1116 ~(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE - 1)); 1117 mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener) 1118 .sendToTarget(); 1119 }}); 1120 } 1121 1122 // TODO: Implement 1123 @Override 1124 public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {} 1125 1126 // TODO: Implement 1127 @Override 1128 public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback) 1129 {} 1130 1131 // TODO: Remove this method override once we handle this message 1132 @Override 1133 public void startFaceDetection() {} 1134 1135 // TODO: Remove this method override once we handle this message 1136 @Override 1137 public void stopFaceDetection() {} 1138 1139 // TODO: Implement 1140 @Override 1141 public void setErrorCallback(Handler handler, CameraErrorCallback cb) {} 1142 1143 // TODO: Implement 1144 @Override 1145 public void setParameters(android.hardware.Camera.Parameters params) {} 1146 1147 // TODO: Implement 1148 @Override 1149 public android.hardware.Camera.Parameters getParameters() { return null; } 1150 1151 @Override 1152 public CameraSettings getSettings() { 1153 if (mLastSettings == null) { 1154 mLastSettings = mCameraHandler.buildSettings(mCapabilities); 1155 } 1156 return mLastSettings; 1157 } 1158 1159 @Override 1160 public boolean applySettings(CameraSettings settings) { 1161 if (settings == null) { 1162 Log.w(TAG, "null parameters in applySettings()"); 1163 return false; 1164 } 1165 if (!(settings instanceof AndroidCamera2Settings)) { 1166 Log.e(TAG, "Provided settings not compatible with the backing framework API"); 1167 return false; 1168 } 1169 1170 // Wait for any state that isn't OPENED 1171 if (applySettingsHelper(settings, ~AndroidCamera2StateHolder.CAMERA_UNOPENED)) { 1172 mLastSettings = settings; 1173 return true; 1174 } 1175 return false; 1176 } 1177 1178 @Override 1179 public void enableShutterSound(boolean enable) { 1180 mShutterSoundEnabled = enable; 1181 } 1182 1183 // TODO: Implement 1184 @Override 1185 public String dumpDeviceSettings() { return null; } 1186 1187 @Override 1188 public Handler getCameraHandler() { 1189 return AndroidCamera2AgentImpl.this.getCameraHandler(); 1190 } 1191 1192 @Override 1193 public DispatchThread getDispatchThread() { 1194 return AndroidCamera2AgentImpl.this.getDispatchThread(); 1195 } 1196 1197 @Override 1198 public CameraStateHolder getCameraState() { 1199 return mCameraState; 1200 } 1201 } 1202 1203 /** A linear state machine: each state entails all the states below it. */ 1204 private static class AndroidCamera2StateHolder extends CameraStateHolder { 1205 // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() -> 1206 // autoFocus() -> takePicture() 1207 // States are mutually exclusive, but must be separate bits so that they can be used with 1208 // the StateHolder#waitForStates() and StateHolder#waitToAvoidStates() methods. 1209 // Do not set the state to be a combination of these values! 1210 /* Camera states */ 1211 /** No camera device is opened. */ 1212 public static final int CAMERA_UNOPENED = 1 << 0; 1213 /** A camera is opened, but no settings have been provided. */ 1214 public static final int CAMERA_UNCONFIGURED = 1 << 1; 1215 /** The open camera has been configured by providing it with settings. */ 1216 public static final int CAMERA_CONFIGURED = 1 << 2; 1217 /** A capture session is ready to stream a preview, but still has no repeating request. */ 1218 public static final int CAMERA_PREVIEW_READY = 1 << 3; 1219 /** A preview is currently being streamed. */ 1220 public static final int CAMERA_PREVIEW_ACTIVE = 1 << 4; 1221 /** The lens is locked on a particular region. */ 1222 public static final int CAMERA_FOCUS_LOCKED = 1 << 5; 1223 1224 public AndroidCamera2StateHolder() { 1225 this(CAMERA_UNOPENED); 1226 } 1227 1228 public AndroidCamera2StateHolder(int state) { 1229 super(state); 1230 } 1231 } 1232 1233 private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo { 1234 private final CameraManager mCameraManager; 1235 private final String[] mCameraIds; 1236 private final int mNumberOfCameras; 1237 private final int mFirstBackCameraId; 1238 private final int mFirstFrontCameraId; 1239 1240 public AndroidCamera2DeviceInfo(CameraManager cameraManager, 1241 String[] cameraIds, int numberOfCameras) { 1242 mCameraManager = cameraManager; 1243 mCameraIds = cameraIds; 1244 mNumberOfCameras = numberOfCameras; 1245 1246 int firstBackId = NO_DEVICE; 1247 int firstFrontId = NO_DEVICE; 1248 for (int id = 0; id < cameraIds.length; ++id) { 1249 try { 1250 int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id]) 1251 .get(CameraCharacteristics.LENS_FACING); 1252 if (firstBackId == NO_DEVICE && 1253 lensDirection == CameraCharacteristics.LENS_FACING_BACK) { 1254 firstBackId = id; 1255 } 1256 if (firstFrontId == NO_DEVICE && 1257 lensDirection == CameraCharacteristics.LENS_FACING_FRONT) { 1258 firstFrontId = id; 1259 } 1260 } catch (CameraAccessException ex) { 1261 Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex); 1262 } 1263 } 1264 mFirstBackCameraId = firstBackId; 1265 mFirstFrontCameraId = firstFrontId; 1266 } 1267 1268 @Override 1269 public Characteristics getCharacteristics(int cameraId) { 1270 String actualId = mCameraIds[cameraId]; 1271 try { 1272 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId); 1273 return new AndroidCharacteristics2(info); 1274 } catch (CameraAccessException ex) { 1275 return null; 1276 } 1277 } 1278 1279 @Override 1280 public int getNumberOfCameras() { 1281 return mNumberOfCameras; 1282 } 1283 1284 @Override 1285 public int getFirstBackCameraId() { 1286 return mFirstBackCameraId; 1287 } 1288 1289 @Override 1290 public int getFirstFrontCameraId() { 1291 return mFirstFrontCameraId; 1292 } 1293 1294 private static class AndroidCharacteristics2 extends Characteristics { 1295 private CameraCharacteristics mCameraInfo; 1296 1297 AndroidCharacteristics2(CameraCharacteristics cameraInfo) { 1298 mCameraInfo = cameraInfo; 1299 } 1300 1301 @Override 1302 public boolean isFacingBack() { 1303 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1304 .equals(CameraCharacteristics.LENS_FACING_BACK); 1305 } 1306 1307 @Override 1308 public boolean isFacingFront() { 1309 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1310 .equals(CameraCharacteristics.LENS_FACING_FRONT); 1311 } 1312 1313 @Override 1314 public int getSensorOrientation() { 1315 return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION); 1316 } 1317 1318 @Override 1319 public Matrix getPreviewTransform(int currentDisplayOrientation, 1320 RectF surfaceDimensions, 1321 RectF desiredBounds) { 1322 if (!orientationIsValid(currentDisplayOrientation)) { 1323 return new Matrix(); 1324 } 1325 1326 // The system transparently transforms the image to fill the surface 1327 // when the device is in its natural orientation. We rotate the 1328 // coordinates of the rectangle's corners to be relative to the 1329 // original image, instead of to the current screen orientation. 1330 float[] surfacePolygon = rotate(convertRectToPoly(surfaceDimensions), 1331 2 * currentDisplayOrientation / 90); 1332 float[] desiredPolygon = convertRectToPoly(desiredBounds); 1333 1334 Matrix transform = new Matrix(); 1335 // Use polygons instead of rectangles so that rotation will be 1336 // calculated, since that is not done by the new camera API. 1337 transform.setPolyToPoly(surfacePolygon, 0, desiredPolygon, 0, 4); 1338 return transform; 1339 } 1340 1341 @Override 1342 public boolean canDisableShutterSound() { 1343 return true; 1344 } 1345 1346 private static float[] convertRectToPoly(RectF rf) { 1347 return new float[] {rf.left, rf.top, rf.right, rf.top, 1348 rf.right, rf.bottom, rf.left, rf.bottom}; 1349 } 1350 1351 private static float[] rotate(float[] arr, int times) { 1352 if (times < 0) { 1353 times = times % arr.length + arr.length; 1354 } 1355 1356 float[] res = new float[arr.length]; 1357 for (int offset = 0; offset < arr.length; ++offset) { 1358 res[offset] = arr[(times + offset) % arr.length]; 1359 } 1360 return res; 1361 } 1362 } 1363 } 1364 1365 private static final CameraExceptionCallback sCameraExceptionCallback = 1366 new CameraExceptionCallback() { 1367 @Override 1368 public synchronized void onCameraException(RuntimeException e) { 1369 throw e; 1370 } 1371 }; 1372} 1373