1 /* 2 * Copyright (C) 2015 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.photo; 18 19 import static com.android.camera.one.v2.core.ResponseListeners.forFrameExposure; 20 import static com.android.camera.one.v2.core.ResponseListeners.forPartialMetadata; 21 22 import android.annotation.TargetApi; 23 import android.hardware.camera2.CameraAccessException; 24 import android.hardware.camera2.CaptureRequest; 25 import android.os.Build; 26 27 import com.android.camera.async.BufferQueue; 28 import com.android.camera.async.Updatable; 29 import com.android.camera.one.v2.autofocus.AETriggerResult; 30 import com.android.camera.one.v2.autofocus.AFTriggerResult; 31 import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException; 32 import com.android.camera.one.v2.camera2proxy.ImageProxy; 33 import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy; 34 import com.android.camera.one.v2.core.FrameServer; 35 import com.android.camera.one.v2.core.Request; 36 import com.android.camera.one.v2.core.RequestBuilder; 37 import com.android.camera.one.v2.core.RequestTemplate; 38 import com.android.camera.one.v2.core.ResourceAcquisitionFailedException; 39 import com.android.camera.one.v2.imagesaver.ImageSaver; 40 import com.android.camera.one.v2.sharedimagereader.ManagedImageReader; 41 import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageStream; 42 import com.google.common.util.concurrent.ListenableFuture; 43 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.List; 47 48 import javax.annotation.ParametersAreNonnullByDefault; 49 50 /** 51 * Captures a burst after waiting for AF and AE convergence. 52 */ 53 @ParametersAreNonnullByDefault 54 @TargetApi(Build.VERSION_CODES.LOLLIPOP) 55 class ConvergedImageCaptureCommand implements ImageCaptureCommand { 56 private final ManagedImageReader mImageReader; 57 private final FrameServer mFrameServer; 58 private final RequestBuilder.Factory mScanRequestTemplate; 59 private final RequestBuilder.Factory mRepeatingRequestBuilder; 60 private final int mRepeatingRequestTemplate; 61 private final int mStillCaptureRequestTemplate; 62 private final List<RequestBuilder.Factory> mBurst; 63 64 private final boolean mWaitForAEConvergence; 65 private final boolean mWaitForAFConvergence; 66 67 private final boolean mCAFSupport; 68 69 /** 70 * Transforms a request template by resetting focus and exposure modes. 71 */ resetFocusExposureModes(RequestBuilder.Factory template, boolean cafSupport)72 private static RequestBuilder.Factory resetFocusExposureModes(RequestBuilder.Factory template, 73 boolean cafSupport) { 74 RequestTemplate result = new RequestTemplate(template); 75 result.setParam(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); 76 if (cafSupport) { 77 result.setParam(CaptureRequest.CONTROL_AF_MODE, 78 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 79 result.setParam(CaptureRequest.CONTROL_AF_TRIGGER, 80 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 81 } 82 result.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 83 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 84 return result; 85 } 86 87 /** 88 * @param imageReader Creates the {@link ImageStream} used for capturing 89 * images to be saved. 90 * @param frameServer Used for interacting with the camera device. 91 * @param repeatingRequestBuilder Creates request builders to use for 92 * repeating requests sent during the scanning phase and after 93 * capture is complete. 94 * @param repeatingRequestTemplate The template type to use for repeating 95 * requests. 96 * @param burst Creates request builders to use for each image captured from 97 * @param waitForAEConvergence 98 */ ConvergedImageCaptureCommand(ManagedImageReader imageReader, FrameServer frameServer, RequestBuilder.Factory repeatingRequestBuilder, int repeatingRequestTemplate, int stillCaptureRequestTemplate, List<RequestBuilder.Factory> burst, boolean waitForAEConvergence)99 public ConvergedImageCaptureCommand(ManagedImageReader imageReader, FrameServer frameServer, 100 RequestBuilder.Factory repeatingRequestBuilder, 101 int repeatingRequestTemplate, int stillCaptureRequestTemplate, 102 List<RequestBuilder.Factory> burst, boolean waitForAEConvergence) { 103 mImageReader = imageReader; 104 mFrameServer = frameServer; 105 mRepeatingRequestBuilder = repeatingRequestBuilder; 106 mRepeatingRequestTemplate = repeatingRequestTemplate; 107 mStillCaptureRequestTemplate = stillCaptureRequestTemplate; 108 mBurst = burst; 109 mWaitForAEConvergence = waitForAEConvergence; 110 mWaitForAFConvergence = false; 111 mCAFSupport = false; 112 113 mScanRequestTemplate = resetFocusExposureModes(repeatingRequestBuilder, mCAFSupport); 114 } 115 116 /** 117 * @param imageReader Creates the {@link ImageStream} used for capturing 118 * images to be saved. 119 * @param frameServer Used for interacting with the camera device. 120 * @param repeatingRequestBuilder Creates request builders to use for 121 * repeating requests sent during the scanning phase and after 122 * capture is complete. 123 * @param repeatingRequestTemplate The template type to use for repeating 124 * requests. 125 * @param burst Creates request builders to use for each image captured from 126 * @param waitForAEConvergence 127 * @param waitForAFConvergence 128 */ ConvergedImageCaptureCommand(ManagedImageReader imageReader, FrameServer frameServer, RequestBuilder.Factory repeatingRequestBuilder, int repeatingRequestTemplate, int stillCaptureRequestTemplate, List<RequestBuilder.Factory> burst, boolean waitForAEConvergence, boolean waitForAFConvergence)129 public ConvergedImageCaptureCommand(ManagedImageReader imageReader, FrameServer frameServer, 130 RequestBuilder.Factory repeatingRequestBuilder, 131 int repeatingRequestTemplate, int stillCaptureRequestTemplate, 132 List<RequestBuilder.Factory> burst, boolean waitForAEConvergence, 133 boolean waitForAFConvergence) { 134 mImageReader = imageReader; 135 mFrameServer = frameServer; 136 mRepeatingRequestBuilder = repeatingRequestBuilder; 137 mRepeatingRequestTemplate = repeatingRequestTemplate; 138 mStillCaptureRequestTemplate = stillCaptureRequestTemplate; 139 mBurst = burst; 140 mWaitForAEConvergence = waitForAEConvergence; 141 mWaitForAFConvergence = waitForAFConvergence; 142 mCAFSupport = true; 143 144 mScanRequestTemplate = resetFocusExposureModes(repeatingRequestBuilder, mCAFSupport); 145 } 146 /** 147 * Sends a request to take a picture and blocks until it completes. 148 */ 149 @Override run(Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver)150 public void run(Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver) throws 151 InterruptedException, CameraAccessException, CameraCaptureSessionClosedException, 152 ResourceAcquisitionFailedException { 153 try (FrameServer.Session session = mFrameServer.createExclusiveSession()) { 154 try (ImageStream imageStream = mImageReader.createPreallocatedStream(mBurst.size())) { 155 if (mWaitForAFConvergence) { 156 waitForAFConvergence(session); 157 } 158 if (mWaitForAEConvergence) { 159 waitForAEConvergence(session); 160 } 161 captureBurst(session, imageStream, imageExposureUpdatable, imageSaver); 162 } finally { 163 // Always reset the repeating stream to ensure AF/AE are not 164 // locked when this exits. 165 // Note that this may still throw if the camera or session is 166 // closed. 167 resetRepeating(session); 168 } 169 } finally { 170 imageSaver.close(); 171 } 172 } 173 waitForAFConvergence(FrameServer.Session session)174 private void waitForAFConvergence(FrameServer.Session session) throws CameraAccessException, 175 InterruptedException, ResourceAcquisitionFailedException, 176 CameraCaptureSessionClosedException { 177 AFTriggerResult afStateMachine = new AFTriggerResult(); 178 179 RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 180 triggerBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest 181 .CONTROL_AF_TRIGGER_START); 182 triggerBuilder.addResponseListener(forPartialMetadata(afStateMachine)); 183 184 RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 185 idleBuilder.addResponseListener(forPartialMetadata(afStateMachine)); 186 187 session.submitRequest(Arrays.asList(idleBuilder.build()), 188 FrameServer.RequestType.REPEATING); 189 190 session.submitRequest(Arrays.asList(triggerBuilder.build()), 191 FrameServer.RequestType.NON_REPEATING); 192 193 // Block until the AF trigger is complete 194 afStateMachine.get(); 195 } 196 waitForAEConvergence(FrameServer.Session session)197 private void waitForAEConvergence(FrameServer.Session session) throws CameraAccessException, 198 InterruptedException, ResourceAcquisitionFailedException, 199 CameraCaptureSessionClosedException { 200 AETriggerResult aeStateMachine = new AETriggerResult(); 201 202 RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 203 triggerBuilder.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 204 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 205 triggerBuilder.addResponseListener(forPartialMetadata(aeStateMachine)); 206 207 RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 208 idleBuilder.addResponseListener(forPartialMetadata(aeStateMachine)); 209 210 session.submitRequest(Arrays.asList(idleBuilder.build()), 211 FrameServer.RequestType.REPEATING); 212 213 session.submitRequest(Arrays.asList(triggerBuilder.build()), 214 FrameServer.RequestType.NON_REPEATING); 215 216 // Wait until the ae state converges to a result. 217 aeStateMachine.get(); 218 } 219 captureBurst(FrameServer.Session session, ImageStream imageStream, Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver)220 private void captureBurst(FrameServer.Session session, ImageStream imageStream, Updatable<Void> 221 imageExposureUpdatable, ImageSaver imageSaver) throws CameraAccessException, 222 InterruptedException, ResourceAcquisitionFailedException, 223 CameraCaptureSessionClosedException { 224 List<Request> burstRequest = new ArrayList<>(mBurst.size()); 225 List<ListenableFuture<TotalCaptureResultProxy>> metadata = new ArrayList<>(mBurst.size()); 226 boolean first = true; 227 for (RequestBuilder.Factory builderTemplate : mBurst) { 228 RequestBuilder builder = builderTemplate.create(mStillCaptureRequestTemplate); 229 230 if (mCAFSupport) { 231 builder.setParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest 232 .CONTROL_AF_MODE_CONTINUOUS_PICTURE); 233 } 234 builder.setParam(CaptureRequest.CONTROL_CAPTURE_INTENT, 235 CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE); 236 237 if (first) { 238 first = false; 239 builder.addResponseListener(forFrameExposure(imageExposureUpdatable)); 240 } 241 242 MetadataFuture metadataFuture = new MetadataFuture(); 243 builder.addResponseListener(metadataFuture); 244 metadata.add(metadataFuture.getMetadata()); 245 246 builder.addStream(imageStream); 247 248 burstRequest.add(builder.build()); 249 } 250 251 session.submitRequest(burstRequest, FrameServer.RequestType.NON_REPEATING); 252 253 for (int i = 0; i < mBurst.size(); i++) { 254 try { 255 ImageProxy image = imageStream.getNext(); 256 imageSaver.addFullSizeImage(image, metadata.get(i)); 257 } catch (BufferQueue.BufferQueueClosedException e) { 258 // No more images will be available, so just quit. 259 return; 260 } 261 } 262 } 263 resetRepeating(FrameServer.Session session)264 private void resetRepeating(FrameServer.Session session) throws InterruptedException, 265 CameraCaptureSessionClosedException, CameraAccessException, 266 ResourceAcquisitionFailedException { 267 RequestBuilder repeatingBuilder = mRepeatingRequestBuilder.create 268 (mRepeatingRequestTemplate); 269 session.submitRequest(Arrays.asList(repeatingBuilder.build()), 270 FrameServer.RequestType.REPEATING); 271 272 RequestBuilder triggerCancelBuilder = mRepeatingRequestBuilder 273 .create(mRepeatingRequestTemplate); 274 triggerCancelBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER, 275 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); 276 session.submitRequest(Arrays.asList(triggerCancelBuilder.build()), 277 FrameServer.RequestType.NON_REPEATING); 278 279 // Some devices (e.g. N6) implicitly lock AE after sending an 280 // AE_PRECAPTURE trigger. (see bug: 19265647) 281 // The implicit lock is released when a request with 282 // INTENT_STILL_CAPTURE is taken. 283 284 // However, if we never get to that point (because the command was 285 // interrupted before the request for a photo was sent), then we must be 286 // sure to cancel this implicit AE lock to resume normal AE behavior. 287 // Sending a request for an explicit AE lock (followed, implicitly, by a 288 // request from the current repeating request, which has AE lock off) 289 // fixes the issue and results in normal AE behavior. 290 RequestBuilder hackAETriggerCancelBuilder = mRepeatingRequestBuilder.create 291 (mRepeatingRequestTemplate); 292 hackAETriggerCancelBuilder.setParam(CaptureRequest.CONTROL_AE_LOCK, true); 293 294 session.submitRequest(Arrays.asList(hackAETriggerCancelBuilder.build()), 295 FrameServer.RequestType.NON_REPEATING); 296 } 297 } 298