AndroidCamera2AgentImpl.java revision 80cda8dae801f09c2ce928d0f3feb8a569939fc2
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 private CameraSettings mLastSettings; 823 824 public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera, 825 CameraDeviceInfo.Characteristics characteristics, 826 CameraCharacteristics properties) { 827 mCameraIndex = cameraIndex; 828 mCamera = camera; 829 mCharacteristics = characteristics; 830 mCapabilities = new AndroidCamera2Capabilities(properties); 831 mLastSettings = null; 832 } 833 834 // TODO: Implement 835 @Override 836 public android.hardware.Camera getCamera() { return null; } 837 838 @Override 839 public int getCameraId() { 840 return mCameraIndex; 841 } 842 843 @Override 844 public CameraDeviceInfo.Characteristics getCharacteristics() { 845 return mCharacteristics; 846 } 847 848 @Override 849 public CameraCapabilities getCapabilities() { 850 return mCapabilities; 851 } 852 853 private AndroidCamera2Capabilities getSpecializedCapabilities() { 854 return mCapabilities; 855 } 856 857 // TODO: Implement 858 @Override 859 public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {} 860 861 // TODO: Implement 862 @Override 863 public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {} 864 865 // TODO: Implement 866 @Override 867 public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb) 868 {} 869 870 // TODO: Implement 871 public void addCallbackBuffer(final byte[] callbackBuffer) {} 872 873 @Override 874 public void autoFocus(final Handler handler, final CameraAFCallback cb) { 875 mDispatchThread.runJob(new Runnable() { 876 @Override 877 public void run() { 878 CameraAFCallback cbForward = null; 879 if (cb != null) { 880 cbForward = new CameraAFCallback() { 881 @Override 882 public void onAutoFocus(final boolean focused, 883 final CameraProxy camera) { 884 handler.post(new Runnable() { 885 @Override 886 public void run() { 887 cb.onAutoFocus(focused, camera); 888 }}); 889 }}; 890 } 891 892 mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE | 893 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 894 mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward) 895 .sendToTarget(); 896 }}); 897 } 898 899 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 900 @Override 901 public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) { 902 mDispatchThread.runJob(new Runnable() { 903 @Override 904 public void run() { 905 CameraAFMoveCallback cbForward = null; 906 if (cb != null) { 907 cbForward = new CameraAFMoveCallback() { 908 @Override 909 public void onAutoFocusMoving(final boolean moving, 910 final CameraProxy camera) { 911 handler.post(new Runnable() { 912 @Override 913 public void run() { 914 cb.onAutoFocusMoving(moving, camera); 915 }}); 916 }}; 917 } 918 919 mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK, 920 cbForward).sendToTarget(); 921 }}); 922 } 923 924 @Override 925 public void takePicture(final Handler handler, 926 final CameraShutterCallback shutter, 927 CameraPictureCallback raw, 928 CameraPictureCallback postview, 929 final CameraPictureCallback jpeg) { 930 // TODO: We never call raw or postview 931 final CaptureAvailableListener picListener = 932 new CaptureAvailableListener() { 933 @Override 934 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 935 long timestamp) { 936 if (shutter != null) { 937 handler.post(new Runnable() { 938 @Override 939 public void run() { 940 mNoisemaker.play(MediaActionSound.SHUTTER_CLICK); 941 shutter.onShutter(AndroidCamera2ProxyImpl.this); 942 }}); 943 } 944 } 945 946 @Override 947 public void onImageAvailable(ImageReader reader) { 948 try (Image image = reader.acquireNextImage()) { 949 if (jpeg != null) { 950 ByteBuffer buffer = image.getPlanes()[0].getBuffer(); 951 final byte[] pixels = new byte[buffer.remaining()]; 952 buffer.get(pixels); 953 handler.post(new Runnable() { 954 @Override 955 public void run() { 956 jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this); 957 }}); 958 } 959 } 960 }}; 961 mDispatchThread.runJob(new Runnable() { 962 @Override 963 public void run() { 964 mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE | 965 AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED); 966 mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener) 967 .sendToTarget(); 968 }}); 969 } 970 971 // TODO: Implement 972 @Override 973 public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {} 974 975 // TODO: Implement 976 @Override 977 public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback) 978 {} 979 980 // TODO: Remove this method override once we handle this message 981 @Override 982 public void startFaceDetection() {} 983 984 // TODO: Remove this method override once we handle this message 985 @Override 986 public void stopFaceDetection() {} 987 988 // TODO: Implement 989 @Override 990 public void setErrorCallback(Handler handler, CameraErrorCallback cb) {} 991 992 // TODO: Implement 993 @Override 994 public void setParameters(android.hardware.Camera.Parameters params) {} 995 996 // TODO: Implement 997 @Override 998 public android.hardware.Camera.Parameters getParameters() { return null; } 999 1000 @Override 1001 public CameraSettings getSettings() { 1002 if (mLastSettings == null) { 1003 mLastSettings = mCameraHandler.buildSettings(mCapabilities); 1004 } 1005 return mLastSettings; 1006 } 1007 1008 @Override 1009 public boolean applySettings(CameraSettings settings) { 1010 if (settings == null) { 1011 Log.w(TAG, "null parameters in applySettings()"); 1012 return false; 1013 } 1014 if (!(settings instanceof AndroidCamera2Settings)) { 1015 Log.e(TAG, "Provided settings not compatible with the backing framework API"); 1016 return false; 1017 } 1018 1019 if (applySettingsHelper(settings, AndroidCamera2StateHolder.CAMERA_UNCONFIGURED | 1020 AndroidCamera2StateHolder.CAMERA_CONFIGURED | 1021 AndroidCamera2StateHolder.CAMERA_PREVIEW_READY)) { 1022 mLastSettings = settings; 1023 return true; 1024 } 1025 return false; 1026 } 1027 1028 // TODO: Implement 1029 @Override 1030 public String dumpDeviceSettings() { return null; } 1031 1032 @Override 1033 public Handler getCameraHandler() { 1034 return AndroidCamera2AgentImpl.this.getCameraHandler(); 1035 } 1036 1037 @Override 1038 public DispatchThread getDispatchThread() { 1039 return AndroidCamera2AgentImpl.this.getDispatchThread(); 1040 } 1041 1042 @Override 1043 public CameraStateHolder getCameraState() { 1044 return mCameraState; 1045 } 1046 } 1047 1048 /** A linear state machine: each state entails all the states below it. */ 1049 private static class AndroidCamera2StateHolder extends CameraStateHolder { 1050 // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() -> 1051 // autoFocus() -> takePicture() 1052 /* Camera states */ 1053 /** No camera device is opened. */ 1054 public static final int CAMERA_UNOPENED = 1; 1055 /** A camera is opened, but no settings have been provided. */ 1056 public static final int CAMERA_UNCONFIGURED = 2; 1057 /** The open camera has been configured by providing it with settings. */ 1058 public static final int CAMERA_CONFIGURED = 3; 1059 /** A capture session is ready to stream a preview, but still has no repeating request. */ 1060 public static final int CAMERA_PREVIEW_READY = 4; 1061 /** A preview is currently being streamed. */ 1062 public static final int CAMERA_PREVIEW_ACTIVE = 5; 1063 /** The lens is locked on a particular region. */ 1064 public static final int CAMERA_FOCUS_LOCKED = 6; 1065 1066 public AndroidCamera2StateHolder() { 1067 this(CAMERA_UNOPENED); 1068 } 1069 1070 public AndroidCamera2StateHolder(int state) { 1071 super(state); 1072 } 1073 } 1074 1075 private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo { 1076 private final CameraManager mCameraManager; 1077 private final String[] mCameraIds; 1078 private final int mNumberOfCameras; 1079 private final int mFirstBackCameraId; 1080 private final int mFirstFrontCameraId; 1081 1082 public AndroidCamera2DeviceInfo(CameraManager cameraManager, 1083 String[] cameraIds, int numberOfCameras) { 1084 mCameraManager = cameraManager; 1085 mCameraIds = cameraIds; 1086 mNumberOfCameras = numberOfCameras; 1087 1088 int firstBackId = NO_DEVICE; 1089 int firstFrontId = NO_DEVICE; 1090 for (int id = 0; id < cameraIds.length; ++id) { 1091 try { 1092 int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id]) 1093 .get(CameraCharacteristics.LENS_FACING); 1094 if (firstBackId == NO_DEVICE && 1095 lensDirection == CameraCharacteristics.LENS_FACING_BACK) { 1096 firstBackId = id; 1097 } 1098 if (firstFrontId == NO_DEVICE && 1099 lensDirection == CameraCharacteristics.LENS_FACING_FRONT) { 1100 firstFrontId = id; 1101 } 1102 } catch (CameraAccessException ex) { 1103 Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex); 1104 } 1105 } 1106 mFirstBackCameraId = firstBackId; 1107 mFirstFrontCameraId = firstFrontId; 1108 } 1109 1110 @Override 1111 public Characteristics getCharacteristics(int cameraId) { 1112 String actualId = mCameraIds[cameraId]; 1113 try { 1114 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId); 1115 return new AndroidCharacteristics2(info); 1116 } catch (CameraAccessException ex) { 1117 return null; 1118 } 1119 } 1120 1121 @Override 1122 public int getNumberOfCameras() { 1123 return mNumberOfCameras; 1124 } 1125 1126 @Override 1127 public int getFirstBackCameraId() { 1128 return mFirstBackCameraId; 1129 } 1130 1131 @Override 1132 public int getFirstFrontCameraId() { 1133 return mFirstFrontCameraId; 1134 } 1135 1136 private static class AndroidCharacteristics2 extends Characteristics { 1137 private CameraCharacteristics mCameraInfo; 1138 1139 AndroidCharacteristics2(CameraCharacteristics cameraInfo) { 1140 mCameraInfo = cameraInfo; 1141 } 1142 1143 @Override 1144 public boolean isFacingBack() { 1145 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1146 .equals(CameraCharacteristics.LENS_FACING_BACK); 1147 } 1148 1149 @Override 1150 public boolean isFacingFront() { 1151 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 1152 .equals(CameraCharacteristics.LENS_FACING_FRONT); 1153 } 1154 1155 @Override 1156 public int getSensorOrientation() { 1157 return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION); 1158 } 1159 1160 @Override 1161 public Matrix getPreviewTransform(int currentDisplayOrientation, 1162 RectF surfaceDimensions, 1163 RectF desiredBounds) { 1164 if (!orientationIsValid(currentDisplayOrientation)) { 1165 return new Matrix(); 1166 } 1167 1168 // The system transparently transforms the image to fill the surface 1169 // when the device is in its natural orientation. We rotate the 1170 // coordinates of the rectangle's corners to be relative to the 1171 // original image, instead of to the current screen orientation. 1172 float[] surfacePolygon = rotate(convertRectToPoly(surfaceDimensions), 1173 2 * currentDisplayOrientation / 90); 1174 float[] desiredPolygon = convertRectToPoly(desiredBounds); 1175 1176 Matrix transform = new Matrix(); 1177 // Use polygons instead of rectangles so that rotation will be 1178 // calculated, since that is not done by the new camera API. 1179 transform.setPolyToPoly(surfacePolygon, 0, desiredPolygon, 0, 4); 1180 return transform; 1181 } 1182 1183 @Override 1184 public boolean canDisableShutterSound() { 1185 // The new API doesn't support this operation, so don't encourage people to try it. 1186 // TODO: What kind of assumptions have callers made about this result's meaning? 1187 return false; 1188 } 1189 1190 private static float[] convertRectToPoly(RectF rf) { 1191 return new float[] {rf.left, rf.top, rf.right, rf.top, 1192 rf.right, rf.bottom, rf.left, rf.bottom}; 1193 } 1194 1195 private static float[] rotate(float[] arr, int times) { 1196 if (times < 0) { 1197 times = times % arr.length + arr.length; 1198 } 1199 1200 float[] res = new float[arr.length]; 1201 for (int offset = 0; offset < arr.length; ++offset) { 1202 res[offset] = arr[(times + offset) % arr.length]; 1203 } 1204 return res; 1205 } 1206 } 1207 } 1208 1209 private static final CameraExceptionCallback sCameraExceptionCallback = 1210 new CameraExceptionCallback() { 1211 @Override 1212 public synchronized void onCameraException(RuntimeException e) { 1213 throw e; 1214 } 1215 }; 1216} 1217