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