AndroidCamera2AgentImpl.java revision 7c8d6957b96c75ce11f7373f4ab2451ee4666360
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 if (mOneshotAfCallback != null) { 858 // A call to autoFocus() was just made to request a focus lock. 859 // Notify the caller that the lens is now indefinitely fixed, 860 // and report whether the image we're stuck with is in focus. 861 mOneshotAfCallback.onAutoFocus( 862 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED, 863 mCameraProxy); 864 mOneshotAfCallback = null; 865 } 866 break; 867 } 868 } 869 } 870 } 871 872 Integer aeStateMaybe = result.get(CaptureResult.CONTROL_AE_STATE); 873 if (aeStateMaybe != null) { 874 int aeState = aeStateMaybe; 875 // Since we handle both partial and total results for multiple frames here, we 876 // might get the final callbacks for an earlier frame after receiving one or 877 // more that correspond to the next one. To prevent our data from oscillating, 878 // we never consider AE states that are older than the last one we've seen. 879 if (aeState != mCurrentAeState && 880 result.getFrameNumber() > mLastAeFrameNumber) { 881 mCurrentAeState = aeStateMaybe; 882 mLastAeFrameNumber = result.getFrameNumber(); 883 884 switch (aeState) { 885 case CaptureResult.CONTROL_AE_STATE_CONVERGED: 886 case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED: 887 case CaptureResult.CONTROL_AE_STATE_LOCKED: { 888 if (mOneshotCaptureCallback != null) { 889 // A call to takePicture() was just made, and autoexposure 890 // converged so it's time to initiate the capture! 891 mCaptureReader.setOnImageAvailableListener( 892 /*listener*/mOneshotCaptureCallback, 893 /*handler*/Camera2Handler.this); 894 try { 895 mSession.capture( 896 mPersistentSettings.createRequest(mCamera, 897 CameraDevice.TEMPLATE_STILL_CAPTURE, 898 mCaptureReader.getSurface()), 899 /*listener*/mOneshotCaptureCallback, 900 /*handler*/Camera2Handler.this); 901 } catch (CameraAccessException ex) { 902 Log.e(TAG, "Unable to initiate capture", ex); 903 } finally { 904 mOneshotCaptureCallback = null; 905 } 906 } 907 break; 908 } 909 } 910 } 911 } 912 } 913 914 @Override 915 public void resetState() { 916 mLastAfState = -1; 917 mLastAfFrameNumber = -1; 918 mLastAeFrameNumber = -1; 919 } 920 921 @Override 922 public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, 923 CaptureFailure failure) { 924 Log.e(TAG, "Capture attempt failed with reason " + failure.getReason()); 925 }}; 926 } 927 928 private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy { 929 private final int mCameraIndex; 930 private final CameraDevice mCamera; 931 private final CameraDeviceInfo.Characteristics mCharacteristics; 932 private final AndroidCamera2Capabilities mCapabilities; 933 private CameraSettings mLastSettings; 934 935 public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera, 936 CameraDeviceInfo.Characteristics characteristics, 937 CameraCharacteristics properties) { 938 mCameraIndex = cameraIndex; 939 mCamera = camera; 940 mCharacteristics = characteristics; 941 mCapabilities = new AndroidCamera2Capabilities(properties); 942 mLastSettings = null; 943 } 944 945 // TODO: Implement 946 @Override 947 public android.hardware.Camera getCamera() { return null; } 948 949 @Override 950 public int getCameraId() { 951 return mCameraIndex; 952 } 953 954 @Override 955 public CameraDeviceInfo.Characteristics getCharacteristics() { 956 return mCharacteristics; 957 } 958 959 @Override 960 public CameraCapabilities getCapabilities() { 961 return mCapabilities; 962 } 963 964 private AndroidCamera2Capabilities getSpecializedCapabilities() { 965 return mCapabilities; 966 } 967 968 // TODO: Implement 969 @Override 970 public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {} 971 972 // TODO: Implement 973 @Override 974 public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {} 975 976 // TODO: Implement 977 @Override 978 public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb) 979 {} 980 981 // TODO: Implement 982 public void addCallbackBuffer(final byte[] callbackBuffer) {} 983 984 @Override 985 public void autoFocus(final Handler handler, final CameraAFCallback cb) { 986 mDispatchThread.runJob(new Runnable() { 987 @Override 988 public void run() { 989 CameraAFCallback cbForward = null; 990 if (cb != null) { 991 cbForward = new CameraAFCallback() { 992 @Override 993 public void onAutoFocus(final boolean focused, 994 final CameraProxy camera) { 995 handler.post(new Runnable() { 996 @Override 997 public void run() { 998 cb.onAutoFocus(focused, camera); 999 }}); 1000 }}; 1001 } 1002 1003 mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE | 1004 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 1005 mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward) 1006 .sendToTarget(); 1007 }}); 1008 } 1009 1010 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1011 @Override 1012 public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) { 1013 mDispatchThread.runJob(new Runnable() { 1014 @Override 1015 public void run() { 1016 CameraAFMoveCallback cbForward = null; 1017 if (cb != null) { 1018 cbForward = new CameraAFMoveCallback() { 1019 @Override 1020 public void onAutoFocusMoving(final boolean moving, 1021 final CameraProxy camera) { 1022 handler.post(new Runnable() { 1023 @Override 1024 public void run() { 1025 cb.onAutoFocusMoving(moving, camera); 1026 }}); 1027 }}; 1028 } 1029 1030 mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK, 1031 cbForward).sendToTarget(); 1032 }}); 1033 } 1034 1035 @Override 1036 public void takePicture(final Handler handler, 1037 final CameraShutterCallback shutter, 1038 CameraPictureCallback raw, 1039 CameraPictureCallback postview, 1040 final CameraPictureCallback jpeg) { 1041 // TODO: We never call raw or postview 1042 final CaptureAvailableListener picListener = 1043 new CaptureAvailableListener() { 1044 @Override 1045 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 1046 long timestamp) { 1047 if (shutter != null) { 1048 handler.post(new Runnable() { 1049 @Override 1050 public void run() { 1051 mNoisemaker.play(MediaActionSound.SHUTTER_CLICK); 1052 shutter.onShutter(AndroidCamera2ProxyImpl.this); 1053 }}); 1054 } 1055 } 1056 1057 @Override 1058 public void onImageAvailable(ImageReader reader) { 1059 try (Image image = reader.acquireNextImage()) { 1060 if (jpeg != null) { 1061 ByteBuffer buffer = image.getPlanes()[0].getBuffer(); 1062 final byte[] pixels = new byte[buffer.remaining()]; 1063 buffer.get(pixels); 1064 handler.post(new Runnable() { 1065 @Override 1066 public void run() { 1067 jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this); 1068 }}); 1069 } 1070 } 1071 }}; 1072 mDispatchThread.runJob(new Runnable() { 1073 @Override 1074 public void run() { 1075 mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE | 1076 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 1077 mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener) 1078 .sendToTarget(); 1079 }}); 1080 } 1081 1082 // TODO: Implement 1083 @Override 1084 public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {} 1085 1086 // TODO: Implement 1087 @Override 1088 public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback) 1089 {} 1090 1091 // TODO: Remove this method override once we handle this message 1092 @Override 1093 public void startFaceDetection() {} 1094 1095 // TODO: Remove this method override once we handle this message 1096 @Override 1097 public void stopFaceDetection() {} 1098 1099 // TODO: Implement 1100 @Override 1101 public void setErrorCallback(Handler handler, CameraErrorCallback cb) {} 1102 1103 // TODO: Implement 1104 @Override 1105 public void setParameters(android.hardware.Camera.Parameters params) {} 1106 1107 // TODO: Implement 1108 @Override 1109 public android.hardware.Camera.Parameters getParameters() { return null; } 1110 1111 @Override 1112 public CameraSettings getSettings() { 1113 if (mLastSettings == null) { 1114 mLastSettings = mCameraHandler.buildSettings(mCapabilities); 1115 } 1116 return mLastSettings; 1117 } 1118 1119 @Override 1120 public boolean applySettings(CameraSettings settings) { 1121 if (settings == null) { 1122 Log.w(TAG, "null parameters in applySettings()"); 1123 return false; 1124 } 1125 if (!(settings instanceof AndroidCamera2Settings)) { 1126 Log.e(TAG, "Provided settings not compatible with the backing framework API"); 1127 return false; 1128 } 1129 1130 if (applySettingsHelper(settings, AndroidCamera2StateHolder.CAMERA_UNCONFIGURED | 1131 AndroidCamera2StateHolder.CAMERA_CONFIGURED | 1132 AndroidCamera2StateHolder.CAMERA_PREVIEW_READY)) { 1133 mLastSettings = settings; 1134 return true; 1135 } 1136 return false; 1137 } 1138 1139 // TODO: Implement 1140 @Override 1141 public String dumpDeviceSettings() { return null; } 1142 1143 @Override 1144 public Handler getCameraHandler() { 1145 return AndroidCamera2AgentImpl.this.getCameraHandler(); 1146 } 1147 1148 @Override 1149 public DispatchThread getDispatchThread() { 1150 return AndroidCamera2AgentImpl.this.getDispatchThread(); 1151 } 1152 1153 @Override 1154 public CameraStateHolder getCameraState() { 1155 return mCameraState; 1156 } 1157 } 1158 1159 /** A linear state machine: each state entails all the states below it. */ 1160 private static class AndroidCamera2StateHolder extends CameraStateHolder { 1161 // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() -> 1162 // autoFocus() -> takePicture() 1163 /* Camera states */ 1164 /** No camera device is opened. */ 1165 public static final int CAMERA_UNOPENED = 1; 1166 /** A camera is opened, but no settings have been provided. */ 1167 public static final int CAMERA_UNCONFIGURED = 2; 1168 /** The open camera has been configured by providing it with settings. */ 1169 public static final int CAMERA_CONFIGURED = 3; 1170 /** A capture session is ready to stream a preview, but still has no repeating request. */ 1171 public static final int CAMERA_PREVIEW_READY = 4; 1172 /** A preview is currently being streamed. */ 1173 public static final int CAMERA_PREVIEW_ACTIVE = 5; 1174 /** The lens is locked on a particular region. */ 1175 public static final int CAMERA_FOCUS_LOCKED = 6; 1176 1177 public AndroidCamera2StateHolder() { 1178 this(CAMERA_UNOPENED); 1179 } 1180 1181 public AndroidCamera2StateHolder(int state) { 1182 super(state); 1183 } 1184 } 1185 1186 private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo { 1187 private final CameraManager mCameraManager; 1188 private final String[] mCameraIds; 1189 private final int mNumberOfCameras; 1190 private final int mFirstBackCameraId; 1191 private final int mFirstFrontCameraId; 1192 1193 public AndroidCamera2DeviceInfo(CameraManager cameraManager, 1194 String[] cameraIds, int numberOfCameras) { 1195 mCameraManager = cameraManager; 1196 mCameraIds = cameraIds; 1197 mNumberOfCameras = numberOfCameras; 1198 1199 int firstBackId = NO_DEVICE; 1200 int firstFrontId = NO_DEVICE; 1201 for (int id = 0; id < cameraIds.length; ++id) { 1202 try { 1203 int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id]) 1204 .get(CameraCharacteristics.LENS_FACING); 1205 if (firstBackId == NO_DEVICE && 1206 lensDirection == CameraCharacteristics.LENS_FACING_BACK) { 1207 firstBackId = id; 1208 } 1209 if (firstFrontId == NO_DEVICE && 1210 lensDirection == CameraCharacteristics.LENS_FACING_FRONT) { 1211 firstFrontId = id; 1212 } 1213 } catch (CameraAccessException ex) { 1214 Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex); 1215 } 1216 } 1217 mFirstBackCameraId = firstBackId; 1218 mFirstFrontCameraId = firstFrontId; 1219 } 1220 1221 @Override 1222 public Characteristics getCharacteristics(int cameraId) { 1223 String actualId = mCameraIds[cameraId]; 1224 try { 1225 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId); 1226 return new AndroidCharacteristics2(info); 1227 } catch (CameraAccessException ex) { 1228 return null; 1229 } 1230 } 1231 1232 @Override 1233 public int getNumberOfCameras() { 1234 return mNumberOfCameras; 1235 } 1236 1237 @Override 1238 public int getFirstBackCameraId() { 1239 return mFirstBackCameraId; 1240 } 1241 1242 @Override 1243 public int getFirstFrontCameraId() { 1244 return mFirstFrontCameraId; 1245 } 1246 1247 private static class AndroidCharacteristics2 extends Characteristics { 1248 private CameraCharacteristics mCameraInfo; 1249 1250 AndroidCharacteristics2(CameraCharacteristics cameraInfo) { 1251 mCameraInfo = cameraInfo; 1252 } 1253 1254 @Override 1255 public boolean isFacingBack() { 1256 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1257 .equals(CameraCharacteristics.LENS_FACING_BACK); 1258 } 1259 1260 @Override 1261 public boolean isFacingFront() { 1262 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1263 .equals(CameraCharacteristics.LENS_FACING_FRONT); 1264 } 1265 1266 @Override 1267 public int getSensorOrientation() { 1268 return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION); 1269 } 1270 1271 @Override 1272 public Matrix getPreviewTransform(int currentDisplayOrientation, 1273 RectF surfaceDimensions, 1274 RectF desiredBounds) { 1275 if (!orientationIsValid(currentDisplayOrientation)) { 1276 return new Matrix(); 1277 } 1278 1279 // The system transparently transforms the image to fill the surface 1280 // when the device is in its natural orientation. We rotate the 1281 // coordinates of the rectangle's corners to be relative to the 1282 // original image, instead of to the current screen orientation. 1283 float[] surfacePolygon = rotate(convertRectToPoly(surfaceDimensions), 1284 2 * currentDisplayOrientation / 90); 1285 float[] desiredPolygon = convertRectToPoly(desiredBounds); 1286 1287 Matrix transform = new Matrix(); 1288 // Use polygons instead of rectangles so that rotation will be 1289 // calculated, since that is not done by the new camera API. 1290 transform.setPolyToPoly(surfacePolygon, 0, desiredPolygon, 0, 4); 1291 return transform; 1292 } 1293 1294 @Override 1295 public boolean canDisableShutterSound() { 1296 // The new API doesn't support this operation, so don't encourage people to try it. 1297 // TODO: What kind of assumptions have callers made about this result's meaning? 1298 return false; 1299 } 1300 1301 private static float[] convertRectToPoly(RectF rf) { 1302 return new float[] {rf.left, rf.top, rf.right, rf.top, 1303 rf.right, rf.bottom, rf.left, rf.bottom}; 1304 } 1305 1306 private static float[] rotate(float[] arr, int times) { 1307 if (times < 0) { 1308 times = times % arr.length + arr.length; 1309 } 1310 1311 float[] res = new float[arr.length]; 1312 for (int offset = 0; offset < arr.length; ++offset) { 1313 res[offset] = arr[(times + offset) % arr.length]; 1314 } 1315 return res; 1316 } 1317 } 1318 } 1319 1320 private static final CameraExceptionCallback sCameraExceptionCallback = 1321 new CameraExceptionCallback() { 1322 @Override 1323 public synchronized void onCameraException(RuntimeException e) { 1324 throw e; 1325 } 1326 }; 1327} 1328