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