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