Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright 2013 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.content.Context;
     22 import android.graphics.ImageFormat;
     23 import android.hardware.camera2.CameraDevice;
     24 import android.hardware.camera2.CameraCharacteristics;
     25 import android.hardware.camera2.CaptureRequest;
     26 import android.hardware.camera2.CaptureResult;
     27 import android.util.Size;
     28 import android.hardware.camera2.cts.helpers.StaticMetadata;
     29 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
     30 import android.media.Image;
     31 import android.media.ImageReader;
     32 import android.os.ConditionVariable;
     33 import android.util.Log;
     34 import android.view.Surface;
     35 
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 /**
     40  * <p>Basic test for ImageReader APIs. It uses CameraDevice as producer, camera
     41  * sends the data to the surface provided by imageReader. Below image formats
     42  * are tested:</p>
     43  *
     44  * <p>YUV_420_888: flexible YUV420, it is mandatory format for camera. </p>
     45  * <p>JPEG: used for JPEG still capture, also mandatory format. </p>
     46  * <p>Some invalid access test. </p>
     47  * <p>TODO: Add more format tests? </p>
     48  */
     49 public class ImageReaderTest extends Camera2AndroidTestCase {
     50     private static final String TAG = "ImageReaderTest";
     51     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     52     // number of frame (for streaming requests) to be verified.
     53     private static final int NUM_FRAME_VERIFIED = 2;
     54     // Max number of images can be accessed simultaneously from ImageReader.
     55     private static final int MAX_NUM_IMAGES = 5;
     56 
     57     private SimpleImageListener mListener;
     58 
     59     @Override
     60     public void setContext(Context context) {
     61         super.setContext(context);
     62     }
     63 
     64     @Override
     65     protected void setUp() throws Exception {
     66         super.setUp();
     67     }
     68 
     69     @Override
     70     protected void tearDown() throws Exception {
     71         super.tearDown();
     72     }
     73 
     74     public void testFlexibleYuv() throws Exception {
     75         for (String id : mCameraIds) {
     76             try {
     77                 Log.i(TAG, "Testing Camera " + id);
     78                 openDevice(id);
     79                 bufferFormatTestByCamera(ImageFormat.YUV_420_888, /*repeating*/true);
     80             } finally {
     81                 closeDevice(id);
     82             }
     83         }
     84     }
     85 
     86     public void testJpeg() throws Exception {
     87         for (String id : mCameraIds) {
     88             try {
     89                 Log.v(TAG, "Testing jpeg capture for Camera " + id);
     90                 openDevice(id);
     91                 if (mStaticInfo.isHardwareLevelLegacy()) {
     92                     Log.i(TAG, "Skipping test on legacy devices");
     93                     continue;
     94                 }
     95                 bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/false);
     96             } finally {
     97                 closeDevice(id);
     98             }
     99         }
    100     }
    101 
    102     public void testRaw() throws Exception {
    103         for (String id : mCameraIds) {
    104             try {
    105                 Log.v(TAG, "Testing raw capture for camera " + id);
    106                 openDevice(id);
    107 
    108                 bufferFormatTestByCamera(ImageFormat.RAW_SENSOR, /*repeating*/false);
    109             } finally {
    110                 closeDevice(id);
    111             }
    112         }
    113     }
    114 
    115     public void testRepeatingJpeg() throws Exception {
    116         for (String id : mCameraIds) {
    117             try {
    118                 Log.v(TAG, "Testing repeating jpeg capture for Camera " + id);
    119                 openDevice(id);
    120                 if (mStaticInfo.isHardwareLevelLegacy()) {
    121                     Log.i(TAG, "Skipping test on legacy devices");
    122                     continue;
    123                 }
    124                 bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/true);
    125             } finally {
    126                 closeDevice(id);
    127             }
    128         }
    129     }
    130 
    131     public void testRepeatingRaw() throws Exception {
    132         for (String id : mCameraIds) {
    133             try {
    134                 Log.v(TAG, "Testing repeating raw capture for camera " + id);
    135                 openDevice(id);
    136 
    137                 bufferFormatTestByCamera(ImageFormat.RAW_SENSOR, /*repeating*/true);
    138             } finally {
    139                 closeDevice(id);
    140             }
    141         }
    142     }
    143 
    144     public void testInvalidAccessTest() {
    145         // TODO: test invalid access case, see if we can receive expected
    146         // exceptions
    147     }
    148 
    149     /**
    150      * Test two image stream (YUV420_888 and JPEG) capture by using ImageReader.
    151      *
    152      * <p>Both stream formats are mandatory for Camera2 API</p>
    153      */
    154     public void testYuvAndJpeg() throws Exception {
    155         for (String id : mCameraIds) {
    156             try {
    157                 Log.v(TAG, "YUV and JPEG testing for camera " + id);
    158                 openDevice(id);
    159                 if (mStaticInfo.isHardwareLevelLegacy()) {
    160                     Log.i(TAG, "Skipping test on legacy devices");
    161                     continue;
    162                 }
    163                 bufferFormatWithYuvTestByCamera(ImageFormat.JPEG);
    164             } finally {
    165                 closeDevice(id);
    166             }
    167         }
    168     }
    169 
    170     /**
    171      * Test two image stream (YUV420_888 and RAW_SENSOR) capture by using ImageReader.
    172      *
    173      */
    174     public void testImageReaderYuvAndRaw() throws Exception {
    175         for (String id : mCameraIds) {
    176             try {
    177                 Log.v(TAG, "YUV and RAW testing for camera " + id);
    178                 openDevice(id);
    179 
    180                 bufferFormatWithYuvTestByCamera(ImageFormat.RAW_SENSOR);
    181             } finally {
    182                 closeDevice(id);
    183             }
    184         }
    185     }
    186 
    187 
    188     /**
    189      * Test capture a given format stream with yuv stream simultaneously.
    190      *
    191      * <p>Use fixed yuv size, varies targeted format capture size. Single capture is tested.</p>
    192      *
    193      * @param format The capture format to be tested along with yuv format.
    194      */
    195     private void bufferFormatWithYuvTestByCamera(int format) throws Exception {
    196         if (format != ImageFormat.JPEG && format != ImageFormat.RAW_SENSOR
    197                 && format != ImageFormat.YUV_420_888) {
    198             throw new IllegalArgumentException("Unsupported format: " + format);
    199         }
    200 
    201         final int NUM_SINGLE_CAPTURE_TESTED = MAX_NUM_IMAGES - 1;
    202         Size maxYuvSz = mOrderedPreviewSizes.get(0);
    203         Size[] targetCaptureSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
    204                 StaticMetadata.StreamDirection.Output);
    205 
    206         for (Size captureSz : targetCaptureSizes) {
    207             if (VERBOSE) {
    208                 Log.v(TAG, "Testing yuv size " + maxYuvSz.toString() + " and capture size "
    209                         + captureSz.toString() + " for camera " + mCamera.getId());
    210             }
    211 
    212             ImageReader captureReader = null;
    213             ImageReader yuvReader = null;
    214             try {
    215                 // Create YUV image reader
    216                 SimpleImageReaderListener yuvListener  = new SimpleImageReaderListener();
    217                 yuvReader = createImageReader(maxYuvSz, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
    218                         yuvListener);
    219                 Surface yuvSurface = yuvReader.getSurface();
    220 
    221                 // Create capture image reader
    222                 SimpleImageReaderListener captureListener = new SimpleImageReaderListener();
    223                 captureReader = createImageReader(captureSz, format, MAX_NUM_IMAGES,
    224                         captureListener);
    225                 Surface captureSurface = captureReader.getSurface();
    226 
    227                 // Capture images.
    228                 List<Surface> outputSurfaces = new ArrayList<Surface>();
    229                 outputSurfaces.add(yuvSurface);
    230                 outputSurfaces.add(captureSurface);
    231                 CaptureRequest.Builder request = prepareCaptureRequestForSurfaces(outputSurfaces);
    232                 SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    233 
    234                 for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) {
    235                     startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
    236                 }
    237 
    238                 // Verify capture result and images
    239                 for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) {
    240                     resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
    241                     if (VERBOSE) {
    242                         Log.v(TAG, " Got the capture result back for " + i + "th capture");
    243                     }
    244 
    245                     Image yuvImage = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
    246                     if (VERBOSE) {
    247                         Log.v(TAG, " Got the yuv image back for " + i + "th capture");
    248                     }
    249 
    250                     Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
    251                     if (VERBOSE) {
    252                         Log.v(TAG, " Got the capture image back for " + i + "th capture");
    253                     }
    254 
    255                     //Validate captured images.
    256                     CameraTestUtils.validateImage(yuvImage, maxYuvSz.getWidth(),
    257                             maxYuvSz.getHeight(), ImageFormat.YUV_420_888, /*filePath*/null);
    258                     CameraTestUtils.validateImage(captureImage, captureSz.getWidth(),
    259                             captureSz.getHeight(), format, /*filePath*/null);
    260                 }
    261 
    262                 // Stop capture, delete the streams.
    263                 stopCapture(/*fast*/false);
    264             } finally {
    265                 closeImageReader(captureReader);
    266                 captureReader = null;
    267                 closeImageReader(yuvReader);
    268                 yuvReader = null;
    269             }
    270         }
    271     }
    272 
    273     private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
    274 
    275         Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
    276                 StaticMetadata.StreamDirection.Output);
    277 
    278         // for each resolution, test imageReader:
    279         for (Size sz : availableSizes) {
    280             try {
    281                 if (VERBOSE) {
    282                     Log.v(TAG, "Testing size " + sz.toString() + " format " + format
    283                             + " for camera " + mCamera.getId());
    284                 }
    285 
    286                 // Create ImageReader.
    287                 mListener  = new SimpleImageListener();
    288                 createDefaultImageReader(sz, format, MAX_NUM_IMAGES, mListener);
    289 
    290                 // Start capture.
    291                 CaptureRequest request = prepareCaptureRequest();
    292                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
    293                 startCapture(request, repeating, listener, mHandler);
    294 
    295                 int numFrameVerified = repeating ? NUM_FRAME_VERIFIED : 1;
    296 
    297                 // Validate images.
    298                 validateImage(sz, format, numFrameVerified, repeating);
    299 
    300                 // Validate capture result.
    301                 validateCaptureResult(format, sz, listener, numFrameVerified);
    302 
    303                 // stop capture.
    304                 stopCapture(/*fast*/false);
    305             } finally {
    306                 closeDefaultImageReader();
    307             }
    308 
    309         }
    310     }
    311 
    312     /**
    313      * Validate capture results.
    314      *
    315      * @param format The format of this capture.
    316      * @param size The capture size.
    317      * @param listener The capture listener to get capture result callbacks.
    318      */
    319     private void validateCaptureResult(int format, Size size, SimpleCaptureCallback listener,
    320             int numFrameVerified) {
    321         for (int i = 0; i < numFrameVerified; i++) {
    322             CaptureResult result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
    323 
    324             // TODO: Update this to use availableResultKeys once shim supports this.
    325             if (mStaticInfo.isCapabilitySupported(
    326                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
    327                 Long exposureTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
    328                 Integer sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
    329                 mCollector.expectInRange(
    330                         String.format(
    331                                 "Capture for format %d, size %s exposure time is invalid.",
    332                                 format, size.toString()),
    333                         exposureTime,
    334                         mStaticInfo.getExposureMinimumOrDefault(),
    335                         mStaticInfo.getExposureMaximumOrDefault()
    336                 );
    337                 mCollector.expectInRange(
    338                         String.format("Capture for format %d, size %s sensitivity is invalid.",
    339                                 format, size.toString()),
    340                         sensitivity,
    341                         mStaticInfo.getSensitivityMinimumOrDefault(),
    342                         mStaticInfo.getSensitivityMaximumOrDefault()
    343                 );
    344             }
    345             // TODO: add more key validations.
    346         }
    347     }
    348 
    349     private final class SimpleImageListener implements ImageReader.OnImageAvailableListener {
    350         private final ConditionVariable imageAvailable = new ConditionVariable();
    351         @Override
    352         public void onImageAvailable(ImageReader reader) {
    353             if (mReader != reader) {
    354                 return;
    355             }
    356 
    357             if (VERBOSE) Log.v(TAG, "new image available");
    358             imageAvailable.open();
    359         }
    360 
    361         public void waitForAnyImageAvailable(long timeout) {
    362             if (imageAvailable.block(timeout)) {
    363                 imageAvailable.close();
    364             } else {
    365                 fail("wait for image available timed out after " + timeout + "ms");
    366             }
    367         }
    368 
    369         public void closePendingImages() {
    370             Image image = mReader.acquireLatestImage();
    371             if (image != null) {
    372                 image.close();
    373             }
    374         }
    375     }
    376 
    377     private CaptureRequest prepareCaptureRequest() throws Exception {
    378         List<Surface> outputSurfaces = new ArrayList<Surface>();
    379         Surface surface = mReader.getSurface();
    380         assertNotNull("Fail to get surface from ImageReader", surface);
    381         outputSurfaces.add(surface);
    382         return prepareCaptureRequestForSurfaces(outputSurfaces).build();
    383     }
    384 
    385     private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
    386             throws Exception {
    387         createSession(surfaces);
    388 
    389         CaptureRequest.Builder captureBuilder =
    390                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    391         assertNotNull("Fail to get captureRequest", captureBuilder);
    392         for (Surface surface : surfaces) {
    393             captureBuilder.addTarget(surface);
    394         }
    395 
    396         return captureBuilder;
    397     }
    398 
    399     private void validateImage(Size sz, int format, int captureCount,  boolean repeating)
    400             throws Exception {
    401         // TODO: Add more format here, and wrap each one as a function.
    402         Image img;
    403         final int MAX_RETRY_COUNT = 20;
    404         int numImageVerified = 0;
    405         int reTryCount = 0;
    406         while (numImageVerified < captureCount) {
    407             assertNotNull("Image listener is null", mListener);
    408             if (VERBOSE) Log.v(TAG, "Waiting for an Image");
    409             mListener.waitForAnyImageAvailable(CAPTURE_WAIT_TIMEOUT_MS);
    410             if (repeating) {
    411                 /**
    412                  * Acquire the latest image in case the validation is slower than
    413                  * the image producing rate.
    414                  */
    415                 img = mReader.acquireLatestImage();
    416                 /**
    417                  * Sometimes if multiple onImageAvailable callbacks being queued,
    418                  * acquireLatestImage will clear all buffer before corresponding callback is
    419                  * executed. Wait for a new frame in that case.
    420                  */
    421                 if (img == null && reTryCount < MAX_RETRY_COUNT) {
    422                     reTryCount++;
    423                     continue;
    424                 }
    425             } else {
    426                 img = mReader.acquireNextImage();
    427             }
    428             assertNotNull("Unable to acquire the latest image", img);
    429             if (VERBOSE) Log.v(TAG, "Got the latest image");
    430             CameraTestUtils.validateImage(img, sz.getWidth(), sz.getHeight(), format,
    431                     DEBUG_FILE_NAME_BASE);
    432             if (VERBOSE) Log.v(TAG, "finish vaildation of image " + numImageVerified);
    433             img.close();
    434             numImageVerified++;
    435             reTryCount = 0;
    436         }
    437 
    438         // Return all pending images to the ImageReader as the validateImage may
    439         // take a while to return and there could be many images pending.
    440         mListener.closePendingImages();
    441     }
    442 }
    443