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.content.Context; 20 import android.graphics.ImageFormat; 21 import android.graphics.Rect; 22 import android.hardware.camera2.CameraAccessException; 23 import android.hardware.camera2.CameraCaptureSession; 24 import android.hardware.camera2.CameraCharacteristics; 25 import android.hardware.camera2.CameraDevice; 26 import android.hardware.camera2.CameraMetadata; 27 import android.hardware.camera2.CaptureRequest; 28 import android.hardware.camera2.CaptureResult; 29 import android.hardware.camera2.TotalCaptureResult; 30 import android.hardware.camera2.params.MeteringRectangle; 31 import android.hardware.camera2.params.StreamConfigurationMap; 32 import android.media.Image; 33 import android.media.ImageReader; 34 import android.net.Uri; 35 import android.os.Handler; 36 import android.os.HandlerThread; 37 import android.os.SystemClock; 38 import android.view.Surface; 39 40 import com.android.camera.CaptureModuleUtil; 41 import com.android.camera.Exif; 42 import com.android.camera.app.MediaSaver.OnMediaSavedListener; 43 import com.android.camera.debug.DebugPropertyHelper; 44 import com.android.camera.debug.Log; 45 import com.android.camera.debug.Log.Tag; 46 import com.android.camera.exif.ExifInterface; 47 import com.android.camera.exif.ExifTag; 48 import com.android.camera.exif.Rational; 49 import com.android.camera.one.AbstractOneCamera; 50 import com.android.camera.one.OneCamera; 51 import com.android.camera.one.Settings3A; 52 import com.android.camera.session.CaptureSession; 53 import com.android.camera.util.CameraUtil; 54 import com.android.camera.util.CaptureDataSerializer; 55 import com.android.camera.util.JpegUtilNative; 56 import com.android.camera.util.Size; 57 58 import java.io.File; 59 import java.io.IOException; 60 import java.nio.ByteBuffer; 61 import java.util.ArrayList; 62 import java.util.LinkedList; 63 import java.util.List; 64 65 /** 66 * {@link OneCamera} implementation directly on top of the Camera2 API. 67 */ 68 public class OneCameraImpl extends AbstractOneCamera { 69 70 /** Captures that are requested but haven't completed yet. */ 71 private static class InFlightCapture { 72 final PhotoCaptureParameters parameters; 73 final CaptureSession session; 74 75 public InFlightCapture(PhotoCaptureParameters parameters, 76 CaptureSession session) { 77 this.parameters = parameters; 78 this.session = session; 79 } 80 } 81 82 private static final Tag TAG = new Tag("OneCameraImpl2"); 83 84 /** If true, will write data about each capture request to disk. */ 85 private static final boolean DEBUG_WRITE_CAPTURE_DATA = DebugPropertyHelper.writeCaptureData(); 86 /** If true, will log per-frame AF info. */ 87 private static final boolean DEBUG_FOCUS_LOG = DebugPropertyHelper.showFrameDebugLog(); 88 89 /** Default JPEG encoding quality. */ 90 private static final Byte JPEG_QUALITY = 90; 91 92 /** 93 * Set to ImageFormat.JPEG, to use the hardware encoder, or 94 * ImageFormat.YUV_420_888 to use the software encoder. No other image 95 * formats are supported. 96 */ 97 private static final int sCaptureImageFormat = ImageFormat.YUV_420_888; 98 99 /** Duration to hold after manual focus tap. */ 100 private static final int FOCUS_HOLD_MILLIS = Settings3A.getFocusHoldMillis(); 101 /** Zero weight 3A region, to reset regions per API. */ 102 MeteringRectangle[] ZERO_WEIGHT_3A_REGION = AutoFocusHelper.getZeroWeightRegion(); 103 104 /** 105 * CaptureRequest tags. 106 * <ul> 107 * <li>{@link #PRESHOT_TRIGGERED_AF}</li> 108 * <li>{@link #CAPTURE}</li> 109 * </ul> 110 */ 111 public static enum RequestTag { 112 /** Request that is part of a pre shot trigger. */ 113 PRESHOT_TRIGGERED_AF, 114 /** Capture request (purely for logging). */ 115 CAPTURE, 116 /** Tap to focus (purely for logging). */ 117 TAP_TO_FOCUS 118 } 119 120 /** Current CONTROL_AF_MODE request to Camera2 API. */ 121 private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE; 122 /** Last OneCamera.AutoFocusState reported. */ 123 private AutoFocusState mLastResultAFState = AutoFocusState.INACTIVE; 124 /** Flag to take a picture when the lens is stopped. */ 125 private boolean mTakePictureWhenLensIsStopped = false; 126 /** Takes a (delayed) picture with appropriate parameters. */ 127 private Runnable mTakePictureRunnable; 128 /** Keep PictureCallback for last requested capture. */ 129 private PictureCallback mLastPictureCallback = null; 130 /** Last time takePicture() was called in uptimeMillis. */ 131 private long mTakePictureStartMillis; 132 /** Runnable that returns to CONTROL_AF_MODE = AF_CONTINUOUS_PICTURE. */ 133 private final Runnable mReturnToContinuousAFRunnable = new Runnable() { 134 @Override 135 public void run() { 136 mAFRegions = ZERO_WEIGHT_3A_REGION; 137 mAERegions = ZERO_WEIGHT_3A_REGION; 138 mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE; 139 repeatingPreview(null); 140 } 141 }; 142 143 /** Current zoom value. 1.0 is no zoom. */ 144 private float mZoomValue = 1f; 145 /** Current crop region: set from mZoomValue. */ 146 private Rect mCropRegion; 147 /** Current AF and AE regions */ 148 private MeteringRectangle[] mAFRegions = ZERO_WEIGHT_3A_REGION; 149 private MeteringRectangle[] mAERegions = ZERO_WEIGHT_3A_REGION; 150 /** Last frame for which CONTROL_AF_STATE was received. */ 151 private long mLastControlAfStateFrameNumber = 0; 152 153 /** 154 * Common listener for preview frame metadata. 155 */ 156 private final CameraCaptureSession.CaptureCallback mAutoFocusStateListener = new 157 CameraCaptureSession.CaptureCallback() { 158 @Override 159 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 160 long timestamp, long frameNumber) { 161 if (request.getTag() == RequestTag.CAPTURE && mLastPictureCallback != null) { 162 mLastPictureCallback.onQuickExpose(); 163 } 164 } 165 166 // AF state information is sometimes available 1 frame before 167 // onCaptureCompleted(), so we take advantage of that. 168 @Override 169 public void onCaptureProgressed(CameraCaptureSession session, 170 CaptureRequest request, 171 CaptureResult partialResult) { 172 autofocusStateChangeDispatcher(partialResult); 173 super.onCaptureProgressed(session, request, partialResult); 174 } 175 176 @Override 177 public void onCaptureCompleted(CameraCaptureSession session, 178 CaptureRequest request, 179 TotalCaptureResult result) { 180 autofocusStateChangeDispatcher(result); 181 // This checks for a HAL implementation error where TotalCaptureResult 182 // is missing CONTROL_AF_STATE. This should not happen. 183 if (result.get(CaptureResult.CONTROL_AF_STATE) == null) { 184 AutoFocusHelper.checkControlAfState(result); 185 } 186 if (DEBUG_FOCUS_LOG) { 187 AutoFocusHelper.logExtraFocusInfo(result); 188 } 189 super.onCaptureCompleted(session, request, result); 190 } 191 }; 192 /** Thread on which the camera operations are running. */ 193 private final HandlerThread mCameraThread; 194 /** Handler of the {@link #mCameraThread}. */ 195 private final Handler mCameraHandler; 196 /** The characteristics of this camera. */ 197 private final CameraCharacteristics mCharacteristics; 198 /** The underlying Camera2 API camera device. */ 199 private final CameraDevice mDevice; 200 201 /** 202 * The aspect ratio (width/height) of the full resolution for this camera. 203 * Usually the native aspect ratio of this camera. 204 */ 205 private final float mFullSizeAspectRatio; 206 /** The Camera2 API capture session currently active. */ 207 private CameraCaptureSession mCaptureSession; 208 /** The surface onto which to render the preview. */ 209 private Surface mPreviewSurface; 210 /** 211 * A queue of capture requests that have been requested but are not done 212 * yet. 213 */ 214 private final LinkedList<InFlightCapture> mCaptureQueue = 215 new LinkedList<InFlightCapture>(); 216 /** Whether closing of this device has been requested. */ 217 private volatile boolean mIsClosed = false; 218 /** A callback that is called when the device is fully closed. */ 219 private CloseCallback mCloseCallback = null; 220 221 /** Receives the normal captured images. */ 222 private final ImageReader mCaptureImageReader; 223 ImageReader.OnImageAvailableListener mCaptureImageListener = 224 new ImageReader.OnImageAvailableListener() { 225 @Override 226 public void onImageAvailable(ImageReader reader) { 227 InFlightCapture capture = mCaptureQueue.remove(); 228 229 // Since this is not an HDR+ session, we will just save the 230 // result. 231 capture.session.startEmpty(); 232 byte[] imageBytes = acquireJpegBytesAndClose(reader); 233 // TODO: The savePicture call here seems to block UI thread. 234 savePicture(imageBytes, capture.parameters, capture.session); 235 broadcastReadyState(true); 236 capture.parameters.callback.onPictureTaken(capture.session); 237 } 238 }; 239 240 /** 241 * Instantiates a new camera based on Camera 2 API. 242 * 243 * @param device The underlying Camera 2 device. 244 * @param characteristics The device's characteristics. 245 * @param pictureSize the size of the final image to be taken. 246 */ 247 OneCameraImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize) { 248 mDevice = device; 249 mCharacteristics = characteristics; 250 mFullSizeAspectRatio = calculateFullSizeAspectRatio(characteristics); 251 252 mCameraThread = new HandlerThread("OneCamera2"); 253 mCameraThread.start(); 254 mCameraHandler = new Handler(mCameraThread.getLooper()); 255 256 mCaptureImageReader = ImageReader.newInstance(pictureSize.getWidth(), 257 pictureSize.getHeight(), 258 sCaptureImageFormat, 2); 259 mCaptureImageReader.setOnImageAvailableListener(mCaptureImageListener, mCameraHandler); 260 Log.d(TAG, "New Camera2 based OneCameraImpl created."); 261 } 262 263 /** 264 * Take picture, initiating an auto focus scan if needed. 265 */ 266 @Override 267 public void takePicture(final PhotoCaptureParameters params, final CaptureSession session) { 268 // Do not do anything when a picture is already requested. 269 if (mTakePictureWhenLensIsStopped) { 270 return; 271 } 272 273 // Not ready until the picture comes back. 274 broadcastReadyState(false); 275 276 mTakePictureRunnable = new Runnable() { 277 @Override 278 public void run() { 279 takePictureNow(params, session); 280 } 281 }; 282 mLastPictureCallback = params.callback; 283 mTakePictureStartMillis = SystemClock.uptimeMillis(); 284 285 // This class implements a very simple version of AF, which 286 // only delays capture if the lens is scanning. 287 if (mLastResultAFState == AutoFocusState.ACTIVE_SCAN) { 288 Log.v(TAG, "Waiting until scan is done before taking shot."); 289 mTakePictureWhenLensIsStopped = true; 290 } else { 291 // We could do CONTROL_AF_TRIGGER_START and wait until lens locks, 292 // but this would slow down the capture. 293 takePictureNow(params, session); 294 } 295 } 296 297 /** 298 * Take picture immediately. Parameters passed through from takePicture(). 299 */ 300 public void takePictureNow(PhotoCaptureParameters params, CaptureSession session) { 301 long dt = SystemClock.uptimeMillis() - mTakePictureStartMillis; 302 Log.v(TAG, "Taking shot with extra AF delay of " + dt + " ms."); 303 // This will throw a RuntimeException, if parameters are not sane. 304 params.checkSanity(); 305 try { 306 // JPEG capture. 307 CaptureRequest.Builder builder = mDevice 308 .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 309 builder.setTag(RequestTag.CAPTURE); 310 addBaselineCaptureKeysToRequest(builder); 311 312 if (sCaptureImageFormat == ImageFormat.JPEG) { 313 builder.set(CaptureRequest.JPEG_QUALITY, JPEG_QUALITY); 314 builder.set(CaptureRequest.JPEG_ORIENTATION, 315 CameraUtil.getJpegRotation(params.orientation, mCharacteristics)); 316 } 317 318 builder.addTarget(mPreviewSurface); 319 builder.addTarget(mCaptureImageReader.getSurface()); 320 CaptureRequest request = builder.build(); 321 322 if (DEBUG_WRITE_CAPTURE_DATA) { 323 final String debugDataDir = makeDebugDir(params.debugDataFolder, 324 "normal_capture_debug"); 325 Log.i(TAG, "Writing capture data to: " + debugDataDir); 326 CaptureDataSerializer.toFile("Normal Capture", request, new File(debugDataDir, 327 "capture.txt")); 328 } 329 330 mCaptureSession.capture(request, mAutoFocusStateListener, mCameraHandler); 331 } catch (CameraAccessException e) { 332 Log.e(TAG, "Could not access camera for still image capture."); 333 broadcastReadyState(true); 334 params.callback.onPictureTakenFailed(); 335 return; 336 } 337 mCaptureQueue.add(new InFlightCapture(params, session)); 338 } 339 340 @Override 341 public void startPreview(Surface previewSurface, CaptureReadyCallback listener) { 342 mPreviewSurface = previewSurface; 343 setupAsync(mPreviewSurface, listener); 344 } 345 346 @Override 347 public void setViewfinderSize(int width, int height) { 348 throw new RuntimeException("Not implemented yet."); 349 } 350 351 @Override 352 public boolean isFlashSupported(boolean enhanced) { 353 throw new RuntimeException("Not implemented yet."); 354 } 355 356 @Override 357 public boolean isSupportingEnhancedMode() { 358 throw new RuntimeException("Not implemented yet."); 359 } 360 361 @Override 362 public void close(CloseCallback closeCallback) { 363 if (mIsClosed) { 364 Log.w(TAG, "Camera is already closed."); 365 return; 366 } 367 try { 368 mCaptureSession.abortCaptures(); 369 } catch (CameraAccessException e) { 370 Log.e(TAG, "Could not abort captures in progress."); 371 } 372 mIsClosed = true; 373 mCloseCallback = closeCallback; 374 mCameraThread.quitSafely(); 375 mDevice.close(); 376 } 377 378 @Override 379 public Size[] getSupportedSizes() { 380 StreamConfigurationMap config = mCharacteristics 381 .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 382 return Size.convert(config.getOutputSizes(sCaptureImageFormat)); 383 } 384 385 @Override 386 public float getFullSizeAspectRatio() { 387 return mFullSizeAspectRatio; 388 } 389 390 @Override 391 public boolean isFrontFacing() { 392 return mCharacteristics.get(CameraCharacteristics.LENS_FACING) 393 == CameraMetadata.LENS_FACING_FRONT; 394 } 395 396 @Override 397 public boolean isBackFacing() { 398 return mCharacteristics.get(CameraCharacteristics.LENS_FACING) 399 == CameraMetadata.LENS_FACING_BACK; 400 } 401 402 private void savePicture(byte[] jpegData, final PhotoCaptureParameters captureParams, 403 CaptureSession session) { 404 int heading = captureParams.heading; 405 int width = 0; 406 int height = 0; 407 int rotation = 0; 408 ExifInterface exif = null; 409 try { 410 exif = new ExifInterface(); 411 exif.readExif(jpegData); 412 413 Integer w = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION); 414 width = (w == null) ? width : w; 415 Integer h = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION); 416 height = (h == null) ? height : h; 417 418 // Get image rotation from EXIF. 419 rotation = Exif.getOrientation(exif); 420 421 // Set GPS heading direction based on sensor, if location is on. 422 if (heading >= 0) { 423 ExifTag directionRefTag = exif.buildTag( 424 ExifInterface.TAG_GPS_IMG_DIRECTION_REF, 425 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION); 426 ExifTag directionTag = exif.buildTag( 427 ExifInterface.TAG_GPS_IMG_DIRECTION, 428 new Rational(heading, 1)); 429 exif.setTag(directionRefTag); 430 exif.setTag(directionTag); 431 } 432 } catch (IOException e) { 433 Log.w(TAG, "Could not read exif from gcam jpeg", e); 434 exif = null; 435 } 436 session.saveAndFinish(jpegData, width, height, rotation, exif, new OnMediaSavedListener() { 437 @Override 438 public void onMediaSaved(Uri uri) { 439 captureParams.callback.onPictureSaved(uri); 440 } 441 }); 442 } 443 444 /** 445 * Asynchronously sets up the capture session. 446 * 447 * @param previewSurface the surface onto which the preview should be 448 * rendered. 449 * @param listener called when setup is completed. 450 */ 451 private void setupAsync(final Surface previewSurface, final CaptureReadyCallback listener) { 452 mCameraHandler.post(new Runnable() { 453 @Override 454 public void run() { 455 setup(previewSurface, listener); 456 } 457 }); 458 } 459 460 /** 461 * Configures and attempts to create a capture session. 462 * 463 * @param previewSurface the surface onto which the preview should be 464 * rendered. 465 * @param listener called when the setup is completed. 466 */ 467 private void setup(Surface previewSurface, final CaptureReadyCallback listener) { 468 try { 469 if (mCaptureSession != null) { 470 mCaptureSession.abortCaptures(); 471 mCaptureSession = null; 472 } 473 List<Surface> outputSurfaces = new ArrayList<Surface>(2); 474 outputSurfaces.add(previewSurface); 475 outputSurfaces.add(mCaptureImageReader.getSurface()); 476 477 mDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() { 478 479 @Override 480 public void onConfigureFailed(CameraCaptureSession session) { 481 listener.onSetupFailed(); 482 } 483 484 @Override 485 public void onConfigured(CameraCaptureSession session) { 486 mCaptureSession = session; 487 mAFRegions = ZERO_WEIGHT_3A_REGION; 488 mAERegions = ZERO_WEIGHT_3A_REGION; 489 mZoomValue = 1f; 490 mCropRegion = cropRegionForZoom(mZoomValue); 491 boolean success = repeatingPreview(null); 492 if (success) { 493 listener.onReadyForCapture(); 494 } else { 495 listener.onSetupFailed(); 496 } 497 } 498 499 @Override 500 public void onClosed(CameraCaptureSession session) { 501 super.onClosed(session); 502 if (mCloseCallback != null) { 503 mCloseCallback.onCameraClosed(); 504 } 505 } 506 }, mCameraHandler); 507 } catch (CameraAccessException ex) { 508 Log.e(TAG, "Could not set up capture session", ex); 509 listener.onSetupFailed(); 510 } 511 } 512 513 /** 514 * Adds current regions to CaptureRequest and base AF mode + AF_TRIGGER_IDLE. 515 * 516 * @param builder Build for the CaptureRequest 517 */ 518 private void addBaselineCaptureKeysToRequest(CaptureRequest.Builder builder) { 519 builder.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions); 520 builder.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions); 521 builder.set(CaptureRequest.SCALER_CROP_REGION, mCropRegion); 522 builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); 523 builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); 524 // Enable face detection 525 builder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, 526 CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL); 527 builder.set(CaptureRequest.CONTROL_SCENE_MODE, 528 CaptureRequest.CONTROL_SCENE_MODE_FACE_PRIORITY); 529 } 530 531 /** 532 * Request preview capture stream with AF_MODE_CONTINUOUS_PICTURE. 533 * 534 * @return true if request was build and sent successfully. 535 * @param tag 536 */ 537 private boolean repeatingPreview(Object tag) { 538 try { 539 CaptureRequest.Builder builder = mDevice. 540 createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 541 builder.addTarget(mPreviewSurface); 542 builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); 543 addBaselineCaptureKeysToRequest(builder); 544 mCaptureSession.setRepeatingRequest(builder.build(), mAutoFocusStateListener, 545 mCameraHandler); 546 Log.v(TAG, String.format("Sent repeating Preview request, zoom = %.2f", mZoomValue)); 547 return true; 548 } catch (CameraAccessException ex) { 549 Log.e(TAG, "Could not access camera setting up preview.", ex); 550 return false; 551 } 552 } 553 554 /** 555 * Request preview capture stream with auto focus trigger cycle. 556 */ 557 private void sendAutoFocusTriggerCaptureRequest(Object tag) { 558 try { 559 // Step 1: Request single frame CONTROL_AF_TRIGGER_START. 560 CaptureRequest.Builder builder; 561 builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 562 builder.addTarget(mPreviewSurface); 563 builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); 564 mControlAFMode = CameraMetadata.CONTROL_AF_MODE_AUTO; 565 addBaselineCaptureKeysToRequest(builder); 566 builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); 567 builder.setTag(tag); 568 mCaptureSession.capture(builder.build(), mAutoFocusStateListener, mCameraHandler); 569 570 // Step 2: Call repeatingPreview to update mControlAFMode. 571 repeatingPreview(tag); 572 resumeContinuousAFAfterDelay(FOCUS_HOLD_MILLIS); 573 } catch (CameraAccessException ex) { 574 Log.e(TAG, "Could not execute preview request.", ex); 575 } 576 } 577 578 /** 579 * Resume AF_MODE_CONTINUOUS_PICTURE after FOCUS_HOLD_MILLIS. 580 */ 581 private void resumeContinuousAFAfterDelay(int millis) { 582 mCameraHandler.removeCallbacks(mReturnToContinuousAFRunnable); 583 mCameraHandler.postDelayed(mReturnToContinuousAFRunnable, millis); 584 } 585 586 /** 587 * This method takes appropriate action if camera2 AF state changes. 588 * <ol> 589 * <li>Reports changes in camera2 AF state to OneCamera.FocusStateListener.</li> 590 * <li>Take picture after AF scan if mTakePictureWhenLensIsStopped true.</li> 591 * </ol> 592 */ 593 private void autofocusStateChangeDispatcher(CaptureResult result) { 594 if (result.getFrameNumber() < mLastControlAfStateFrameNumber || 595 result.get(CaptureResult.CONTROL_AF_STATE) == null) { 596 return; 597 } 598 mLastControlAfStateFrameNumber = result.getFrameNumber(); 599 600 // Convert to OneCamera mode and state. 601 AutoFocusState resultAFState = AutoFocusHelper. 602 stateFromCamera2State(result.get(CaptureResult.CONTROL_AF_STATE)); 603 604 // TODO: Consider using LENS_STATE. 605 boolean lensIsStopped = resultAFState == AutoFocusState.ACTIVE_FOCUSED || 606 resultAFState == AutoFocusState.ACTIVE_UNFOCUSED || 607 resultAFState == AutoFocusState.PASSIVE_FOCUSED || 608 resultAFState == AutoFocusState.PASSIVE_UNFOCUSED; 609 610 if (mTakePictureWhenLensIsStopped && lensIsStopped) { 611 // Take the shot. 612 mCameraHandler.post(mTakePictureRunnable); 613 mTakePictureWhenLensIsStopped = false; 614 } 615 616 // Report state change when AF state has changed. 617 if (resultAFState != mLastResultAFState && mFocusStateListener != null) { 618 mFocusStateListener.onFocusStatusUpdate(resultAFState, result.getFrameNumber()); 619 } 620 mLastResultAFState = resultAFState; 621 } 622 623 @Override 624 public void triggerFocusAndMeterAtPoint(float nx, float ny) { 625 int sensorOrientation = mCharacteristics.get( 626 CameraCharacteristics.SENSOR_ORIENTATION); 627 mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation); 628 mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation); 629 630 sendAutoFocusTriggerCaptureRequest(RequestTag.TAP_TO_FOCUS); 631 } 632 633 @Override 634 public float getMaxZoom() { 635 return mCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); 636 } 637 638 @Override 639 public void setZoom(float zoom) { 640 mZoomValue = zoom; 641 mCropRegion = cropRegionForZoom(zoom); 642 repeatingPreview(null); 643 } 644 645 @Override 646 public Size pickPreviewSize(Size pictureSize, Context context) { 647 float pictureAspectRatio = pictureSize.getWidth() / (float) pictureSize.getHeight(); 648 return CaptureModuleUtil.getOptimalPreviewSize(context, getSupportedSizes(), 649 pictureAspectRatio); 650 } 651 652 private Rect cropRegionForZoom(float zoom) { 653 return AutoFocusHelper.cropRegionForZoom(mCharacteristics, zoom); 654 } 655 656 /** 657 * Calculate the aspect ratio of the full size capture on this device. 658 * 659 * @param characteristics the characteristics of the camera device. 660 * @return The aspect ration, in terms of width/height of the full capture 661 * size. 662 */ 663 private static float calculateFullSizeAspectRatio(CameraCharacteristics characteristics) { 664 Rect activeArraySize = 665 characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 666 return ((float)(activeArraySize.width())) / activeArraySize.height(); 667 } 668 669 /** 670 * Given an image reader, extracts the JPEG image bytes and then closes the 671 * reader. 672 * 673 * @param reader the reader to read the JPEG data from. 674 * @return The bytes of the JPEG image. Newly allocated. 675 */ 676 private static byte[] acquireJpegBytesAndClose(ImageReader reader) { 677 Image img = reader.acquireLatestImage(); 678 679 ByteBuffer buffer; 680 681 if (img.getFormat() == ImageFormat.JPEG) { 682 Image.Plane plane0 = img.getPlanes()[0]; 683 buffer = plane0.getBuffer(); 684 } else if (img.getFormat() == ImageFormat.YUV_420_888) { 685 buffer = ByteBuffer.allocateDirect(img.getWidth() * img.getHeight() * 3); 686 687 Log.v(TAG, "Compressing JPEG with software encoder."); 688 int numBytes = JpegUtilNative.compressJpegFromYUV420Image(img, buffer, JPEG_QUALITY); 689 690 if (numBytes < 0) { 691 throw new RuntimeException("Error compressing jpeg."); 692 } 693 694 buffer.limit(numBytes); 695 } else { 696 throw new RuntimeException("Unsupported image format."); 697 } 698 699 byte[] imageBytes = new byte[buffer.remaining()]; 700 buffer.get(imageBytes); 701 buffer.rewind(); 702 img.close(); 703 return imageBytes; 704 } 705 } 706