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 capture once, then submit additional captures in order to ensure that 389 * 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 */ captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler)407 protected int captureRequestsSynchronized( 408 CaptureRequest request, CaptureCallback listener, Handler handler) 409 throws CameraAccessException { 410 return captureRequestsSynchronized(request, /*count*/1, listener, handler); 411 } 412 413 /** 414 * Submit a capture {@code count} times, then submit additional captures in order to ensure that 415 * the camera will be synchronized. 416 * 417 * <p> 418 * The additional capture count is determined by android.sync.maxLatency (or 419 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 420 * </p> 421 * 422 * <p>Returns the number of captures that were submitted (at least 1), which is useful 423 * with {@link #waitForNumResults}.</p> 424 * 425 * @param request capture request to forward to {@link CameraDevice#capture} 426 * @param count the number of times to submit the request (minimally), must be at least 1 427 * @param listener request listener to forward to {@link CameraDevice#capture} 428 * @param handler handler to forward to {@link CameraDevice#capture} 429 * 430 * @return the number of captures that were submitted 431 * 432 * @throws IllegalArgumentException if {@code count} was not at least 1 433 * @throws CameraAccessException if capturing failed 434 */ captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler)435 protected int captureRequestsSynchronized( 436 CaptureRequest request, int count, CaptureCallback listener, Handler handler) 437 throws CameraAccessException { 438 if (count < 1) { 439 throw new IllegalArgumentException("count must be positive"); 440 } 441 442 int maxLatency = mStaticInfo.getSyncMaxLatency(); 443 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 444 maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY; 445 } 446 447 assertTrue("maxLatency is non-negative", maxLatency >= 0); 448 449 int numCaptures = maxLatency + count; 450 451 for (int i = 0; i < numCaptures; ++i) { 452 mSession.capture(request, listener, handler); 453 } 454 455 return numCaptures; 456 } 457 458 /** 459 * Wait for numResultWait frames 460 * 461 * @param resultListener The capture listener to get capture result back. 462 * @param numResultsWait Number of frame to wait 463 * 464 * @return the last result, or {@code null} if there was none 465 */ waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait)466 protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener, 467 int numResultsWait) { 468 return CameraTestUtils.waitForNumResults(resultListener, numResultsWait, 469 WAIT_FOR_RESULT_TIMEOUT_MS); 470 } 471 472 /** 473 * Wait for enough results for settings to be applied 474 * 475 * @param resultListener The capture listener to get capture result back. 476 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 477 * unknown. 478 */ waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)479 protected void waitForSettingsApplied(SimpleCaptureCallback resultListener, 480 int numResultWaitForUnknownLatency) { 481 int maxLatency = mStaticInfo.getSyncMaxLatency(); 482 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 483 maxLatency = numResultWaitForUnknownLatency; 484 } 485 // Wait for settings to take effect 486 waitForNumResults(resultListener, maxLatency); 487 } 488 489 /** 490 * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED. 491 * 492 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 493 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 494 * is unknown.</p> 495 * 496 * <p>This is a no-op for {@code LEGACY} devices since they don't report 497 * the {@code aeState} result.</p> 498 * 499 * @param resultListener The capture listener to get capture result back. 500 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 501 * unknown. 502 */ waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)503 protected void waitForAeStable(SimpleCaptureCallback resultListener, 504 int numResultWaitForUnknownLatency) { 505 CameraTestUtils.waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY, 506 mStaticInfo, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT); 507 } 508 509 /** 510 * Wait for AE to be: LOCKED 511 * 512 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 513 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 514 * is unknown.</p> 515 * 516 * <p>This is a no-op for {@code LEGACY} devices since they don't report 517 * the {@code aeState} result.</p> 518 * 519 * @param resultListener The capture listener to get capture result back. 520 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 521 * unknown. 522 */ waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)523 protected void waitForAeLocked(SimpleCaptureCallback resultListener, 524 int numResultWaitForUnknownLatency) { 525 526 waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); 527 528 if (!mStaticInfo.isHardwareLevelAtLeastLimited()) { 529 // No-op for legacy devices 530 return; 531 } 532 533 List<Integer> expectedAeStates = new ArrayList<Integer>(); 534 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED)); 535 CameraTestUtils.waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, 536 expectedAeStates, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT); 537 } 538 539 /** 540 * Create an {@link ImageReader} object and get the surface. 541 * 542 * @param size The size of this ImageReader to be created. 543 * @param format The format of this ImageReader to be created 544 * @param maxNumImages The max number of images that can be acquired simultaneously. 545 * @param listener The listener used by this ImageReader to notify callbacks. 546 */ createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)547 protected void createImageReader(Size size, int format, int maxNumImages, 548 ImageReader.OnImageAvailableListener listener) throws Exception { 549 closeImageReader(); 550 551 ImageReader r = makeImageReader(size, format, maxNumImages, listener, 552 mHandler); 553 mReader = r; 554 mReaderSurface = r.getSurface(); 555 } 556 557 /** 558 * Close the pending images then close current active {@link ImageReader} object. 559 */ closeImageReader()560 protected void closeImageReader() { 561 CameraTestUtils.closeImageReader(mReader); 562 mReader = null; 563 mReaderSurface = null; 564 } 565 566 /** 567 * Close the pending images then close current active {@link ImageReader} objects. 568 */ closeImageReaders(ImageReader[] readers)569 protected void closeImageReaders(ImageReader[] readers) { 570 CameraTestUtils.closeImageReaders(readers); 571 } 572 573 /** 574 * Setup still capture configuration and start preview. 575 * 576 * @param previewRequest The capture request to be used for preview 577 * @param stillRequest The capture request to be used for still capture 578 * @param previewSz Preview size 579 * @param captureSizes Still capture sizes 580 * @param formats The single capture image formats 581 * @param resultListener Capture result listener 582 * @param maxNumImages The max number of images set to the image reader 583 * @param imageListeners The single capture capture image listeners 584 * @param isHeic HEIC still capture if true, JPEG still capture if false 585 */ prepareStillCaptureAndStartPreview( CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners, boolean isHeic)586 protected ImageReader[] prepareStillCaptureAndStartPreview( 587 CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, 588 Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener, 589 int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners, 590 boolean isHeic) 591 throws Exception { 592 593 if ((captureSizes == null) || (formats == null) || (imageListeners == null) && 594 (captureSizes.length != formats.length) || 595 (formats.length != imageListeners.length)) { 596 throw new IllegalArgumentException("Invalid capture sizes/formats or image listeners!"); 597 } 598 599 if (VERBOSE) { 600 Log.v(TAG, String.format("Prepare still capture and preview (%s)", 601 previewSz.toString())); 602 } 603 604 // Update preview size. 605 updatePreviewSurface(previewSz); 606 607 ImageReader[] readers = new ImageReader[captureSizes.length]; 608 List<Surface> outputSurfaces = new ArrayList<Surface>(); 609 outputSurfaces.add(mPreviewSurface); 610 for (int i = 0; i < captureSizes.length; i++) { 611 readers[i] = makeImageReader(captureSizes[i], formats[i], maxNumImages, 612 imageListeners[i], mHandler); 613 outputSurfaces.add(readers[i].getSurface()); 614 } 615 616 mSessionListener = new BlockingSessionCallback(); 617 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 618 619 // Configure the requests. 620 previewRequest.addTarget(mPreviewSurface); 621 stillRequest.addTarget(mPreviewSurface); 622 for (int i = 0; i < readers.length; i++) { 623 stillRequest.addTarget(readers[i].getSurface()); 624 } 625 626 // Start preview. 627 mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); 628 629 return readers; 630 } 631 632 /** 633 * Open a camera device and get the StaticMetadata for a given camera id. 634 * 635 * @param cameraId The id of the camera device to be opened. 636 */ openDevice(String cameraId)637 protected void openDevice(String cameraId) throws Exception { 638 mCamera = CameraTestUtils.openCamera( 639 mCameraManager, cameraId, mCameraListener, mHandler); 640 mCollector.setCameraId(cameraId); 641 mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 642 CheckLevel.ASSERT, /*collector*/null); 643 if (mStaticInfo.isColorOutputSupported()) { 644 mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 645 getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND)); 646 m1080pBoundedOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 647 PREVIEW_SIZE_BOUND); 648 mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND); 649 mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null); 650 // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview 651 // in public API side. 652 mMinPreviewFrameDurationMap = 653 mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888); 654 } 655 } 656 657 /** 658 * Close the current actively used camera device. 659 */ closeDevice()660 protected void closeDevice() { 661 if (mCamera != null) { 662 mCamera.close(); 663 mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS); 664 mCamera = null; 665 mSession = null; 666 mSessionListener = null; 667 mStaticInfo = null; 668 mOrderedPreviewSizes = null; 669 mOrderedVideoSizes = null; 670 mOrderedStillSizes = null; 671 } 672 } 673 674 /** 675 * Update the preview surface size. 676 * 677 * @param size The preview size to be updated. 678 */ updatePreviewSurface(Size size)679 protected void updatePreviewSurface(Size size) { 680 if (size.equals(mPreviewSize) && mPreviewSurface != null) { 681 Log.w(TAG, "Skipping update preview surface size..."); 682 return; 683 } 684 685 mPreviewSize = size; 686 Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity(); 687 final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder(); 688 Handler handler = new Handler(Looper.getMainLooper()); 689 handler.post(new Runnable() { 690 @Override 691 public void run() { 692 holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 693 } 694 }); 695 696 boolean res = ctsActivity.waitForSurfaceSizeChanged( 697 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(), 698 mPreviewSize.getHeight()); 699 assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res); 700 mPreviewHolder = holder; 701 mPreviewSurface = holder.getSurface(); 702 assertNotNull("Preview surface is null", mPreviewSurface); 703 assertTrue("Preview surface is invalid", mPreviewSurface.isValid()); 704 } 705 706 /** 707 * Recreate the SurfaceView's Surface 708 * 709 * Hide and unhide the activity's preview SurfaceView, so that its backing Surface is 710 * recreated 711 */ recreatePreviewSurface()712 protected void recreatePreviewSurface() { 713 Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity(); 714 setPreviewVisibility(View.GONE); 715 boolean res = ctsActivity.waitForSurfaceState( 716 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ false); 717 assertTrue("wait for surface destroyed timed out", res); 718 setPreviewVisibility(View.VISIBLE); 719 res = ctsActivity.waitForSurfaceState( 720 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ true); 721 assertTrue("wait for surface created timed out", res); 722 } 723 724 /** 725 * Show/hide the preview SurfaceView. 726 * 727 * If set to View.GONE, the surfaceDestroyed callback will fire 728 * @param visibility the new new visibility to set, one of View.VISIBLE / INVISIBLE / GONE 729 */ setPreviewVisibility(int visibility)730 protected void setPreviewVisibility(int visibility) { 731 final Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity(); 732 Handler handler = new Handler(Looper.getMainLooper()); 733 handler.post(new Runnable() { 734 @Override 735 public void run() { 736 ctsActivity.getSurfaceView().setVisibility(visibility); 737 } 738 }); 739 } 740 741 /** 742 * Setup single capture configuration and start preview. 743 * 744 * @param previewRequest The capture request to be used for preview 745 * @param stillRequest The capture request to be used for still capture 746 * @param previewSz Preview size 747 * @param captureSz Still capture size 748 * @param format The single capture image format 749 * @param resultListener Capture result listener 750 * @param maxNumImages The max number of images set to the image reader 751 * @param imageListener The single capture capture image listener 752 */ prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)753 protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 754 CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, 755 CaptureCallback resultListener, int maxNumImages, 756 ImageReader.OnImageAvailableListener imageListener) throws Exception { 757 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, captureSz, 758 format, resultListener, null, maxNumImages, imageListener); 759 } 760 761 /** 762 * Setup single capture configuration and start preview. 763 * 764 * @param previewRequest The capture request to be used for preview 765 * @param stillRequest The capture request to be used for still capture 766 * @param previewSz Preview size 767 * @param captureSz Still capture size 768 * @param format The single capture image format 769 * @param resultListener Capture result listener 770 * @param sessionListener Session listener 771 * @param maxNumImages The max number of images set to the image reader 772 * @param imageListener The single capture capture image listener 773 */ prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)774 protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 775 CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, 776 CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, 777 int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception { 778 if (VERBOSE) { 779 Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)", 780 captureSz.toString(), previewSz.toString())); 781 } 782 783 // Update preview size. 784 updatePreviewSurface(previewSz); 785 786 // Create ImageReader. 787 createImageReader(captureSz, format, maxNumImages, imageListener); 788 789 // Configure output streams with preview and jpeg streams. 790 List<Surface> outputSurfaces = new ArrayList<Surface>(); 791 outputSurfaces.add(mPreviewSurface); 792 outputSurfaces.add(mReaderSurface); 793 if (sessionListener == null) { 794 mSessionListener = new BlockingSessionCallback(); 795 } else { 796 mSessionListener = new BlockingSessionCallback(sessionListener); 797 } 798 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 799 800 // Configure the requests. 801 previewRequest.addTarget(mPreviewSurface); 802 stillRequest.addTarget(mPreviewSurface); 803 stillRequest.addTarget(mReaderSurface); 804 805 // Start preview. 806 mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); 807 } 808 809 /** 810 * Get the max preview size that supports the given fpsRange. 811 * 812 * @param fpsRange The fps range the returned size must support. 813 * @return max size that support the given fps range. 814 */ getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange)815 protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) { 816 if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) { 817 throw new IllegalArgumentException("Invalid fps range argument"); 818 } 819 if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) { 820 throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap" 821 + " must be initialized"); 822 } 823 824 long[] frameDurationRange = 825 new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())}; 826 for (Size size : mOrderedPreviewSizes) { 827 Long minDuration = mMinPreviewFrameDurationMap.get(size); 828 if (minDuration == null || 829 minDuration == 0) { 830 if (mStaticInfo.isCapabilitySupported( 831 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 832 throw new IllegalArgumentException( 833 "No min frame duration available for the size " + size); 834 } 835 continue; 836 } 837 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 838 return size; 839 } 840 } 841 842 // Search again for sizes not bounded by display size 843 for (Size size : m1080pBoundedOrderedPreviewSizes) { 844 Long minDuration = mMinPreviewFrameDurationMap.get(size); 845 if (minDuration == null || 846 minDuration == 0) { 847 if (mStaticInfo.isCapabilitySupported( 848 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 849 throw new IllegalArgumentException( 850 "No min frame duration available for the size " + size); 851 } 852 continue; 853 } 854 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 855 return size; 856 } 857 } 858 return null; 859 } 860 isReprocessSupported(String cameraId, int format)861 protected boolean isReprocessSupported(String cameraId, int format) 862 throws CameraAccessException { 863 if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) { 864 throw new IllegalArgumentException( 865 "format " + format + " is not supported for reprocessing"); 866 } 867 868 StaticMetadata info = 869 new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 870 CheckLevel.ASSERT, /*collector*/ null); 871 int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING; 872 if (format == ImageFormat.PRIVATE) { 873 cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; 874 } 875 return info.isCapabilitySupported(cap); 876 } 877 getSuitableFpsRangeForDuration(String cameraId, long frameDuration)878 protected Range<Integer> getSuitableFpsRangeForDuration(String cameraId, long frameDuration) { 879 return CameraTestUtils.getSuitableFpsRangeForDuration(cameraId, frameDuration, mStaticInfo); 880 } 881 } 882