• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }