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