Home | History | Annotate | Download | only in helpers
      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 
     17 package com.android.mediaframeworktest.helpers;
     18 
     19 import com.android.ex.camera2.pos.AutoFocusStateMachine;
     20 import com.android.ex.camera2.pos.AutoFocusStateMachine.AutoFocusStateListener;
     21 
     22 import android.hardware.camera2.CameraAccessException;
     23 import android.hardware.camera2.CameraCaptureSession;
     24 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
     25 import android.hardware.camera2.CameraCharacteristics;
     26 import android.hardware.camera2.CameraDevice;
     27 import android.hardware.camera2.CaptureRequest;
     28 import android.hardware.camera2.CaptureResult;
     29 import android.hardware.camera2.TotalCaptureResult;
     30 import android.hardware.camera2.params.MeteringRectangle;
     31 import android.os.Handler;
     32 import android.util.Log;
     33 import 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  */
     49 public 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