Home | History | Annotate | Download | only in cts
      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;
     18 
     19 import static android.hardware.camera2.cts.CameraTestUtils.*;
     20 
     21 import android.graphics.ImageFormat;
     22 import android.hardware.camera2.CameraDevice;
     23 import android.hardware.camera2.CaptureRequest;
     24 import android.hardware.camera2.CaptureResult;
     25 import android.hardware.camera2.CameraCharacteristics;
     26 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
     27 import android.hardware.camera2.params.StreamConfigurationMap;
     28 import android.media.Image;
     29 import android.platform.test.annotations.AppModeFull;
     30 import android.util.Log;
     31 import android.util.Range;
     32 import android.util.Size;
     33 
     34 import java.util.List;
     35 import java.util.ArrayList;
     36 
     37 @AppModeFull
     38 public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
     39     private static final String TAG = "BurstCaptureTest";
     40     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     41     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     42 
     43     /**
     44      * Test YUV burst capture with full-AUTO control.
     45      * Also verifies sensor settings operation if READ_SENSOR_SETTINGS is available.
     46      */
     47     public void testYuvBurst() throws Exception {
     48         for (int i = 0; i < mCameraIds.length; i++) {
     49             try {
     50                 String id = mCameraIds[i];
     51                 Log.i(TAG, "Testing YUV Burst for camera " + id);
     52                 openDevice(id);
     53 
     54                 if (!mStaticInfo.isColorOutputSupported()) {
     55                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
     56                 }
     57                 if (!mStaticInfo.isAeLockSupported() || !mStaticInfo.isAwbLockSupported()) {
     58                     Log.i(TAG, "AE/AWB lock is not supported in camera " + id +
     59                             ". Skip the test");
     60                     continue;
     61                 }
     62 
     63                 if (mStaticInfo.isHardwareLevelLegacy()) {
     64                     Log.i(TAG, "Legacy camera doesn't report min frame duration" +
     65                             ". Skip the test");
     66                     continue;
     67                 }
     68 
     69                 yuvBurstTestByCamera(id);
     70             } finally {
     71                 closeDevice();
     72                 closeImageReader();
     73             }
     74         }
     75     }
     76 
     77     private void yuvBurstTestByCamera(String cameraId) throws Exception {
     78         // Parameters
     79         final int MAX_CONVERGENCE_FRAMES = 150; // 5 sec at 30fps
     80         final long MAX_PREVIEW_RESULT_TIMEOUT_MS = 1000;
     81         final int BURST_SIZE = 100;
     82         final float FRAME_DURATION_MARGIN_FRACTION = 0.1f;
     83 
     84         // Find a good preview size (bound to 1080p)
     85         final Size previewSize = mOrderedPreviewSizes.get(0);
     86 
     87         // Get maximum YUV_420_888 size
     88         final Size stillSize = getSortedSizesForFormat(
     89                 cameraId, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null).get(0);
     90 
     91         // Find max pipeline depth and sync latency
     92         final int maxPipelineDepth = mStaticInfo.getCharacteristics().get(
     93             CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH);
     94         final int maxSyncLatency = mStaticInfo.getCharacteristics().get(
     95             CameraCharacteristics.SYNC_MAX_LATENCY);
     96 
     97         // Find minimum frame duration for full-res YUV_420_888
     98         StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
     99             CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    100         final long minStillFrameDuration =
    101                 config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, stillSize);
    102 
    103 
    104         Range<Integer> targetRange = getSuitableFpsRangeForDuration(cameraId, minStillFrameDuration);
    105 
    106         Log.i(TAG, String.format("Selected frame rate range %d - %d for YUV burst",
    107                         targetRange.getLower(), targetRange.getUpper()));
    108 
    109         // Check if READ_SENSOR_SETTINGS is supported
    110         final boolean checkSensorSettings = mStaticInfo.isCapabilitySupported(
    111             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS);
    112 
    113         // Configure basic preview and burst settings
    114 
    115         CaptureRequest.Builder previewBuilder =
    116             mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    117         CaptureRequest.Builder burstBuilder =
    118             mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    119 
    120         previewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
    121                 targetRange);
    122         burstBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
    123                 targetRange);
    124         burstBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
    125         burstBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
    126 
    127         // Create session and start up preview
    128 
    129         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    130         ImageDropperListener imageDropper = new ImageDropperListener();
    131 
    132         prepareCaptureAndStartPreview(
    133             previewBuilder, burstBuilder,
    134             previewSize, stillSize,
    135             ImageFormat.YUV_420_888, resultListener,
    136             /*maxNumImages*/ 3, imageDropper);
    137 
    138         // Create burst
    139 
    140         List<CaptureRequest> burst = new ArrayList<>();
    141         for (int i = 0; i < BURST_SIZE; i++) {
    142             burst.add(burstBuilder.build());
    143         }
    144 
    145         // Converge AE/AWB
    146 
    147         int frameCount = 0;
    148         while (true) {
    149             CaptureResult result = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
    150             int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
    151             int awbState = result.get(CaptureResult.CONTROL_AWB_STATE);
    152 
    153             if (DEBUG) {
    154                 Log.d(TAG, "aeState: " + aeState + ". awbState: " + awbState);
    155             }
    156 
    157             if ((aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
    158                     aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) &&
    159                     awbState == CaptureResult.CONTROL_AWB_STATE_CONVERGED) {
    160                 break;
    161             }
    162             frameCount++;
    163             assertTrue(String.format("Cam %s: Can not converge AE and AWB within %d frames",
    164                     cameraId, MAX_CONVERGENCE_FRAMES),
    165                 frameCount < MAX_CONVERGENCE_FRAMES);
    166         }
    167 
    168         // Lock AF if there's a focuser
    169 
    170         if (mStaticInfo.hasFocuser()) {
    171             previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
    172                 CaptureRequest.CONTROL_AF_TRIGGER_START);
    173             mSession.capture(previewBuilder.build(), resultListener, mHandler);
    174             previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
    175                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
    176 
    177             frameCount = 0;
    178             while (true) {
    179                 CaptureResult result = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
    180                 int afState = result.get(CaptureResult.CONTROL_AF_STATE);
    181 
    182                 if (DEBUG) {
    183                     Log.d(TAG, "afState: " + afState);
    184                 }
    185 
    186                 if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
    187                     afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
    188                     break;
    189                 }
    190                 frameCount++;
    191                 assertTrue(String.format("Cam %s: Cannot lock AF within %d frames", cameraId,
    192                         MAX_CONVERGENCE_FRAMES),
    193                     frameCount < MAX_CONVERGENCE_FRAMES);
    194             }
    195         }
    196 
    197         // Lock AE/AWB
    198 
    199         previewBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
    200         previewBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
    201 
    202         CaptureRequest lockedRequest = previewBuilder.build();
    203         mSession.setRepeatingRequest(lockedRequest, resultListener, mHandler);
    204 
    205         // Wait for first result with locking
    206         resultListener.drain();
    207         CaptureResult lockedResult =
    208                 resultListener.getCaptureResultForRequest(lockedRequest, maxPipelineDepth);
    209 
    210         int pipelineDepth = lockedResult.get(CaptureResult.REQUEST_PIPELINE_DEPTH);
    211 
    212         // Then start waiting on results to get the first result that should be synced
    213         // up, and also fire the burst as soon as possible
    214 
    215         if (maxSyncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL) {
    216             // The locked result we have is already synchronized so start the burst
    217             mSession.captureBurst(burst, resultListener, mHandler);
    218         } else {
    219             // Need to get a synchronized result, and may need to start burst later to
    220             // be synchronized correctly
    221 
    222             boolean burstSent = false;
    223 
    224             // Calculate how many requests we need to still send down to camera before we
    225             // know the settings have settled for the burst
    226 
    227             int numFramesWaited = maxSyncLatency;
    228             if (numFramesWaited == CameraCharacteristics.SYNC_MAX_LATENCY_UNKNOWN) {
    229                 numFramesWaited = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
    230             }
    231 
    232             int requestsNeededToSync = numFramesWaited - pipelineDepth;
    233             for (int i = 0; i < numFramesWaited; i++) {
    234                 if (!burstSent && requestsNeededToSync <= 0) {
    235                     mSession.captureBurst(burst, resultListener, mHandler);
    236                     burstSent = true;
    237                 }
    238                 lockedResult = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
    239                 requestsNeededToSync--;
    240             }
    241 
    242             assertTrue("Cam " + cameraId + ": Burst failed to fire!", burstSent);
    243         }
    244 
    245         // Read in locked settings if supported
    246 
    247         long burstExposure = 0;
    248         long burstFrameDuration = 0;
    249         int burstSensitivity = 0;
    250         if (checkSensorSettings) {
    251             burstExposure = lockedResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
    252             burstFrameDuration = lockedResult.get(CaptureResult.SENSOR_FRAME_DURATION);
    253             burstSensitivity = lockedResult.get(CaptureResult.SENSOR_SENSITIVITY);
    254 
    255             assertTrue(String.format("Cam %s: Frame duration %d ns too short compared to " +
    256                     "exposure time %d ns", cameraId, burstFrameDuration, burstExposure),
    257                 burstFrameDuration >= burstExposure);
    258 
    259             assertTrue(String.format("Cam %s: Exposure time is not valid: %d",
    260                     cameraId, burstExposure),
    261                 burstExposure > 0);
    262             assertTrue(String.format("Cam %s: Frame duration is not valid: %d",
    263                     cameraId, burstFrameDuration),
    264                 burstFrameDuration > 0);
    265             assertTrue(String.format("Cam %s: Sensitivity is not valid: %d",
    266                     cameraId, burstSensitivity),
    267                 burstSensitivity > 0);
    268         }
    269 
    270         // Process burst results
    271         int burstIndex = 0;
    272         CaptureResult burstResult =
    273                 resultListener.getCaptureResultForRequest(burst.get(burstIndex),
    274                     maxPipelineDepth + 1);
    275         long prevTimestamp = -1;
    276         final long frameDurationBound = (long)
    277                 (minStillFrameDuration * (1 + FRAME_DURATION_MARGIN_FRACTION) );
    278 
    279         List<Long> frameDurations = new ArrayList<>();
    280 
    281         while(true) {
    282             // Verify the result
    283             assertTrue("Cam " + cameraId + ": Result doesn't match expected request",
    284                     burstResult.getRequest() == burst.get(burstIndex));
    285 
    286             // Verify locked settings
    287             if (checkSensorSettings) {
    288                 long exposure = burstResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
    289                 int sensitivity = burstResult.get(CaptureResult.SENSOR_SENSITIVITY);
    290                 assertTrue("Cam " + cameraId + ": Exposure not locked!",
    291                     exposure == burstExposure);
    292                 assertTrue("Cam " + cameraId + ": Sensitivity not locked!",
    293                     sensitivity == burstSensitivity);
    294             }
    295 
    296             // Collect inter-frame durations
    297             long timestamp = burstResult.get(CaptureResult.SENSOR_TIMESTAMP);
    298             if (prevTimestamp != -1) {
    299                 long frameDuration = timestamp - prevTimestamp;
    300                 frameDurations.add(frameDuration);
    301                 if (DEBUG) {
    302                     Log.i(TAG, String.format("Frame %03d    Duration %.2f ms", burstIndex,
    303                             frameDuration/1e6));
    304                 }
    305             }
    306             prevTimestamp = timestamp;
    307 
    308             // Get next result
    309             burstIndex++;
    310             if (burstIndex == BURST_SIZE) break;
    311             burstResult = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
    312         }
    313 
    314         // Verify inter-frame durations
    315 
    316         long meanFrameSum = 0;
    317         for (Long duration : frameDurations) {
    318             meanFrameSum += duration;
    319         }
    320         float meanFrameDuration = (float) meanFrameSum / frameDurations.size();
    321 
    322         float stddevSum = 0;
    323         for (Long duration : frameDurations) {
    324             stddevSum += (duration - meanFrameDuration) * (duration - meanFrameDuration);
    325         }
    326         float stddevFrameDuration = (float)
    327                 Math.sqrt(1.f / (frameDurations.size() - 1 ) * stddevSum);
    328 
    329         Log.i(TAG, String.format("Cam %s: Burst frame duration mean: %.1f, stddev: %.1f", cameraId,
    330                 meanFrameDuration, stddevFrameDuration));
    331 
    332         assertTrue(
    333             String.format("Cam %s: Burst frame duration mean %.1f ns is larger than acceptable, " +
    334                 "expecting below %d ns, allowing below %d", cameraId,
    335                 meanFrameDuration, minStillFrameDuration, frameDurationBound),
    336             meanFrameDuration <= frameDurationBound);
    337 
    338         // Calculate upper 97.5% bound (assuming durations are normally distributed...)
    339         float limit95FrameDuration = meanFrameDuration + 2 * stddevFrameDuration;
    340 
    341         // Don't enforce this yet, but warn
    342         if (limit95FrameDuration > frameDurationBound) {
    343             Log.w(TAG,
    344                 String.format("Cam %s: Standard deviation is too large compared to limit: " +
    345                     "mean: %.1f ms, stddev: %.1f ms: 95%% bound: %f ms", cameraId,
    346                     meanFrameDuration/1e6, stddevFrameDuration/1e6,
    347                     limit95FrameDuration/1e6));
    348         }
    349     }
    350 }
    351