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