• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 com.android.cts.verifier.camera.its;
18 
19 import android.content.Context;
20 import android.graphics.ImageFormat;
21 import android.hardware.camera2.CameraDevice;
22 import android.hardware.camera2.CameraCharacteristics;
23 import android.hardware.camera2.CaptureRequest;
24 import android.hardware.camera2.CaptureResult;
25 import android.hardware.camera2.params.MeteringRectangle;
26 import android.hardware.camera2.params.StreamConfigurationMap;
27 import android.media.Image;
28 import android.media.Image.Plane;
29 import android.net.Uri;
30 import android.os.Environment;
31 import android.util.Log;
32 import android.util.Size;
33 
34 import org.json.JSONArray;
35 import org.json.JSONObject;
36 
37 import java.nio.ByteBuffer;
38 import java.nio.charset.Charset;
39 import java.util.ArrayList;
40 import java.util.concurrent.Semaphore;
41 import java.util.List;
42 
43 
44 public class ItsUtils {
45     public static final String TAG = ItsUtils.class.getSimpleName();
46 
jsonToByteBuffer(JSONObject jsonObj)47     public static ByteBuffer jsonToByteBuffer(JSONObject jsonObj) {
48         return ByteBuffer.wrap(jsonObj.toString().getBytes(Charset.defaultCharset()));
49     }
50 
getJsonWeightedRectsFromArray( JSONArray a, boolean normalized, int width, int height)51     public static MeteringRectangle[] getJsonWeightedRectsFromArray(
52             JSONArray a, boolean normalized, int width, int height)
53             throws ItsException {
54         try {
55             // Returns [x0,y0,x1,y1,wgt,  x0,y0,x1,y1,wgt,  x0,y0,x1,y1,wgt,  ...]
56             assert(a.length() % 5 == 0);
57             MeteringRectangle[] ma = new MeteringRectangle[a.length() / 5];
58             for (int i = 0; i < a.length(); i += 5) {
59                 int x,y,w,h;
60                 if (normalized) {
61                     x = (int)Math.floor(a.getDouble(i+0) * width + 0.5f);
62                     y = (int)Math.floor(a.getDouble(i+1) * height + 0.5f);
63                     w = (int)Math.floor(a.getDouble(i+2) * width + 0.5f);
64                     h = (int)Math.floor(a.getDouble(i+3) * height + 0.5f);
65                 } else {
66                     x = a.getInt(i+0);
67                     y = a.getInt(i+1);
68                     w = a.getInt(i+2);
69                     h = a.getInt(i+3);
70                 }
71                 x = Math.max(x, 0);
72                 y = Math.max(y, 0);
73                 w = Math.min(w, width-x);
74                 h = Math.min(h, height-y);
75                 int wgt = a.getInt(i+4);
76                 ma[i/5] = new MeteringRectangle(x,y,w,h,wgt);
77             }
78             return ma;
79         } catch (org.json.JSONException e) {
80             throw new ItsException("JSON error: ", e);
81         }
82     }
83 
getOutputSpecs(JSONObject jsonObjTop)84     public static JSONArray getOutputSpecs(JSONObject jsonObjTop)
85             throws ItsException {
86         try {
87             if (jsonObjTop.has("outputSurfaces")) {
88                 return jsonObjTop.getJSONArray("outputSurfaces");
89             }
90             return null;
91         } catch (org.json.JSONException e) {
92             throw new ItsException("JSON error: ", e);
93         }
94     }
95 
getRaw16OutputSizes(CameraCharacteristics ccs)96     public static Size[] getRaw16OutputSizes(CameraCharacteristics ccs)
97             throws ItsException {
98         return getOutputSizes(ccs, ImageFormat.RAW_SENSOR);
99     }
100 
getRaw10OutputSizes(CameraCharacteristics ccs)101     public static Size[] getRaw10OutputSizes(CameraCharacteristics ccs)
102             throws ItsException {
103         return getOutputSizes(ccs, ImageFormat.RAW10);
104     }
105 
getRaw12OutputSizes(CameraCharacteristics ccs)106     public static Size[] getRaw12OutputSizes(CameraCharacteristics ccs)
107             throws ItsException {
108         return getOutputSizes(ccs, ImageFormat.RAW12);
109     }
110 
getJpegOutputSizes(CameraCharacteristics ccs)111     public static Size[] getJpegOutputSizes(CameraCharacteristics ccs)
112             throws ItsException {
113         return getOutputSizes(ccs, ImageFormat.JPEG);
114     }
115 
getYuvOutputSizes(CameraCharacteristics ccs)116     public static Size[] getYuvOutputSizes(CameraCharacteristics ccs)
117             throws ItsException {
118         return getOutputSizes(ccs, ImageFormat.YUV_420_888);
119     }
120 
getMaxOutputSize(CameraCharacteristics ccs, int format)121     public static Size getMaxOutputSize(CameraCharacteristics ccs, int format)
122             throws ItsException {
123         return getMaxSize(getOutputSizes(ccs, format));
124     }
125 
getOutputSizes(CameraCharacteristics ccs, int format)126     private static Size[] getOutputSizes(CameraCharacteristics ccs, int format)
127             throws ItsException {
128         StreamConfigurationMap configMap = ccs.get(
129                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
130         if (configMap == null) {
131             throw new ItsException("Failed to get stream config");
132         }
133         Size[] normalSizes = configMap.getOutputSizes(format);
134         Size[] slowSizes = configMap.getHighResolutionOutputSizes(format);
135         Size[] allSizes = null;
136         if (normalSizes != null && slowSizes != null) {
137             allSizes = new Size[normalSizes.length + slowSizes.length];
138             System.arraycopy(normalSizes, 0, allSizes, 0,
139                     normalSizes.length);
140             System.arraycopy(slowSizes, 0, allSizes, normalSizes.length,
141                     slowSizes.length);
142         } else if (normalSizes != null) {
143             allSizes = normalSizes;
144         } else if (slowSizes != null) {
145             allSizes = slowSizes;
146         }
147         return allSizes;
148     }
149 
getMaxSize(Size[] sizes)150     public static Size getMaxSize(Size[] sizes) {
151         if (sizes == null || sizes.length == 0) {
152             throw new IllegalArgumentException("sizes was empty");
153         }
154 
155         Size maxSize = sizes[0];
156         for (int i = 1; i < sizes.length; i++) {
157             if (sizes[i].getWidth() * sizes[i].getHeight() >
158                     maxSize.getWidth() * maxSize.getHeight()) {
159                 maxSize = sizes[i];
160             }
161         }
162 
163         return maxSize;
164     }
165 
getDataFromImage(Image image, Semaphore quota)166     public static byte[] getDataFromImage(Image image, Semaphore quota)
167             throws ItsException {
168         int format = image.getFormat();
169         int width = image.getWidth();
170         int height = image.getHeight();
171         byte[] data = null;
172 
173         // Read image data
174         Plane[] planes = image.getPlanes();
175 
176         // Check image validity
177         if (!checkAndroidImageFormat(image)) {
178             throw new ItsException(
179                     "Invalid image format passed to getDataFromImage: " + image.getFormat());
180         }
181 
182         if (format == ImageFormat.JPEG) {
183             // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer.
184             ByteBuffer buffer = planes[0].getBuffer();
185             if (quota != null) {
186                 try {
187                     quota.acquire(buffer.capacity());
188                 } catch (java.lang.InterruptedException e) {
189                     Logt.e(TAG, "getDataFromImage error acquiring memory quota. Interrupted", e);
190                 }
191             }
192             data = new byte[buffer.capacity()];
193             buffer.get(data);
194             return data;
195         } else if (format == ImageFormat.YUV_420_888 || format == ImageFormat.RAW_SENSOR
196                 || format == ImageFormat.RAW10 || format == ImageFormat.RAW12) {
197             int offset = 0;
198             int dataSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
199             if (quota != null) {
200                 try {
201                     quota.acquire(dataSize);
202                 } catch (java.lang.InterruptedException e) {
203                     Logt.e(TAG, "getDataFromImage error acquiring memory quota. Interrupted", e);
204                 }
205             }
206             data = new byte[dataSize];
207             int maxRowSize = planes[0].getRowStride();
208             for (int i = 0; i < planes.length; i++) {
209                 if (maxRowSize < planes[i].getRowStride()) {
210                     maxRowSize = planes[i].getRowStride();
211                 }
212             }
213             byte[] rowData = new byte[maxRowSize];
214             for (int i = 0; i < planes.length; i++) {
215                 ByteBuffer buffer = planes[i].getBuffer();
216                 int rowStride = planes[i].getRowStride();
217                 int pixelStride = planes[i].getPixelStride();
218                 int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
219                 Logt.i(TAG, String.format(
220                         "Reading image: fmt %d, plane %d, w %d, h %d, rowStride %d, pixStride %d",
221                         format, i, width, height, rowStride, pixelStride));
222                 // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
223                 int w = (i == 0) ? width : width / 2;
224                 int h = (i == 0) ? height : height / 2;
225                 for (int row = 0; row < h; row++) {
226                     if (pixelStride == bytesPerPixel) {
227                         // Special case: optimized read of the entire row
228                         int length = w * bytesPerPixel;
229                         buffer.get(data, offset, length);
230                         // Advance buffer the remainder of the row stride
231                         if (row < h - 1) {
232                             buffer.position(buffer.position() + rowStride - length);
233                         }
234                         offset += length;
235                     } else {
236                         // Generic case: should work for any pixelStride but slower.
237                         // Use intermediate buffer to avoid read byte-by-byte from
238                         // DirectByteBuffer, which is very bad for performance.
239                         // Also need avoid access out of bound by only reading the available
240                         // bytes in the bytebuffer.
241                         int readSize = rowStride;
242                         if (buffer.remaining() < readSize) {
243                             readSize = buffer.remaining();
244                         }
245                         buffer.get(rowData, 0, readSize);
246                         if (pixelStride >= 1) {
247                             for (int col = 0; col < w; col++) {
248                                 data[offset++] = rowData[col * pixelStride];
249                             }
250                         } else {
251                             // PixelStride of 0 can mean pixel isn't a multiple of 8 bits, for
252                             // example with RAW10. Just copy the buffer, dropping any padding at
253                             // the end of the row.
254                             int length = (w * ImageFormat.getBitsPerPixel(format)) / 8;
255                             System.arraycopy(rowData,0,data,offset,length);
256                             offset += length;
257                         }
258                     }
259                 }
260             }
261             Logt.i(TAG, String.format("Done reading image, format %d", format));
262             return data;
263         } else {
264             throw new ItsException("Unsupported image format: " + format);
265         }
266     }
267 
checkAndroidImageFormat(Image image)268     private static boolean checkAndroidImageFormat(Image image) {
269         int format = image.getFormat();
270         Plane[] planes = image.getPlanes();
271         switch (format) {
272             case ImageFormat.YUV_420_888:
273             case ImageFormat.NV21:
274             case ImageFormat.YV12:
275                 return 3 == planes.length;
276             case ImageFormat.RAW_SENSOR:
277             case ImageFormat.RAW10:
278             case ImageFormat.RAW12:
279             case ImageFormat.JPEG:
280                 return 1 == planes.length;
281             default:
282                 return false;
283         }
284     }
285 }
286