• 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.camera2.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.List;
41 
42 public class ItsUtils {
43     public static final String TAG = ItsUtils.class.getSimpleName();
44 
jsonToByteBuffer(JSONObject jsonObj)45     public static ByteBuffer jsonToByteBuffer(JSONObject jsonObj) {
46         return ByteBuffer.wrap(jsonObj.toString().getBytes(Charset.defaultCharset()));
47     }
48 
getJsonWeightedRectsFromArray( JSONArray a, boolean normalized, int width, int height)49     public static MeteringRectangle[] getJsonWeightedRectsFromArray(
50             JSONArray a, boolean normalized, int width, int height)
51             throws ItsException {
52         try {
53             // Returns [x0,y0,x1,y1,wgt,  x0,y0,x1,y1,wgt,  x0,y0,x1,y1,wgt,  ...]
54             assert(a.length() % 5 == 0);
55             MeteringRectangle[] ma = new MeteringRectangle[a.length() / 5];
56             for (int i = 0; i < a.length(); i += 5) {
57                 int x,y,w,h;
58                 if (normalized) {
59                     x = (int)Math.floor(a.getDouble(i+0) * width + 0.5f);
60                     y = (int)Math.floor(a.getDouble(i+1) * height + 0.5f);
61                     w = (int)Math.floor(a.getDouble(i+2) * width + 0.5f);
62                     h = (int)Math.floor(a.getDouble(i+3) * height + 0.5f);
63                 } else {
64                     x = a.getInt(i+0);
65                     y = a.getInt(i+1);
66                     w = a.getInt(i+2);
67                     h = a.getInt(i+3);
68                 }
69                 x = Math.max(x, 0);
70                 y = Math.max(y, 0);
71                 w = Math.min(w, width-x);
72                 h = Math.min(h, height-y);
73                 int wgt = a.getInt(i+4);
74                 ma[i/5] = new MeteringRectangle(x,y,w,h,wgt);
75             }
76             return ma;
77         } catch (org.json.JSONException e) {
78             throw new ItsException("JSON error: ", e);
79         }
80     }
81 
getOutputSpecs(JSONObject jsonObjTop)82     public static JSONArray getOutputSpecs(JSONObject jsonObjTop)
83             throws ItsException {
84         try {
85             if (jsonObjTop.has("outputSurfaces")) {
86                 return jsonObjTop.getJSONArray("outputSurfaces");
87             }
88             return null;
89         } catch (org.json.JSONException e) {
90             throw new ItsException("JSON error: ", e);
91         }
92     }
93 
getRawOutputSizes(CameraCharacteristics ccs)94     public static Size[] getRawOutputSizes(CameraCharacteristics ccs)
95             throws ItsException {
96         return getOutputSizes(ccs, ImageFormat.RAW_SENSOR);
97     }
98 
getJpegOutputSizes(CameraCharacteristics ccs)99     public static Size[] getJpegOutputSizes(CameraCharacteristics ccs)
100             throws ItsException {
101         return getOutputSizes(ccs, ImageFormat.JPEG);
102     }
103 
getYuvOutputSizes(CameraCharacteristics ccs)104     public static Size[] getYuvOutputSizes(CameraCharacteristics ccs)
105             throws ItsException {
106         return getOutputSizes(ccs, ImageFormat.YUV_420_888);
107     }
108 
getOutputSizes(CameraCharacteristics ccs, int format)109     private static Size[] getOutputSizes(CameraCharacteristics ccs, int format)
110             throws ItsException {
111         StreamConfigurationMap configMap = ccs.get(
112                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
113         if (configMap == null) {
114             throw new ItsException("Failed to get stream config");
115         }
116         return configMap.getOutputSizes(format);
117     }
118 
getDataFromImage(Image image)119     public static byte[] getDataFromImage(Image image)
120             throws ItsException {
121         int format = image.getFormat();
122         int width = image.getWidth();
123         int height = image.getHeight();
124         byte[] data = null;
125 
126         // Read image data
127         Plane[] planes = image.getPlanes();
128 
129         // Check image validity
130         if (!checkAndroidImageFormat(image)) {
131             throw new ItsException(
132                     "Invalid image format passed to getDataFromImage: " + image.getFormat());
133         }
134 
135         if (format == ImageFormat.JPEG) {
136             // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer.
137             ByteBuffer buffer = planes[0].getBuffer();
138             data = new byte[buffer.capacity()];
139             buffer.get(data);
140             return data;
141         } else if (format == ImageFormat.YUV_420_888 || format == ImageFormat.RAW_SENSOR
142                 || format == ImageFormat.RAW10) {
143             int offset = 0;
144             data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
145             byte[] rowData = new byte[planes[0].getRowStride()];
146             for (int i = 0; i < planes.length; i++) {
147                 ByteBuffer buffer = planes[i].getBuffer();
148                 int rowStride = planes[i].getRowStride();
149                 int pixelStride = planes[i].getPixelStride();
150                 int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
151                 Logt.i(TAG, String.format(
152                         "Reading image: fmt %d, plane %d, w %d, h %d, rowStride %d, pixStride %d",
153                         format, i, width, height, rowStride, pixelStride));
154                 // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
155                 int w = (i == 0) ? width : width / 2;
156                 int h = (i == 0) ? height : height / 2;
157                 for (int row = 0; row < h; row++) {
158                     if (pixelStride == bytesPerPixel) {
159                         // Special case: optimized read of the entire row
160                         int length = w * bytesPerPixel;
161                         buffer.get(data, offset, length);
162                         // Advance buffer the remainder of the row stride
163                         buffer.position(buffer.position() + rowStride - length);
164                         offset += length;
165                     } else {
166                         // Generic case: should work for any pixelStride but slower.
167                         // Use intermediate buffer to avoid read byte-by-byte from
168                         // DirectByteBuffer, which is very bad for performance.
169                         // Also need avoid access out of bound by only reading the available
170                         // bytes in the bytebuffer.
171                         int readSize = rowStride;
172                         if (buffer.remaining() < readSize) {
173                             readSize = buffer.remaining();
174                         }
175                         buffer.get(rowData, 0, readSize);
176                         if (pixelStride >= 1) {
177                             for (int col = 0; col < w; col++) {
178                                 data[offset++] = rowData[col * pixelStride];
179                             }
180                         } else {
181                             // PixelStride of 0 can mean pixel isn't a multiple of 8 bits, for
182                             // example with RAW10. Just copy the buffer, dropping any padding at
183                             // the end of the row.
184                             int length = (w * ImageFormat.getBitsPerPixel(format)) / 8;
185                             System.arraycopy(rowData,0,data,offset,length);
186                             offset += length;
187                         }
188                     }
189                 }
190             }
191             Logt.i(TAG, String.format("Done reading image, format %d", format));
192             return data;
193         } else {
194             throw new ItsException("Unsupported image format: " + format);
195         }
196     }
197 
checkAndroidImageFormat(Image image)198     private static boolean checkAndroidImageFormat(Image image) {
199         int format = image.getFormat();
200         Plane[] planes = image.getPlanes();
201         switch (format) {
202             case ImageFormat.YUV_420_888:
203             case ImageFormat.NV21:
204             case ImageFormat.YV12:
205                 return 3 == planes.length;
206             case ImageFormat.RAW_SENSOR:
207             case ImageFormat.RAW10:
208             case ImageFormat.JPEG:
209                 return 1 == planes.length;
210             default:
211                 return false;
212         }
213     }
214 }
215 
216