AndroidCamera2AgentImpl.java revision bb013aa3e197e881756be5ad13e6ad30bfb4aeff
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 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.CaptureCallback deferredCallbackSetter = 546 new CameraCaptureSession.CaptureCallback() { 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 mCameraResultStateCallback 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 mCameraResultStateCallback.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*/mCameraResultStateCallback, /*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 mCameraPreviewStateCallback, 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 mCameraResultStateCallback.resetState(); 735 } 736 } 737 } 738 739 // This callback monitors our connection to and disconnection from camera devices. 740 private CameraDevice.StateCallback mCameraDeviceStateCallback = 741 new CameraDevice.StateCallback() { 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 callback monitors our camera session (i.e. our transition into and out of preview). 782 private CameraCaptureSession.StateCallback mCameraPreviewStateCallback = 783 new CameraCaptureSession.StateCallback() { 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 CameraResultStateCallback 806 extends CameraCaptureSession.CaptureCallback { 807 public abstract void monitorControlStates(CaptureResult result); 808 809 public abstract void resetState(); 810 } 811 812 // This callback monitors requested captures and notifies any relevant callbacks. 813 private CameraResultStateCallback mCameraResultStateCallback = 814 new CameraResultStateCallback() { 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 /*callback*/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 private boolean mShutterSoundEnabled; 944 945 public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera, 946 CameraDeviceInfo.Characteristics characteristics, 947 CameraCharacteristics properties) { 948 mCameraIndex = cameraIndex; 949 mCamera = camera; 950 mCharacteristics = characteristics; 951 mCapabilities = new AndroidCamera2Capabilities(properties); 952 mLastSettings = null; 953 mShutterSoundEnabled = true; 954 } 955 956 // TODO: Implement 957 @Override 958 public android.hardware.Camera getCamera() { return null; } 959 960 @Override 961 public int getCameraId() { 962 return mCameraIndex; 963 } 964 965 @Override 966 public CameraDeviceInfo.Characteristics getCharacteristics() { 967 return mCharacteristics; 968 } 969 970 @Override 971 public CameraCapabilities getCapabilities() { 972 return mCapabilities; 973 } 974 975 private AndroidCamera2Capabilities getSpecializedCapabilities() { 976 return mCapabilities; 977 } 978 979 // FIXME: Unlock the sizes in stopPreview(), as per the corresponding 980 // explanation on the STOP_PREVIEW case in the handler. 981 @Override 982 public void setPreviewTexture(SurfaceTexture surfaceTexture) { 983 // Once the Surface has been selected, we configure the session and 984 // are no longer able to change the sizes. 985 getSettings().setSizesLocked(true); 986 super.setPreviewTexture(surfaceTexture); 987 } 988 989 // FIXME: Unlock the sizes in stopPreview(), as per the corresponding 990 // explanation on the STOP_PREVIEW case in the handler. 991 @Override 992 public void setPreviewTextureSync(SurfaceTexture surfaceTexture) { 993 // Once the Surface has been selected, we configure the session and 994 // are no longer able to change the sizes. 995 getSettings().setSizesLocked(true); 996 super.setPreviewTexture(surfaceTexture); 997 } 998 999 // TODO: Implement 1000 @Override 1001 public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {} 1002 1003 // TODO: Implement 1004 @Override 1005 public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {} 1006 1007 // TODO: Implement 1008 @Override 1009 public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb) 1010 {} 1011 1012 // TODO: Implement 1013 public void addCallbackBuffer(final byte[] callbackBuffer) {} 1014 1015 @Override 1016 public void autoFocus(final Handler handler, final CameraAFCallback cb) { 1017 mDispatchThread.runJob(new Runnable() { 1018 @Override 1019 public void run() { 1020 CameraAFCallback cbForward = null; 1021 if (cb != null) { 1022 cbForward = new CameraAFCallback() { 1023 @Override 1024 public void onAutoFocus(final boolean focused, 1025 final CameraProxy camera) { 1026 handler.post(new Runnable() { 1027 @Override 1028 public void run() { 1029 cb.onAutoFocus(focused, camera); 1030 }}); 1031 }}; 1032 } 1033 1034 mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE | 1035 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 1036 mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward) 1037 .sendToTarget(); 1038 }}); 1039 } 1040 1041 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1042 @Override 1043 public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) { 1044 mDispatchThread.runJob(new Runnable() { 1045 @Override 1046 public void run() { 1047 CameraAFMoveCallback cbForward = null; 1048 if (cb != null) { 1049 cbForward = new CameraAFMoveCallback() { 1050 @Override 1051 public void onAutoFocusMoving(final boolean moving, 1052 final CameraProxy camera) { 1053 handler.post(new Runnable() { 1054 @Override 1055 public void run() { 1056 cb.onAutoFocusMoving(moving, camera); 1057 }}); 1058 }}; 1059 } 1060 1061 mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK, 1062 cbForward).sendToTarget(); 1063 }}); 1064 } 1065 1066 @Override 1067 public void takePicture(final Handler handler, 1068 final CameraShutterCallback shutter, 1069 CameraPictureCallback raw, 1070 CameraPictureCallback postview, 1071 final CameraPictureCallback jpeg) { 1072 // TODO: We never call raw or postview 1073 final CaptureAvailableListener picListener = 1074 new CaptureAvailableListener() { 1075 @Override 1076 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 1077 long timestamp) { 1078 if (shutter != null) { 1079 handler.post(new Runnable() { 1080 @Override 1081 public void run() { 1082 if (mShutterSoundEnabled) { 1083 mNoisemaker.play(MediaActionSound.SHUTTER_CLICK); 1084 } 1085 shutter.onShutter(AndroidCamera2ProxyImpl.this); 1086 }}); 1087 } 1088 } 1089 1090 @Override 1091 public void onImageAvailable(ImageReader reader) { 1092 try (Image image = reader.acquireNextImage()) { 1093 if (jpeg != null) { 1094 ByteBuffer buffer = image.getPlanes()[0].getBuffer(); 1095 final byte[] pixels = new byte[buffer.remaining()]; 1096 buffer.get(pixels); 1097 handler.post(new Runnable() { 1098 @Override 1099 public void run() { 1100 jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this); 1101 }}); 1102 } 1103 } 1104 }}; 1105 mDispatchThread.runJob(new Runnable() { 1106 @Override 1107 public void run() { 1108 // Wait until PREVIEW_ACTIVE or better 1109 mCameraState.waitForStates( 1110 ~(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE - 1)); 1111 mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener) 1112 .sendToTarget(); 1113 }}); 1114 } 1115 1116 // TODO: Implement 1117 @Override 1118 public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {} 1119 1120 // TODO: Implement 1121 @Override 1122 public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback) 1123 {} 1124 1125 // TODO: Remove this method override once we handle this message 1126 @Override 1127 public void startFaceDetection() {} 1128 1129 // TODO: Remove this method override once we handle this message 1130 @Override 1131 public void stopFaceDetection() {} 1132 1133 // TODO: Implement 1134 @Override 1135 public void setErrorCallback(Handler handler, CameraErrorCallback cb) {} 1136 1137 // TODO: Implement 1138 @Override 1139 public void setParameters(android.hardware.Camera.Parameters params) {} 1140 1141 // TODO: Implement 1142 @Override 1143 public android.hardware.Camera.Parameters getParameters() { return null; } 1144 1145 @Override 1146 public CameraSettings getSettings() { 1147 if (mLastSettings == null) { 1148 mLastSettings = mCameraHandler.buildSettings(mCapabilities); 1149 } 1150 return mLastSettings; 1151 } 1152 1153 @Override 1154 public boolean applySettings(CameraSettings settings) { 1155 if (settings == null) { 1156 Log.w(TAG, "null parameters in applySettings()"); 1157 return false; 1158 } 1159 if (!(settings instanceof AndroidCamera2Settings)) { 1160 Log.e(TAG, "Provided settings not compatible with the backing framework API"); 1161 return false; 1162 } 1163 1164 // Wait for any state that isn't OPENED 1165 if (applySettingsHelper(settings, ~AndroidCamera2StateHolder.CAMERA_UNOPENED)) { 1166 mLastSettings = settings; 1167 return true; 1168 } 1169 return false; 1170 } 1171 1172 @Override 1173 public void enableShutterSound(boolean enable) { 1174 mShutterSoundEnabled = enable; 1175 } 1176 1177 // TODO: Implement 1178 @Override 1179 public String dumpDeviceSettings() { return null; } 1180 1181 @Override 1182 public Handler getCameraHandler() { 1183 return AndroidCamera2AgentImpl.this.getCameraHandler(); 1184 } 1185 1186 @Override 1187 public DispatchThread getDispatchThread() { 1188 return AndroidCamera2AgentImpl.this.getDispatchThread(); 1189 } 1190 1191 @Override 1192 public CameraStateHolder getCameraState() { 1193 return mCameraState; 1194 } 1195 } 1196 1197 /** A linear state machine: each state entails all the states below it. */ 1198 private static class AndroidCamera2StateHolder extends CameraStateHolder { 1199 // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() -> 1200 // autoFocus() -> takePicture() 1201 // States are mutually exclusive, but must be separate bits so that they can be used with 1202 // the StateHolder#waitForStates() and StateHolder#waitToAvoidStates() methods. 1203 // Do not set the state to be a combination of these values! 1204 /* Camera states */ 1205 /** No camera device is opened. */ 1206 public static final int CAMERA_UNOPENED = 1 << 0; 1207 /** A camera is opened, but no settings have been provided. */ 1208 public static final int CAMERA_UNCONFIGURED = 1 << 1; 1209 /** The open camera has been configured by providing it with settings. */ 1210 public static final int CAMERA_CONFIGURED = 1 << 2; 1211 /** A capture session is ready to stream a preview, but still has no repeating request. */ 1212 public static final int CAMERA_PREVIEW_READY = 1 << 3; 1213 /** A preview is currently being streamed. */ 1214 public static final int CAMERA_PREVIEW_ACTIVE = 1 << 4; 1215 /** The lens is locked on a particular region. */ 1216 public static final int CAMERA_FOCUS_LOCKED = 1 << 5; 1217 1218 public AndroidCamera2StateHolder() { 1219 this(CAMERA_UNOPENED); 1220 } 1221 1222 public AndroidCamera2StateHolder(int state) { 1223 super(state); 1224 } 1225 } 1226 1227 private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo { 1228 private final CameraManager mCameraManager; 1229 private final String[] mCameraIds; 1230 private final int mNumberOfCameras; 1231 private final int mFirstBackCameraId; 1232 private final int mFirstFrontCameraId; 1233 1234 public AndroidCamera2DeviceInfo(CameraManager cameraManager, 1235 String[] cameraIds, int numberOfCameras) { 1236 mCameraManager = cameraManager; 1237 mCameraIds = cameraIds; 1238 mNumberOfCameras = numberOfCameras; 1239 1240 int firstBackId = NO_DEVICE; 1241 int firstFrontId = NO_DEVICE; 1242 for (int id = 0; id < cameraIds.length; ++id) { 1243 try { 1244 int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id]) 1245 .get(CameraCharacteristics.LENS_FACING); 1246 if (firstBackId == NO_DEVICE && 1247 lensDirection == CameraCharacteristics.LENS_FACING_BACK) { 1248 firstBackId = id; 1249 } 1250 if (firstFrontId == NO_DEVICE && 1251 lensDirection == CameraCharacteristics.LENS_FACING_FRONT) { 1252 firstFrontId = id; 1253 } 1254 } catch (CameraAccessException ex) { 1255 Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex); 1256 } 1257 } 1258 mFirstBackCameraId = firstBackId; 1259 mFirstFrontCameraId = firstFrontId; 1260 } 1261 1262 @Override 1263 public Characteristics getCharacteristics(int cameraId) { 1264 String actualId = mCameraIds[cameraId]; 1265 try { 1266 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId); 1267 return new AndroidCharacteristics2(info); 1268 } catch (CameraAccessException ex) { 1269 return null; 1270 } 1271 } 1272 1273 @Override 1274 public int getNumberOfCameras() { 1275 return mNumberOfCameras; 1276 } 1277 1278 @Override 1279 public int getFirstBackCameraId() { 1280 return mFirstBackCameraId; 1281 } 1282 1283 @Override 1284 public int getFirstFrontCameraId() { 1285 return mFirstFrontCameraId; 1286 } 1287 1288 private static class AndroidCharacteristics2 extends Characteristics { 1289 private CameraCharacteristics mCameraInfo; 1290 1291 AndroidCharacteristics2(CameraCharacteristics cameraInfo) { 1292 mCameraInfo = cameraInfo; 1293 } 1294 1295 @Override 1296 public boolean isFacingBack() { 1297 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1298 .equals(CameraCharacteristics.LENS_FACING_BACK); 1299 } 1300 1301 @Override 1302 public boolean isFacingFront() { 1303 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1304 .equals(CameraCharacteristics.LENS_FACING_FRONT); 1305 } 1306 1307 @Override 1308 public int getSensorOrientation() { 1309 return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION); 1310 } 1311 1312 @Override 1313 public Matrix getPreviewTransform(int currentDisplayOrientation, 1314 RectF surfaceDimensions, 1315 RectF desiredBounds) { 1316 if (!orientationIsValid(currentDisplayOrientation)) { 1317 return new Matrix(); 1318 } 1319 1320 // The system transparently transforms the image to fill the surface 1321 // when the device is in its natural orientation. We rotate the 1322 // coordinates of the rectangle's corners to be relative to the 1323 // original image, instead of to the current screen orientation. 1324 float[] surfacePolygon = rotate(convertRectToPoly(surfaceDimensions), 1325 2 * currentDisplayOrientation / 90); 1326 float[] desiredPolygon = convertRectToPoly(desiredBounds); 1327 1328 Matrix transform = new Matrix(); 1329 // Use polygons instead of rectangles so that rotation will be 1330 // calculated, since that is not done by the new camera API. 1331 transform.setPolyToPoly(surfacePolygon, 0, desiredPolygon, 0, 4); 1332 return transform; 1333 } 1334 1335 @Override 1336 public boolean canDisableShutterSound() { 1337 return true; 1338 } 1339 1340 private static float[] convertRectToPoly(RectF rf) { 1341 return new float[] {rf.left, rf.top, rf.right, rf.top, 1342 rf.right, rf.bottom, rf.left, rf.bottom}; 1343 } 1344 1345 private static float[] rotate(float[] arr, int times) { 1346 if (times < 0) { 1347 times = times % arr.length + arr.length; 1348 } 1349 1350 float[] res = new float[arr.length]; 1351 for (int offset = 0; offset < arr.length; ++offset) { 1352 res[offset] = arr[(times + offset) % arr.length]; 1353 } 1354 return res; 1355 } 1356 } 1357 } 1358 1359 private static final CameraExceptionCallback sCameraExceptionCallback = 1360 new CameraExceptionCallback() { 1361 @Override 1362 public synchronized void onCameraException(RuntimeException e) { 1363 throw e; 1364 } 1365 }; 1366} 1367