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.testcases; 18 19 import static android.hardware.camera2.cts.CameraTestUtils.*; 20 21 import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED; 22 23 import android.content.Context; 24 import android.graphics.ImageFormat; 25 import android.hardware.camera2.CameraAccessException; 26 import android.hardware.camera2.CameraCaptureSession; 27 import android.hardware.camera2.CameraCaptureSession.CaptureCallback; 28 import android.hardware.camera2.CameraCharacteristics; 29 import android.hardware.camera2.CameraDevice; 30 import android.hardware.camera2.CameraManager; 31 import android.hardware.camera2.CameraMetadata; 32 import android.hardware.camera2.CaptureRequest; 33 import android.hardware.camera2.CaptureResult; 34 import android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity; 35 import android.hardware.camera2.cts.CameraTestUtils; 36 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback; 37 import android.hardware.camera2.cts.helpers.CameraErrorCollector; 38 import android.hardware.camera2.cts.helpers.StaticMetadata; 39 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel; 40 import android.media.ImageReader; 41 import android.os.Handler; 42 import android.os.HandlerThread; 43 import android.os.Looper; 44 import android.util.Log; 45 import android.util.Range; 46 import android.util.Size; 47 import android.view.Surface; 48 import android.view.SurfaceHolder; 49 import android.view.View; 50 import android.view.WindowManager; 51 52 import androidx.test.rule.ActivityTestRule; 53 54 import com.android.ex.camera2.blocking.BlockingSessionCallback; 55 import com.android.ex.camera2.blocking.BlockingStateCallback; 56 import com.android.ex.camera2.exceptions.TimeoutRuntimeException; 57 58 import org.junit.After; 59 import org.junit.Before; 60 import org.junit.Rule; 61 62 import java.io.File; 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.HashMap; 66 import java.util.List; 67 68 /** 69 * Camera2 Preview test case base class by using SurfaceView as rendering target. 70 * 71 * <p>This class encapsulates the SurfaceView based preview common functionalities. 72 * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs 73 * and CameraStateCallback are handled in this class. Some basic preview related utility 74 * functions are provided to facilitate the derived preview-based test classes. 75 * </p> 76 */ 77 78 public class Camera2SurfaceViewTestCase { 79 private static final String TAG = "SurfaceViewTestCase"; 80 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 81 private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000; 82 83 protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000; 84 protected static final float FRAME_DURATION_ERROR_MARGIN = 0.01f; // 1 percent error margin. 85 protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100; 86 protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8; 87 protected static final int MIN_FRAME_DURATION_ERROR_MARGIN = 100; // ns 88 89 protected Context mContext; 90 protected CameraManager mCameraManager; 91 protected String[] mCameraIds; 92 protected HandlerThread mHandlerThread; 93 protected Handler mHandler; 94 protected BlockingStateCallback mCameraListener; 95 protected BlockingSessionCallback mSessionListener; 96 protected CameraErrorCollector mCollector; 97 protected HashMap<String, StaticMetadata> mAllStaticInfo; 98 // Per device fields: 99 protected StaticMetadata mStaticInfo; 100 protected CameraDevice mCamera; 101 protected CameraCaptureSession mSession; 102 protected ImageReader mReader; 103 protected Surface mReaderSurface; 104 protected Surface mPreviewSurface; 105 protected SurfaceHolder mPreviewHolder; 106 protected Size mPreviewSize; 107 protected List<Size> mOrderedPreviewSizes; // In descending order. 108 protected List<Size> m1080pBoundedOrderedPreviewSizes; // In descending order. 109 protected List<Size> mOrderedVideoSizes; // In descending order. 110 protected List<Size> mOrderedStillSizes; // In descending order. 111 protected HashMap<Size, Long> mMinPreviewFrameDurationMap; 112 protected String mDebugFileNameBase; 113 114 protected WindowManager mWindowManager; 115 116 @Rule 117 public ActivityTestRule<Camera2SurfaceViewCtsActivity> mActivityRule = 118 new ActivityTestRule<>(Camera2SurfaceViewCtsActivity.class); 119 120 @Before setUp()121 public void setUp() throws Exception { 122 mContext = mActivityRule.getActivity().getApplicationContext(); 123 /** 124 * Workaround for mockito and JB-MR2 incompatibility 125 * 126 * Avoid java.lang.IllegalArgumentException: dexcache == null 127 * https://code.google.com/p/dexmaker/issues/detail?id=2 128 */ 129 System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString()); 130 mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); 131 assertNotNull("Unable to get CameraManager", mCameraManager); 132 mCameraIds = mCameraManager.getCameraIdList(); 133 assertNotNull("Unable to get camera ids", mCameraIds); 134 mHandlerThread = new HandlerThread(TAG); 135 mHandlerThread.start(); 136 mHandler = new Handler(mHandlerThread.getLooper()); 137 mCameraListener = new BlockingStateCallback(); 138 mCollector = new CameraErrorCollector(); 139 140 File filesDir = mContext.getPackageManager().isInstantApp() 141 ? mContext.getFilesDir() 142 : mContext.getExternalFilesDir(null); 143 144 mDebugFileNameBase = filesDir.getPath(); 145 146 mAllStaticInfo = new HashMap<String, StaticMetadata>(); 147 List<String> hiddenPhysicalIds = new ArrayList<>(); 148 for (String cameraId : mCameraIds) { 149 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId); 150 StaticMetadata staticMetadata = new StaticMetadata(props, 151 CheckLevel.ASSERT, /*collector*/null); 152 mAllStaticInfo.put(cameraId, staticMetadata); 153 154 for (String physicalId : props.getPhysicalCameraIds()) { 155 if (!Arrays.asList(mCameraIds).contains(physicalId) && 156 !hiddenPhysicalIds.contains(physicalId)) { 157 hiddenPhysicalIds.add(physicalId); 158 props = mCameraManager.getCameraCharacteristics(physicalId); 159 staticMetadata = new StaticMetadata( 160 mCameraManager.getCameraCharacteristics(physicalId), 161 CheckLevel.ASSERT, /*collector*/null); 162 mAllStaticInfo.put(physicalId, staticMetadata); 163 } 164 } 165 } 166 167 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 168 } 169 170 @After tearDown()171 public void tearDown() throws Exception { 172 String[] cameraIdsPostTest = mCameraManager.getCameraIdList(); 173 assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest); 174 Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds)); 175 Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest)); 176 assertTrue( 177 "Number of cameras changed from " + mCameraIds.length + " to " + 178 cameraIdsPostTest.length, 179 mCameraIds.length == cameraIdsPostTest.length); 180 // Teardown the camera preview required environments. 181 mHandlerThread.quitSafely(); 182 mHandler = null; 183 mCameraListener = null; 184 185 try { 186 mCollector.verify(); 187 } catch (Throwable e) { 188 // When new Exception(e) is used, exception info will be printed twice. 189 throw new Exception(e.getMessage()); 190 } 191 } 192 193 /** 194 * Start camera preview by using the given request, preview size and capture 195 * listener. 196 * <p> 197 * If preview is already started, calling this function will stop the 198 * current preview stream and start a new preview stream with given 199 * parameters. No need to call stopPreview between two startPreview calls. 200 * </p> 201 * 202 * @param request The request builder used to start the preview. 203 * @param previewSz The size of the camera device output preview stream. 204 * @param listener The callbacks the camera device will notify when preview 205 * capture is available. 206 */ startPreview(CaptureRequest.Builder request, Size previewSz, CaptureCallback listener)207 protected void startPreview(CaptureRequest.Builder request, Size previewSz, 208 CaptureCallback listener) throws Exception { 209 // Update preview size. 210 updatePreviewSurface(previewSz); 211 if (VERBOSE) { 212 Log.v(TAG, "start preview with size " + mPreviewSize.toString()); 213 } 214 215 configurePreviewOutput(request); 216 217 mSession.setRepeatingRequest(request.build(), listener, mHandler); 218 } 219 220 /** 221 * Configure the preview output stream. 222 * 223 * @param request The request to be configured with preview surface 224 */ configurePreviewOutput(CaptureRequest.Builder request)225 protected void configurePreviewOutput(CaptureRequest.Builder request) 226 throws CameraAccessException { 227 List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1); 228 outputSurfaces.add(mPreviewSurface); 229 mSessionListener = new BlockingSessionCallback(); 230 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 231 232 request.addTarget(mPreviewSurface); 233 } 234 235 /** 236 * Create a {@link CaptureRequest#Builder} and add the default preview surface. 237 * 238 * @return The {@link CaptureRequest#Builder} to be created 239 * @throws CameraAccessException When create capture request from camera fails 240 */ createRequestForPreview()241 protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException { 242 if (mPreviewSurface == null) { 243 throw new IllegalStateException( 244 "Preview surface is not set yet, call updatePreviewSurface or startPreview" 245 + "first to set the preview surface properly."); 246 } 247 CaptureRequest.Builder requestBuilder = 248 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 249 requestBuilder.addTarget(mPreviewSurface); 250 return requestBuilder; 251 } 252 253 /** 254 * Stop preview for current camera device by closing the session. 255 * Does _not_ wait for the device to go idle 256 */ stopPreview()257 protected void stopPreview() throws Exception { 258 // Stop repeat, wait for captures to complete, and disconnect from surfaces 259 if (mSession != null) { 260 if (VERBOSE) Log.v(TAG, "Stopping preview"); 261 mSession.close(); 262 } 263 } 264 265 /** 266 * Stop preview for current camera device by closing the session and waiting for it to close, 267 * resulting in an idle device. 268 */ stopPreviewAndDrain()269 protected void stopPreviewAndDrain() throws Exception { 270 // Stop repeat, wait for captures to complete, and disconnect from surfaces 271 if (mSession != null) { 272 if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle"); 273 mSession.close(); 274 mSessionListener.getStateWaiter().waitForState(BlockingSessionCallback.SESSION_CLOSED, 275 /*timeoutMs*/WAIT_FOR_RESULT_TIMEOUT_MS); 276 } 277 } 278 279 /** 280 * Setup still (JPEG) capture configuration and start preview. 281 * <p> 282 * The default max number of image is set to image reader. 283 * </p> 284 * 285 * @param previewRequest The capture request to be used for preview 286 * @param stillRequest The capture request to be used for still capture 287 * @param previewSz Preview size 288 * @param stillSz The still capture size 289 * @param resultListener Capture result listener 290 * @param imageListener The still capture image listener 291 */ prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener, boolean isHeic)292 protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 293 CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, 294 CaptureCallback resultListener, 295 ImageReader.OnImageAvailableListener imageListener, boolean isHeic) throws Exception { 296 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, 297 isHeic ? ImageFormat.HEIC : ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, 298 imageListener); 299 } 300 301 /** 302 * Setup still (JPEG) capture configuration and start preview. 303 * 304 * @param previewRequest The capture request to be used for preview 305 * @param stillRequest The capture request to be used for still capture 306 * @param previewSz Preview size 307 * @param stillSz The still capture size 308 * @param resultListener Capture result listener 309 * @param maxNumImages The max number of images set to the image reader 310 * @param imageListener The still capture image listener 311 */ prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener, boolean isHeic)312 protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 313 CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, 314 CaptureCallback resultListener, int maxNumImages, 315 ImageReader.OnImageAvailableListener imageListener, boolean isHeic) throws Exception { 316 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, 317 isHeic ? ImageFormat.HEIC : ImageFormat.JPEG, resultListener, maxNumImages, imageListener); 318 } 319 320 /** 321 * Setup raw capture configuration and start preview. 322 * 323 * <p> 324 * The default max number of image is set to image reader. 325 * </p> 326 * 327 * @param previewRequest The capture request to be used for preview 328 * @param rawRequest The capture request to be used for raw capture 329 * @param previewSz Preview size 330 * @param rawSz The raw capture size 331 * @param resultListener Capture result listener 332 * @param imageListener The raw capture image listener 333 */ prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)334 protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 335 CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, 336 CaptureCallback resultListener, 337 ImageReader.OnImageAvailableListener imageListener) throws Exception { 338 prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz, 339 ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener); 340 } 341 342 /** 343 * Wait for expected result key value available in a certain number of results. 344 * 345 * <p> 346 * Check the result immediately if numFramesWait is 0. 347 * </p> 348 * 349 * @param listener The capture listener to get capture result 350 * @param resultKey The capture result key associated with the result value 351 * @param expectedValue The result value need to be waited for 352 * @param numResultsWait Number of frame to wait before times out 353 * @throws TimeoutRuntimeException If more than numResultsWait results are 354 * seen before the result matching myRequest arrives, or each individual wait 355 * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. 356 */ waitForResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, T expectedValue, int numResultsWait)357 protected static <T> void waitForResultValue(SimpleCaptureCallback listener, 358 CaptureResult.Key<T> resultKey, 359 T expectedValue, int numResultsWait) { 360 CameraTestUtils.waitForResultValue(listener, resultKey, expectedValue, 361 numResultsWait, WAIT_FOR_RESULT_TIMEOUT_MS); 362 } 363 364 /** 365 * Wait for any expected result key values available in a certain number of results. 366 * 367 * <p> 368 * Check the result immediately if numFramesWait is 0. 369 * </p> 370 * 371 * @param listener The capture listener to get capture result. 372 * @param resultKey The capture result key associated with the result value. 373 * @param expectedValues The list of result value need to be waited for, 374 * return immediately if the list is empty. 375 * @param numResultsWait Number of frame to wait before times out. 376 * @throws TimeoutRuntimeException If more than numResultsWait results are. 377 * seen before the result matching myRequest arrives, or each individual wait 378 * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. 379 */ waitForAnyResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, List<T> expectedValues, int numResultsWait)380 protected static <T> void waitForAnyResultValue(SimpleCaptureCallback listener, 381 CaptureResult.Key<T> resultKey, 382 List<T> expectedValues, int numResultsWait) { 383 CameraTestUtils.waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait, 384 WAIT_FOR_RESULT_TIMEOUT_MS); 385 } 386 387 /** 388 * Submit a burst of the same capture request, then submit additional captures in order to 389 * ensure that the camera will be synchronized. 390 * 391 * <p> 392 * The additional capture count is determined by android.sync.maxLatency (or 393 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 394 * </p> 395 * 396 * <p>Returns the number of captures that were submitted (at least 1), which is useful 397 * with {@link #waitForNumResults}.</p> 398 * 399 * @param request capture request to forward to {@link CameraDevice#capture} 400 * @param listener request listener to forward to {@link CameraDevice#capture} 401 * @param handler handler to forward to {@link CameraDevice#capture} 402 * 403 * @return the number of captures that were submitted 404 * 405 * @throws CameraAccessException if capturing failed 406 */ captureRequestsSynchronizedBurst( CaptureRequest request, int count, CaptureCallback listener, Handler handler)407 protected int captureRequestsSynchronizedBurst( 408 CaptureRequest request, int count, CaptureCallback listener, Handler handler) 409 throws CameraAccessException { 410 return captureRequestsSynchronizedImpl(request, count, listener, handler, true); 411 } 412 /** 413 * Submit a capture once, then submit additional captures in order to ensure that 414 * the camera will be synchronized. 415 * 416 * <p> 417 * The additional capture count is determined by android.sync.maxLatency (or 418 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 419 * </p> 420 * 421 * <p>Returns the number of captures that were submitted (at least 1), which is useful 422 * with {@link #waitForNumResults}.</p> 423 * 424 * @param request capture request to forward to {@link CameraDevice#capture} 425 * @param listener request listener to forward to {@link CameraDevice#capture} 426 * @param handler handler to forward to {@link CameraDevice#capture} 427 * 428 * @return the number of captures that were submitted 429 * 430 * @throws CameraAccessException if capturing failed 431 */ captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler)432 protected int captureRequestsSynchronized( 433 CaptureRequest request, CaptureCallback listener, Handler handler) 434 throws CameraAccessException { 435 return captureRequestsSynchronizedImpl(request, /*count*/1, listener, handler, false); 436 } 437 438 /** 439 * Submit a capture {@code count} times, then submit additional captures in order to ensure that 440 * the camera will be synchronized. 441 * 442 * <p> 443 * The additional capture count is determined by android.sync.maxLatency (or 444 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 445 * </p> 446 * 447 * <p>Returns the number of captures that were submitted (at least 1), which is useful 448 * with {@link #waitForNumResults}.</p> 449 * 450 * @param request capture request to forward to {@link CameraDevice#capture} 451 * @param count the number of times to submit the request (minimally), must be at least 1 452 * @param listener request listener to forward to {@link CameraDevice#capture} 453 * @param handler handler to forward to {@link CameraDevice#capture} 454 * 455 * @return the number of captures that were submitted 456 * 457 * @throws IllegalArgumentException if {@code count} was not at least 1 458 * @throws CameraAccessException if capturing failed 459 */ captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler)460 protected int captureRequestsSynchronized( 461 CaptureRequest request, int count, CaptureCallback listener, Handler handler) 462 throws CameraAccessException { 463 return captureRequestsSynchronizedImpl(request, count, listener, handler, false); 464 } 465 466 /** 467 * Wait for numResultWait frames 468 * 469 * @param resultListener The capture listener to get capture result back. 470 * @param numResultsWait Number of frame to wait 471 * 472 * @return the last result, or {@code null} if there was none 473 */ waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait)474 protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener, 475 int numResultsWait) { 476 return CameraTestUtils.waitForNumResults(resultListener, numResultsWait, 477 WAIT_FOR_RESULT_TIMEOUT_MS); 478 } 479 480 /** 481 * Wait for enough results for settings to be applied 482 * 483 * @param resultListener The capture listener to get capture result back. 484 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 485 * unknown. 486 */ waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)487 protected void waitForSettingsApplied(SimpleCaptureCallback resultListener, 488 int numResultWaitForUnknownLatency) { 489 int maxLatency = mStaticInfo.getSyncMaxLatency(); 490 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 491 maxLatency = numResultWaitForUnknownLatency; 492 } 493 // Wait for settings to take effect 494 waitForNumResults(resultListener, maxLatency); 495 } 496 497 /** 498 * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED. 499 * 500 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 501 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 502 * is unknown.</p> 503 * 504 * <p>This is a no-op for {@code LEGACY} devices since they don't report 505 * the {@code aeState} result.</p> 506 * 507 * @param resultListener The capture listener to get capture result back. 508 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 509 * unknown. 510 */ waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)511 protected void waitForAeStable(SimpleCaptureCallback resultListener, 512 int numResultWaitForUnknownLatency) { 513 CameraTestUtils.waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY, 514 mStaticInfo, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT); 515 } 516 517 /** 518 * Wait for AE to be: LOCKED 519 * 520 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 521 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 522 * is unknown.</p> 523 * 524 * <p>This is a no-op for {@code LEGACY} devices since they don't report 525 * the {@code aeState} result.</p> 526 * 527 * @param resultListener The capture listener to get capture result back. 528 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 529 * unknown. 530 */ waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)531 protected void waitForAeLocked(SimpleCaptureCallback resultListener, 532 int numResultWaitForUnknownLatency) { 533 534 waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); 535 536 if (!mStaticInfo.isHardwareLevelAtLeastLimited()) { 537 // No-op for legacy devices 538 return; 539 } 540 541 List<Integer> expectedAeStates = new ArrayList<Integer>(); 542 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED)); 543 CameraTestUtils.waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, 544 expectedAeStates, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT); 545 } 546 547 /** 548 * Create an {@link ImageReader} object and get the surface. 549 * 550 * @param size The size of this ImageReader to be created. 551 * @param format The format of this ImageReader to be created 552 * @param maxNumImages The max number of images that can be acquired simultaneously. 553 * @param listener The listener used by this ImageReader to notify callbacks. 554 */ createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)555 protected void createImageReader(Size size, int format, int maxNumImages, 556 ImageReader.OnImageAvailableListener listener) throws Exception { 557 closeImageReader(); 558 559 ImageReader r = makeImageReader(size, format, maxNumImages, listener, 560 mHandler); 561 mReader = r; 562 mReaderSurface = r.getSurface(); 563 } 564 565 /** 566 * Close the pending images then close current active {@link ImageReader} object. 567 */ closeImageReader()568 protected void closeImageReader() { 569 CameraTestUtils.closeImageReader(mReader); 570 mReader = null; 571 mReaderSurface = null; 572 } 573 574 /** 575 * Close the pending images then close current active {@link ImageReader} objects. 576 */ closeImageReaders(ImageReader[] readers)577 protected void closeImageReaders(ImageReader[] readers) { 578 CameraTestUtils.closeImageReaders(readers); 579 } 580 581 /** 582 * Setup still capture configuration and start preview. 583 * 584 * @param previewRequest The capture request to be used for preview 585 * @param stillRequest The capture request to be used for still capture 586 * @param previewSz Preview size 587 * @param captureSizes Still capture sizes 588 * @param formats The single capture image formats 589 * @param resultListener Capture result listener 590 * @param maxNumImages The max number of images set to the image reader 591 * @param imageListeners The single capture capture image listeners 592 * @param isHeic HEIC still capture if true, JPEG still capture if false 593 */ prepareStillCaptureAndStartPreview( CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners, boolean isHeic)594 protected ImageReader[] prepareStillCaptureAndStartPreview( 595 CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, 596 Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener, 597 int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners, 598 boolean isHeic) 599 throws Exception { 600 601 if ((captureSizes == null) || (formats == null) || (imageListeners == null) && 602 (captureSizes.length != formats.length) || 603 (formats.length != imageListeners.length)) { 604 throw new IllegalArgumentException("Invalid capture sizes/formats or image listeners!"); 605 } 606 607 if (VERBOSE) { 608 Log.v(TAG, String.format("Prepare still capture and preview (%s)", 609 previewSz.toString())); 610 } 611 612 // Update preview size. 613 updatePreviewSurface(previewSz); 614 615 ImageReader[] readers = new ImageReader[captureSizes.length]; 616 List<Surface> outputSurfaces = new ArrayList<Surface>(); 617 outputSurfaces.add(mPreviewSurface); 618 for (int i = 0; i < captureSizes.length; i++) { 619 readers[i] = makeImageReader(captureSizes[i], formats[i], maxNumImages, 620 imageListeners[i], mHandler); 621 outputSurfaces.add(readers[i].getSurface()); 622 } 623 624 mSessionListener = new BlockingSessionCallback(); 625 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 626 627 // Configure the requests. 628 previewRequest.addTarget(mPreviewSurface); 629 stillRequest.addTarget(mPreviewSurface); 630 for (int i = 0; i < readers.length; i++) { 631 stillRequest.addTarget(readers[i].getSurface()); 632 } 633 634 // Start preview. 635 mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); 636 637 return readers; 638 } 639 640 /** 641 * Open a camera device and get the StaticMetadata for a given camera id. 642 * 643 * @param cameraId The id of the camera device to be opened. 644 */ openDevice(String cameraId)645 protected void openDevice(String cameraId) throws Exception { 646 mCamera = CameraTestUtils.openCamera( 647 mCameraManager, cameraId, mCameraListener, mHandler); 648 mCollector.setCameraId(cameraId); 649 mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 650 CheckLevel.ASSERT, /*collector*/null); 651 if (mStaticInfo.isColorOutputSupported()) { 652 mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 653 getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND)); 654 m1080pBoundedOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 655 PREVIEW_SIZE_BOUND); 656 mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND); 657 mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null); 658 // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview 659 // in public API side. 660 mMinPreviewFrameDurationMap = 661 mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888); 662 } 663 } 664 665 /** 666 * Close the current actively used camera device. 667 */ closeDevice()668 protected void closeDevice() { 669 if (mCamera != null) { 670 mCamera.close(); 671 mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS); 672 mCamera = null; 673 mSession = null; 674 mSessionListener = null; 675 mStaticInfo = null; 676 mOrderedPreviewSizes = null; 677 mOrderedVideoSizes = null; 678 mOrderedStillSizes = null; 679 } 680 } 681 682 /** 683 * Update the preview surface size. 684 * 685 * @param size The preview size to be updated. 686 */ updatePreviewSurface(Size size)687 protected void updatePreviewSurface(Size size) { 688 if (size.equals(mPreviewSize) && mPreviewSurface != null) { 689 Log.w(TAG, "Skipping update preview surface size..."); 690 return; 691 } 692 693 mPreviewSize = size; 694 Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity(); 695 final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder(); 696 Handler handler = new Handler(Looper.getMainLooper()); 697 handler.post(new Runnable() { 698 @Override 699 public void run() { 700 holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 701 } 702 }); 703 704 boolean res = ctsActivity.waitForSurfaceSizeChanged( 705 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(), 706 mPreviewSize.getHeight()); 707 assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res); 708 mPreviewHolder = holder; 709 mPreviewSurface = holder.getSurface(); 710 assertNotNull("Preview surface is null", mPreviewSurface); 711 assertTrue("Preview surface is invalid", mPreviewSurface.isValid()); 712 } 713 714 /** 715 * Recreate the SurfaceView's Surface 716 * 717 * Hide and unhide the activity's preview SurfaceView, so that its backing Surface is 718 * recreated 719 */ recreatePreviewSurface()720 protected void recreatePreviewSurface() { 721 Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity(); 722 setPreviewVisibility(View.GONE); 723 boolean res = ctsActivity.waitForSurfaceState( 724 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ false); 725 assertTrue("wait for surface destroyed timed out", res); 726 setPreviewVisibility(View.VISIBLE); 727 res = ctsActivity.waitForSurfaceState( 728 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ true); 729 assertTrue("wait for surface created timed out", res); 730 } 731 732 /** 733 * Show/hide the preview SurfaceView. 734 * 735 * If set to View.GONE, the surfaceDestroyed callback will fire 736 * @param visibility the new new visibility to set, one of View.VISIBLE / INVISIBLE / GONE 737 */ setPreviewVisibility(int visibility)738 protected void setPreviewVisibility(int visibility) { 739 final Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity(); 740 Handler handler = new Handler(Looper.getMainLooper()); 741 handler.post(new Runnable() { 742 @Override 743 public void run() { 744 ctsActivity.getSurfaceView().setVisibility(visibility); 745 } 746 }); 747 } 748 749 /** 750 * Setup single capture configuration and start preview. 751 * 752 * @param previewRequest The capture request to be used for preview 753 * @param stillRequest The capture request to be used for still capture 754 * @param previewSz Preview size 755 * @param captureSz Still capture size 756 * @param format The single capture image format 757 * @param resultListener Capture result listener 758 * @param maxNumImages The max number of images set to the image reader 759 * @param imageListener The single capture capture image listener 760 */ prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)761 protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 762 CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, 763 CaptureCallback resultListener, int maxNumImages, 764 ImageReader.OnImageAvailableListener imageListener) throws Exception { 765 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, captureSz, 766 format, resultListener, null, maxNumImages, imageListener); 767 } 768 769 /** 770 * Setup single capture configuration and start preview. 771 * 772 * @param previewRequest The capture request to be used for preview 773 * @param stillRequest The capture request to be used for still capture 774 * @param previewSz Preview size 775 * @param captureSz Still capture size 776 * @param format The single capture image format 777 * @param resultListener Capture result listener 778 * @param sessionListener Session listener 779 * @param maxNumImages The max number of images set to the image reader 780 * @param imageListener The single capture capture image listener 781 */ prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)782 protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 783 CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, 784 CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, 785 int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception { 786 if (VERBOSE) { 787 Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)", 788 captureSz.toString(), previewSz.toString())); 789 } 790 791 // Update preview size. 792 updatePreviewSurface(previewSz); 793 794 // Create ImageReader. 795 createImageReader(captureSz, format, maxNumImages, imageListener); 796 797 // Configure output streams with preview and jpeg streams. 798 List<Surface> outputSurfaces = new ArrayList<Surface>(); 799 outputSurfaces.add(mPreviewSurface); 800 outputSurfaces.add(mReaderSurface); 801 if (sessionListener == null) { 802 mSessionListener = new BlockingSessionCallback(); 803 } else { 804 mSessionListener = new BlockingSessionCallback(sessionListener); 805 } 806 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 807 808 // Configure the requests. 809 previewRequest.addTarget(mPreviewSurface); 810 stillRequest.addTarget(mPreviewSurface); 811 stillRequest.addTarget(mReaderSurface); 812 813 // Start preview. 814 mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); 815 } 816 817 /** 818 * Get the max preview size that supports the given fpsRange. 819 * 820 * @param fpsRange The fps range the returned size must support. 821 * @return max size that support the given fps range. 822 */ getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange)823 protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) { 824 if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) { 825 throw new IllegalArgumentException("Invalid fps range argument"); 826 } 827 if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) { 828 throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap" 829 + " must be initialized"); 830 } 831 832 long[] frameDurationRange = 833 new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())}; 834 for (Size size : mOrderedPreviewSizes) { 835 Long minDuration = mMinPreviewFrameDurationMap.get(size); 836 if (minDuration == null || 837 minDuration == 0) { 838 if (mStaticInfo.isCapabilitySupported( 839 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 840 throw new IllegalArgumentException( 841 "No min frame duration available for the size " + size); 842 } 843 continue; 844 } 845 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 846 return size; 847 } 848 } 849 850 // Search again for sizes not bounded by display size 851 for (Size size : m1080pBoundedOrderedPreviewSizes) { 852 Long minDuration = mMinPreviewFrameDurationMap.get(size); 853 if (minDuration == null || 854 minDuration == 0) { 855 if (mStaticInfo.isCapabilitySupported( 856 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 857 throw new IllegalArgumentException( 858 "No min frame duration available for the size " + size); 859 } 860 continue; 861 } 862 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 863 return size; 864 } 865 } 866 return null; 867 } 868 isReprocessSupported(String cameraId, int format)869 protected boolean isReprocessSupported(String cameraId, int format) 870 throws CameraAccessException { 871 if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) { 872 throw new IllegalArgumentException( 873 "format " + format + " is not supported for reprocessing"); 874 } 875 876 StaticMetadata info = 877 new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 878 CheckLevel.ASSERT, /*collector*/ null); 879 int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING; 880 if (format == ImageFormat.PRIVATE) { 881 cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; 882 } 883 return info.isCapabilitySupported(cap); 884 } 885 getSuitableFpsRangeForDuration(String cameraId, long frameDuration)886 protected Range<Integer> getSuitableFpsRangeForDuration(String cameraId, long frameDuration) { 887 return CameraTestUtils.getSuitableFpsRangeForDuration(cameraId, frameDuration, mStaticInfo); 888 } 889 captureRequestsSynchronizedImpl( CaptureRequest request, int count, CaptureCallback listener, Handler handler, boolean isBurst)890 private int captureRequestsSynchronizedImpl( 891 CaptureRequest request, int count, CaptureCallback listener, Handler handler, 892 boolean isBurst) throws CameraAccessException { 893 if (count < 1) { 894 throw new IllegalArgumentException("count must be positive"); 895 } 896 897 int maxLatency = mStaticInfo.getSyncMaxLatency(); 898 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 899 maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY; 900 } 901 902 assertTrue("maxLatency is non-negative", maxLatency >= 0); 903 904 int numCaptures = maxLatency + count; 905 ArrayList<CaptureRequest> burstCaptureRequests = new ArrayList<>(); 906 for (int i = 0; i < numCaptures; ++i) { 907 if (isBurst) { 908 burstCaptureRequests.add(request); 909 } else { 910 mSession.capture(request, listener, handler); 911 } 912 } 913 if (isBurst) { 914 mSession.captureBurst(burstCaptureRequests, listener, handler); 915 } 916 917 return numCaptures; 918 } 919 } 920