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