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