Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014 The Android Open Source Project |
| 3 | * |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| 5 | * in compliance with the License. You may obtain a copy of the License at |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 6 | * |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 8 | * |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License |
| 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| 11 | * or implied. See the License for the specific language governing permissions and limitations under |
| 12 | * the License. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 13 | */ |
| 14 | |
| 15 | package com.android.camera.one.v2; |
| 16 | |
| 17 | import android.annotation.TargetApi; |
| 18 | import android.content.Context; |
| 19 | import android.graphics.ImageFormat; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 20 | import android.graphics.Rect; |
| 21 | import android.hardware.camera2.CameraAccessException; |
| 22 | import android.hardware.camera2.CameraCaptureSession; |
| 23 | import android.hardware.camera2.CameraCharacteristics; |
| 24 | import android.hardware.camera2.CameraDevice; |
| 25 | import android.hardware.camera2.CameraMetadata; |
| 26 | import android.hardware.camera2.CaptureRequest; |
| 27 | import android.hardware.camera2.CaptureResult; |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 28 | import android.hardware.camera2.CaptureResult.Key; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 29 | import android.hardware.camera2.TotalCaptureResult; |
| 30 | import android.hardware.camera2.params.MeteringRectangle; |
| 31 | import android.hardware.camera2.params.StreamConfigurationMap; |
| 32 | import android.media.CameraProfile; |
| 33 | import android.media.Image; |
| 34 | import android.media.ImageReader; |
Andy Huibers | 17d9084 | 2014-08-28 15:05:57 -0700 | [diff] [blame] | 35 | import android.media.MediaActionSound; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 36 | import android.net.Uri; |
| 37 | import android.os.Build; |
| 38 | import android.os.Handler; |
| 39 | import android.os.HandlerThread; |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 40 | import android.os.SystemClock; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 41 | import android.support.v4.util.Pools; |
| 42 | import android.view.Surface; |
| 43 | |
| 44 | import com.android.camera.CaptureModuleUtil; |
| 45 | import com.android.camera.app.MediaSaver.OnMediaSavedListener; |
| 46 | import com.android.camera.debug.Log; |
| 47 | import com.android.camera.debug.Log.Tag; |
| 48 | import com.android.camera.exif.ExifInterface; |
| 49 | import com.android.camera.exif.ExifTag; |
| 50 | import com.android.camera.exif.Rational; |
| 51 | import com.android.camera.one.AbstractOneCamera; |
| 52 | import com.android.camera.one.OneCamera; |
| 53 | import com.android.camera.one.OneCamera.PhotoCaptureParameters.Flash; |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 54 | import com.android.camera.one.Settings3A; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 55 | import com.android.camera.one.v2.ImageCaptureManager.ImageCaptureListener; |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 56 | import com.android.camera.one.v2.ImageCaptureManager.MetadataChangeListener; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 57 | import com.android.camera.session.CaptureSession; |
| 58 | import com.android.camera.util.CameraUtil; |
Puneet Lall | c2387dc | 2014-10-15 18:21:12 -0700 | [diff] [blame] | 59 | import com.android.camera.util.ListenerCombiner; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 60 | import com.android.camera.util.JpegUtilNative; |
| 61 | import com.android.camera.util.Size; |
| 62 | |
| 63 | import java.nio.ByteBuffer; |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 64 | import java.security.InvalidParameterException; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 65 | import java.util.ArrayList; |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 66 | import java.util.Collections; |
| 67 | import java.util.HashSet; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 68 | import java.util.List; |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 69 | import java.util.Set; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 70 | import java.util.concurrent.LinkedBlockingQueue; |
| 71 | import java.util.concurrent.ThreadPoolExecutor; |
| 72 | import java.util.concurrent.TimeUnit; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 73 | |
| 74 | /** |
| 75 | * {@link OneCamera} implementation directly on top of the Camera2 API with zero |
| 76 | * shutter lag.<br> |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 77 | * TODO: Determine what the maximum number of full YUV capture frames is. |
| 78 | */ |
Dianne Hackborn | 43d66a0 | 2014-10-08 09:37:44 -0700 | [diff] [blame] | 79 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 80 | public class OneCameraZslImpl extends AbstractOneCamera { |
| 81 | private static final Tag TAG = new Tag("OneCameraZslImpl2"); |
| 82 | |
| 83 | /** Default JPEG encoding quality. */ |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 84 | private static final int JPEG_QUALITY = |
| 85 | CameraProfile.getJpegEncodingQualityParameter(CameraProfile.QUALITY_HIGH); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 86 | /** |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 87 | * The maximum number of images to store in the full-size ZSL ring buffer. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 88 | * <br> |
| 89 | * TODO: Determine this number dynamically based on available memory and the |
| 90 | * size of frames. |
| 91 | */ |
Puneet Lall | 9cd94d7 | 2014-10-14 14:05:11 -0700 | [diff] [blame] | 92 | private static final int MAX_CAPTURE_IMAGES = 10; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 93 | /** |
| 94 | * True if zero-shutter-lag images should be captured. Some devices produce |
| 95 | * lower-quality images for the high-frequency stream, so we may wish to |
| 96 | * disable ZSL in that case. |
| 97 | */ |
| 98 | private static final boolean ZSL_ENABLED = true; |
| 99 | |
| 100 | /** |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 101 | * Tags which may be used in CaptureRequests. |
| 102 | */ |
| 103 | private static enum RequestTag { |
| 104 | /** |
| 105 | * Indicates that the request was explicitly sent for a single |
| 106 | * high-quality still capture. Unlike other requests, such as the |
| 107 | * repeating (ZSL) stream and AF/AE triggers, requests with this tag |
| 108 | * should always be saved. |
| 109 | */ |
| 110 | EXPLICIT_CAPTURE |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 111 | } |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 112 | |
| 113 | /** |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 114 | * Set to ImageFormat.JPEG to use the hardware encoder, or |
| 115 | * ImageFormat.YUV_420_888 to use the software encoder. No other image |
| 116 | * formats are supported. |
| 117 | */ |
| 118 | private static final int sCaptureImageFormat = ImageFormat.YUV_420_888; |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 119 | /** |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 120 | * Token for callbacks posted to {@link #mCameraHandler} to resume |
| 121 | * continuous AF. |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 122 | */ |
| 123 | private static final String FOCUS_RESUME_CALLBACK_TOKEN = "RESUME_CONTINUOUS_AF"; |
Jiawen Chen | 9cefdd4 | 2014-09-09 14:14:39 -0700 | [diff] [blame] | 124 | |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 125 | /** Zero weight 3A region, to reset regions per API. */ |
Jiawen Chen | 9cefdd4 | 2014-09-09 14:14:39 -0700 | [diff] [blame] | 126 | /*package*/ MeteringRectangle[] ZERO_WEIGHT_3A_REGION = AutoFocusHelper.getZeroWeightRegion(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 127 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 128 | /** |
| 129 | * Thread on which high-priority camera operations, such as grabbing preview |
| 130 | * frames for the viewfinder, are running. |
| 131 | */ |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 132 | private final HandlerThread mCameraThread; |
| 133 | /** Handler of the {@link #mCameraThread}. */ |
| 134 | private final Handler mCameraHandler; |
| 135 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 136 | /** Thread on which low-priority camera listeners are running. */ |
| 137 | private final HandlerThread mCameraListenerThread; |
| 138 | private final Handler mCameraListenerHandler; |
| 139 | |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 140 | /** The characteristics of this camera. */ |
| 141 | private final CameraCharacteristics mCharacteristics; |
| 142 | /** The underlying Camera2 API camera device. */ |
| 143 | private final CameraDevice mDevice; |
| 144 | |
| 145 | /** |
| 146 | * The aspect ratio (width/height) of the full resolution for this camera. |
| 147 | * Usually the native aspect ratio of this camera. |
| 148 | */ |
Jiawen Chen | 9cefdd4 | 2014-09-09 14:14:39 -0700 | [diff] [blame] | 149 | private final float mFullSizeAspectRatio; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 150 | /** The Camera2 API capture session currently active. */ |
| 151 | private CameraCaptureSession mCaptureSession; |
| 152 | /** The surface onto which to render the preview. */ |
| 153 | private Surface mPreviewSurface; |
| 154 | /** Whether closing of this device has been requested. */ |
| 155 | private volatile boolean mIsClosed = false; |
| 156 | /** A callback that is called when the device is fully closed. */ |
| 157 | private CloseCallback mCloseCallback = null; |
| 158 | |
| 159 | /** Receives the normal captured images. */ |
| 160 | private final ImageReader mCaptureImageReader; |
| 161 | |
| 162 | /** |
| 163 | * Maintains a buffer of images and their associated {@link CaptureResult}s. |
| 164 | */ |
| 165 | private ImageCaptureManager mCaptureManager; |
| 166 | |
| 167 | /** |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 168 | * The sensor timestamps (which may not be relative to the system time) of |
| 169 | * the most recently captured images. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 170 | */ |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 171 | private final Set<Long> mCapturedImageTimestamps = Collections.synchronizedSet( |
| 172 | new HashSet<Long>()); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 173 | |
| 174 | /** Thread pool for performing slow jpeg encoding and saving tasks. */ |
| 175 | private final ThreadPoolExecutor mImageSaverThreadPool; |
| 176 | |
| 177 | /** Pool of native byte buffers on which to store jpeg-encoded images. */ |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 178 | private final Pools.SynchronizedPool<ByteBuffer> mJpegByteBufferPool = |
| 179 | new Pools.SynchronizedPool<ByteBuffer>(64); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 180 | |
| 181 | /** Current zoom value. 1.0 is no zoom. */ |
| 182 | private float mZoomValue = 1f; |
| 183 | /** Current crop region: set from mZoomValue. */ |
| 184 | private Rect mCropRegion; |
| 185 | /** Current AE, AF, and AWB regions */ |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 186 | private MeteringRectangle[] mAFRegions = ZERO_WEIGHT_3A_REGION; |
| 187 | private MeteringRectangle[] mAERegions = ZERO_WEIGHT_3A_REGION; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 188 | |
Andy Huibers | 17d9084 | 2014-08-28 15:05:57 -0700 | [diff] [blame] | 189 | private MediaActionSound mMediaActionSound = new MediaActionSound(); |
| 190 | |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 191 | /** |
Puneet Lall | 142b5bf | 2014-09-03 14:58:51 -0700 | [diff] [blame] | 192 | * Ready state (typically displayed by the UI shutter-button) depends on two |
| 193 | * things:<br> |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 194 | * <ol> |
| 195 | * <li>{@link #mCaptureManager} must be ready.</li> |
| 196 | * <li>We must not be in the process of capturing a single, high-quality, |
| 197 | * image.</li> |
| 198 | * </ol> |
Puneet Lall | c2387dc | 2014-10-15 18:21:12 -0700 | [diff] [blame] | 199 | * See {@link ListenerCombiner} and {@link #mReadyStateManager} for |
Puneet Lall | 142b5bf | 2014-09-03 14:58:51 -0700 | [diff] [blame] | 200 | * details of how this is managed. |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 201 | */ |
Puneet Lall | 68c25ac | 2014-08-28 18:36:33 -0700 | [diff] [blame] | 202 | private static enum ReadyStateRequirement { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 203 | CAPTURE_MANAGER_READY, CAPTURE_NOT_IN_PROGRESS |
Puneet Lall | 68c25ac | 2014-08-28 18:36:33 -0700 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | /** |
| 207 | * Handles the thread-safe logic of dispatching whenever the logical AND of |
| 208 | * these constraints changes. |
| 209 | */ |
Puneet Lall | c2387dc | 2014-10-15 18:21:12 -0700 | [diff] [blame] | 210 | private final ListenerCombiner<ReadyStateRequirement> |
| 211 | mReadyStateManager = new ListenerCombiner<ReadyStateRequirement>( |
| 212 | ReadyStateRequirement.class, new ListenerCombiner.StateChangeListener() { |
Puneet Lall | 142b5bf | 2014-09-03 14:58:51 -0700 | [diff] [blame] | 213 | @Override |
Puneet Lall | c2387dc | 2014-10-15 18:21:12 -0700 | [diff] [blame] | 214 | public void onStateChange(boolean state) { |
Puneet Lall | 142b5bf | 2014-09-03 14:58:51 -0700 | [diff] [blame] | 215 | broadcastReadyState(state); |
| 216 | } |
| 217 | }); |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 218 | |
| 219 | /** |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 220 | * An {@link ImageCaptureListener} which will compress and save an image to |
| 221 | * disk. |
| 222 | */ |
| 223 | private class ImageCaptureTask implements ImageCaptureListener { |
| 224 | private final PhotoCaptureParameters mParams; |
| 225 | private final CaptureSession mSession; |
| 226 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 227 | public ImageCaptureTask(PhotoCaptureParameters parameters, CaptureSession session) { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 228 | mParams = parameters; |
| 229 | mSession = session; |
| 230 | } |
| 231 | |
| 232 | @Override |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 233 | public void onImageCaptured(Image image, TotalCaptureResult captureResult) { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 234 | long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP); |
| 235 | |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 236 | // We should only capture the image if it hasn't been captured |
| 237 | // before. Synchronization is necessary since |
| 238 | // mCapturedImageTimestamps is read & modified elsewhere. |
| 239 | synchronized (mCapturedImageTimestamps) { |
| 240 | if (!mCapturedImageTimestamps.contains(timestamp)) { |
| 241 | mCapturedImageTimestamps.add(timestamp); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 242 | } else { |
| 243 | // There was a more recent (or identical) image which has |
| 244 | // begun being saved, so abort. |
| 245 | return; |
| 246 | } |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 247 | |
| 248 | // Clear out old timestamps from the set. |
| 249 | // We must keep old timestamps in the set a little longer (a |
| 250 | // factor of 2 seems adequate) to ensure they are cleared out of |
| 251 | // the ring buffer before their timestamp is removed from the |
| 252 | // set. |
| 253 | long maxTimestamps = MAX_CAPTURE_IMAGES * 2; |
| 254 | if (mCapturedImageTimestamps.size() > maxTimestamps) { |
| 255 | ArrayList<Long> timestamps = new ArrayList<Long>(mCapturedImageTimestamps); |
| 256 | Collections.sort(timestamps); |
| 257 | for (int i = 0; i < timestamps.size() |
| 258 | && mCapturedImageTimestamps.size() > maxTimestamps; i++) { |
| 259 | mCapturedImageTimestamps.remove(timestamps.get(i)); |
| 260 | } |
| 261 | } |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 262 | } |
| 263 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 264 | mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS, true); |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 265 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 266 | mSession.startEmpty(); |
| 267 | savePicture(image, mParams, mSession); |
| 268 | mParams.callback.onPictureTaken(mSession); |
| 269 | Log.v(TAG, "Image saved. Frame number = " + captureResult.getFrameNumber()); |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | /** |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 274 | * Instantiates a new camera based on Camera 2 API. |
| 275 | * |
| 276 | * @param device The underlying Camera 2 device. |
| 277 | * @param characteristics The device's characteristics. |
| 278 | * @param pictureSize the size of the final image to be taken. |
| 279 | */ |
| 280 | OneCameraZslImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize) { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 281 | Log.v(TAG, "Creating new OneCameraZslImpl"); |
| 282 | |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 283 | mDevice = device; |
| 284 | mCharacteristics = characteristics; |
| 285 | mFullSizeAspectRatio = calculateFullSizeAspectRatio(characteristics); |
| 286 | |
| 287 | mCameraThread = new HandlerThread("OneCamera2"); |
| 288 | // If this thread stalls, it will delay viewfinder frames. |
| 289 | mCameraThread.setPriority(Thread.MAX_PRIORITY); |
| 290 | mCameraThread.start(); |
| 291 | mCameraHandler = new Handler(mCameraThread.getLooper()); |
| 292 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 293 | mCameraListenerThread = new HandlerThread("OneCamera2-Listener"); |
| 294 | mCameraListenerThread.start(); |
| 295 | mCameraListenerHandler = new Handler(mCameraListenerThread.getLooper()); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 296 | |
| 297 | // TODO: Encoding on multiple cores results in preview jank due to |
| 298 | // excessive GC. |
| 299 | int numEncodingCores = CameraUtil.getNumCpuCores(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 300 | mImageSaverThreadPool = new ThreadPoolExecutor(numEncodingCores, numEncodingCores, 10, |
| 301 | TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); |
| 302 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 303 | mCaptureManager = |
| 304 | new ImageCaptureManager(MAX_CAPTURE_IMAGES, mCameraListenerHandler, |
| 305 | mImageSaverThreadPool); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 306 | mCaptureManager.setCaptureReadyListener(new ImageCaptureManager.CaptureReadyListener() { |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 307 | @Override |
| 308 | public void onReadyStateChange(boolean capturePossible) { |
Puneet Lall | 68c25ac | 2014-08-28 18:36:33 -0700 | [diff] [blame] | 309 | mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_MANAGER_READY, |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 310 | capturePossible); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 311 | } |
| 312 | }); |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 313 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 314 | // Listen for changes to auto focus state and dispatch to |
| 315 | // mFocusStateListener. |
| 316 | mCaptureManager.addMetadataChangeListener(CaptureResult.CONTROL_AF_STATE, |
| 317 | new ImageCaptureManager.MetadataChangeListener() { |
| 318 | @Override |
| 319 | public void onImageMetadataChange(Key<?> key, Object oldValue, Object newValue, |
| 320 | CaptureResult result) { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 321 | FocusStateListener listener = mFocusStateListener; |
| 322 | if (listener != null) { |
| 323 | listener.onFocusStatusUpdate( |
| 324 | AutoFocusHelper.stateFromCamera2State( |
Andy Huibers | 4a74862 | 2014-09-08 14:17:16 -0700 | [diff] [blame] | 325 | result.get(CaptureResult.CONTROL_AF_STATE)), |
Andy Huibers | ca3c564 | 2014-09-06 20:44:25 -0700 | [diff] [blame] | 326 | result.getFrameNumber()); |
Andy Huibers | 4a74862 | 2014-09-08 14:17:16 -0700 | [diff] [blame] | 327 | } |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 328 | } |
| 329 | }); |
| 330 | |
| 331 | // Allocate the image reader to store all images received from the |
| 332 | // camera. |
Puneet Lall | 80f4444 | 2014-09-03 10:43:35 -0700 | [diff] [blame] | 333 | if (pictureSize == null) { |
Puneet Lall | 4d6d676 | 2014-09-03 13:22:01 -0700 | [diff] [blame] | 334 | // TODO The default should be selected by the caller, and |
| 335 | // pictureSize should never be null. |
| 336 | pictureSize = getDefaultPictureSize(); |
Puneet Lall | 80f4444 | 2014-09-03 10:43:35 -0700 | [diff] [blame] | 337 | } |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 338 | mCaptureImageReader = ImageReader.newInstance(pictureSize.getWidth(), |
| 339 | pictureSize.getHeight(), |
| 340 | sCaptureImageFormat, MAX_CAPTURE_IMAGES); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 341 | |
| 342 | mCaptureImageReader.setOnImageAvailableListener(mCaptureManager, mCameraHandler); |
Andy Huibers | 17d9084 | 2014-08-28 15:05:57 -0700 | [diff] [blame] | 343 | mMediaActionSound.load(MediaActionSound.SHUTTER_CLICK); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 344 | } |
| 345 | |
| 346 | /** |
Puneet Lall | 4d6d676 | 2014-09-03 13:22:01 -0700 | [diff] [blame] | 347 | * @return The largest supported picture size. |
| 348 | */ |
| 349 | public Size getDefaultPictureSize() { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 350 | StreamConfigurationMap configs = |
| 351 | mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); |
Puneet Lall | 4d6d676 | 2014-09-03 13:22:01 -0700 | [diff] [blame] | 352 | android.util.Size[] supportedSizes = configs.getOutputSizes(sCaptureImageFormat); |
| 353 | |
| 354 | // Find the largest supported size. |
| 355 | android.util.Size largestSupportedSize = supportedSizes[0]; |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 356 | long largestSupportedSizePixels = |
| 357 | largestSupportedSize.getWidth() * largestSupportedSize.getHeight(); |
Puneet Lall | 4d6d676 | 2014-09-03 13:22:01 -0700 | [diff] [blame] | 358 | for (int i = 0; i < supportedSizes.length; i++) { |
| 359 | long numPixels = supportedSizes[i].getWidth() * supportedSizes[i].getHeight(); |
| 360 | if (numPixels > largestSupportedSizePixels) { |
| 361 | largestSupportedSize = supportedSizes[i]; |
| 362 | largestSupportedSizePixels = numPixels; |
| 363 | } |
| 364 | } |
| 365 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 366 | return new Size(largestSupportedSize.getWidth(), largestSupportedSize.getHeight()); |
Puneet Lall | 4d6d676 | 2014-09-03 13:22:01 -0700 | [diff] [blame] | 367 | } |
| 368 | |
Andy Huibers | 17d9084 | 2014-08-28 15:05:57 -0700 | [diff] [blame] | 369 | private void onShutterInvokeUI(final PhotoCaptureParameters params) { |
| 370 | // Tell CaptureModule shutter has occurred so it can flash the screen. |
| 371 | params.callback.onQuickExpose(); |
| 372 | // Play shutter click sound. |
| 373 | mMediaActionSound.play(MediaActionSound.SHUTTER_CLICK); |
| 374 | } |
| 375 | |
Puneet Lall | 4d6d676 | 2014-09-03 13:22:01 -0700 | [diff] [blame] | 376 | /** |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 377 | * Take a picture. |
| 378 | */ |
| 379 | @Override |
| 380 | public void takePicture(final PhotoCaptureParameters params, final CaptureSession session) { |
| 381 | params.checkSanity(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 382 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 383 | mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS, false); |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 384 | |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 385 | boolean useZSL = ZSL_ENABLED; |
| 386 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 387 | // We will only capture images from the zsl ring-buffer which satisfy |
| 388 | // this constraint. |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 389 | ArrayList<ImageCaptureManager.CapturedImageConstraint> zslConstraints = |
| 390 | new ArrayList<ImageCaptureManager.CapturedImageConstraint>(); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 391 | zslConstraints.add(new ImageCaptureManager.CapturedImageConstraint() { |
| 392 | @Override |
| 393 | public boolean satisfiesConstraint(TotalCaptureResult captureResult) { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 394 | Long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP); |
| 395 | Integer lensState = captureResult.get(CaptureResult.LENS_STATE); |
| 396 | Integer flashState = captureResult.get(CaptureResult.FLASH_STATE); |
| 397 | Integer flashMode = captureResult.get(CaptureResult.FLASH_MODE); |
| 398 | Integer aeState = captureResult.get(CaptureResult.CONTROL_AE_STATE); |
| 399 | Integer afState = captureResult.get(CaptureResult.CONTROL_AF_STATE); |
| 400 | Integer awbState = captureResult.get(CaptureResult.CONTROL_AWB_STATE); |
| 401 | |
Puneet Lall | bbd6516 | 2014-09-05 15:44:24 -0700 | [diff] [blame] | 402 | if (lensState == null) { |
| 403 | lensState = CaptureResult.LENS_STATE_STATIONARY; |
| 404 | } |
| 405 | if (flashState == null) { |
| 406 | flashState = CaptureResult.FLASH_STATE_UNAVAILABLE; |
| 407 | } |
| 408 | if (flashMode == null) { |
| 409 | flashMode = CaptureResult.FLASH_MODE_OFF; |
| 410 | } |
| 411 | if (aeState == null) { |
| 412 | aeState = CaptureResult.CONTROL_AE_STATE_INACTIVE; |
| 413 | } |
| 414 | if (afState == null) { |
| 415 | afState = CaptureResult.CONTROL_AF_STATE_INACTIVE; |
| 416 | } |
| 417 | if (awbState == null) { |
| 418 | awbState = CaptureResult.CONTROL_AWB_STATE_INACTIVE; |
| 419 | } |
| 420 | |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 421 | synchronized (mCapturedImageTimestamps) { |
| 422 | if (mCapturedImageTimestamps.contains(timestamp)) { |
| 423 | // Don't save frames which we've already saved. |
| 424 | return false; |
| 425 | } |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 426 | } |
| 427 | |
| 428 | if (lensState == CaptureResult.LENS_STATE_MOVING) { |
| 429 | // If we know the lens was moving, don't use this image. |
| 430 | return false; |
| 431 | } |
| 432 | |
| 433 | if (aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING |
| 434 | || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { |
| 435 | return false; |
| 436 | } |
| 437 | switch (params.flashMode) { |
| 438 | case OFF: |
| 439 | break; |
| 440 | case ON: |
| 441 | if (flashState != CaptureResult.FLASH_STATE_FIRED |
| 442 | || flashMode != CaptureResult.FLASH_MODE_SINGLE) { |
| 443 | return false; |
| 444 | } |
| 445 | break; |
| 446 | case AUTO: |
| 447 | if (aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED |
| 448 | && flashState != CaptureResult.FLASH_STATE_FIRED) { |
| 449 | return false; |
| 450 | } |
| 451 | break; |
| 452 | } |
| 453 | |
| 454 | if (afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN |
| 455 | || afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN) { |
| 456 | return false; |
| 457 | } |
| 458 | |
| 459 | if (awbState == CaptureResult.CONTROL_AWB_STATE_SEARCHING) { |
| 460 | return false; |
| 461 | } |
| 462 | |
| 463 | return true; |
| 464 | } |
| 465 | }); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 466 | // This constraint lets us capture images which have been explicitly |
| 467 | // requested. See {@link RequestTag.EXPLICIT_CAPTURE}. |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 468 | ArrayList<ImageCaptureManager.CapturedImageConstraint> singleCaptureConstraint = |
| 469 | new ArrayList<ImageCaptureManager.CapturedImageConstraint>(); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 470 | singleCaptureConstraint.add(new ImageCaptureManager.CapturedImageConstraint() { |
| 471 | @Override |
| 472 | public boolean satisfiesConstraint(TotalCaptureResult captureResult) { |
| 473 | Object tag = captureResult.getRequest().getTag(); |
| 474 | return tag == RequestTag.EXPLICIT_CAPTURE; |
| 475 | } |
| 476 | }); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 477 | |
| 478 | // If we can use ZSL, try to save a previously-captured frame, if an |
| 479 | // acceptable one exists in the buffer. |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 480 | if (useZSL) { |
| 481 | boolean capturedPreviousFrame = mCaptureManager.tryCaptureExistingImage( |
| 482 | new ImageCaptureTask(params, session), zslConstraints); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 483 | if (capturedPreviousFrame) { |
| 484 | Log.v(TAG, "Saving previous frame"); |
Andy Huibers | 17d9084 | 2014-08-28 15:05:57 -0700 | [diff] [blame] | 485 | onShutterInvokeUI(params); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 486 | } else { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 487 | Log.v(TAG, "No good image Available. Capturing next available good image."); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 488 | // If there was no good frame available in the ring buffer |
| 489 | // already, capture the next good image. |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 490 | // TODO Disable the shutter button until this image is captured. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 491 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 492 | if (params.flashMode == Flash.ON || params.flashMode == Flash.AUTO) { |
| 493 | // We must issue a request for a single capture using the |
| 494 | // flash, including an AE precapture trigger. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 495 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 496 | // The following sets up a sequence of events which will |
| 497 | // occur in reverse order to the associated method |
| 498 | // calls: |
| 499 | // 1. Send a request to trigger the Auto Exposure Precapture |
| 500 | // 2. Wait for the AE_STATE to leave the PRECAPTURE state, |
| 501 | // and then send a request for a single image, with the |
| 502 | // appropriate flash settings. |
| 503 | // 3. Capture the next appropriate image, which should be |
| 504 | // the one we requested in (2). |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 505 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 506 | mCaptureManager.captureNextImage(new ImageCaptureTask(params, session), |
| 507 | singleCaptureConstraint); |
| 508 | |
| 509 | mCaptureManager.addMetadataChangeListener(CaptureResult.CONTROL_AE_STATE, |
| 510 | new MetadataChangeListener() { |
| 511 | @Override |
| 512 | public void onImageMetadataChange(Key<?> key, Object oldValue, |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 513 | Object newValue, |
| 514 | CaptureResult result) { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 515 | Log.v(TAG, "AE State Changed"); |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 516 | if (oldValue.equals(Integer.valueOf( |
| 517 | CaptureResult.CONTROL_AE_STATE_PRECAPTURE))) { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 518 | mCaptureManager.removeMetadataChangeListener(key, this); |
| 519 | sendSingleRequest(params); |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 520 | // TODO: Delay this until |
| 521 | // onCaptureStarted(). |
Andy Huibers | 17d9084 | 2014-08-28 15:05:57 -0700 | [diff] [blame] | 522 | onShutterInvokeUI(params); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 523 | } |
| 524 | } |
| 525 | }); |
| 526 | |
| 527 | sendAutoExposureTriggerRequest(params.flashMode); |
| 528 | } else { |
| 529 | // We may get here if, for example, the auto focus is in the |
| 530 | // middle of a scan. |
| 531 | // If the flash is off, we should just wait for the next |
| 532 | // image that arrives. This will have minimal delay since we |
| 533 | // do not need to send a new capture request. |
| 534 | mCaptureManager.captureNextImage(new ImageCaptureTask(params, session), |
| 535 | zslConstraints); |
| 536 | } |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 537 | } |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 538 | } else { |
| 539 | // TODO If we can't save a previous frame, create a new capture |
| 540 | // request to do what we need (e.g. flash) and call |
| 541 | // captureNextImage(). |
| 542 | throw new UnsupportedOperationException("Non-ZSL capture not yet supported"); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 543 | } |
| 544 | } |
| 545 | |
| 546 | @Override |
| 547 | public void startPreview(Surface previewSurface, CaptureReadyCallback listener) { |
| 548 | mPreviewSurface = previewSurface; |
| 549 | setupAsync(mPreviewSurface, listener); |
| 550 | } |
| 551 | |
| 552 | @Override |
Jiawen Chen | 9cefdd4 | 2014-09-09 14:14:39 -0700 | [diff] [blame] | 553 | public void setViewfinderSize(int width, int height) { |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 554 | throw new RuntimeException("Not implemented yet."); |
| 555 | } |
| 556 | |
| 557 | @Override |
| 558 | public boolean isFlashSupported(boolean enhanced) { |
| 559 | throw new RuntimeException("Not implemented yet."); |
| 560 | } |
| 561 | |
| 562 | @Override |
| 563 | public boolean isSupportingEnhancedMode() { |
| 564 | throw new RuntimeException("Not implemented yet."); |
| 565 | } |
| 566 | |
| 567 | @Override |
| 568 | public void close(CloseCallback closeCallback) { |
| 569 | if (mIsClosed) { |
| 570 | Log.w(TAG, "Camera is already closed."); |
| 571 | return; |
| 572 | } |
| 573 | try { |
Puneet Lall | 9cd94d7 | 2014-10-14 14:05:11 -0700 | [diff] [blame] | 574 | mCaptureSession.stopRepeating(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 575 | } catch (CameraAccessException e) { |
| 576 | Log.e(TAG, "Could not abort captures in progress."); |
| 577 | } |
| 578 | mIsClosed = true; |
| 579 | mCloseCallback = closeCallback; |
| 580 | mCameraThread.quitSafely(); |
| 581 | mDevice.close(); |
| 582 | mCaptureManager.close(); |
Puneet Lall | 9cd94d7 | 2014-10-14 14:05:11 -0700 | [diff] [blame] | 583 | mCaptureImageReader.close(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 584 | } |
| 585 | |
| 586 | @Override |
Sascha Haeberling | c6da1a1 | 2014-11-06 09:50:51 -0800 | [diff] [blame^] | 587 | public Size[] getSupportedPreviewSizes() { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 588 | StreamConfigurationMap config = |
| 589 | mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 590 | return Size.convert(config.getOutputSizes(sCaptureImageFormat)); |
| 591 | } |
| 592 | |
| 593 | @Override |
Jiawen Chen | 9cefdd4 | 2014-09-09 14:14:39 -0700 | [diff] [blame] | 594 | public float getFullSizeAspectRatio() { |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 595 | return mFullSizeAspectRatio; |
| 596 | } |
| 597 | |
| 598 | @Override |
| 599 | public boolean isFrontFacing() { |
| 600 | return mCharacteristics.get(CameraCharacteristics.LENS_FACING) |
| 601 | == CameraMetadata.LENS_FACING_FRONT; |
| 602 | } |
| 603 | |
| 604 | @Override |
| 605 | public boolean isBackFacing() { |
| 606 | return mCharacteristics.get(CameraCharacteristics.LENS_FACING) |
| 607 | == CameraMetadata.LENS_FACING_BACK; |
| 608 | } |
| 609 | |
| 610 | private void savePicture(Image image, final PhotoCaptureParameters captureParams, |
| 611 | CaptureSession session) { |
| 612 | int heading = captureParams.heading; |
| 613 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 614 | int degrees = (captureParams.orientation + 270) % 360; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 615 | ExifInterface exif = null; |
| 616 | |
| 617 | exif = new ExifInterface(); |
| 618 | // TODO: Add more exif tags here. |
| 619 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 620 | Size size = getImageSizeForOrientation(image.getWidth(), image.getHeight(), |
| 621 | degrees); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 622 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 623 | exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_X_DIMENSION, size.getWidth())); |
| 624 | exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_Y_DIMENSION, size.getHeight())); |
| 625 | |
| 626 | exif.setTag( |
| 627 | exif.buildTag(ExifInterface.TAG_ORIENTATION, ExifInterface.Orientation.TOP_LEFT)); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 628 | |
| 629 | // Set GPS heading direction based on sensor, if location is on. |
| 630 | if (heading >= 0) { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 631 | ExifTag directionRefTag = exif.buildTag(ExifInterface.TAG_GPS_IMG_DIRECTION_REF, |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 632 | ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION); |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 633 | ExifTag directionTag = |
| 634 | exif.buildTag(ExifInterface.TAG_GPS_IMG_DIRECTION, new Rational(heading, 1)); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 635 | exif.setTag(directionRefTag); |
| 636 | exif.setTag(directionTag); |
| 637 | } |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 638 | // TODO Find out why this is off by -90 degrees. |
| 639 | session.saveAndFinish(acquireJpegBytes(image, degrees), |
| 640 | size.getWidth(), size.getHeight(), 0, exif, new OnMediaSavedListener() { |
| 641 | @Override |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 642 | public void onMediaSaved(Uri uri) { |
| 643 | captureParams.callback.onPictureSaved(uri); |
| 644 | } |
| 645 | }); |
| 646 | } |
| 647 | |
| 648 | /** |
| 649 | * Asynchronously sets up the capture session. |
| 650 | * |
| 651 | * @param previewSurface the surface onto which the preview should be |
| 652 | * rendered. |
| 653 | * @param listener called when setup is completed. |
| 654 | */ |
| 655 | private void setupAsync(final Surface previewSurface, final CaptureReadyCallback listener) { |
| 656 | mCameraHandler.post(new Runnable() { |
| 657 | @Override |
| 658 | public void run() { |
| 659 | setup(previewSurface, listener); |
| 660 | } |
| 661 | }); |
| 662 | } |
| 663 | |
| 664 | /** |
| 665 | * Configures and attempts to create a capture session. |
| 666 | * |
| 667 | * @param previewSurface the surface onto which the preview should be |
| 668 | * rendered. |
| 669 | * @param listener called when the setup is completed. |
| 670 | */ |
| 671 | private void setup(Surface previewSurface, final CaptureReadyCallback listener) { |
| 672 | try { |
| 673 | if (mCaptureSession != null) { |
| 674 | mCaptureSession.abortCaptures(); |
| 675 | mCaptureSession = null; |
| 676 | } |
| 677 | List<Surface> outputSurfaces = new ArrayList<Surface>(2); |
| 678 | outputSurfaces.add(previewSurface); |
| 679 | outputSurfaces.add(mCaptureImageReader.getSurface()); |
| 680 | |
Alan Newberger | f5c6253 | 2014-09-08 11:26:27 -0700 | [diff] [blame] | 681 | mDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() { |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 682 | @Override |
| 683 | public void onConfigureFailed(CameraCaptureSession session) { |
| 684 | listener.onSetupFailed(); |
| 685 | } |
| 686 | |
| 687 | @Override |
| 688 | public void onConfigured(CameraCaptureSession session) { |
| 689 | mCaptureSession = session; |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 690 | mAFRegions = ZERO_WEIGHT_3A_REGION; |
| 691 | mAERegions = ZERO_WEIGHT_3A_REGION; |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 692 | mZoomValue = 1f; |
| 693 | mCropRegion = cropRegionForZoom(mZoomValue); |
| 694 | boolean success = sendRepeatingCaptureRequest(); |
| 695 | if (success) { |
Puneet Lall | 142b5bf | 2014-09-03 14:58:51 -0700 | [diff] [blame] | 696 | mReadyStateManager.setInput(ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS, |
| 697 | true); |
| 698 | mReadyStateManager.notifyListeners(); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 699 | listener.onReadyForCapture(); |
| 700 | } else { |
| 701 | listener.onSetupFailed(); |
| 702 | } |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 703 | } |
| 704 | |
| 705 | @Override |
| 706 | public void onClosed(CameraCaptureSession session) { |
| 707 | super.onClosed(session); |
| 708 | if (mCloseCallback != null) { |
| 709 | mCloseCallback.onCameraClosed(); |
| 710 | } |
| 711 | } |
| 712 | }, mCameraHandler); |
| 713 | } catch (CameraAccessException ex) { |
| 714 | Log.e(TAG, "Could not set up capture session", ex); |
| 715 | listener.onSetupFailed(); |
| 716 | } |
| 717 | } |
| 718 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 719 | private void addRegionsToCaptureRequestBuilder(CaptureRequest.Builder builder) { |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 720 | builder.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions); |
| 721 | builder.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 722 | builder.set(CaptureRequest.SCALER_CROP_REGION, mCropRegion); |
| 723 | } |
| 724 | |
| 725 | private void addFlashToCaptureRequestBuilder(CaptureRequest.Builder builder, Flash flashMode) { |
| 726 | switch (flashMode) { |
| 727 | case ON: |
| 728 | builder.set(CaptureRequest.CONTROL_AE_MODE, |
| 729 | CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH); |
| 730 | builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE); |
| 731 | break; |
| 732 | case OFF: |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 733 | builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 734 | builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); |
| 735 | break; |
| 736 | case AUTO: |
| 737 | builder.set(CaptureRequest.CONTROL_AE_MODE, |
| 738 | CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); |
| 739 | break; |
| 740 | } |
| 741 | } |
| 742 | |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 743 | /** |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 744 | * Request a stream of images. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 745 | * |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 746 | * @return true if successful, false if there was an error submitting the |
| 747 | * capture request. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 748 | */ |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 749 | private boolean sendRepeatingCaptureRequest() { |
| 750 | Log.v(TAG, "sendRepeatingCaptureRequest()"); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 751 | try { |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 752 | CaptureRequest.Builder builder; |
| 753 | if (ZSL_ENABLED) { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 754 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 755 | } else { |
| 756 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); |
| 757 | } |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 758 | |
| 759 | builder.addTarget(mPreviewSurface); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 760 | |
| 761 | if (ZSL_ENABLED) { |
| 762 | builder.addTarget(mCaptureImageReader.getSurface()); |
| 763 | } |
| 764 | |
| 765 | builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); |
| 766 | |
| 767 | builder.set(CaptureRequest.CONTROL_AF_MODE, |
| 768 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); |
| 769 | builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); |
| 770 | |
| 771 | builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); |
| 772 | builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); |
| 773 | |
| 774 | addRegionsToCaptureRequestBuilder(builder); |
| 775 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 776 | mCaptureSession.setRepeatingRequest(builder.build(), mCaptureManager, mCameraHandler); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 777 | return true; |
| 778 | } catch (CameraAccessException e) { |
| 779 | if (ZSL_ENABLED) { |
| 780 | Log.v(TAG, "Could not execute zero-shutter-lag repeating request.", e); |
| 781 | } else { |
| 782 | Log.v(TAG, "Could not execute preview request.", e); |
| 783 | } |
| 784 | return false; |
| 785 | } |
| 786 | } |
| 787 | |
| 788 | /** |
| 789 | * Request a single image. |
| 790 | * |
| 791 | * @return true if successful, false if there was an error submitting the |
| 792 | * capture request. |
| 793 | */ |
| 794 | private boolean sendSingleRequest(OneCamera.PhotoCaptureParameters params) { |
| 795 | Log.v(TAG, "sendSingleRequest()"); |
| 796 | try { |
| 797 | CaptureRequest.Builder builder; |
| 798 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); |
| 799 | |
| 800 | builder.addTarget(mPreviewSurface); |
| 801 | |
| 802 | // Always add this surface for single image capture requests. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 803 | builder.addTarget(mCaptureImageReader.getSurface()); |
| 804 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 805 | builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); |
| 806 | |
| 807 | addFlashToCaptureRequestBuilder(builder, params.flashMode); |
| 808 | addRegionsToCaptureRequestBuilder(builder); |
| 809 | |
| 810 | builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); |
| 811 | builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); |
| 812 | |
| 813 | // Tag this as a special request which should be saved. |
| 814 | builder.setTag(RequestTag.EXPLICIT_CAPTURE); |
| 815 | |
| 816 | if (sCaptureImageFormat == ImageFormat.JPEG) { |
| 817 | builder.set(CaptureRequest.JPEG_QUALITY, (byte) (JPEG_QUALITY)); |
| 818 | builder.set(CaptureRequest.JPEG_ORIENTATION, |
| 819 | CameraUtil.getJpegRotation(params.orientation, mCharacteristics)); |
| 820 | } |
| 821 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 822 | mCaptureSession.capture(builder.build(), mCaptureManager, mCameraHandler); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 823 | return true; |
| 824 | } catch (CameraAccessException e) { |
| 825 | Log.v(TAG, "Could not execute single still capture request.", e); |
| 826 | return false; |
| 827 | } |
| 828 | } |
| 829 | |
| 830 | private boolean sendAutoExposureTriggerRequest(Flash flashMode) { |
| 831 | Log.v(TAG, "sendAutoExposureTriggerRequest()"); |
| 832 | try { |
| 833 | CaptureRequest.Builder builder; |
| 834 | if (ZSL_ENABLED) { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 835 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 836 | } else { |
| 837 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); |
| 838 | } |
| 839 | |
| 840 | builder.addTarget(mPreviewSurface); |
| 841 | |
| 842 | if (ZSL_ENABLED) { |
| 843 | builder.addTarget(mCaptureImageReader.getSurface()); |
| 844 | } |
| 845 | |
| 846 | builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); |
| 847 | |
| 848 | builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, |
| 849 | CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); |
| 850 | |
| 851 | addRegionsToCaptureRequestBuilder(builder); |
| 852 | addFlashToCaptureRequestBuilder(builder, flashMode); |
| 853 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 854 | mCaptureSession.capture(builder.build(), mCaptureManager, mCameraHandler); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 855 | |
| 856 | return true; |
| 857 | } catch (CameraAccessException e) { |
| 858 | Log.v(TAG, "Could not execute auto exposure trigger request.", e); |
| 859 | return false; |
| 860 | } |
| 861 | } |
| 862 | |
| 863 | /** |
| 864 | */ |
| 865 | private boolean sendAutoFocusTriggerRequest() { |
| 866 | Log.v(TAG, "sendAutoFocusTriggerRequest()"); |
| 867 | try { |
| 868 | CaptureRequest.Builder builder; |
| 869 | if (ZSL_ENABLED) { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 870 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 871 | } else { |
| 872 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); |
| 873 | } |
| 874 | |
| 875 | builder.addTarget(mPreviewSurface); |
| 876 | |
| 877 | if (ZSL_ENABLED) { |
| 878 | builder.addTarget(mCaptureImageReader.getSurface()); |
| 879 | } |
| 880 | |
| 881 | builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); |
| 882 | |
| 883 | addRegionsToCaptureRequestBuilder(builder); |
| 884 | |
| 885 | builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO); |
| 886 | builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); |
| 887 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 888 | mCaptureSession.capture(builder.build(), mCaptureManager, mCameraHandler); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 889 | |
| 890 | return true; |
| 891 | } catch (CameraAccessException e) { |
| 892 | Log.v(TAG, "Could not execute auto focus trigger request.", e); |
| 893 | return false; |
| 894 | } |
| 895 | } |
| 896 | |
| 897 | /** |
| 898 | * Like {@link #sendRepeatingCaptureRequest()}, but with the focus held |
| 899 | * constant. |
| 900 | * |
| 901 | * @return true if successful, false if there was an error submitting the |
| 902 | * capture request. |
| 903 | */ |
| 904 | private boolean sendAutoFocusHoldRequest() { |
| 905 | Log.v(TAG, "sendAutoFocusHoldRequest()"); |
| 906 | try { |
| 907 | CaptureRequest.Builder builder; |
| 908 | if (ZSL_ENABLED) { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 909 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 910 | } else { |
| 911 | builder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); |
| 912 | } |
| 913 | |
| 914 | builder.addTarget(mPreviewSurface); |
| 915 | |
| 916 | if (ZSL_ENABLED) { |
| 917 | builder.addTarget(mCaptureImageReader.getSurface()); |
| 918 | } |
| 919 | |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 920 | builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); |
| 921 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 922 | builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO); |
| 923 | builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 924 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 925 | addRegionsToCaptureRequestBuilder(builder); |
| 926 | // TODO: This should fire the torch, if appropriate. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 927 | |
| 928 | mCaptureSession.setRepeatingRequest(builder.build(), mCaptureManager, mCameraHandler); |
| 929 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 930 | return true; |
| 931 | } catch (CameraAccessException e) { |
| 932 | Log.v(TAG, "Could not execute auto focus hold request.", e); |
| 933 | return false; |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 934 | } |
| 935 | } |
| 936 | |
| 937 | /** |
| 938 | * Calculate the aspect ratio of the full size capture on this device. |
| 939 | * |
| 940 | * @param characteristics the characteristics of the camera device. |
| 941 | * @return The aspect ration, in terms of width/height of the full capture |
| 942 | * size. |
| 943 | */ |
Jiawen Chen | 9cefdd4 | 2014-09-09 14:14:39 -0700 | [diff] [blame] | 944 | private static float calculateFullSizeAspectRatio(CameraCharacteristics characteristics) { |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 945 | Rect activeArraySize = |
| 946 | characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); |
Jiawen Chen | 9cefdd4 | 2014-09-09 14:14:39 -0700 | [diff] [blame] | 947 | return ((float) activeArraySize.width()) / activeArraySize.height(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 948 | } |
| 949 | |
| 950 | /** |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 951 | * @param originalWidth the width of the original image captured from the |
| 952 | * camera |
| 953 | * @param originalHeight the height of the original image captured from the |
| 954 | * camera |
| 955 | * @param orientation the rotation to apply, in degrees. |
| 956 | * @return The size of the final rotated image |
| 957 | */ |
| 958 | private Size getImageSizeForOrientation(int originalWidth, int originalHeight, |
| 959 | int orientation) { |
| 960 | if (orientation == 0 || orientation == 180) { |
| 961 | return new Size(originalWidth, originalHeight); |
| 962 | } else if (orientation == 90 || orientation == 270) { |
| 963 | return new Size(originalHeight, originalWidth); |
| 964 | } else { |
| 965 | throw new InvalidParameterException("Orientation not supported."); |
| 966 | } |
| 967 | } |
| 968 | |
| 969 | /** |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 970 | * Given an image reader, extracts the JPEG image bytes and then closes the |
| 971 | * reader. |
| 972 | * |
Puneet Lall | 182ba6f | 2014-08-28 16:44:08 -0700 | [diff] [blame] | 973 | * @param img the image from which to extract jpeg bytes or compress to |
| 974 | * jpeg. |
Puneet Lall | 863a5ab | 2014-09-05 17:14:41 -0700 | [diff] [blame] | 975 | * @param degrees the angle to rotate the image, in degrees. Rotation is |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 976 | * only applied to YUV images. |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 977 | * @return The bytes of the JPEG image. Newly allocated. |
| 978 | */ |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 979 | private byte[] acquireJpegBytes(Image img, int degrees) { |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 980 | ByteBuffer buffer; |
| 981 | |
| 982 | if (img.getFormat() == ImageFormat.JPEG) { |
| 983 | Image.Plane plane0 = img.getPlanes()[0]; |
| 984 | buffer = plane0.getBuffer(); |
| 985 | |
| 986 | byte[] imageBytes = new byte[buffer.remaining()]; |
| 987 | buffer.get(imageBytes); |
| 988 | buffer.rewind(); |
| 989 | return imageBytes; |
| 990 | } else if (img.getFormat() == ImageFormat.YUV_420_888) { |
| 991 | buffer = mJpegByteBufferPool.acquire(); |
| 992 | if (buffer == null) { |
| 993 | buffer = ByteBuffer.allocateDirect(img.getWidth() * img.getHeight() * 3); |
| 994 | } |
| 995 | |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 996 | int numBytes = JpegUtilNative.compressJpegFromYUV420Image(img, buffer, JPEG_QUALITY, |
| 997 | degrees); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 998 | |
| 999 | if (numBytes < 0) { |
| 1000 | throw new RuntimeException("Error compressing jpeg."); |
| 1001 | } |
| 1002 | |
| 1003 | buffer.limit(numBytes); |
| 1004 | |
| 1005 | byte[] imageBytes = new byte[buffer.remaining()]; |
| 1006 | buffer.get(imageBytes); |
| 1007 | |
| 1008 | buffer.clear(); |
| 1009 | mJpegByteBufferPool.release(buffer); |
| 1010 | |
| 1011 | return imageBytes; |
| 1012 | } else { |
| 1013 | throw new RuntimeException("Unsupported image format."); |
| 1014 | } |
| 1015 | } |
| 1016 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 1017 | private void startAFCycle() { |
| 1018 | // Clean up any existing AF cycle's pending callbacks. |
| 1019 | mCameraHandler.removeCallbacksAndMessages(FOCUS_RESUME_CALLBACK_TOKEN); |
| 1020 | |
| 1021 | // Send a single CONTROL_AF_TRIGGER_START capture request. |
| 1022 | sendAutoFocusTriggerRequest(); |
| 1023 | |
| 1024 | // Immediately send a request for a regular preview stream, but with |
| 1025 | // CONTROL_AF_MODE_AUTO set so that the focus remains constant after the |
| 1026 | // AF cycle completes. |
| 1027 | sendAutoFocusHoldRequest(); |
| 1028 | |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 1029 | // Waits Settings3A.getFocusHoldMillis() milliseconds before sending |
| 1030 | // a request for a regular preview stream to resume. |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 1031 | mCameraHandler.postAtTime(new Runnable() { |
Puneet Lall | 80f4444 | 2014-09-03 10:43:35 -0700 | [diff] [blame] | 1032 | @Override |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 1033 | public void run() { |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 1034 | mAERegions = ZERO_WEIGHT_3A_REGION; |
| 1035 | mAFRegions = ZERO_WEIGHT_3A_REGION; |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 1036 | sendRepeatingCaptureRequest(); |
| 1037 | } |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 1038 | }, FOCUS_RESUME_CALLBACK_TOKEN, |
| 1039 | SystemClock.uptimeMillis() + Settings3A.getFocusHoldMillis()); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1040 | } |
| 1041 | |
| 1042 | /** |
| 1043 | * @see com.android.camera.one.OneCamera#triggerFocusAndMeterAtPoint(float, |
| 1044 | * float) |
| 1045 | */ |
| 1046 | @Override |
| 1047 | public void triggerFocusAndMeterAtPoint(float nx, float ny) { |
Jiawen Chen | 0971376 | 2014-09-16 17:05:37 -0700 | [diff] [blame] | 1048 | int sensorOrientation = mCharacteristics.get( |
| 1049 | CameraCharacteristics.SENSOR_ORIENTATION); |
| 1050 | mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation); |
| 1051 | mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1052 | |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 1053 | startAFCycle(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1054 | } |
| 1055 | |
| 1056 | @Override |
| 1057 | public Size pickPreviewSize(Size pictureSize, Context context) { |
Puneet Lall | 4d6d676 | 2014-09-03 13:22:01 -0700 | [diff] [blame] | 1058 | if (pictureSize == null) { |
| 1059 | // TODO The default should be selected by the caller, and |
| 1060 | // pictureSize should never be null. |
| 1061 | pictureSize = getDefaultPictureSize(); |
| 1062 | } |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1063 | float pictureAspectRatio = pictureSize.getWidth() / (float) pictureSize.getHeight(); |
Sascha Haeberling | c6da1a1 | 2014-11-06 09:50:51 -0800 | [diff] [blame^] | 1064 | return CaptureModuleUtil.getOptimalPreviewSize(context, getSupportedPreviewSizes(), |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1065 | pictureAspectRatio); |
| 1066 | } |
| 1067 | |
| 1068 | @Override |
| 1069 | public float getMaxZoom() { |
| 1070 | return mCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); |
| 1071 | } |
| 1072 | |
| 1073 | @Override |
| 1074 | public void setZoom(float zoom) { |
| 1075 | mZoomValue = zoom; |
| 1076 | mCropRegion = cropRegionForZoom(zoom); |
Puneet Lall | 580f451 | 2014-08-27 19:27:50 -0700 | [diff] [blame] | 1077 | sendRepeatingCaptureRequest(); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1078 | } |
| 1079 | |
| 1080 | private Rect cropRegionForZoom(float zoom) { |
Andy Huibers | b868274 | 2014-08-27 15:28:42 -0700 | [diff] [blame] | 1081 | return AutoFocusHelper.cropRegionForZoom(mCharacteristics, zoom); |
Puneet Lall | 512001d | 2014-08-13 18:53:53 -0700 | [diff] [blame] | 1082 | } |
| 1083 | } |