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 /** 68 * Transforms a request template by resetting focus and exposure modes. 69 */ resetFocusExposureModes(RequestBuilder.Factory template)70 private static RequestBuilder.Factory resetFocusExposureModes(RequestBuilder.Factory template) { 71 RequestTemplate result = new RequestTemplate(template); 72 result.setParam(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); 73 result.setParam(CaptureRequest.CONTROL_AF_MODE, 74 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 75 result.setParam(CaptureRequest.CONTROL_AF_TRIGGER, 76 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 77 result.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 78 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 79 return result; 80 } 81 82 /** 83 * @param imageReader Creates the {@link ImageStream} used for capturing 84 * images to be saved. 85 * @param frameServer Used for interacting with the camera device. 86 * @param repeatingRequestBuilder Creates request builders to use for 87 * repeating requests sent during the scanning phase and after 88 * capture is complete. 89 * @param repeatingRequestTemplate The template type to use for repeating 90 * requests. 91 * @param burst Creates request builders to use for each image captured from 92 * @param waitForAEConvergence 93 * @param waitForAFConvergence 94 */ ConvergedImageCaptureCommand(ManagedImageReader imageReader, FrameServer frameServer, RequestBuilder.Factory repeatingRequestBuilder, int repeatingRequestTemplate, int stillCaptureRequestTemplate, List<RequestBuilder.Factory> burst, boolean waitForAEConvergence, boolean waitForAFConvergence)95 public ConvergedImageCaptureCommand(ManagedImageReader imageReader, FrameServer frameServer, 96 RequestBuilder.Factory repeatingRequestBuilder, 97 int repeatingRequestTemplate, int stillCaptureRequestTemplate, 98 List<RequestBuilder.Factory> burst, boolean waitForAEConvergence, 99 boolean waitForAFConvergence) { 100 mImageReader = imageReader; 101 mFrameServer = frameServer; 102 mRepeatingRequestBuilder = repeatingRequestBuilder; 103 mRepeatingRequestTemplate = repeatingRequestTemplate; 104 mStillCaptureRequestTemplate = stillCaptureRequestTemplate; 105 mBurst = burst; 106 mWaitForAEConvergence = waitForAEConvergence; 107 mWaitForAFConvergence = waitForAFConvergence; 108 109 mScanRequestTemplate = resetFocusExposureModes(repeatingRequestBuilder); 110 } 111 112 /** 113 * Sends a request to take a picture and blocks until it completes. 114 */ 115 @Override run(Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver)116 public void run(Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver) throws 117 InterruptedException, CameraAccessException, CameraCaptureSessionClosedException, 118 ResourceAcquisitionFailedException { 119 try (FrameServer.Session session = mFrameServer.createExclusiveSession()) { 120 try (ImageStream imageStream = mImageReader.createPreallocatedStream(mBurst.size())) { 121 if (mWaitForAFConvergence) { 122 waitForAFConvergence(session); 123 } 124 if (mWaitForAEConvergence) { 125 waitForAEConvergence(session); 126 } 127 captureBurst(session, imageStream, imageExposureUpdatable, imageSaver); 128 } finally { 129 // Always reset the repeating stream to ensure AF/AE are not 130 // locked when this exits. 131 // Note that this may still throw if the camera or session is 132 // closed. 133 resetRepeating(session); 134 } 135 } finally { 136 imageSaver.close(); 137 } 138 } 139 waitForAFConvergence(FrameServer.Session session)140 private void waitForAFConvergence(FrameServer.Session session) throws CameraAccessException, 141 InterruptedException, ResourceAcquisitionFailedException, 142 CameraCaptureSessionClosedException { 143 AFTriggerResult afStateMachine = new AFTriggerResult(); 144 145 RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 146 triggerBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest 147 .CONTROL_AF_TRIGGER_START); 148 triggerBuilder.addResponseListener(forPartialMetadata(afStateMachine)); 149 150 RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 151 idleBuilder.addResponseListener(forPartialMetadata(afStateMachine)); 152 153 session.submitRequest(Arrays.asList(idleBuilder.build()), 154 FrameServer.RequestType.REPEATING); 155 156 session.submitRequest(Arrays.asList(triggerBuilder.build()), 157 FrameServer.RequestType.NON_REPEATING); 158 159 // Block until the AF trigger is complete 160 afStateMachine.get(); 161 } 162 waitForAEConvergence(FrameServer.Session session)163 private void waitForAEConvergence(FrameServer.Session session) throws CameraAccessException, 164 InterruptedException, ResourceAcquisitionFailedException, 165 CameraCaptureSessionClosedException { 166 AETriggerResult aeStateMachine = new AETriggerResult(); 167 168 RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 169 triggerBuilder.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 170 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 171 triggerBuilder.addResponseListener(forPartialMetadata(aeStateMachine)); 172 173 RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate); 174 idleBuilder.addResponseListener(forPartialMetadata(aeStateMachine)); 175 176 session.submitRequest(Arrays.asList(idleBuilder.build()), 177 FrameServer.RequestType.REPEATING); 178 179 session.submitRequest(Arrays.asList(triggerBuilder.build()), 180 FrameServer.RequestType.NON_REPEATING); 181 182 // Wait until the ae state converges to a result. 183 aeStateMachine.get(); 184 } 185 captureBurst(FrameServer.Session session, ImageStream imageStream, Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver)186 private void captureBurst(FrameServer.Session session, ImageStream imageStream, Updatable<Void> 187 imageExposureUpdatable, ImageSaver imageSaver) throws CameraAccessException, 188 InterruptedException, ResourceAcquisitionFailedException, 189 CameraCaptureSessionClosedException { 190 List<Request> burstRequest = new ArrayList<>(mBurst.size()); 191 List<ListenableFuture<TotalCaptureResultProxy>> metadata = new ArrayList<>(mBurst.size()); 192 boolean first = true; 193 for (RequestBuilder.Factory builderTemplate : mBurst) { 194 RequestBuilder builder = builderTemplate.create(mStillCaptureRequestTemplate); 195 196 builder.setParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest 197 .CONTROL_AF_MODE_CONTINUOUS_PICTURE); 198 builder.setParam(CaptureRequest.CONTROL_CAPTURE_INTENT, 199 CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE); 200 201 if (first) { 202 first = false; 203 builder.addResponseListener(forFrameExposure(imageExposureUpdatable)); 204 } 205 206 MetadataFuture metadataFuture = new MetadataFuture(); 207 builder.addResponseListener(metadataFuture); 208 metadata.add(metadataFuture.getMetadata()); 209 210 builder.addStream(imageStream); 211 212 burstRequest.add(builder.build()); 213 } 214 215 session.submitRequest(burstRequest, FrameServer.RequestType.NON_REPEATING); 216 217 for (int i = 0; i < mBurst.size(); i++) { 218 try { 219 ImageProxy image = imageStream.getNext(); 220 imageSaver.addFullSizeImage(image, metadata.get(i)); 221 } catch (BufferQueue.BufferQueueClosedException e) { 222 // No more images will be available, so just quit. 223 return; 224 } 225 } 226 } 227 resetRepeating(FrameServer.Session session)228 private void resetRepeating(FrameServer.Session session) throws InterruptedException, 229 CameraCaptureSessionClosedException, CameraAccessException, 230 ResourceAcquisitionFailedException { 231 RequestBuilder repeatingBuilder = mRepeatingRequestBuilder.create 232 (mRepeatingRequestTemplate); 233 session.submitRequest(Arrays.asList(repeatingBuilder.build()), 234 FrameServer.RequestType.REPEATING); 235 236 RequestBuilder triggerCancelBuilder = mRepeatingRequestBuilder 237 .create(mRepeatingRequestTemplate); 238 triggerCancelBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER, 239 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); 240 session.submitRequest(Arrays.asList(triggerCancelBuilder.build()), 241 FrameServer.RequestType.NON_REPEATING); 242 243 // Some devices (e.g. N6) implicitly lock AE after sending an 244 // AE_PRECAPTURE trigger. (see bug: 19265647) 245 // The implicit lock is released when a request with 246 // INTENT_STILL_CAPTURE is taken. 247 248 // However, if we never get to that point (because the command was 249 // interrupted before the request for a photo was sent), then we must be 250 // sure to cancel this implicit AE lock to resume normal AE behavior. 251 // Sending a request for an explicit AE lock (followed, implicitly, by a 252 // request from the current repeating request, which has AE lock off) 253 // fixes the issue and results in normal AE behavior. 254 RequestBuilder hackAETriggerCancelBuilder = mRepeatingRequestBuilder.create 255 (mRepeatingRequestTemplate); 256 hackAETriggerCancelBuilder.setParam(CaptureRequest.CONTROL_AE_LOCK, true); 257 258 session.submitRequest(Arrays.asList(hackAETriggerCancelBuilder.build()), 259 FrameServer.RequestType.NON_REPEATING); 260 } 261 } 262