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