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