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 // Teardown the camera preview required environments. 148 mHandlerThread.quitSafely(); 149 mHandler = null; 150 mCameraListener = null; 151 152 try { 153 mCollector.verify(); 154 } catch (Throwable e) { 155 // When new Exception(e) is used, exception info will be printed twice. 156 throw new Exception(e.getMessage()); 157 } finally { 158 super.tearDown(); 159 } 160 } 161 162 /** 163 * Start camera preview by using the given request, preview size and capture 164 * listener. 165 * <p> 166 * If preview is already started, calling this function will stop the 167 * current preview stream and start a new preview stream with given 168 * parameters. No need to call stopPreview between two startPreview calls. 169 * </p> 170 * 171 * @param request The request builder used to start the preview. 172 * @param previewSz The size of the camera device output preview stream. 173 * @param listener The callbacks the camera device will notify when preview 174 * capture is available. 175 */ startPreview(CaptureRequest.Builder request, Size previewSz, CaptureCallback listener)176 protected void startPreview(CaptureRequest.Builder request, Size previewSz, 177 CaptureCallback listener) throws Exception { 178 // Update preview size. 179 updatePreviewSurface(previewSz); 180 if (VERBOSE) { 181 Log.v(TAG, "start preview with size " + mPreviewSize.toString()); 182 } 183 184 configurePreviewOutput(request); 185 186 mSession.setRepeatingRequest(request.build(), listener, mHandler); 187 } 188 189 /** 190 * Configure the preview output stream. 191 * 192 * @param request The request to be configured with preview surface 193 */ configurePreviewOutput(CaptureRequest.Builder request)194 protected void configurePreviewOutput(CaptureRequest.Builder request) 195 throws CameraAccessException { 196 List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1); 197 outputSurfaces.add(mPreviewSurface); 198 mSessionListener = new BlockingSessionCallback(); 199 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 200 201 request.addTarget(mPreviewSurface); 202 } 203 204 /** 205 * Create a {@link CaptureRequest#Builder} and add the default preview surface. 206 * 207 * @return The {@link CaptureRequest#Builder} to be created 208 * @throws CameraAccessException When create capture request from camera fails 209 */ createRequestForPreview()210 protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException { 211 if (mPreviewSurface == null) { 212 throw new IllegalStateException( 213 "Preview surface is not set yet, call updatePreviewSurface or startPreview" 214 + "first to set the preview surface properly."); 215 } 216 CaptureRequest.Builder requestBuilder = 217 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 218 requestBuilder.addTarget(mPreviewSurface); 219 return requestBuilder; 220 } 221 222 /** 223 * Stop preview for current camera device by closing the session. 224 * Does _not_ wait for the device to go idle 225 */ stopPreview()226 protected void stopPreview() throws Exception { 227 // Stop repeat, wait for captures to complete, and disconnect from surfaces 228 if (mSession != null) { 229 if (VERBOSE) Log.v(TAG, "Stopping preview"); 230 mSession.close(); 231 } 232 } 233 234 /** 235 * Stop preview for current camera device by closing the session and waiting for it to close, 236 * resulting in an idle device. 237 */ stopPreviewAndDrain()238 protected void stopPreviewAndDrain() throws Exception { 239 // Stop repeat, wait for captures to complete, and disconnect from surfaces 240 if (mSession != null) { 241 if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle"); 242 mSession.close(); 243 mSessionListener.getStateWaiter().waitForState(BlockingSessionCallback.SESSION_CLOSED, 244 /*timeoutMs*/WAIT_FOR_RESULT_TIMEOUT_MS); 245 } 246 } 247 248 /** 249 * Setup still (JPEG) capture configuration and start preview. 250 * <p> 251 * The default max number of image is set to image reader. 252 * </p> 253 * 254 * @param previewRequest The capture request to be used for preview 255 * @param stillRequest The capture request to be used for still capture 256 * @param previewSz Preview size 257 * @param stillSz The still capture size 258 * @param resultListener Capture result listener 259 * @param imageListener The still capture image listener 260 */ prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)261 protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 262 CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, 263 CaptureCallback resultListener, 264 ImageReader.OnImageAvailableListener imageListener) throws Exception { 265 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, 266 ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, imageListener); 267 } 268 269 /** 270 * Setup still (JPEG) capture configuration and start preview. 271 * 272 * @param previewRequest The capture request to be used for preview 273 * @param stillRequest The capture request to be used for still capture 274 * @param previewSz Preview size 275 * @param stillSz The still capture size 276 * @param resultListener Capture result listener 277 * @param maxNumImages The max number of images set to the image reader 278 * @param imageListener The still capture image listener 279 */ prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)280 protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 281 CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, 282 CaptureCallback resultListener, int maxNumImages, 283 ImageReader.OnImageAvailableListener imageListener) throws Exception { 284 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, 285 ImageFormat.JPEG, resultListener, maxNumImages, imageListener); 286 } 287 288 /** 289 * Setup raw capture configuration and start preview. 290 * 291 * <p> 292 * The default max number of image is set to image reader. 293 * </p> 294 * 295 * @param previewRequest The capture request to be used for preview 296 * @param rawRequest The capture request to be used for raw capture 297 * @param previewSz Preview size 298 * @param rawSz The raw capture size 299 * @param resultListener Capture result listener 300 * @param imageListener The raw capture image listener 301 */ prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)302 protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 303 CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, 304 CaptureCallback resultListener, 305 ImageReader.OnImageAvailableListener imageListener) throws Exception { 306 prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz, 307 ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener); 308 } 309 310 /** 311 * Wait for expected result key value available in a certain number of results. 312 * 313 * <p> 314 * Check the result immediately if numFramesWait is 0. 315 * </p> 316 * 317 * @param listener The capture listener to get capture result 318 * @param resultKey The capture result key associated with the result value 319 * @param expectedValue The result value need to be waited for 320 * @param numResultsWait Number of frame to wait before times out 321 * @throws TimeoutRuntimeException If more than numResultsWait results are 322 * seen before the result matching myRequest arrives, or each individual wait 323 * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. 324 */ waitForResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, T expectedValue, int numResultsWait)325 protected static <T> void waitForResultValue(SimpleCaptureCallback listener, 326 CaptureResult.Key<T> resultKey, 327 T expectedValue, int numResultsWait) { 328 List<T> expectedValues = new ArrayList<T>(); 329 expectedValues.add(expectedValue); 330 waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait); 331 } 332 333 /** 334 * Wait for any expected result key values available in a certain number of results. 335 * 336 * <p> 337 * Check the result immediately if numFramesWait is 0. 338 * </p> 339 * 340 * @param listener The capture listener to get capture result. 341 * @param resultKey The capture result key associated with the result value. 342 * @param expectedValues The list of result value need to be waited for, 343 * return immediately if the list is empty. 344 * @param numResultsWait Number of frame to wait before times out. 345 * @throws TimeoutRuntimeException If more than numResultsWait results are. 346 * seen before the result matching myRequest arrives, or each individual wait 347 * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. 348 */ waitForAnyResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, List<T> expectedValues, int numResultsWait)349 protected static <T> void waitForAnyResultValue(SimpleCaptureCallback listener, 350 CaptureResult.Key<T> resultKey, 351 List<T> expectedValues, int numResultsWait) { 352 if (numResultsWait < 0 || listener == null || expectedValues == null) { 353 throw new IllegalArgumentException( 354 "Input must be non-negative number and listener/expectedValues " 355 + "must be non-null"); 356 } 357 358 int i = 0; 359 CaptureResult result; 360 do { 361 result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); 362 T value = result.get(resultKey); 363 for ( T expectedValue : expectedValues) { 364 if (VERBOSE) { 365 Log.v(TAG, "Current result value for key " + resultKey.getName() + " is: " 366 + value.toString()); 367 } 368 if (value.equals(expectedValue)) { 369 return; 370 } 371 } 372 } while (i++ < numResultsWait); 373 374 throw new TimeoutRuntimeException( 375 "Unable to get the expected result value " + expectedValues + " for key " + 376 resultKey.getName() + " after waiting for " + numResultsWait + " results"); 377 } 378 379 /** 380 * Submit a capture once, then submit additional captures in order to ensure that 381 * the camera will be synchronized. 382 * 383 * <p> 384 * The additional capture count is determined by android.sync.maxLatency (or 385 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 386 * </p> 387 * 388 * <p>Returns the number of captures that were submitted (at least 1), which is useful 389 * with {@link #waitForNumResults}.</p> 390 * 391 * @param request capture request to forward to {@link CameraDevice#capture} 392 * @param listener request listener to forward to {@link CameraDevice#capture} 393 * @param handler handler to forward to {@link CameraDevice#capture} 394 * 395 * @return the number of captures that were submitted 396 * 397 * @throws CameraAccessException if capturing failed 398 */ captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler)399 protected int captureRequestsSynchronized( 400 CaptureRequest request, CaptureCallback listener, Handler handler) 401 throws CameraAccessException { 402 return captureRequestsSynchronized(request, /*count*/1, listener, handler); 403 } 404 405 /** 406 * Submit a capture {@code count} times, then submit additional captures in order to ensure that 407 * the camera will be synchronized. 408 * 409 * <p> 410 * The additional capture count is determined by android.sync.maxLatency (or 411 * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). 412 * </p> 413 * 414 * <p>Returns the number of captures that were submitted (at least 1), which is useful 415 * with {@link #waitForNumResults}.</p> 416 * 417 * @param request capture request to forward to {@link CameraDevice#capture} 418 * @param count the number of times to submit the request (minimally), must be at least 1 419 * @param listener request listener to forward to {@link CameraDevice#capture} 420 * @param handler handler to forward to {@link CameraDevice#capture} 421 * 422 * @return the number of captures that were submitted 423 * 424 * @throws IllegalArgumentException if {@code count} was not at least 1 425 * @throws CameraAccessException if capturing failed 426 */ captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler)427 protected int captureRequestsSynchronized( 428 CaptureRequest request, int count, CaptureCallback listener, Handler handler) 429 throws CameraAccessException { 430 if (count < 1) { 431 throw new IllegalArgumentException("count must be positive"); 432 } 433 434 int maxLatency = mStaticInfo.getSyncMaxLatency(); 435 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 436 maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY; 437 } 438 439 assertTrue("maxLatency is non-negative", maxLatency >= 0); 440 441 int numCaptures = maxLatency + count; 442 443 for (int i = 0; i < numCaptures; ++i) { 444 mSession.capture(request, listener, handler); 445 } 446 447 return numCaptures; 448 } 449 450 /** 451 * Wait for numResultWait frames 452 * 453 * @param resultListener The capture listener to get capture result back. 454 * @param numResultsWait Number of frame to wait 455 * 456 * @return the last result, or {@code null} if there was none 457 */ waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait)458 protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener, 459 int numResultsWait) { 460 if (numResultsWait < 0 || resultListener == null) { 461 throw new IllegalArgumentException( 462 "Input must be positive number and listener must be non-null"); 463 } 464 465 CaptureResult result = null; 466 for (int i = 0; i < numResultsWait; i++) { 467 result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); 468 } 469 470 return result; 471 } 472 473 /** 474 * Wait for enough results for settings to be applied 475 * 476 * @param resultListener The capture listener to get capture result back. 477 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 478 * unknown. 479 */ waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)480 protected void waitForSettingsApplied(SimpleCaptureCallback resultListener, 481 int numResultWaitForUnknownLatency) { 482 int maxLatency = mStaticInfo.getSyncMaxLatency(); 483 if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { 484 maxLatency = numResultWaitForUnknownLatency; 485 } 486 // Wait for settings to take effect 487 waitForNumResults(resultListener, maxLatency); 488 } 489 490 491 /** 492 * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED. 493 * 494 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 495 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 496 * is unknown.</p> 497 * 498 * <p>This is a no-op for {@code LEGACY} devices since they don't report 499 * the {@code aeState} result.</p> 500 * 501 * @param resultListener The capture listener to get capture result back. 502 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 503 * unknown. 504 */ waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)505 protected void waitForAeStable(SimpleCaptureCallback resultListener, 506 int numResultWaitForUnknownLatency) { 507 waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); 508 509 if (!mStaticInfo.isHardwareLevelAtLeastLimited()) { 510 // No-op for metadata 511 return; 512 } 513 List<Integer> expectedAeStates = new ArrayList<Integer>(); 514 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_CONVERGED)); 515 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED)); 516 waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates, 517 NUM_RESULTS_WAIT_TIMEOUT); 518 } 519 520 /** 521 * Wait for AE to be: LOCKED 522 * 523 * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure 524 * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency 525 * is unknown.</p> 526 * 527 * <p>This is a no-op for {@code LEGACY} devices since they don't report 528 * the {@code aeState} result.</p> 529 * 530 * @param resultListener The capture listener to get capture result back. 531 * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is 532 * unknown. 533 */ waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)534 protected void waitForAeLocked(SimpleCaptureCallback resultListener, 535 int numResultWaitForUnknownLatency) { 536 537 waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); 538 539 if (!mStaticInfo.isHardwareLevelAtLeastLimited()) { 540 // No-op for legacy devices 541 return; 542 } 543 544 List<Integer> expectedAeStates = new ArrayList<Integer>(); 545 expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED)); 546 waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates, 547 NUM_RESULTS_WAIT_TIMEOUT); 548 } 549 550 /** 551 * Create an {@link ImageReader} object and get the surface. 552 * 553 * @param size The size of this ImageReader to be created. 554 * @param format The format of this ImageReader to be created 555 * @param maxNumImages The max number of images that can be acquired simultaneously. 556 * @param listener The listener used by this ImageReader to notify callbacks. 557 */ createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)558 protected void createImageReader(Size size, int format, int maxNumImages, 559 ImageReader.OnImageAvailableListener listener) throws Exception { 560 closeImageReader(); 561 562 ImageReader r = makeImageReader(size, format, maxNumImages, listener, 563 mHandler); 564 mReader = r; 565 mReaderSurface = r.getSurface(); 566 } 567 568 /** 569 * Close the pending images then close current active {@link ImageReader} object. 570 */ closeImageReader()571 protected void closeImageReader() { 572 CameraTestUtils.closeImageReader(mReader); 573 mReader = null; 574 mReaderSurface = null; 575 } 576 577 /** 578 * Open a camera device and get the StaticMetadata for a given camera id. 579 * 580 * @param cameraId The id of the camera device to be opened. 581 */ openDevice(String cameraId)582 protected void openDevice(String cameraId) throws Exception { 583 mCamera = CameraTestUtils.openCamera( 584 mCameraManager, cameraId, mCameraListener, mHandler); 585 mCollector.setCameraId(cameraId); 586 mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 587 CheckLevel.ASSERT, /*collector*/null); 588 if (mStaticInfo.isColorOutputSupported()) { 589 mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 590 getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND)); 591 m1080pBoundedOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, 592 PREVIEW_SIZE_BOUND); 593 mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND); 594 mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null); 595 // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview 596 // in public API side. 597 mMinPreviewFrameDurationMap = 598 mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888); 599 } 600 } 601 602 /** 603 * Close the current actively used camera device. 604 */ closeDevice()605 protected void closeDevice() { 606 if (mCamera != null) { 607 mCamera.close(); 608 mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS); 609 mCamera = null; 610 mSession = null; 611 mSessionListener = null; 612 mStaticInfo = null; 613 mOrderedPreviewSizes = null; 614 mOrderedVideoSizes = null; 615 mOrderedStillSizes = null; 616 } 617 } 618 619 /** 620 * Update the preview surface size. 621 * 622 * @param size The preview size to be updated. 623 */ updatePreviewSurface(Size size)624 protected void updatePreviewSurface(Size size) { 625 if (size.equals(mPreviewSize) && mPreviewSurface != null) { 626 Log.w(TAG, "Skipping update preview surface size..."); 627 return; 628 } 629 630 mPreviewSize = size; 631 Camera2SurfaceViewCtsActivity ctsActivity = getActivity(); 632 final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder(); 633 Handler handler = new Handler(Looper.getMainLooper()); 634 handler.post(new Runnable() { 635 @Override 636 public void run() { 637 holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 638 } 639 }); 640 641 boolean res = ctsActivity.waitForSurfaceSizeChanged( 642 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(), 643 mPreviewSize.getHeight()); 644 assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res); 645 mPreviewHolder = holder; 646 mPreviewSurface = holder.getSurface(); 647 assertNotNull("Preview surface is null", mPreviewSurface); 648 assertTrue("Preview surface is invalid", mPreviewSurface.isValid()); 649 } 650 651 /** 652 * Recreate the SurfaceView's Surface 653 * 654 * Hide and unhide the activity's preview SurfaceView, so that its backing Surface is 655 * recreated 656 */ recreatePreviewSurface()657 protected void recreatePreviewSurface() { 658 Camera2SurfaceViewCtsActivity ctsActivity = getActivity(); 659 setPreviewVisibility(View.GONE); 660 boolean res = ctsActivity.waitForSurfaceState( 661 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ false); 662 assertTrue("wait for surface destroyed timed out", res); 663 setPreviewVisibility(View.VISIBLE); 664 res = ctsActivity.waitForSurfaceState( 665 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ true); 666 assertTrue("wait for surface created timed out", res); 667 } 668 669 /** 670 * Show/hide the preview SurfaceView. 671 * 672 * If set to View.GONE, the surfaceDestroyed callback will fire 673 * @param visibility the new new visibility to set, one of View.VISIBLE / INVISIBLE / GONE 674 */ setPreviewVisibility(int visibility)675 protected void setPreviewVisibility(int visibility) { 676 final Camera2SurfaceViewCtsActivity ctsActivity = getActivity(); 677 Handler handler = new Handler(Looper.getMainLooper()); 678 handler.post(new Runnable() { 679 @Override 680 public void run() { 681 ctsActivity.getSurfaceView().setVisibility(visibility); 682 } 683 }); 684 } 685 686 /** 687 * Setup single capture configuration and start preview. 688 * 689 * @param previewRequest The capture request to be used for preview 690 * @param stillRequest The capture request to be used for still capture 691 * @param previewSz Preview size 692 * @param captureSz Still capture size 693 * @param format The single capture image format 694 * @param resultListener Capture result listener 695 * @param maxNumImages The max number of images set to the image reader 696 * @param imageListener The single capture capture image listener 697 */ prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)698 protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 699 CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, 700 CaptureCallback resultListener, int maxNumImages, 701 ImageReader.OnImageAvailableListener imageListener) throws Exception { 702 prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, captureSz, 703 format, resultListener, null, maxNumImages, imageListener); 704 } 705 706 /** 707 * Setup single capture configuration and start preview. 708 * 709 * @param previewRequest The capture request to be used for preview 710 * @param stillRequest The capture request to be used for still capture 711 * @param previewSz Preview size 712 * @param captureSz Still capture size 713 * @param format The single capture image format 714 * @param resultListener Capture result listener 715 * @param sessionListener Session listener 716 * @param maxNumImages The max number of images set to the image reader 717 * @param imageListener The single capture capture image listener 718 */ prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)719 protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, 720 CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, 721 CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, 722 int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception { 723 if (VERBOSE) { 724 Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)", 725 captureSz.toString(), previewSz.toString())); 726 } 727 728 // Update preview size. 729 updatePreviewSurface(previewSz); 730 731 // Create ImageReader. 732 createImageReader(captureSz, format, maxNumImages, imageListener); 733 734 // Configure output streams with preview and jpeg streams. 735 List<Surface> outputSurfaces = new ArrayList<Surface>(); 736 outputSurfaces.add(mPreviewSurface); 737 outputSurfaces.add(mReaderSurface); 738 if (sessionListener == null) { 739 mSessionListener = new BlockingSessionCallback(); 740 } else { 741 mSessionListener = new BlockingSessionCallback(sessionListener); 742 } 743 mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); 744 745 // Configure the requests. 746 previewRequest.addTarget(mPreviewSurface); 747 stillRequest.addTarget(mPreviewSurface); 748 stillRequest.addTarget(mReaderSurface); 749 750 // Start preview. 751 mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); 752 } 753 754 /** 755 * Get the max preview size that supports the given fpsRange. 756 * 757 * @param fpsRange The fps range the returned size must support. 758 * @return max size that support the given fps range. 759 */ getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange)760 protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) { 761 if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) { 762 throw new IllegalArgumentException("Invalid fps range argument"); 763 } 764 if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) { 765 throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap" 766 + " must be initialized"); 767 } 768 769 long[] frameDurationRange = 770 new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())}; 771 for (Size size : mOrderedPreviewSizes) { 772 Long minDuration = mMinPreviewFrameDurationMap.get(size); 773 if (minDuration == null || 774 minDuration == 0) { 775 if (mStaticInfo.isCapabilitySupported( 776 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 777 throw new IllegalArgumentException( 778 "No min frame duration available for the size " + size); 779 } 780 continue; 781 } 782 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 783 return size; 784 } 785 } 786 787 // Search again for sizes not bounded by display size 788 for (Size size : m1080pBoundedOrderedPreviewSizes) { 789 Long minDuration = mMinPreviewFrameDurationMap.get(size); 790 if (minDuration == null || 791 minDuration == 0) { 792 if (mStaticInfo.isCapabilitySupported( 793 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 794 throw new IllegalArgumentException( 795 "No min frame duration available for the size " + size); 796 } 797 continue; 798 } 799 if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { 800 return size; 801 } 802 } 803 return null; 804 } 805 isReprocessSupported(String cameraId, int format)806 protected boolean isReprocessSupported(String cameraId, int format) 807 throws CameraAccessException { 808 if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) { 809 throw new IllegalArgumentException( 810 "format " + format + " is not supported for reprocessing"); 811 } 812 813 StaticMetadata info = 814 new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), 815 CheckLevel.ASSERT, /*collector*/ null); 816 int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING; 817 if (format == ImageFormat.PRIVATE) { 818 cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; 819 } 820 return info.isCapabilitySupported(cap); 821 } 822 getSuitableFpsRangeForDuration(String cameraId, long frameDuration)823 protected Range<Integer> getSuitableFpsRangeForDuration(String cameraId, long frameDuration) { 824 // Add 0.05 here so Fps like 29.99 evaluated to 30 825 int minBurstFps = (int) Math.floor(1e9 / frameDuration + 0.05f); 826 boolean foundConstantMaxYUVRange = false; 827 boolean foundYUVStreamingRange = false; 828 829 // Find suitable target FPS range - as high as possible that covers the max YUV rate 830 // Also verify that there's a good preview rate as well 831 List<Range<Integer> > fpsRanges = Arrays.asList( 832 mStaticInfo.getAeAvailableTargetFpsRangesChecked()); 833 Range<Integer> targetRange = null; 834 for (Range<Integer> fpsRange : fpsRanges) { 835 if (fpsRange.getLower() == minBurstFps && fpsRange.getUpper() == minBurstFps) { 836 foundConstantMaxYUVRange = true; 837 targetRange = fpsRange; 838 } 839 if (fpsRange.getLower() <= 15 && fpsRange.getUpper() == minBurstFps) { 840 foundYUVStreamingRange = true; 841 } 842 } 843 844 assertTrue(String.format("Cam %s: Target FPS range of (%d, %d) must be supported", 845 cameraId, minBurstFps, minBurstFps), foundConstantMaxYUVRange); 846 assertTrue(String.format( 847 "Cam %s: Target FPS range of (x, %d) where x <= 15 must be supported", 848 cameraId, minBurstFps), foundYUVStreamingRange); 849 return targetRange; 850 } 851 } 852