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