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 17 package com.android.camera.one.v2; 18 19 import android.annotation.TargetApi; 20 import android.app.Activity; 21 import android.graphics.ImageFormat; 22 import android.graphics.Rect; 23 import android.graphics.SurfaceTexture; 24 import android.hardware.camera2.CameraAccessException; 25 import android.hardware.camera2.CameraCaptureSession; 26 import android.hardware.camera2.CameraCharacteristics; 27 import android.hardware.camera2.CameraDevice; 28 import android.hardware.camera2.CameraMetadata; 29 import android.hardware.camera2.CaptureRequest; 30 import android.hardware.camera2.CaptureResult; 31 import android.hardware.camera2.DngCreator; 32 import android.hardware.camera2.TotalCaptureResult; 33 import android.hardware.camera2.params.MeteringRectangle; 34 import android.hardware.camera2.params.StreamConfigurationMap; 35 import android.location.Location; 36 import android.media.Image; 37 import android.media.ImageReader; 38 import android.net.Uri; 39 import android.os.Build; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.SystemClock; 43 import android.view.Surface; 44 45 import com.android.camera.CaptureModuleUtil; 46 import com.android.camera.Exif; 47 import com.android.camera.Storage; 48 import com.android.camera.debug.DebugPropertyHelper; 49 import com.android.camera.debug.Log; 50 import com.android.camera.debug.Log.Tag; 51 import com.android.camera.exif.ExifInterface; 52 import com.android.camera.exif.ExifTag; 53 import com.android.camera.exif.Rational; 54 import com.android.camera.one.AbstractOneCamera; 55 import com.android.camera.one.CameraDirectionProvider; 56 import com.android.camera.one.OneCamera; 57 import com.android.camera.one.Settings3A; 58 import com.android.camera.one.v2.camera2proxy.AndroidCaptureResultProxy; 59 import com.android.camera.one.v2.camera2proxy.AndroidImageProxy; 60 import com.android.camera.one.v2.camera2proxy.CaptureResultProxy; 61 import com.android.camera.processing.imagebackend.TaskImageContainer; 62 import com.android.camera.session.CaptureSession; 63 import com.android.camera.ui.focus.LensRangeCalculator; 64 import com.android.camera.ui.motion.LinearScale; 65 import com.android.camera.util.CameraUtil; 66 import com.android.camera.util.CaptureDataSerializer; 67 import com.android.camera.util.ExifUtil; 68 import com.android.camera.util.JpegUtilNative; 69 import com.android.camera.util.Size; 70 import com.google.common.base.Optional; 71 import com.google.common.util.concurrent.FutureCallback; 72 import com.google.common.util.concurrent.Futures; 73 import com.google.common.util.concurrent.ListenableFuture; 74 import com.google.common.util.concurrent.MoreExecutors; 75 76 import java.io.File; 77 import java.io.FileOutputStream; 78 import java.io.IOException; 79 import java.nio.ByteBuffer; 80 import java.util.ArrayList; 81 import java.util.LinkedList; 82 import java.util.List; 83 84 /** 85 * {@link OneCamera} implementation directly on top of the Camera2 API for 86 * cameras without API 2 FULL support (limited or legacy). 87 */ 88 @TargetApi(Build.VERSION_CODES.LOLLIPOP) 89 public class OneCameraImpl extends AbstractOneCamera { 90 /** Captures that are requested but haven't completed yet. */ 91 private static class InFlightCapture { 92 final PhotoCaptureParameters parameters; 93 final CaptureSession session; 94 Image image; 95 TotalCaptureResult totalCaptureResult; 96 InFlightCapture(PhotoCaptureParameters parameters, CaptureSession session)97 public InFlightCapture(PhotoCaptureParameters parameters, 98 CaptureSession session) { 99 this.parameters = parameters; 100 this.session = session; 101 } 102 103 /** Set the image once it's been received. */ setImage(Image capturedImage)104 public InFlightCapture setImage(Image capturedImage) { 105 image = capturedImage; 106 return this; 107 } 108 109 /** Set the total capture result once it's been received. */ setCaptureResult(TotalCaptureResult result)110 public InFlightCapture setCaptureResult(TotalCaptureResult result) { 111 totalCaptureResult = result; 112 return this; 113 } 114 115 /** 116 * Returns whether the capture is complete (which is the case once the 117 * image and capture result are both present. 118 */ isCaptureComplete()119 boolean isCaptureComplete() { 120 return image != null && totalCaptureResult != null; 121 } 122 } 123 124 private static final Tag TAG = new Tag("OneCameraImpl2"); 125 126 /** If true, will write data about each capture request to disk. */ 127 private static final boolean DEBUG_WRITE_CAPTURE_DATA = DebugPropertyHelper.writeCaptureData(); 128 /** If true, will log per-frame AF info. */ 129 private static final boolean DEBUG_FOCUS_LOG = DebugPropertyHelper.showFrameDebugLog(); 130 131 /** Default JPEG encoding quality. */ 132 private static final Byte JPEG_QUALITY = 90; 133 134 /** 135 * Set to ImageFormat.JPEG, to use the hardware encoder, or 136 * ImageFormat.YUV_420_888 to use the software encoder. You can also try 137 * RAW_SENSOR experimentally. 138 */ 139 private static final int sCaptureImageFormat = DebugPropertyHelper.isCaptureDngEnabled() ? 140 ImageFormat.RAW_SENSOR : ImageFormat.JPEG; 141 142 /** Duration to hold after manual focus tap. */ 143 private static final int FOCUS_HOLD_MILLIS = Settings3A.getFocusHoldMillis(); 144 /** Zero weight 3A region, to reset regions per API. */ 145 private static final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = AutoFocusHelper 146 .getZeroWeightRegion(); 147 148 /** 149 * CaptureRequest tags. 150 * <ul> 151 * <li>{@link #PRESHOT_TRIGGERED_AF}</li> 152 * <li>{@link #CAPTURE}</li> 153 * </ul> 154 */ 155 public static enum RequestTag { 156 /** Request that is part of a pre shot trigger. */ 157 PRESHOT_TRIGGERED_AF, 158 /** Capture request (purely for logging). */ 159 CAPTURE, 160 /** Tap to focus (purely for logging). */ 161 TAP_TO_FOCUS 162 } 163 164 /** Directory to store raw DNG files in. */ 165 private final File mRawDirectory; 166 167 /** Current CONTROL_AF_MODE request to Camera2 API. */ 168 private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE; 169 /** Last OneCamera.AutoFocusState reported. */ 170 private AutoFocusState mLastResultAFState = AutoFocusState.INACTIVE; 171 /** Flag to take a picture when the lens is stopped. */ 172 private boolean mTakePictureWhenLensIsStopped = false; 173 /** Takes a (delayed) picture with appropriate parameters. */ 174 private Runnable mTakePictureRunnable; 175 /** Keep PictureCallback for last requested capture. */ 176 private PictureCallback mLastPictureCallback = null; 177 /** Last time takePicture() was called in uptimeMillis. */ 178 private long mTakePictureStartMillis; 179 /** Runnable that returns to CONTROL_AF_MODE = AF_CONTINUOUS_PICTURE. */ 180 private final Runnable mReturnToContinuousAFRunnable = new Runnable() { 181 @Override 182 public void run() { 183 mAFRegions = ZERO_WEIGHT_3A_REGION; 184 mAERegions = ZERO_WEIGHT_3A_REGION; 185 mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE; 186 repeatingPreview(null); 187 } 188 }; 189 190 /** Current zoom value. 1.0 is no zoom. */ 191 private float mZoomValue = 1f; 192 /** Current crop region: set from mZoomValue. */ 193 private Rect mCropRegion; 194 /** Current AF and AE regions */ 195 private MeteringRectangle[] mAFRegions = ZERO_WEIGHT_3A_REGION; 196 private MeteringRectangle[] mAERegions = ZERO_WEIGHT_3A_REGION; 197 /** Last frame for which CONTROL_AF_STATE was received. */ 198 private long mLastControlAfStateFrameNumber = 0; 199 200 /** 201 * Common listener for preview frame metadata. 202 */ 203 private final CameraCaptureSession.CaptureCallback mCaptureCallback = 204 new CameraCaptureSession.CaptureCallback() { 205 @Override 206 public void onCaptureStarted(CameraCaptureSession session, 207 CaptureRequest request, long timestamp, 208 long frameNumber) { 209 if (request.getTag() == RequestTag.CAPTURE 210 && mLastPictureCallback != null) { 211 mLastPictureCallback.onQuickExpose(); 212 } 213 } 214 215 // AF state information is sometimes available 1 frame before 216 // onCaptureCompleted(), so we take advantage of that. 217 @Override 218 public void onCaptureProgressed(CameraCaptureSession session, 219 CaptureRequest request, CaptureResult partialResult) { 220 autofocusStateChangeDispatcher(partialResult); 221 super.onCaptureProgressed(session, request, partialResult); 222 } 223 224 @Override 225 public void onCaptureCompleted(CameraCaptureSession session, 226 CaptureRequest request, TotalCaptureResult result) { 227 autofocusStateChangeDispatcher(result); 228 // This checks for a HAL implementation error where 229 // TotalCaptureResult 230 // is missing CONTROL_AF_STATE. This should not happen. 231 if (result.get(CaptureResult.CONTROL_AF_STATE) == null) { 232 AutoFocusHelper.checkControlAfState(result); 233 } 234 if (DEBUG_FOCUS_LOG) { 235 AutoFocusHelper.logExtraFocusInfo(result); 236 } 237 238 Float diopter = result.get(CaptureResult.LENS_FOCUS_DISTANCE); 239 if (diopter != null && mFocusDistanceListener != null) { 240 mFocusDistanceListener.onFocusDistance(diopter, mLensRange); 241 } 242 243 if (request.getTag() == RequestTag.CAPTURE) { 244 // Add the capture result to the latest in-flight 245 // capture. If all the data for that capture is 246 // complete, store the image on disk. 247 InFlightCapture capture = null; 248 synchronized (mCaptureQueue) { 249 if (mCaptureQueue.getFirst().setCaptureResult(result) 250 .isCaptureComplete()) { 251 capture = mCaptureQueue.removeFirst(); 252 } 253 } 254 if (capture != null) { 255 OneCameraImpl.this.onCaptureCompleted(capture); 256 } 257 } 258 super.onCaptureCompleted(session, request, result); 259 } 260 }; 261 /** Thread on which the camera operations are running. */ 262 private final HandlerThread mCameraThread; 263 /** Handler of the {@link #mCameraThread}. */ 264 private final Handler mCameraHandler; 265 /** The characteristics of this camera. */ 266 private final CameraCharacteristics mCharacteristics; 267 private final LinearScale mLensRange; 268 /** The underlying Camera2 API camera device. */ 269 private final CameraDevice mDevice; 270 private final CameraDirectionProvider mDirectionProvider; 271 272 /** 273 * The aspect ratio (width/height) of the full resolution for this camera. 274 * Usually the native aspect ratio of this camera. 275 */ 276 private final float mFullSizeAspectRatio; 277 /** The Camera2 API capture session currently active. */ 278 private CameraCaptureSession mCaptureSession; 279 /** The surface onto which to render the preview. */ 280 private Surface mPreviewSurface; 281 /** 282 * A queue of capture requests that have been requested but are not done 283 * yet. 284 */ 285 private final LinkedList<InFlightCapture> mCaptureQueue = 286 new LinkedList<InFlightCapture>(); 287 /** Whether closing of this device has been requested. */ 288 private volatile boolean mIsClosed = false; 289 290 /** Receives the normal captured images. */ 291 private final ImageReader mCaptureImageReader; 292 ImageReader.OnImageAvailableListener mCaptureImageListener = 293 new ImageReader.OnImageAvailableListener() { 294 @Override 295 public void onImageAvailable(ImageReader reader) { 296 // Add the image data to the latest in-flight capture. 297 // If all the data for that capture is complete, store the 298 // image data. 299 InFlightCapture capture = null; 300 synchronized (mCaptureQueue) { 301 if (mCaptureQueue.getFirst().setImage(reader.acquireLatestImage()) 302 .isCaptureComplete()) { 303 capture = mCaptureQueue.removeFirst(); 304 } 305 } 306 if (capture != null) { 307 onCaptureCompleted(capture); 308 } 309 } 310 }; 311 312 /** 313 * Instantiates a new camera based on Camera 2 API. 314 * 315 * @param device The underlying Camera 2 device. 316 * @param characteristics The device's characteristics. 317 * @param pictureSize the size of the final image to be taken. 318 */ OneCameraImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize)319 OneCameraImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize) { 320 mDevice = device; 321 mCharacteristics = characteristics; 322 mLensRange = LensRangeCalculator.getDiopterToRatioCalculator(characteristics); 323 mDirectionProvider = new CameraDirectionProvider(characteristics); 324 mFullSizeAspectRatio = calculateFullSizeAspectRatio(characteristics); 325 mRawDirectory = new File(Storage.instance().DIRECTORY, "DNG"); 326 327 // Override pictureSize for RAW (our picture size settings don't include 328 // RAW, which typically only supports one size (sensor size). This also 329 // typically differs from the larges JPEG or YUV size. 330 // TODO: If we ever want to support RAW properly, it should be one entry 331 // in the picture quality list, which should then lead to the right 332 // pictureSize being passes into here. 333 if (sCaptureImageFormat == ImageFormat.RAW_SENSOR) { 334 pictureSize = getDefaultPictureSize(); 335 } 336 337 mCameraThread = new HandlerThread("OneCamera2"); 338 mCameraThread.start(); 339 mCameraHandler = new Handler(mCameraThread.getLooper()); 340 341 mCaptureImageReader = ImageReader.newInstance(pictureSize.getWidth(), 342 pictureSize.getHeight(), 343 sCaptureImageFormat, 2); 344 mCaptureImageReader.setOnImageAvailableListener(mCaptureImageListener, mCameraHandler); 345 Log.d(TAG, "New Camera2 based OneCameraImpl created."); 346 } 347 348 /** 349 * Take picture, initiating an auto focus scan if needed. 350 */ 351 @Override takePicture(final PhotoCaptureParameters params, final CaptureSession session)352 public void takePicture(final PhotoCaptureParameters params, final CaptureSession session) { 353 // Do not do anything when a picture is already requested. 354 if (mTakePictureWhenLensIsStopped) { 355 return; 356 } 357 358 // Not ready until the picture comes back. 359 broadcastReadyState(false); 360 361 mTakePictureRunnable = new Runnable() { 362 @Override 363 public void run() { 364 takePictureNow(params, session); 365 } 366 }; 367 mLastPictureCallback = params.callback; 368 mTakePictureStartMillis = SystemClock.uptimeMillis(); 369 370 // This class implements a very simple version of AF, which 371 // only delays capture if the lens is scanning. 372 if (mLastResultAFState == AutoFocusState.ACTIVE_SCAN) { 373 Log.v(TAG, "Waiting until scan is done before taking shot."); 374 mTakePictureWhenLensIsStopped = true; 375 } else { 376 // We could do CONTROL_AF_TRIGGER_START and wait until lens locks, 377 // but this would slow down the capture. 378 takePictureNow(params, session); 379 } 380 } 381 382 /** 383 * Take picture immediately. Parameters passed through from takePicture(). 384 */ takePictureNow(PhotoCaptureParameters params, CaptureSession session)385 public void takePictureNow(PhotoCaptureParameters params, CaptureSession session) { 386 long dt = SystemClock.uptimeMillis() - mTakePictureStartMillis; 387 Log.v(TAG, "Taking shot with extra AF delay of " + dt + " ms."); 388 try { 389 // JPEG capture. 390 CaptureRequest.Builder builder = mDevice 391 .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 392 builder.setTag(RequestTag.CAPTURE); 393 addBaselineCaptureKeysToRequest(builder); 394 395 // Enable lens-shading correction for even better DNGs. 396 if (sCaptureImageFormat == ImageFormat.RAW_SENSOR) { 397 builder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE, 398 CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON); 399 } else if (sCaptureImageFormat == ImageFormat.JPEG) { 400 builder.set(CaptureRequest.JPEG_QUALITY, JPEG_QUALITY); 401 builder.set(CaptureRequest.JPEG_ORIENTATION, 402 CameraUtil.getJpegRotation(params.orientation, mCharacteristics)); 403 } 404 405 builder.addTarget(mPreviewSurface); 406 builder.addTarget(mCaptureImageReader.getSurface()); 407 CaptureRequest request = builder.build(); 408 409 if (DEBUG_WRITE_CAPTURE_DATA) { 410 final String debugDataDir = makeDebugDir(params.debugDataFolder, 411 "normal_capture_debug"); 412 Log.i(TAG, "Writing capture data to: " + debugDataDir); 413 CaptureDataSerializer.toFile("Normal Capture", request, new File(debugDataDir, 414 "capture.txt")); 415 } 416 417 mCaptureSession.capture(request, mCaptureCallback, mCameraHandler); 418 } catch (CameraAccessException e) { 419 Log.e(TAG, "Could not access camera for still image capture."); 420 broadcastReadyState(true); 421 params.callback.onPictureTakingFailed(); 422 return; 423 } 424 synchronized (mCaptureQueue) { 425 mCaptureQueue.add(new InFlightCapture(params, session)); 426 } 427 } 428 429 @Override startPreview(Surface previewSurface, CaptureReadyCallback listener)430 public void startPreview(Surface previewSurface, CaptureReadyCallback listener) { 431 mPreviewSurface = previewSurface; 432 setupAsync(mPreviewSurface, listener); 433 } 434 435 @Override close()436 public void close() { 437 if (mIsClosed) { 438 Log.w(TAG, "Camera is already closed."); 439 return; 440 } 441 try { 442 if (mCaptureSession != null) { 443 mCaptureSession.abortCaptures(); 444 } 445 } catch (CameraAccessException e) { 446 Log.e(TAG, "Could not abort captures in progress."); 447 } 448 mIsClosed = true; 449 mCameraThread.quitSafely(); 450 mDevice.close(); 451 } 452 getSupportedPreviewSizes()453 public Size[] getSupportedPreviewSizes() { 454 StreamConfigurationMap config = mCharacteristics 455 .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 456 return Size.convert(config.getOutputSizes(SurfaceTexture.class)); 457 } 458 getFullSizeAspectRatio()459 public float getFullSizeAspectRatio() { 460 return mFullSizeAspectRatio; 461 } 462 463 @Override getDirection()464 public Facing getDirection() { 465 return mDirectionProvider.getDirection(); 466 } 467 saveJpegPicture(byte[] jpegData, final PhotoCaptureParameters captureParams, CaptureSession session, CaptureResult result)468 private void saveJpegPicture(byte[] jpegData, final PhotoCaptureParameters captureParams, 469 CaptureSession session, CaptureResult result) { 470 int heading = captureParams.heading; 471 int width = 0; 472 int height = 0; 473 int rotation = 0; 474 ExifInterface exif = null; 475 try { 476 exif = new ExifInterface(); 477 exif.readExif(jpegData); 478 479 Integer w = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION); 480 width = (w == null) ? width : w; 481 Integer h = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION); 482 height = (h == null) ? height : h; 483 484 // Get image rotation from EXIF. 485 rotation = Exif.getOrientation(exif); 486 487 // Set GPS heading direction based on sensor, if location is on. 488 if (heading >= 0) { 489 ExifTag directionRefTag = exif.buildTag( 490 ExifInterface.TAG_GPS_IMG_DIRECTION_REF, 491 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION); 492 ExifTag directionTag = exif.buildTag( 493 ExifInterface.TAG_GPS_IMG_DIRECTION, 494 new Rational(heading, 1)); 495 exif.setTag(directionRefTag); 496 exif.setTag(directionTag); 497 } 498 new ExifUtil(exif).populateExif(Optional.<TaskImageContainer.TaskImage> absent(), 499 Optional.of((CaptureResultProxy) new AndroidCaptureResultProxy(result)), 500 Optional.<Location> absent()); 501 } catch (IOException e) { 502 Log.w(TAG, "Could not read exif from gcam jpeg", e); 503 exif = null; 504 } 505 ListenableFuture<Optional<Uri>> futureUri = session.saveAndFinish(jpegData, width, height, 506 rotation, exif); 507 Futures.addCallback(futureUri, new FutureCallback<Optional<Uri>>() { 508 @Override 509 public void onSuccess(Optional<Uri> uriOptional) { 510 captureParams.callback.onPictureSaved(uriOptional.orNull()); 511 } 512 513 @Override 514 public void onFailure(Throwable throwable) { 515 captureParams.callback.onPictureSaved(null); 516 } 517 }, MoreExecutors.directExecutor()); 518 } 519 520 /** 521 * Asynchronously sets up the capture session. 522 * 523 * @param previewSurface the surface onto which the preview should be 524 * rendered. 525 * @param listener called when setup is completed. 526 */ setupAsync(final Surface previewSurface, final CaptureReadyCallback listener)527 private void setupAsync(final Surface previewSurface, final CaptureReadyCallback listener) { 528 mCameraHandler.post(new Runnable() { 529 @Override 530 public void run() { 531 setup(previewSurface, listener); 532 } 533 }); 534 } 535 536 /** 537 * Configures and attempts to create a capture session. 538 * 539 * @param previewSurface the surface onto which the preview should be 540 * rendered. 541 * @param listener called when the setup is completed. 542 */ setup(Surface previewSurface, final CaptureReadyCallback listener)543 private void setup(Surface previewSurface, final CaptureReadyCallback listener) { 544 try { 545 if (mCaptureSession != null) { 546 mCaptureSession.abortCaptures(); 547 mCaptureSession = null; 548 } 549 List<Surface> outputSurfaces = new ArrayList<Surface>(2); 550 outputSurfaces.add(previewSurface); 551 outputSurfaces.add(mCaptureImageReader.getSurface()); 552 553 mDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() { 554 555 @Override 556 public void onConfigureFailed(CameraCaptureSession session) { 557 listener.onSetupFailed(); 558 } 559 560 @Override 561 public void onConfigured(CameraCaptureSession session) { 562 mCaptureSession = session; 563 mAFRegions = ZERO_WEIGHT_3A_REGION; 564 mAERegions = ZERO_WEIGHT_3A_REGION; 565 mZoomValue = 1f; 566 mCropRegion = cropRegionForZoom(mZoomValue); 567 boolean success = repeatingPreview(null); 568 if (success) { 569 listener.onReadyForCapture(); 570 } else { 571 listener.onSetupFailed(); 572 } 573 } 574 575 @Override 576 public void onClosed(CameraCaptureSession session) { 577 super.onClosed(session); 578 } 579 }, mCameraHandler); 580 } catch (CameraAccessException ex) { 581 Log.e(TAG, "Could not set up capture session", ex); 582 listener.onSetupFailed(); 583 } 584 } 585 586 /** 587 * Adds current regions to CaptureRequest and base AF mode + 588 * AF_TRIGGER_IDLE. 589 * 590 * @param builder Build for the CaptureRequest 591 */ addBaselineCaptureKeysToRequest(CaptureRequest.Builder builder)592 private void addBaselineCaptureKeysToRequest(CaptureRequest.Builder builder) { 593 builder.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions); 594 builder.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions); 595 builder.set(CaptureRequest.SCALER_CROP_REGION, mCropRegion); 596 builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); 597 builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); 598 // Enable face detection 599 builder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, 600 CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL); 601 builder.set(CaptureRequest.CONTROL_SCENE_MODE, 602 CaptureRequest.CONTROL_SCENE_MODE_FACE_PRIORITY); 603 } 604 605 /** 606 * Request preview capture stream with AF_MODE_CONTINUOUS_PICTURE. 607 * 608 * @return true if request was build and sent successfully. 609 * @param tag 610 */ repeatingPreview(Object tag)611 private boolean repeatingPreview(Object tag) { 612 try { 613 CaptureRequest.Builder builder = mDevice. 614 createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 615 builder.addTarget(mPreviewSurface); 616 builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); 617 addBaselineCaptureKeysToRequest(builder); 618 mCaptureSession.setRepeatingRequest(builder.build(), mCaptureCallback, 619 mCameraHandler); 620 Log.v(TAG, String.format("Sent repeating Preview request, zoom = %.2f", mZoomValue)); 621 return true; 622 } catch (CameraAccessException ex) { 623 Log.e(TAG, "Could not access camera setting up preview.", ex); 624 return false; 625 } 626 } 627 628 /** 629 * Request preview capture stream with auto focus trigger cycle. 630 */ sendAutoFocusTriggerCaptureRequest(Object tag)631 private void sendAutoFocusTriggerCaptureRequest(Object tag) { 632 try { 633 // Step 1: Request single frame CONTROL_AF_TRIGGER_START. 634 CaptureRequest.Builder builder; 635 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 636 builder.addTarget(mPreviewSurface); 637 builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); 638 mControlAFMode = CameraMetadata.CONTROL_AF_MODE_AUTO; 639 addBaselineCaptureKeysToRequest(builder); 640 builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); 641 builder.setTag(tag); 642 mCaptureSession.capture(builder.build(), mCaptureCallback, mCameraHandler); 643 644 // Step 2: Call repeatingPreview to update mControlAFMode. 645 repeatingPreview(tag); 646 resumeContinuousAFAfterDelay(FOCUS_HOLD_MILLIS); 647 } catch (CameraAccessException ex) { 648 Log.e(TAG, "Could not execute preview request.", ex); 649 } 650 } 651 652 /** 653 * Resume AF_MODE_CONTINUOUS_PICTURE after FOCUS_HOLD_MILLIS. 654 */ resumeContinuousAFAfterDelay(int millis)655 private void resumeContinuousAFAfterDelay(int millis) { 656 mCameraHandler.removeCallbacks(mReturnToContinuousAFRunnable); 657 mCameraHandler.postDelayed(mReturnToContinuousAFRunnable, millis); 658 } 659 660 /** 661 * This method takes appropriate action if camera2 AF state changes. 662 * <ol> 663 * <li>Reports changes in camera2 AF state to OneCamera.FocusStateListener.</li> 664 * <li>Take picture after AF scan if mTakePictureWhenLensIsStopped true.</li> 665 * </ol> 666 */ autofocusStateChangeDispatcher(CaptureResult result)667 private void autofocusStateChangeDispatcher(CaptureResult result) { 668 if (result.getFrameNumber() < mLastControlAfStateFrameNumber || 669 result.get(CaptureResult.CONTROL_AF_STATE) == null) { 670 return; 671 } 672 mLastControlAfStateFrameNumber = result.getFrameNumber(); 673 674 // Convert to OneCamera mode and state. 675 AutoFocusState resultAFState = AutoFocusHelper. 676 stateFromCamera2State(result.get(CaptureResult.CONTROL_AF_STATE)); 677 678 // TODO: Consider using LENS_STATE. 679 boolean lensIsStopped = resultAFState == AutoFocusState.ACTIVE_FOCUSED || 680 resultAFState == AutoFocusState.ACTIVE_UNFOCUSED || 681 resultAFState == AutoFocusState.PASSIVE_FOCUSED || 682 resultAFState == AutoFocusState.PASSIVE_UNFOCUSED; 683 684 if (mTakePictureWhenLensIsStopped && lensIsStopped) { 685 // Take the shot. 686 mCameraHandler.post(mTakePictureRunnable); 687 mTakePictureWhenLensIsStopped = false; 688 } 689 690 // Report state change when AF state has changed. 691 if (resultAFState != mLastResultAFState && mFocusStateListener != null) { 692 mFocusStateListener.onFocusStatusUpdate(resultAFState, result.getFrameNumber()); 693 } 694 mLastResultAFState = resultAFState; 695 } 696 697 @Override triggerFocusAndMeterAtPoint(float nx, float ny)698 public void triggerFocusAndMeterAtPoint(float nx, float ny) { 699 int sensorOrientation = mCharacteristics.get( 700 CameraCharacteristics.SENSOR_ORIENTATION); 701 mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(nx, ny, mCropRegion, 702 sensorOrientation); 703 mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(nx, ny, mCropRegion, 704 sensorOrientation); 705 706 sendAutoFocusTriggerCaptureRequest(RequestTag.TAP_TO_FOCUS); 707 } 708 709 @Override getMaxZoom()710 public float getMaxZoom() { 711 return mCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); 712 } 713 714 @Override setZoom(float zoom)715 public void setZoom(float zoom) { 716 mZoomValue = zoom; 717 mCropRegion = cropRegionForZoom(zoom); 718 repeatingPreview(null); 719 } 720 721 @Override pickPreviewSize(Size pictureSize, Activity context)722 public Size pickPreviewSize(Size pictureSize, Activity context) { 723 if (pictureSize == null) { 724 // TODO The default should be selected by the caller, and 725 // pictureSize should never be null. 726 pictureSize = getDefaultPictureSize(); 727 } 728 float pictureAspectRatio = pictureSize.getWidth() / (float) pictureSize.getHeight(); 729 Size[] supportedSizes = getSupportedPreviewSizes(); 730 731 // Since devices only have one raw resolution we need to be more 732 // flexible for selecting a matching preview resolution. 733 Double aspectRatioTolerance = sCaptureImageFormat == ImageFormat.RAW_SENSOR ? 10d : null; 734 Size size = CaptureModuleUtil.getOptimalPreviewSize(supportedSizes, 735 pictureAspectRatio, aspectRatioTolerance, context); 736 Log.d(TAG, "Selected preview size: " + size); 737 return size; 738 } 739 cropRegionForZoom(float zoom)740 private Rect cropRegionForZoom(float zoom) { 741 return AutoFocusHelper.cropRegionForZoom(mCharacteristics, zoom); 742 } 743 744 /** 745 * Calculate the aspect ratio of the full size capture on this device. 746 * 747 * @param characteristics the characteristics of the camera device. 748 * @return The aspect ration, in terms of width/height of the full capture 749 * size. 750 */ calculateFullSizeAspectRatio(CameraCharacteristics characteristics)751 private static float calculateFullSizeAspectRatio(CameraCharacteristics characteristics) { 752 Rect activeArraySize = 753 characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 754 return ((float) (activeArraySize.width())) / activeArraySize.height(); 755 } 756 757 /* 758 * Called when a capture that is in flight is completed. 759 * @param capture the in-flight capture which needs to contain the received 760 * image and capture data 761 */ onCaptureCompleted(InFlightCapture capture)762 private void onCaptureCompleted(InFlightCapture capture) { 763 764 // Experimental support for writing RAW. We do not have a usable JPEG 765 // here, so we don't use the usual capture session mechanism and instead 766 // just store the RAW file in its own directory. 767 // TODO: If we make this a real feature we should probably put the DNGs 768 // into the Camera directly. 769 if (sCaptureImageFormat == ImageFormat.RAW_SENSOR) { 770 if (!mRawDirectory.exists()) { 771 if (!mRawDirectory.mkdirs()) { 772 throw new RuntimeException("Could not create RAW directory."); 773 } 774 } 775 File dngFile = new File(mRawDirectory, capture.session.getTitle() + ".dng"); 776 writeDngBytesAndClose(capture.image, capture.totalCaptureResult, 777 mCharacteristics, dngFile); 778 } else { 779 // Since this is not an HDR+ session, we will just save the 780 // result. 781 byte[] imageBytes = acquireJpegBytesAndClose(capture.image); 782 saveJpegPicture(imageBytes, capture.parameters, capture.session, 783 capture.totalCaptureResult); 784 } 785 broadcastReadyState(true); 786 capture.parameters.callback.onPictureTaken(capture.session); 787 } 788 789 /** 790 * Take the given RAW image and capture result, convert it to a DNG and 791 * write it to disk. 792 * 793 * @param image the image containing the 16-bit RAW data (RAW_SENSOR) 794 * @param captureResult the capture result for the image 795 * @param characteristics the camera characteristics of the camera that took 796 * the RAW image 797 * @param dngFile the destination to where the resulting DNG data is written 798 * to 799 */ writeDngBytesAndClose(Image image, TotalCaptureResult captureResult, CameraCharacteristics characteristics, File dngFile)800 private static void writeDngBytesAndClose(Image image, TotalCaptureResult captureResult, 801 CameraCharacteristics characteristics, File dngFile) { 802 try (DngCreator dngCreator = new DngCreator(characteristics, captureResult); 803 FileOutputStream outputStream = new FileOutputStream(dngFile)) { 804 // TODO: Add DngCreator#setThumbnail and add the DNG to the normal 805 // filmstrip. 806 dngCreator.writeImage(outputStream, image); 807 outputStream.close(); 808 image.close(); 809 } catch (IOException e) { 810 Log.e(TAG, "Could not store DNG file", e); 811 return; 812 } 813 Log.i(TAG, "Successfully stored DNG file: " + dngFile.getAbsolutePath()); 814 } 815 816 /** 817 * Given an image reader, this extracts the final image. If the image in the 818 * reader is JPEG, we extract and return it as is. If the image is YUV, we 819 * convert it to JPEG and return the result. 820 * 821 * @param image the image we got from the image reader. 822 * @return A valid JPEG image. 823 */ acquireJpegBytesAndClose(Image image)824 private static byte[] acquireJpegBytesAndClose(Image image) { 825 ByteBuffer buffer; 826 if (image.getFormat() == ImageFormat.JPEG) { 827 Image.Plane plane0 = image.getPlanes()[0]; 828 buffer = plane0.getBuffer(); 829 } else if (image.getFormat() == ImageFormat.YUV_420_888) { 830 buffer = ByteBuffer.allocateDirect(image.getWidth() * image.getHeight() * 3); 831 832 Log.v(TAG, "Compressing JPEG with software encoder."); 833 int numBytes = JpegUtilNative.compressJpegFromYUV420Image( 834 new AndroidImageProxy(image), buffer, JPEG_QUALITY); 835 836 if (numBytes < 0) { 837 throw new RuntimeException("Error compressing jpeg."); 838 } 839 buffer.limit(numBytes); 840 } else { 841 throw new RuntimeException("Unsupported image format."); 842 } 843 844 byte[] imageBytes = new byte[buffer.remaining()]; 845 buffer.get(imageBytes); 846 buffer.rewind(); 847 image.close(); 848 return imageBytes; 849 } 850 851 /** 852 * @return The largest supported picture size. 853 */ getDefaultPictureSize()854 public Size getDefaultPictureSize() { 855 StreamConfigurationMap configs = 856 mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 857 android.util.Size[] supportedSizes = configs.getOutputSizes(sCaptureImageFormat); 858 859 // Find the largest supported size. 860 android.util.Size largestSupportedSize = supportedSizes[0]; 861 long largestSupportedSizePixels = 862 largestSupportedSize.getWidth() * largestSupportedSize.getHeight(); 863 for (int i = 1; i < supportedSizes.length; i++) { 864 long numPixels = supportedSizes[i].getWidth() * supportedSizes[i].getHeight(); 865 if (numPixels > largestSupportedSizePixels) { 866 largestSupportedSize = supportedSizes[i]; 867 largestSupportedSizePixels = numPixels; 868 } 869 } 870 return new Size(largestSupportedSize.getWidth(), largestSupportedSize.getHeight()); 871 } 872 } 873