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