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