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 android.graphics.ImageFormat; 20 import android.hardware.camera2.CameraAccessException; 21 import android.hardware.camera2.CameraDevice; 22 import android.hardware.camera2.CameraManager; 23 import android.hardware.camera2.CameraMetadata; 24 import android.hardware.camera2.CameraCharacteristics; 25 import android.hardware.camera2.Size; 26 import android.media.Image; 27 import android.media.ImageReader; 28 import android.media.Image.Plane; 29 import android.os.Handler; 30 import android.util.Log; 31 32 import com.android.ex.camera2.blocking.BlockingCameraManager; 33 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException; 34 35 import junit.framework.Assert; 36 37 import java.io.FileOutputStream; 38 import java.io.IOException; 39 import java.lang.reflect.Array; 40 import java.nio.ByteBuffer; 41 import java.util.Arrays; 42 43 /** 44 * A package private utility class for wrapping up the camera2 cts test common utility functions 45 */ 46 class CameraTestUtils extends Assert { 47 private static final String TAG = "CameraTestUtils"; 48 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 49 50 // Default timeouts for reaching various states 51 public static final int CAMERA_OPEN_TIMEOUT_MS = 500; 52 public static final int CAMERA_IDLE_TIMEOUT_MS = 2000; 53 public static final int CAMERA_ACTIVE_TIMEOUT_MS = 500; 54 public static final int CAMERA_BUSY_TIMEOUT_MS = 500; 55 56 public static class ImageDropperListener implements ImageReader.OnImageAvailableListener { 57 @Override onImageAvailable(ImageReader reader)58 public void onImageAvailable(ImageReader reader) { 59 Image image = null; 60 try { 61 image = reader.acquireNextImage(); 62 } finally { 63 if (image != null) { 64 image.close(); 65 } 66 } 67 } 68 } 69 70 /** 71 * Block until the camera is opened. 72 * 73 * <p>Don't use this to test #onDisconnected/#onError since this will throw 74 * an AssertionError if it fails to open the camera device.</p> 75 * 76 * @return CameraDevice opened camera device 77 * @throws BlockingOpenException 78 * 79 * @throws AssertionError if the camera fails to open (or times out) 80 */ openCamera(CameraManager manager, String cameraId, CameraDevice.StateListener listener, Handler handler)81 public static CameraDevice openCamera(CameraManager manager, String cameraId, 82 CameraDevice.StateListener listener, Handler handler) throws CameraAccessException, 83 BlockingOpenException { 84 85 /** 86 * Although camera2 API allows 'null' Handler (it will just use the current 87 * thread's Looper), this is not what we want for CTS. 88 * 89 * In CTS the default looper is used only to process events in between test runs, 90 * so anything sent there would not be executed inside a test and the test would fail. 91 * 92 * In this case, BlockingCameraManager#openCamera performs the check for us. 93 */ 94 return (new BlockingCameraManager(manager)).openCamera(cameraId, listener, handler); 95 } 96 97 98 /** 99 * Block until the camera is opened. 100 * 101 * <p>Don't use this to test #onDisconnected/#onError since this will throw 102 * an AssertionError if it fails to open the camera device.</p> 103 * 104 * @return CameraDevice opened camera device 105 * 106 * @throws AssertionError if the camera fails to open (or times out) 107 */ openCamera(CameraManager manager, String cameraId, Handler handler)108 public static CameraDevice openCamera(CameraManager manager, String cameraId, Handler handler) 109 throws CameraAccessException, 110 BlockingOpenException { 111 return openCamera(manager, cameraId, /*listener*/null, handler); 112 } 113 assertArrayNotEmpty(T arr, String message)114 public static <T> void assertArrayNotEmpty(T arr, String message) { 115 assertTrue(message, arr != null && Array.getLength(arr) > 0); 116 } 117 118 /** 119 * Check if the format is a legal YUV format camera supported. 120 */ checkYuvFormat(int format)121 public static void checkYuvFormat(int format) { 122 if ((format != ImageFormat.YUV_420_888) && 123 (format != ImageFormat.NV21) && 124 (format != ImageFormat.YV12) && 125 (format != ImageFormat.Y8) && 126 (format != ImageFormat.Y16)) { 127 fail("Wrong formats: " + format); 128 } 129 } 130 131 /** 132 * Check if image size and format match given size and format. 133 */ checkImage(Image image, int width, int height, int format)134 public static void checkImage(Image image, int width, int height, int format) { 135 assertNotNull("Input image is invalid", image); 136 assertEquals("Format doesn't match", format, image.getFormat()); 137 assertEquals("Width doesn't match", width, image.getWidth()); 138 assertEquals("Height doesn't match", height, image.getHeight()); 139 } 140 141 /** 142 * <p>Read data from all planes of an Image into a contiguous unpadded, unpacked 143 * 1-D linear byte array, such that it can be write into disk, or accessed by 144 * software conveniently. It supports YUV_420_888/NV21/YV12 and JPEG input 145 * Image format.</p> 146 * 147 * <p>For YUV_420_888/NV21/YV12/Y8/Y16, it returns a byte array that contains 148 * the Y plane data first, followed by U(Cb), V(Cr) planes if there is any 149 * (xstride = width, ystride = height for chroma and luma components).</p> 150 * 151 * <p>For JPEG, it returns a 1-D byte array contains a complete JPEG image.</p> 152 */ getDataFromImage(Image image)153 public static byte[] getDataFromImage(Image image) { 154 assertNotNull("Invalid image:", image); 155 int format = image.getFormat(); 156 int width = image.getWidth(); 157 int height = image.getHeight(); 158 int rowStride, pixelStride; 159 byte[] data = null; 160 161 // Read image data 162 Plane[] planes = image.getPlanes(); 163 assertTrue("Fail to get image planes", planes != null && planes.length > 0); 164 165 // Check image validity 166 checkAndroidImageFormat(image); 167 168 ByteBuffer buffer = null; 169 // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer. 170 if (format == ImageFormat.JPEG) { 171 buffer = planes[0].getBuffer(); 172 assertNotNull("Fail to get jpeg ByteBuffer", buffer); 173 data = new byte[buffer.capacity()]; 174 buffer.get(data); 175 return data; 176 } 177 178 int offset = 0; 179 data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8]; 180 byte[] rowData = new byte[planes[0].getRowStride()]; 181 if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes"); 182 for (int i = 0; i < planes.length; i++) { 183 buffer = planes[i].getBuffer(); 184 assertNotNull("Fail to get bytebuffer from plane", buffer); 185 rowStride = planes[i].getRowStride(); 186 assertTrue("rowStride should be no less than width", rowStride >= width); 187 pixelStride = planes[i].getPixelStride(); 188 assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0); 189 if (VERBOSE) { 190 Log.v(TAG, "pixelStride " + pixelStride); 191 Log.v(TAG, "rowStride " + rowStride); 192 Log.v(TAG, "width " + width); 193 Log.v(TAG, "height " + height); 194 } 195 // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling. 196 int w = (i == 0) ? width : width / 2; 197 int h = (i == 0) ? height : height / 2; 198 assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w); 199 for (int row = 0; row < h; row++) { 200 int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8; 201 if (pixelStride == bytesPerPixel) { 202 // Special case: optimized read of the entire row 203 int length = w * bytesPerPixel; 204 buffer.get(data, offset, length); 205 // Advance buffer the remainder of the row stride 206 buffer.position(buffer.position() + rowStride - length); 207 offset += length; 208 } else { 209 // Generic case: should work for any pixelStride but slower. 210 // Use intermediate buffer to avoid read byte-by-byte from 211 // DirectByteBuffer, which is very bad for performance 212 buffer.get(rowData, 0, rowStride); 213 for (int col = 0; col < w; col++) { 214 data[offset++] = rowData[col * pixelStride]; 215 } 216 } 217 } 218 if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i); 219 } 220 return data; 221 } 222 223 /** 224 * <p>Check android image format validity for an image, only support below formats:</p> 225 * 226 * <p>YUV_420_888/NV21/YV12/Y8/Y16, can add more for future</p> 227 */ checkAndroidImageFormat(Image image)228 public static void checkAndroidImageFormat(Image image) { 229 int format = image.getFormat(); 230 Plane[] planes = image.getPlanes(); 231 switch (format) { 232 case ImageFormat.YUV_420_888: 233 case ImageFormat.NV21: 234 case ImageFormat.YV12: 235 assertEquals("YUV420 format Images should have 3 planes", 3, planes.length); 236 break; 237 case ImageFormat.Y8: 238 case ImageFormat.Y16: 239 assertEquals("Y8/Y16 Image should have 1 plane", 1, planes.length); 240 break; 241 case ImageFormat.JPEG: 242 assertEquals("Jpeg Image should have one plane", 1, planes.length); 243 break; 244 default: 245 fail("Unsupported Image Format: " + format); 246 } 247 } 248 dumpFile(String fileName, byte[] data)249 public static void dumpFile(String fileName, byte[] data) { 250 FileOutputStream outStream; 251 try { 252 Log.v(TAG, "output will be saved as " + fileName); 253 outStream = new FileOutputStream(fileName); 254 } catch (IOException ioe) { 255 throw new RuntimeException("Unable to create debug output file " + fileName, ioe); 256 } 257 258 try { 259 outStream.write(data); 260 outStream.close(); 261 } catch (IOException ioe) { 262 throw new RuntimeException("failed writing data to file " + fileName, ioe); 263 } 264 } 265 getSupportedSizeForFormat(int format, String cameraId, CameraManager cameraManager)266 public static Size[] getSupportedSizeForFormat(int format, String cameraId, 267 CameraManager cameraManager) throws Exception { 268 CameraMetadata.Key<Size[]> key = null; 269 CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId); 270 assertNotNull("Can't get camera characteristics!", properties); 271 if (VERBOSE) { 272 Log.v(TAG, "get camera characteristics for camera: " + cameraId); 273 } 274 switch (format) { 275 case ImageFormat.JPEG: 276 key = CameraCharacteristics.SCALER_AVAILABLE_JPEG_SIZES; 277 break; 278 case ImageFormat.YUV_420_888: 279 case ImageFormat.YV12: 280 case ImageFormat.NV21: 281 case ImageFormat.Y8: 282 case ImageFormat.Y16: 283 key = CameraCharacteristics.SCALER_AVAILABLE_PROCESSED_SIZES; 284 break; 285 default: 286 throw new UnsupportedOperationException( 287 String.format("Invalid format specified 0x%x", format)); 288 } 289 Size[] availableSizes = properties.get(key); 290 if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes)); 291 return availableSizes; 292 } 293 }