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