• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
InFlightCapture(PhotoCaptureParameters parameters, CaptureSession session)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      */
OneCameraImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize)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
takePicture(final PhotoCaptureParameters params, final CaptureSession session)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      */
takePictureNow(PhotoCaptureParameters params, CaptureSession session)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
startPreview(Surface previewSurface, CaptureReadyCallback listener)341     public void startPreview(Surface previewSurface, CaptureReadyCallback listener) {
342         mPreviewSurface = previewSurface;
343         setupAsync(mPreviewSurface, listener);
344     }
345 
346     @Override
setViewfinderSize(int width, int height)347     public void setViewfinderSize(int width, int height) {
348         throw new RuntimeException("Not implemented yet.");
349     }
350 
351     @Override
isFlashSupported(boolean enhanced)352     public boolean isFlashSupported(boolean enhanced) {
353         throw new RuntimeException("Not implemented yet.");
354     }
355 
356     @Override
isSupportingEnhancedMode()357     public boolean isSupportingEnhancedMode() {
358         throw new RuntimeException("Not implemented yet.");
359     }
360 
361     @Override
close(CloseCallback closeCallback)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
getSupportedSizes()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
getFullSizeAspectRatio()386     public float getFullSizeAspectRatio() {
387         return mFullSizeAspectRatio;
388     }
389 
390     @Override
isFrontFacing()391     public boolean isFrontFacing() {
392         return mCharacteristics.get(CameraCharacteristics.LENS_FACING)
393                 == CameraMetadata.LENS_FACING_FRONT;
394     }
395 
396     @Override
isBackFacing()397     public boolean isBackFacing() {
398         return mCharacteristics.get(CameraCharacteristics.LENS_FACING)
399                 == CameraMetadata.LENS_FACING_BACK;
400     }
401 
savePicture(byte[] jpegData, final PhotoCaptureParameters captureParams, CaptureSession session)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      */
setupAsync(final Surface previewSurface, final CaptureReadyCallback listener)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      */
setup(Surface previewSurface, final CaptureReadyCallback listener)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      */
addBaselineCaptureKeysToRequest(CaptureRequest.Builder builder)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      */
repeatingPreview(Object tag)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      */
sendAutoFocusTriggerCaptureRequest(Object tag)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      */
resumeContinuousAFAfterDelay(int millis)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      */
autofocusStateChangeDispatcher(CaptureResult result)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
triggerFocusAndMeterAtPoint(float nx, float ny)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
getMaxZoom()634     public float getMaxZoom() {
635         return mCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
636     }
637 
638     @Override
setZoom(float zoom)639     public void setZoom(float zoom) {
640         mZoomValue = zoom;
641         mCropRegion = cropRegionForZoom(zoom);
642         repeatingPreview(null);
643     }
644 
645     @Override
pickPreviewSize(Size pictureSize, Context context)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 
cropRegionForZoom(float zoom)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      */
calculateFullSizeAspectRatio(CameraCharacteristics characteristics)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      */
acquireJpegBytesAndClose(ImageReader reader)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