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.media.Image; 26 import android.media.Image.Plane; 27 import android.net.Uri; 28 import android.os.Environment; 29 import android.util.Log; 30 31 import org.json.JSONArray; 32 import org.json.JSONObject; 33 34 import java.nio.ByteBuffer; 35 import java.nio.charset.Charset; 36 import java.util.List; 37 38 public class ItsUtils { 39 public static final String TAG = ItsUtils.class.getSimpleName(); 40 jsonToByteBuffer(JSONObject jsonObj)41 public static ByteBuffer jsonToByteBuffer(JSONObject jsonObj) { 42 return ByteBuffer.wrap(jsonObj.toString().getBytes(Charset.defaultCharset())); 43 } 44 getJsonRectFromArray( JSONArray a, boolean normalized, int width, int height)45 public static int[] getJsonRectFromArray( 46 JSONArray a, boolean normalized, int width, int height) 47 throws ItsException { 48 try { 49 // Returns [x,y,w,h] 50 if (normalized) { 51 return new int[]{(int)Math.floor(a.getDouble(0) * width + 0.5f), 52 (int)Math.floor(a.getDouble(1) * height + 0.5f), 53 (int)Math.floor(a.getDouble(2) * width + 0.5f), 54 (int)Math.floor(a.getDouble(3) * height + 0.5f) }; 55 } else { 56 return new int[]{a.getInt(0), 57 a.getInt(1), 58 a.getInt(2), 59 a.getInt(3) }; 60 } 61 } catch (org.json.JSONException e) { 62 throw new ItsException("JSON error: ", e); 63 } 64 } 65 getCallbacksPerCapture(int format)66 public static int getCallbacksPerCapture(int format) 67 throws ItsException { 68 // Regardless of the format, there is one callback for the CaptureResult object; this 69 // prepares the output metadata file. 70 int n = 1; 71 72 switch (format) { 73 case ImageFormat.YUV_420_888: 74 case ImageFormat.JPEG: 75 // A single output image callback is made, with either the JPEG or the YUV data. 76 n += 1; 77 break; 78 79 default: 80 throw new ItsException("Unsupported format: " + format); 81 } 82 83 return n; 84 } 85 getOutputSpecs(JSONObject jsonObjTop)86 public static JSONObject getOutputSpecs(JSONObject jsonObjTop) 87 throws ItsException { 88 try { 89 if (jsonObjTop.has("outputSurface")) { 90 return jsonObjTop.getJSONObject("outputSurface"); 91 } 92 return null; 93 } catch (org.json.JSONException e) { 94 throw new ItsException("JSON error: ", e); 95 } 96 } 97 getDataFromImage(Image image)98 public static byte[] getDataFromImage(Image image) 99 throws ItsException { 100 int format = image.getFormat(); 101 int width = image.getWidth(); 102 int height = image.getHeight(); 103 int rowStride, pixelStride; 104 byte[] data = null; 105 106 // Read image data 107 Plane[] planes = image.getPlanes(); 108 109 // Check image validity 110 if (!checkAndroidImageFormat(image)) { 111 throw new ItsException( 112 "Invalid image format passed to getDataFromImage: " + image.getFormat()); 113 } 114 115 if (format == ImageFormat.JPEG) { 116 // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer. 117 ByteBuffer buffer = planes[0].getBuffer(); 118 data = new byte[buffer.capacity()]; 119 buffer.get(data); 120 return data; 121 } else if (format == ImageFormat.YUV_420_888) { 122 int offset = 0; 123 data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8]; 124 byte[] rowData = new byte[planes[0].getRowStride()]; 125 for (int i = 0; i < planes.length; i++) { 126 ByteBuffer buffer = planes[i].getBuffer(); 127 rowStride = planes[i].getRowStride(); 128 pixelStride = planes[i].getPixelStride(); 129 // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling. 130 int w = (i == 0) ? width : width / 2; 131 int h = (i == 0) ? height : height / 2; 132 for (int row = 0; row < h; row++) { 133 int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8; 134 if (pixelStride == bytesPerPixel) { 135 // Special case: optimized read of the entire row 136 int length = w * bytesPerPixel; 137 buffer.get(data, offset, length); 138 // Advance buffer the remainder of the row stride 139 buffer.position(buffer.position() + rowStride - length); 140 offset += length; 141 } else { 142 // Generic case: should work for any pixelStride but slower. 143 // Use use intermediate buffer to avoid read byte-by-byte from 144 // DirectByteBuffer, which is very bad for performance. 145 // Also need avoid access out of bound by only reading the available 146 // bytes in the bytebuffer. 147 int readSize = rowStride; 148 if (buffer.remaining() < readSize) { 149 readSize = buffer.remaining(); 150 } 151 buffer.get(rowData, 0, readSize); 152 for (int col = 0; col < w; col++) { 153 data[offset++] = rowData[col * pixelStride]; 154 } 155 } 156 } 157 } 158 return data; 159 } else { 160 throw new ItsException("Unsupported image format: " + format); 161 } 162 } 163 checkAndroidImageFormat(Image image)164 private static boolean checkAndroidImageFormat(Image image) { 165 int format = image.getFormat(); 166 Plane[] planes = image.getPlanes(); 167 switch (format) { 168 case ImageFormat.YUV_420_888: 169 case ImageFormat.NV21: 170 case ImageFormat.YV12: 171 return 3 == planes.length; 172 case ImageFormat.JPEG: 173 return 1 == planes.length; 174 default: 175 return false; 176 } 177 } 178 } 179 180