1 /*
2  * Copyright (C) 2014 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 android.hardware.camera2.cts.helpers;
18 
19 import android.graphics.Rect;
20 import android.hardware.camera2.CameraAccessException;
21 import android.hardware.camera2.CameraCaptureSession;
22 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
23 import android.hardware.camera2.CameraCharacteristics;
24 import android.hardware.camera2.CameraDevice;
25 import android.hardware.camera2.params.MeteringRectangle;
26 import android.hardware.camera2.CaptureRequest;
27 import android.hardware.camera2.CaptureResult;
28 import android.hardware.camera2.TotalCaptureResult;
29 import android.os.Handler;
30 import android.util.Log;
31 import android.view.Surface;
32 
33 import com.android.ex.camera2.pos.AutoFocusStateMachine;
34 import com.android.ex.camera2.pos.AutoFocusStateMachine.AutoFocusStateListener;
35 
36 /**
37  * A focuser utility class to assist camera to do auto focus.
38  * <p>
39  * This class need create repeating request and single request to do auto focus.
40  * The repeating request is used to get the auto focus states; the single
41  * request is used to trigger the auto focus. This class assumes the camera device
42  * supports auto-focus. Don't use this class if the camera device doesn't have focuser
43  * unit.
44  * </p>
45  */
46 public class Camera2Focuser implements AutoFocusStateListener {
47     private static final String TAG = "Focuser";
48     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
49 
50     private final AutoFocusStateMachine mAutoFocus = new AutoFocusStateMachine(this);
51     private final Handler mHandler;
52     private final AutoFocusListener mAutoFocusListener;
53     private final CameraDevice mCamera;
54     private final CameraCaptureSession mSession;
55     private final Surface mRequestSurface;
56     private final StaticMetadata mStaticInfo;
57 
58     private int mAfRun = 0;
59     private MeteringRectangle[] mAfRegions;
60     private boolean mLocked = false;
61     private boolean mSuccess = false;
62     private CaptureRequest.Builder mRepeatingBuilder;
63 
64     /**
65      * The callback interface to notify auto focus result.
66      */
67     public interface AutoFocusListener {
68         /**
69          * This callback is called when auto focus completes and locked.
70          *
71          * @param success true if focus was successful, false if otherwise
72          */
onAutoFocusLocked(boolean success)73         void onAutoFocusLocked(boolean success);
74     }
75 
76     /**
77      * Construct a focuser object, with given capture requestSurface, listener
78      * and handler.
79      * <p>
80      * The focuser object will use camera and requestSurface to submit capture
81      * request and receive focus state changes. The {@link AutoFocusListener} is
82      * used to notify the auto focus callback.
83      * </p>
84      *
85      * @param camera The camera device associated with this focuser
86      * @param session The camera capture session associated with this focuser
87      * @param requestSurface The surface to issue the capture request with
88      * @param listener The auto focus listener to notify AF result
89      * @param staticInfo The CameraCharacteristics of the camera device
90      * @param handler The handler used to post auto focus callbacks
91      * @throws CameraAccessException
92      */
Camera2Focuser(CameraDevice camera, CameraCaptureSession session, Surface requestSurface, AutoFocusListener listener, CameraCharacteristics staticInfo, Handler handler)93     public Camera2Focuser(CameraDevice camera, CameraCaptureSession session, Surface requestSurface,
94             AutoFocusListener listener, CameraCharacteristics staticInfo, Handler handler)
95             throws CameraAccessException {
96         if (camera == null) {
97             throw new IllegalArgumentException("camera must not be null");
98         }
99         if (session == null) {
100             throw new IllegalArgumentException("session must not be null");
101         }
102         if (listener == null) {
103             throw new IllegalArgumentException("listener must not be null");
104         }
105         if (handler == null) {
106             throw new IllegalArgumentException("handler must not be null");
107         }
108         if (requestSurface == null) {
109             throw new IllegalArgumentException("requestSurface must not be null");
110         }
111         if (staticInfo == null) {
112             throw new IllegalArgumentException("staticInfo must not be null");
113         }
114 
115         mCamera = camera;
116         mSession = session;
117         mRequestSurface = requestSurface;
118         mAutoFocusListener = listener;
119         mStaticInfo = new StaticMetadata(staticInfo,
120                 StaticMetadata.CheckLevel.ASSERT, /*collector*/null);
121         mHandler = handler;
122 
123         if (!mStaticInfo.hasFocuser()) {
124             throw new IllegalArgumentException("this camera doesn't have a focuser");
125         }
126 
127         /**
128          * Begin by always being in passive auto focus.
129          */
130         cancelAutoFocus();
131     }
132 
133     @Override
onAutoFocusSuccess(CaptureResult result, boolean locked)134     public synchronized void onAutoFocusSuccess(CaptureResult result, boolean locked) {
135         mSuccess = true;
136         mLocked = locked;
137 
138         if (locked) {
139             dispatchAutoFocusStatusLocked(/*success*/true);
140         }
141     }
142 
143     @Override
onAutoFocusFail(CaptureResult result, boolean locked)144     public synchronized void onAutoFocusFail(CaptureResult result, boolean locked) {
145         mSuccess = false;
146         mLocked = locked;
147 
148         if (locked) {
149             dispatchAutoFocusStatusLocked(/*success*/false);
150         }
151     }
152 
153     @Override
onAutoFocusScan(CaptureResult result)154     public synchronized void onAutoFocusScan(CaptureResult result) {
155         mSuccess = false;
156         mLocked = false;
157     }
158 
159     @Override
onAutoFocusInactive(CaptureResult result)160     public synchronized void onAutoFocusInactive(CaptureResult result) {
161         mSuccess = false;
162         mLocked = false;
163     }
164 
165     /**
166      * Start a active auto focus scan based on the given regions.
167      *
168      * <p>This is usually used for touch for focus, it can make the auto-focus converge based
169      * on some particular region aggressively. But it is usually slow as a full active scan
170      * is initiated. After the auto focus is converged, the {@link cancelAutoFocus} must be called
171      * to resume the continuous auto-focus.</p>
172      *
173      * @param afRegions The AF regions used by focuser auto focus, full active
174      * array size is used if afRegions is null.
175      * @throws CameraAccessException
176      */
touchForAutoFocus(MeteringRectangle[] afRegions)177     public synchronized void touchForAutoFocus(MeteringRectangle[] afRegions)
178             throws CameraAccessException {
179         startAutoFocusLocked(/*active*/true, afRegions);
180     }
181 
182     /**
183      * Start auto focus scan.
184      * <p>
185      * Start an auto focus scan if it was not done yet. If AF passively focused,
186      * lock it. If AF is already locked, return. Otherwise, initiate a full
187      * active scan. This is suitable for still capture: focus should need to be
188      * accurate, but the AF latency also need to be as short as possible.
189      * </p>
190      *
191      * @param afRegions The AF regions used by focuser auto focus, full active
192      *            array size is used if afRegions is null.
193      * @throws CameraAccessException
194      */
startAutoFocus(MeteringRectangle[] afRegions)195     public synchronized void startAutoFocus(MeteringRectangle[] afRegions)
196             throws CameraAccessException {
197         startAutoFocusLocked(/*forceActive*/false, afRegions);
198     }
199 
200     /**
201      * Cancel ongoing auto focus, unlock the auto-focus if it was locked, and
202      * resume to passive continuous auto focus.
203      *
204      * @throws CameraAccessException
205      */
cancelAutoFocus()206     public synchronized void cancelAutoFocus() throws CameraAccessException {
207         mSuccess = false;
208         mLocked = false;
209 
210         // reset the AF regions:
211         setAfRegions(null);
212 
213         // Create request builders, the af regions are automatically updated.
214         mRepeatingBuilder = createRequestBuilder();
215         CaptureRequest.Builder requestBuilder = createRequestBuilder();
216         mAutoFocus.setPassiveAutoFocus(/*picture*/true, mRepeatingBuilder);
217         mAutoFocus.unlockAutoFocus(mRepeatingBuilder, requestBuilder);
218         CaptureCallback listener = createCaptureListener();
219         mSession.setRepeatingRequest(mRepeatingBuilder.build(), listener, mHandler);
220         mSession.capture(requestBuilder.build(), listener, mHandler);
221     }
222 
223     /**
224      * Get current AF mode.
225      * @return current AF mode
226      * @throws IllegalStateException if there auto focus is not running.
227      */
getCurrentAfMode()228     public synchronized int getCurrentAfMode() {
229         if (mRepeatingBuilder == null) {
230             throw new IllegalStateException("Auto focus is not running, unable to get AF mode");
231         }
232 
233         return mRepeatingBuilder.get(CaptureRequest.CONTROL_AF_MODE);
234     }
235 
startAutoFocusLocked( boolean forceActive, MeteringRectangle[] afRegions)236     private void startAutoFocusLocked(
237             boolean forceActive, MeteringRectangle[] afRegions) throws CameraAccessException {
238 
239         setAfRegions(afRegions);
240         mAfRun++;
241 
242         // Create request builders, the af regions are automatically updated.
243         mRepeatingBuilder = createRequestBuilder();
244         CaptureRequest.Builder requestBuilder = createRequestBuilder();
245         if (forceActive) {
246             startAutoFocusFullActiveLocked();
247         } else {
248             // Not forcing a full active scan. If AF passively focused, lock it. If AF is already
249             // locked, return. Otherwise, initiate a full active scan.
250             if (mSuccess && mLocked) {
251                 dispatchAutoFocusStatusLocked(/*success*/true);
252                 return;
253             } else if (mSuccess) {
254                 mAutoFocus.lockAutoFocus(mRepeatingBuilder, requestBuilder);
255                 CaptureCallback listener = createCaptureListener();
256                 mSession.setRepeatingRequest(mRepeatingBuilder.build(), listener, mHandler);
257                 mSession.capture(requestBuilder.build(), listener, mHandler);
258             } else {
259                 startAutoFocusFullActiveLocked();
260             }
261         }
262     }
263 
startAutoFocusFullActiveLocked()264     private void startAutoFocusFullActiveLocked() throws CameraAccessException {
265         // Create request builders, the af regions are automatically updated.
266         mRepeatingBuilder = createRequestBuilder();
267         CaptureRequest.Builder requestBuilder = createRequestBuilder();
268         mAutoFocus.setActiveAutoFocus(mRepeatingBuilder, requestBuilder);
269         if (mRepeatingBuilder.get(CaptureRequest.CONTROL_AF_TRIGGER)
270                 != CaptureRequest.CONTROL_AF_TRIGGER_IDLE) {
271             throw new AssertionError("Wrong trigger set in repeating request");
272         }
273         if (requestBuilder.get(CaptureRequest.CONTROL_AF_TRIGGER)
274                 != CaptureRequest.CONTROL_AF_TRIGGER_START) {
275             throw new AssertionError("Wrong trigger set in queued request");
276         }
277         mAutoFocus.resetState();
278 
279         CaptureCallback listener = createCaptureListener();
280         mSession.setRepeatingRequest(mRepeatingBuilder.build(), listener, mHandler);
281         mSession.capture(requestBuilder.build(), listener, mHandler);
282     }
283 
dispatchAutoFocusStatusLocked(final boolean success)284     private void dispatchAutoFocusStatusLocked(final boolean success) {
285         mHandler.post(new Runnable() {
286             @Override
287             public void run() {
288                 mAutoFocusListener.onAutoFocusLocked(success);
289             }
290         });
291     }
292 
293     /**
294      * Create request builder, set the af regions.
295      * @throws CameraAccessException
296      */
createRequestBuilder()297     private CaptureRequest.Builder createRequestBuilder() throws CameraAccessException {
298         CaptureRequest.Builder requestBuilder =
299                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
300 
301         requestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, mAfRegions);
302         requestBuilder.addTarget(mRequestSurface);
303 
304         return requestBuilder;
305     }
306 
307     /**
308      * Set AF regions, fall back to default region if afRegions is null.
309      *
310      * @param afRegions The AF regions to set
311      * @throws IllegalArgumentException if the region is malformed (length is 0).
312      */
setAfRegions(MeteringRectangle[] afRegions)313     private void setAfRegions(MeteringRectangle[] afRegions) {
314         if (afRegions == null) {
315             setDefaultAfRegions();
316             return;
317         }
318         // Throw IAE if AF regions are malformed.
319         if (afRegions.length == 0) {
320             throw new IllegalArgumentException("afRegions is malformed, length: 0");
321         }
322 
323         mAfRegions = afRegions;
324     }
325 
326     /**
327      * Set default AF region to full active array size.
328      */
setDefaultAfRegions()329     private void setDefaultAfRegions() {
330         // Initialize AF regions with all zeros, meaning that it is up to camera device to device
331         // the regions used by AF.
332         mAfRegions = new MeteringRectangle[] {
333                 new MeteringRectangle(0, 0, 0, 0, MeteringRectangle.METERING_WEIGHT_DONT_CARE)};
334     }
createCaptureListener()335     private CaptureCallback createCaptureListener() {
336 
337         int thisAfRun;
338         synchronized (this) {
339             thisAfRun = mAfRun;
340         }
341 
342         final int finalAfRun = thisAfRun;
343 
344         return new CaptureCallback() {
345             private long mLatestFrameCount = -1;
346 
347             @Override
348             public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
349                     CaptureResult result) {
350                 // In case of a partial result, send to focuser if necessary
351                 // 3A fields are present
352                 if (result.get(CaptureResult.CONTROL_AF_STATE) != null &&
353                         result.get(CaptureResult.CONTROL_AF_MODE) != null) {
354                     if (VERBOSE) {
355                         Log.v(TAG, "Focuser - got early AF state");
356                     }
357 
358                     dispatchToFocuser(result);
359                 }
360             }
361 
362             @Override
363             public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
364                     TotalCaptureResult result) {
365                     dispatchToFocuser(result);
366             }
367 
368             private void dispatchToFocuser(CaptureResult result) {
369                 int afRun;
370                 synchronized (Camera2Focuser.this) {
371                     // In case of partial results, don't send AF update twice
372                     long frameCount = result.getFrameNumber();
373                     if (frameCount <= mLatestFrameCount) return;
374                     mLatestFrameCount = frameCount;
375 
376                     afRun = mAfRun;
377                 }
378 
379                 if (afRun != finalAfRun) {
380                     if (VERBOSE) {
381                         Log.w(TAG,
382                                 "onCaptureCompleted - Ignoring results from previous AF run "
383                                 + finalAfRun);
384                     }
385                     return;
386                 }
387 
388                 mAutoFocus.onCaptureCompleted(result);
389             }
390         };
391     }
392 }
393