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.graphics.Point; 20 import android.graphics.Rect; 21 import android.hardware.camera2.CameraCharacteristics; 22 import android.hardware.camera2.CameraDevice; 23 import android.hardware.camera2.CameraMetadata; 24 import android.hardware.camera2.CaptureResult; 25 import android.hardware.camera2.CaptureRequest; 26 import android.hardware.camera2.TotalCaptureResult; 27 import android.hardware.camera2.params.BlackLevelPattern; 28 import android.hardware.camera2.params.ColorSpaceTransform; 29 import android.hardware.camera2.params.Face; 30 import android.hardware.camera2.params.LensShadingMap; 31 import android.hardware.camera2.params.MeteringRectangle; 32 import android.hardware.camera2.params.RggbChannelVector; 33 import android.hardware.camera2.params.StreamConfigurationMap; 34 import android.hardware.camera2.params.TonemapCurve; 35 import android.location.Location; 36 import android.util.Log; 37 import android.util.Pair; 38 import android.util.Rational; 39 import android.util.Size; 40 import android.util.SizeF; 41 import android.util.Range; 42 43 import org.json.JSONArray; 44 import org.json.JSONObject; 45 46 import java.lang.reflect.Array; 47 import java.lang.reflect.Field; 48 import java.lang.reflect.GenericArrayType; 49 import java.lang.reflect.Modifier; 50 import java.lang.reflect.ParameterizedType; 51 import java.lang.reflect.Type; 52 import java.util.Arrays; 53 import java.util.LinkedList; 54 import java.util.List; 55 import java.util.Set; 56 57 /** 58 * Class to deal with serializing and deserializing between JSON and Camera2 objects. 59 */ 60 public class ItsSerializer { 61 public static final String TAG = ItsSerializer.class.getSimpleName(); 62 63 private static class MetadataEntry { MetadataEntry(String k, Object v)64 public MetadataEntry(String k, Object v) { 65 key = k; 66 value = v; 67 } 68 public String key; 69 public Object value; 70 } 71 72 @SuppressWarnings("unchecked") serializeRational(Rational rat)73 private static Object serializeRational(Rational rat) throws org.json.JSONException { 74 JSONObject ratObj = new JSONObject(); 75 ratObj.put("numerator", rat.getNumerator()); 76 ratObj.put("denominator", rat.getDenominator()); 77 return ratObj; 78 } 79 80 @SuppressWarnings("unchecked") serializeSize(Size size)81 private static Object serializeSize(Size size) throws org.json.JSONException { 82 JSONObject sizeObj = new JSONObject(); 83 sizeObj.put("width", size.getWidth()); 84 sizeObj.put("height", size.getHeight()); 85 return sizeObj; 86 } 87 88 @SuppressWarnings("unchecked") serializeSizeF(SizeF size)89 private static Object serializeSizeF(SizeF size) throws org.json.JSONException { 90 JSONObject sizeObj = new JSONObject(); 91 sizeObj.put("width", size.getWidth()); 92 sizeObj.put("height", size.getHeight()); 93 return sizeObj; 94 } 95 96 @SuppressWarnings("unchecked") serializeRect(Rect rect)97 private static Object serializeRect(Rect rect) throws org.json.JSONException { 98 JSONObject rectObj = new JSONObject(); 99 rectObj.put("left", rect.left); 100 rectObj.put("right", rect.right); 101 rectObj.put("top", rect.top); 102 rectObj.put("bottom", rect.bottom); 103 return rectObj; 104 } 105 serializePoint(Point point)106 private static Object serializePoint(Point point) throws org.json.JSONException { 107 JSONObject pointObj = new JSONObject(); 108 pointObj.put("x", point.x); 109 pointObj.put("y", point.y); 110 return pointObj; 111 } 112 113 @SuppressWarnings("unchecked") serializeFace(Face face)114 private static Object serializeFace(Face face) 115 throws org.json.JSONException { 116 JSONObject faceObj = new JSONObject(); 117 faceObj.put("bounds", serializeRect(face.getBounds())); 118 faceObj.put("score", face.getScore()); 119 faceObj.put("id", face.getId()); 120 if (face.getLeftEyePosition() != null) { 121 faceObj.put("leftEye", serializePoint(face.getLeftEyePosition())); 122 } 123 if (face.getRightEyePosition() != null) { 124 faceObj.put("rightEye", serializePoint(face.getRightEyePosition())); 125 } 126 if (face.getMouthPosition() != null) { 127 faceObj.put("mouth", serializePoint(face.getMouthPosition())); 128 } 129 return faceObj; 130 } 131 132 @SuppressWarnings("unchecked") serializeStreamConfigurationMap( StreamConfigurationMap map)133 private static Object serializeStreamConfigurationMap( 134 StreamConfigurationMap map) 135 throws org.json.JSONException { 136 // TODO: Serialize the rest of the StreamConfigurationMap fields. 137 JSONObject mapObj = new JSONObject(); 138 JSONArray cfgArray = new JSONArray(); 139 int fmts[] = map.getOutputFormats(); 140 if (fmts != null) { 141 for (int fi = 0; fi < Array.getLength(fmts); fi++) { 142 Size sizes[] = map.getOutputSizes(fmts[fi]); 143 if (sizes != null) { 144 for (int si = 0; si < Array.getLength(sizes); si++) { 145 JSONObject obj = new JSONObject(); 146 obj.put("format", fmts[fi]); 147 obj.put("width",sizes[si].getWidth()); 148 obj.put("height", sizes[si].getHeight()); 149 obj.put("input", false); 150 obj.put("minFrameDuration", 151 map.getOutputMinFrameDuration(fmts[fi],sizes[si])); 152 cfgArray.put(obj); 153 } 154 } 155 sizes = map.getHighResolutionOutputSizes(fmts[fi]); 156 if (sizes != null) { 157 for (int si = 0; si < Array.getLength(sizes); si++) { 158 JSONObject obj = new JSONObject(); 159 obj.put("format", fmts[fi]); 160 obj.put("width",sizes[si].getWidth()); 161 obj.put("height", sizes[si].getHeight()); 162 obj.put("input", false); 163 obj.put("minFrameDuration", 164 map.getOutputMinFrameDuration(fmts[fi],sizes[si])); 165 cfgArray.put(obj); 166 } 167 } 168 } 169 } 170 mapObj.put("availableStreamConfigurations", cfgArray); 171 return mapObj; 172 } 173 174 @SuppressWarnings("unchecked") serializeMeteringRectangle(MeteringRectangle rect)175 private static Object serializeMeteringRectangle(MeteringRectangle rect) 176 throws org.json.JSONException { 177 JSONObject rectObj = new JSONObject(); 178 rectObj.put("x", rect.getX()); 179 rectObj.put("y", rect.getY()); 180 rectObj.put("width", rect.getWidth()); 181 rectObj.put("height", rect.getHeight()); 182 rectObj.put("weight", rect.getMeteringWeight()); 183 return rectObj; 184 } 185 186 @SuppressWarnings("unchecked") serializePair(Pair pair)187 private static Object serializePair(Pair pair) 188 throws org.json.JSONException { 189 JSONArray pairObj = new JSONArray(); 190 pairObj.put(pair.first); 191 pairObj.put(pair.second); 192 return pairObj; 193 } 194 195 @SuppressWarnings("unchecked") serializeRange(Range range)196 private static Object serializeRange(Range range) 197 throws org.json.JSONException { 198 JSONArray rangeObj = new JSONArray(); 199 rangeObj.put(range.getLower()); 200 rangeObj.put(range.getUpper()); 201 return rangeObj; 202 } 203 204 @SuppressWarnings("unchecked") serializeColorSpaceTransform(ColorSpaceTransform xform)205 private static Object serializeColorSpaceTransform(ColorSpaceTransform xform) 206 throws org.json.JSONException { 207 JSONArray xformObj = new JSONArray(); 208 for (int row = 0; row < 3; row++) { 209 for (int col = 0; col < 3; col++) { 210 xformObj.put(serializeRational(xform.getElement(col,row))); 211 } 212 } 213 return xformObj; 214 } 215 216 @SuppressWarnings("unchecked") serializeTonemapCurve(TonemapCurve curve)217 private static Object serializeTonemapCurve(TonemapCurve curve) 218 throws org.json.JSONException { 219 JSONObject curveObj = new JSONObject(); 220 String names[] = {"red", "green", "blue"}; 221 for (int ch = 0; ch < 3; ch++) { 222 JSONArray curveArr = new JSONArray(); 223 int len = curve.getPointCount(ch); 224 for (int i = 0; i < len; i++) { 225 curveArr.put(curve.getPoint(ch,i).x); 226 curveArr.put(curve.getPoint(ch,i).y); 227 } 228 curveObj.put(names[ch], curveArr); 229 } 230 return curveObj; 231 } 232 233 @SuppressWarnings("unchecked") serializeRggbChannelVector(RggbChannelVector vec)234 private static Object serializeRggbChannelVector(RggbChannelVector vec) 235 throws org.json.JSONException { 236 JSONArray vecObj = new JSONArray(); 237 vecObj.put(vec.getRed()); 238 vecObj.put(vec.getGreenEven()); 239 vecObj.put(vec.getGreenOdd()); 240 vecObj.put(vec.getBlue()); 241 return vecObj; 242 } 243 244 @SuppressWarnings("unchecked") serializeBlackLevelPattern(BlackLevelPattern pat)245 private static Object serializeBlackLevelPattern(BlackLevelPattern pat) 246 throws org.json.JSONException { 247 int patVals[] = new int[4]; 248 pat.copyTo(patVals, 0); 249 JSONArray patObj = new JSONArray(); 250 patObj.put(patVals[0]); 251 patObj.put(patVals[1]); 252 patObj.put(patVals[2]); 253 patObj.put(patVals[3]); 254 return patObj; 255 } 256 257 @SuppressWarnings("unchecked") serializeLocation(Location loc)258 private static Object serializeLocation(Location loc) 259 throws org.json.JSONException { 260 return loc.toString(); 261 } 262 263 @SuppressWarnings("unchecked") serializeLensShadingMap(LensShadingMap map)264 private static Object serializeLensShadingMap(LensShadingMap map) 265 throws org.json.JSONException { 266 JSONObject mapObj = new JSONObject(); 267 JSONArray mapArr = new JSONArray(); 268 for (int row = 0; row < map.getRowCount(); row++) { 269 for (int col = 0; col < map.getColumnCount(); col++) { 270 for (int ch = 0; ch < 4; ch++) { 271 mapArr.put(map.getGainFactor(ch, col, row)); 272 } 273 } 274 } 275 mapObj.put("width", map.getColumnCount()); 276 mapObj.put("height", map.getRowCount()); 277 mapObj.put("map", mapArr); 278 return mapObj; 279 } 280 getKeyName(Object keyObj)281 private static String getKeyName(Object keyObj) throws ItsException { 282 if (keyObj.getClass() == CaptureResult.Key.class 283 || keyObj.getClass() == TotalCaptureResult.class) { 284 return ((CaptureResult.Key)keyObj).getName(); 285 } else if (keyObj.getClass() == CaptureRequest.Key.class) { 286 return ((CaptureRequest.Key)keyObj).getName(); 287 } else if (keyObj.getClass() == CameraCharacteristics.Key.class) { 288 return ((CameraCharacteristics.Key)keyObj).getName(); 289 } 290 throw new ItsException("Invalid key object"); 291 } 292 getKeyValue(CameraMetadata md, Object keyObj)293 private static Object getKeyValue(CameraMetadata md, Object keyObj) throws ItsException { 294 if (md.getClass() == CaptureResult.class || md.getClass() == TotalCaptureResult.class) { 295 return ((CaptureResult)md).get((CaptureResult.Key)keyObj); 296 } else if (md.getClass() == CaptureRequest.class) { 297 return ((CaptureRequest)md).get((CaptureRequest.Key)keyObj); 298 } else if (md.getClass() == CameraCharacteristics.class) { 299 return ((CameraCharacteristics)md).get((CameraCharacteristics.Key)keyObj); 300 } 301 throw new ItsException("Invalid key object"); 302 } 303 304 @SuppressWarnings("unchecked") serializeEntry(Type keyType, Object keyObj, CameraMetadata md)305 private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md) 306 throws ItsException { 307 String keyName = getKeyName(keyObj); 308 309 try { 310 Object keyValue = getKeyValue(md, keyObj); 311 if (keyValue == null) { 312 return new MetadataEntry(keyName, JSONObject.NULL); 313 } else if (keyType == Float.class) { 314 // The JSON serializer doesn't handle floating point NaN or Inf. 315 if (((Float)keyValue).isInfinite() || ((Float)keyValue).isNaN()) { 316 Logt.w(TAG, "Inf/NaN floating point value serialized: " + keyName); 317 return null; 318 } 319 return new MetadataEntry(keyName, keyValue); 320 } else if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class || 321 keyType == Boolean.class || keyType == String.class) { 322 return new MetadataEntry(keyName, keyValue); 323 } else if (keyType == Rational.class) { 324 return new MetadataEntry(keyName, serializeRational((Rational)keyValue)); 325 } else if (keyType == Size.class) { 326 return new MetadataEntry(keyName, serializeSize((Size)keyValue)); 327 } else if (keyType == SizeF.class) { 328 return new MetadataEntry(keyName, serializeSizeF((SizeF)keyValue)); 329 } else if (keyType == Rect.class) { 330 return new MetadataEntry(keyName, serializeRect((Rect)keyValue)); 331 } else if (keyType == Face.class) { 332 return new MetadataEntry(keyName, serializeFace((Face)keyValue)); 333 } else if (keyType == StreamConfigurationMap.class) { 334 return new MetadataEntry(keyName, 335 serializeStreamConfigurationMap((StreamConfigurationMap)keyValue)); 336 } else if (keyType instanceof ParameterizedType && 337 ((ParameterizedType)keyType).getRawType() == Range.class) { 338 return new MetadataEntry(keyName, serializeRange((Range)keyValue)); 339 } else if (keyType == ColorSpaceTransform.class) { 340 return new MetadataEntry(keyName, 341 serializeColorSpaceTransform((ColorSpaceTransform)keyValue)); 342 } else if (keyType == MeteringRectangle.class) { 343 return new MetadataEntry(keyName, 344 serializeMeteringRectangle((MeteringRectangle)keyValue)); 345 } else if (keyType == Location.class) { 346 return new MetadataEntry(keyName, 347 serializeLocation((Location)keyValue)); 348 } else if (keyType == RggbChannelVector.class) { 349 return new MetadataEntry(keyName, 350 serializeRggbChannelVector((RggbChannelVector)keyValue)); 351 } else if (keyType == BlackLevelPattern.class) { 352 return new MetadataEntry(keyName, 353 serializeBlackLevelPattern((BlackLevelPattern)keyValue)); 354 } else if (keyType == TonemapCurve.class) { 355 return new MetadataEntry(keyName, 356 serializeTonemapCurve((TonemapCurve)keyValue)); 357 } else if (keyType == Point.class) { 358 return new MetadataEntry(keyName, 359 serializePoint((Point)keyValue)); 360 } else if (keyType == LensShadingMap.class) { 361 return new MetadataEntry(keyName, 362 serializeLensShadingMap((LensShadingMap)keyValue)); 363 } else { 364 Logt.w(TAG, String.format("Serializing unsupported key type: " + keyType)); 365 return null; 366 } 367 } catch (org.json.JSONException e) { 368 throw new ItsException("JSON error for key: " + keyName + ": ", e); 369 } 370 } 371 372 @SuppressWarnings("unchecked") serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md)373 private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md) 374 throws ItsException { 375 String keyName = getKeyName(keyObj); 376 try { 377 Object keyValue = getKeyValue(md, keyObj); 378 if (keyValue == null) { 379 return null; 380 } 381 int arrayLen = Array.getLength(keyValue); 382 Type elmtType = ((GenericArrayType)keyType).getGenericComponentType(); 383 if (elmtType == int.class || elmtType == float.class || elmtType == byte.class || 384 elmtType == long.class || elmtType == double.class || elmtType == boolean.class) { 385 return new MetadataEntry(keyName, new JSONArray(keyValue)); 386 } else if (elmtType == Rational.class) { 387 JSONArray jsonArray = new JSONArray(); 388 for (int i = 0; i < arrayLen; i++) { 389 jsonArray.put(serializeRational((Rational)Array.get(keyValue,i))); 390 } 391 return new MetadataEntry(keyName, jsonArray); 392 } else if (elmtType == Size.class) { 393 JSONArray jsonArray = new JSONArray(); 394 for (int i = 0; i < arrayLen; i++) { 395 jsonArray.put(serializeSize((Size)Array.get(keyValue,i))); 396 } 397 return new MetadataEntry(keyName, jsonArray); 398 } else if (elmtType == Rect.class) { 399 JSONArray jsonArray = new JSONArray(); 400 for (int i = 0; i < arrayLen; i++) { 401 jsonArray.put(serializeRect((Rect)Array.get(keyValue,i))); 402 } 403 return new MetadataEntry(keyName, jsonArray); 404 } else if (elmtType == Face.class) { 405 JSONArray jsonArray = new JSONArray(); 406 for (int i = 0; i < arrayLen; i++) { 407 jsonArray.put(serializeFace((Face)Array.get(keyValue, i))); 408 } 409 return new MetadataEntry(keyName, jsonArray); 410 } else if (elmtType == StreamConfigurationMap.class) { 411 JSONArray jsonArray = new JSONArray(); 412 for (int i = 0; i < arrayLen; i++) { 413 jsonArray.put(serializeStreamConfigurationMap( 414 (StreamConfigurationMap)Array.get(keyValue,i))); 415 } 416 return new MetadataEntry(keyName, jsonArray); 417 } else if (elmtType instanceof ParameterizedType && 418 ((ParameterizedType)elmtType).getRawType() == Range.class) { 419 JSONArray jsonArray = new JSONArray(); 420 for (int i = 0; i < arrayLen; i++) { 421 jsonArray.put(serializeRange((Range)Array.get(keyValue,i))); 422 } 423 return new MetadataEntry(keyName, jsonArray); 424 } else if (elmtType instanceof ParameterizedType && 425 ((ParameterizedType)elmtType).getRawType() == Pair.class) { 426 JSONArray jsonArray = new JSONArray(); 427 for (int i = 0; i < arrayLen; i++) { 428 jsonArray.put(serializePair((Pair)Array.get(keyValue,i))); 429 } 430 return new MetadataEntry(keyName, jsonArray); 431 } else if (elmtType == MeteringRectangle.class) { 432 JSONArray jsonArray = new JSONArray(); 433 for (int i = 0; i < arrayLen; i++) { 434 jsonArray.put(serializeMeteringRectangle( 435 (MeteringRectangle)Array.get(keyValue,i))); 436 } 437 return new MetadataEntry(keyName, jsonArray); 438 } else if (elmtType == Location.class) { 439 JSONArray jsonArray = new JSONArray(); 440 for (int i = 0; i < arrayLen; i++) { 441 jsonArray.put(serializeLocation((Location)Array.get(keyValue,i))); 442 } 443 return new MetadataEntry(keyName, jsonArray); 444 } else if (elmtType == RggbChannelVector.class) { 445 JSONArray jsonArray = new JSONArray(); 446 for (int i = 0; i < arrayLen; i++) { 447 jsonArray.put(serializeRggbChannelVector( 448 (RggbChannelVector)Array.get(keyValue,i))); 449 } 450 return new MetadataEntry(keyName, jsonArray); 451 } else if (elmtType == BlackLevelPattern.class) { 452 JSONArray jsonArray = new JSONArray(); 453 for (int i = 0; i < arrayLen; i++) { 454 jsonArray.put(serializeBlackLevelPattern( 455 (BlackLevelPattern)Array.get(keyValue,i))); 456 } 457 return new MetadataEntry(keyName, jsonArray); 458 } else if (elmtType == Point.class) { 459 JSONArray jsonArray = new JSONArray(); 460 for (int i = 0; i < arrayLen; i++) { 461 jsonArray.put(serializePoint((Point)Array.get(keyValue,i))); 462 } 463 return new MetadataEntry(keyName, jsonArray); 464 } else { 465 Logt.w(TAG, String.format("Serializing unsupported array type: " + elmtType)); 466 return null; 467 } 468 } catch (org.json.JSONException e) { 469 throw new ItsException("JSON error for key: " + keyName + ": ", e); 470 } 471 } 472 473 @SuppressWarnings("unchecked") serialize(CameraMetadata md)474 public static JSONObject serialize(CameraMetadata md) 475 throws ItsException { 476 JSONObject jsonObj = new JSONObject(); 477 Field[] allFields = md.getClass().getDeclaredFields(); 478 if (md.getClass() == TotalCaptureResult.class) { 479 allFields = CaptureResult.class.getDeclaredFields(); 480 } 481 if (md.getClass() == CameraCharacteristics.class) { 482 // Special handling for information not stored in metadata keys 483 CameraCharacteristics chars = (CameraCharacteristics) md; 484 List<CameraCharacteristics.Key<?>> charsKeys = chars.getKeys(); 485 List<CaptureRequest.Key<?>> requestKeys = chars.getAvailableCaptureRequestKeys(); 486 Set<String> physicalCamIds = chars.getPhysicalCameraIds(); 487 488 try { 489 JSONArray charKeysArr = new JSONArray(); 490 for (CameraCharacteristics.Key<?> k : charsKeys) { 491 charKeysArr.put(k.getName()); 492 } 493 JSONArray reqKeysArr = new JSONArray(); 494 for (CaptureRequest.Key<?> k : requestKeys) { 495 reqKeysArr.put(k.getName()); 496 } 497 // Avoid using the hidden metadata key name here to prevent confliction 498 jsonObj.put("camera.characteristics.keys", charKeysArr); 499 jsonObj.put("camera.characteristics.requestKeys", reqKeysArr); 500 501 if (!physicalCamIds.isEmpty()) { 502 JSONArray physCamIdsArr = new JSONArray(); 503 for (String id : physicalCamIds) { 504 physCamIdsArr.put(id); 505 } 506 jsonObj.put("camera.characteristics.physicalCamIds", physCamIdsArr); 507 } 508 } catch (org.json.JSONException e) { 509 throw new ItsException("JSON error for CameraCharacteristics:", e); 510 } 511 } 512 for (Field field : allFields) { 513 if (Modifier.isPublic(field.getModifiers()) && 514 Modifier.isStatic(field.getModifiers()) && 515 (field.getType() == CaptureRequest.Key.class 516 || field.getType() == CaptureResult.Key.class 517 || field.getType() == TotalCaptureResult.Key.class 518 || field.getType() == CameraCharacteristics.Key.class) && 519 field.getGenericType() instanceof ParameterizedType) { 520 ParameterizedType paramType = (ParameterizedType)field.getGenericType(); 521 Type[] argTypes = paramType.getActualTypeArguments(); 522 if (argTypes.length > 0) { 523 try { 524 Type keyType = argTypes[0]; 525 Object keyObj = field.get(md); 526 MetadataEntry entry; 527 if (keyType instanceof GenericArrayType) { 528 entry = serializeArrayEntry(keyType, keyObj, md); 529 } else { 530 entry = serializeEntry(keyType, keyObj, md); 531 } 532 533 // TODO: Figure this weird case out. 534 // There is a weird case where the entry is non-null but the toString 535 // of the entry is null, and if this happens, the null-ness spreads like 536 // a virus and makes the whole JSON object null from the top level down. 537 // Not sure if it's a bug in the library or I'm just not using it right. 538 // Workaround by checking for this case explicitly and not adding the 539 // value to the jsonObj when it is detected. 540 if (entry != null && entry.key != null && entry.value != null 541 && entry.value.toString() == null) { 542 Logt.w(TAG, "Error encountered serializing value for key: " + entry.key); 543 } else if (entry != null) { 544 jsonObj.put(entry.key, entry.value); 545 } else { 546 // Ignore. 547 } 548 } catch (IllegalAccessException e) { 549 throw new ItsException( 550 "Access error for field: " + field + ": ", e); 551 } catch (org.json.JSONException e) { 552 throw new ItsException( 553 "JSON error for field: " + field + ": ", e); 554 } 555 } 556 } 557 } 558 return jsonObj; 559 } 560 561 @SuppressWarnings("unchecked") deserialize(CaptureRequest.Builder mdDefault, JSONObject jsonReq)562 public static CaptureRequest.Builder deserialize(CaptureRequest.Builder mdDefault, 563 JSONObject jsonReq) throws ItsException { 564 try { 565 Logt.i(TAG, "Parsing JSON capture request ..."); 566 567 // Iterate over the CaptureRequest reflected fields. 568 CaptureRequest.Builder md = mdDefault; 569 Field[] allFields = CaptureRequest.class.getDeclaredFields(); 570 for (Field field : allFields) { 571 if (Modifier.isPublic(field.getModifiers()) && 572 Modifier.isStatic(field.getModifiers()) && 573 field.getType() == CaptureRequest.Key.class && 574 field.getGenericType() instanceof ParameterizedType) { 575 ParameterizedType paramType = (ParameterizedType)field.getGenericType(); 576 Type[] argTypes = paramType.getActualTypeArguments(); 577 if (argTypes.length > 0) { 578 CaptureRequest.Key key = (CaptureRequest.Key)field.get(md); 579 String keyName = key.getName(); 580 Type keyType = argTypes[0]; 581 582 // For each reflected CaptureRequest entry, look inside the JSON object 583 // to see if it is being set. If it is found, remove the key from the 584 // JSON object. After this process, there should be no keys left in the 585 // JSON (otherwise an invalid key was specified). 586 587 if (jsonReq.has(keyName) && !jsonReq.isNull(keyName)) { 588 if (keyType instanceof GenericArrayType) { 589 Type elmtType = 590 ((GenericArrayType)keyType).getGenericComponentType(); 591 JSONArray ja = jsonReq.getJSONArray(keyName); 592 Object val[] = new Object[ja.length()]; 593 for (int i = 0; i < ja.length(); i++) { 594 if (elmtType == int.class) { 595 Array.set(val, i, ja.getInt(i)); 596 } else if (elmtType == byte.class) { 597 Array.set(val, i, (byte)ja.getInt(i)); 598 } else if (elmtType == float.class) { 599 Array.set(val, i, (float)ja.getDouble(i)); 600 } else if (elmtType == long.class) { 601 Array.set(val, i, ja.getLong(i)); 602 } else if (elmtType == double.class) { 603 Array.set(val, i, ja.getDouble(i)); 604 } else if (elmtType == boolean.class) { 605 Array.set(val, i, ja.getBoolean(i)); 606 } else if (elmtType == String.class) { 607 Array.set(val, i, ja.getString(i)); 608 } else if (elmtType == Size.class){ 609 JSONObject obj = ja.getJSONObject(i); 610 Array.set(val, i, new Size( 611 obj.getInt("width"), obj.getInt("height"))); 612 } else if (elmtType == Rect.class) { 613 JSONObject obj = ja.getJSONObject(i); 614 Array.set(val, i, new Rect( 615 obj.getInt("left"), obj.getInt("top"), 616 obj.getInt("bottom"), obj.getInt("right"))); 617 } else if (elmtType == Rational.class) { 618 JSONObject obj = ja.getJSONObject(i); 619 Array.set(val, i, new Rational( 620 obj.getInt("numerator"), 621 obj.getInt("denominator"))); 622 } else if (elmtType == RggbChannelVector.class) { 623 JSONArray arr = ja.getJSONArray(i); 624 Array.set(val, i, new RggbChannelVector( 625 (float)arr.getDouble(0), 626 (float)arr.getDouble(1), 627 (float)arr.getDouble(2), 628 (float)arr.getDouble(3))); 629 } else if (elmtType == ColorSpaceTransform.class) { 630 JSONArray arr = ja.getJSONArray(i); 631 Rational xform[] = new Rational[9]; 632 for (int j = 0; j < 9; j++) { 633 xform[j] = new Rational( 634 arr.getJSONObject(j).getInt("numerator"), 635 arr.getJSONObject(j).getInt("denominator")); 636 } 637 Array.set(val, i, new ColorSpaceTransform(xform)); 638 } else if (elmtType == MeteringRectangle.class) { 639 JSONObject obj = ja.getJSONObject(i); 640 Array.set(val, i, new MeteringRectangle( 641 obj.getInt("x"), 642 obj.getInt("y"), 643 obj.getInt("width"), 644 obj.getInt("height"), 645 obj.getInt("weight"))); 646 } else { 647 throw new ItsException( 648 "Failed to parse key from JSON: " + keyName); 649 } 650 } 651 if (val != null) { 652 Logt.i(TAG, "Set: "+keyName+" -> "+Arrays.toString(val)); 653 md.set(key, val); 654 jsonReq.remove(keyName); 655 } 656 } else { 657 Object val = null; 658 if (keyType == Integer.class) { 659 val = jsonReq.getInt(keyName); 660 } else if (keyType == Byte.class) { 661 val = (byte)jsonReq.getInt(keyName); 662 } else if (keyType == Double.class) { 663 val = jsonReq.getDouble(keyName); 664 } else if (keyType == Long.class) { 665 val = jsonReq.getLong(keyName); 666 } else if (keyType == Float.class) { 667 val = (float)jsonReq.getDouble(keyName); 668 } else if (keyType == Boolean.class) { 669 val = jsonReq.getBoolean(keyName); 670 } else if (keyType == String.class) { 671 val = jsonReq.getString(keyName); 672 } else if (keyType == Size.class) { 673 JSONObject obj = jsonReq.getJSONObject(keyName); 674 val = new Size( 675 obj.getInt("width"), obj.getInt("height")); 676 } else if (keyType == Rect.class) { 677 JSONObject obj = jsonReq.getJSONObject(keyName); 678 val = new Rect( 679 obj.getInt("left"), obj.getInt("top"), 680 obj.getInt("right"), obj.getInt("bottom")); 681 } else if (keyType == Rational.class) { 682 JSONObject obj = jsonReq.getJSONObject(keyName); 683 val = new Rational(obj.getInt("numerator"), 684 obj.getInt("denominator")); 685 } else if (keyType == RggbChannelVector.class) { 686 JSONObject obj = jsonReq.optJSONObject(keyName); 687 JSONArray arr = jsonReq.optJSONArray(keyName); 688 if (arr != null) { 689 val = new RggbChannelVector( 690 (float)arr.getDouble(0), 691 (float)arr.getDouble(1), 692 (float)arr.getDouble(2), 693 (float)arr.getDouble(3)); 694 } else if (obj != null) { 695 val = new RggbChannelVector( 696 (float)obj.getDouble("red"), 697 (float)obj.getDouble("greenEven"), 698 (float)obj.getDouble("greenOdd"), 699 (float)obj.getDouble("blue")); 700 } else { 701 throw new ItsException("Invalid RggbChannelVector object"); 702 } 703 } else if (keyType == ColorSpaceTransform.class) { 704 JSONArray arr = jsonReq.getJSONArray(keyName); 705 Rational a[] = new Rational[9]; 706 for (int i = 0; i < 9; i++) { 707 a[i] = new Rational( 708 arr.getJSONObject(i).getInt("numerator"), 709 arr.getJSONObject(i).getInt("denominator")); 710 } 711 val = new ColorSpaceTransform(a); 712 } else if (keyType == TonemapCurve.class) { 713 JSONObject obj = jsonReq.optJSONObject(keyName); 714 String names[] = {"red", "green", "blue"}; 715 float[][] curves = new float[3][]; 716 for (int ch = 0; ch < 3; ch++) { 717 JSONArray ja = obj.getJSONArray(names[ch]); 718 curves[ch] = new float[ja.length()]; 719 for (int i = 0; i < ja.length(); i++) { 720 Array.set(curves[ch], i, (float)ja.getDouble(i)); 721 } 722 } 723 val = new TonemapCurve(curves[0], curves[1], curves[2]); 724 } else if (keyType instanceof ParameterizedType && 725 ((ParameterizedType)keyType).getRawType() == Range.class && 726 ((ParameterizedType)keyType).getActualTypeArguments().length == 1 && 727 ((ParameterizedType)keyType).getActualTypeArguments()[0] == Integer.class) { 728 JSONArray arr = jsonReq.getJSONArray(keyName); 729 val = new Range<Integer>(arr.getInt(0), arr.getInt(1)); 730 } else { 731 throw new ItsException( 732 "Failed to parse key from JSON: " + 733 keyName + ", " + keyType); 734 } 735 if (val != null) { 736 Logt.i(TAG, "Set: " + keyName + " -> " + val); 737 md.set(key ,val); 738 jsonReq.remove(keyName); 739 } 740 } 741 } 742 } 743 } 744 } 745 746 // Ensure that there were no invalid keys in the JSON request object. 747 if (jsonReq.length() != 0) { 748 throw new ItsException("Invalid JSON key(s): " + jsonReq.toString()); 749 } 750 751 Logt.i(TAG, "Parsing JSON capture request completed"); 752 return md; 753 } catch (java.lang.IllegalAccessException e) { 754 throw new ItsException("Access error: ", e); 755 } catch (org.json.JSONException e) { 756 throw new ItsException("JSON error: ", e); 757 } 758 } 759 760 @SuppressWarnings("unchecked") deserializeRequestList( CameraDevice device, JSONObject jsonObjTop, String requestKey)761 public static List<CaptureRequest.Builder> deserializeRequestList( 762 CameraDevice device, JSONObject jsonObjTop, String requestKey) 763 throws ItsException { 764 try { 765 List<CaptureRequest.Builder> requests = null; 766 JSONArray jsonReqs = jsonObjTop.getJSONArray(requestKey); 767 requests = new LinkedList<CaptureRequest.Builder>(); 768 for (int i = 0; i < jsonReqs.length(); i++) { 769 CaptureRequest.Builder templateReq = device.createCaptureRequest( 770 CameraDevice.TEMPLATE_STILL_CAPTURE); 771 requests.add( 772 deserialize(templateReq, jsonReqs.getJSONObject(i))); 773 } 774 return requests; 775 } catch (org.json.JSONException e) { 776 throw new ItsException("JSON error: ", e); 777 } catch (android.hardware.camera2.CameraAccessException e) { 778 throw new ItsException("Access error: ", e); 779 } 780 } 781 } 782