CameraMetadataNative.java revision 7a36a0fbb0a5f14047a3680668da954beca05dcb
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 17package android.hardware.camera2.impl; 18 19import android.hardware.camera2.CameraMetadata; 20import android.hardware.camera2.Rational; 21import android.os.Parcelable; 22import android.os.Parcel; 23import android.util.Log; 24 25import java.lang.reflect.Array; 26import java.nio.ByteBuffer; 27import java.nio.ByteOrder; 28import java.util.ArrayList; 29import java.util.HashMap; 30 31/** 32 * Implementation of camera metadata marshal/unmarshal across Binder to 33 * the camera service 34 */ 35public class CameraMetadataNative extends CameraMetadata implements Parcelable { 36 37 private static final String TAG = "CameraMetadataJV"; 38 39 public CameraMetadataNative() { 40 super(); 41 mMetadataPtr = nativeAllocate(); 42 if (mMetadataPtr == 0) { 43 throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); 44 } 45 } 46 47 /** 48 * Copy constructor - clone metadata 49 */ 50 public CameraMetadataNative(CameraMetadataNative other) { 51 super(); 52 mMetadataPtr = nativeAllocateCopy(other); 53 if (mMetadataPtr == 0) { 54 throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); 55 } 56 } 57 58 public static final Parcelable.Creator<CameraMetadataNative> CREATOR = 59 new Parcelable.Creator<CameraMetadataNative>() { 60 @Override 61 public CameraMetadataNative createFromParcel(Parcel in) { 62 CameraMetadataNative metadata = new CameraMetadataNative(); 63 metadata.readFromParcel(in); 64 return metadata; 65 } 66 67 @Override 68 public CameraMetadataNative[] newArray(int size) { 69 return new CameraMetadataNative[size]; 70 } 71 }; 72 73 @Override 74 public int describeContents() { 75 return 0; 76 } 77 78 @Override 79 public void writeToParcel(Parcel dest, int flags) { 80 nativeWriteToParcel(dest); 81 } 82 83 @SuppressWarnings("unchecked") 84 @Override 85 public <T> T get(Key<T> key) { 86 int tag = key.getTag(); 87 byte[] values = readValues(tag); 88 if (values == null) { 89 return null; 90 } 91 92 int nativeType = getNativeType(tag); 93 94 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); 95 return unpackSingle(buffer, key.getType(), nativeType); 96 } 97 98 public void readFromParcel(Parcel in) { 99 nativeReadFromParcel(in); 100 } 101 102 /** 103 * Set a camera metadata field to a value. The field definitions can be 104 * found in {@link CameraProperties}, {@link CaptureResult}, and 105 * {@link CaptureRequest}. 106 * 107 * @param key The metadata field to write. 108 * @param value The value to set the field to, which must be of a matching 109 * type to the key. 110 */ 111 public <T> void set(Key<T> key, T value) { 112 int tag = key.getTag(); 113 114 if (value == null) { 115 writeValues(tag, null); 116 return; 117 } 118 119 int nativeType = getNativeType(tag); 120 121 int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); 122 123 // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. 124 byte[] values = new byte[size]; 125 126 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); 127 packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); 128 129 writeValues(tag, values); 130 } 131 132 // Keep up-to-date with camera_metadata.h 133 /** 134 * @hide 135 */ 136 public static final int TYPE_BYTE = 0; 137 /** 138 * @hide 139 */ 140 public static final int TYPE_INT32 = 1; 141 /** 142 * @hide 143 */ 144 public static final int TYPE_FLOAT = 2; 145 /** 146 * @hide 147 */ 148 public static final int TYPE_INT64 = 3; 149 /** 150 * @hide 151 */ 152 public static final int TYPE_DOUBLE = 4; 153 /** 154 * @hide 155 */ 156 public static final int TYPE_RATIONAL = 5; 157 /** 158 * @hide 159 */ 160 public static final int NUM_TYPES = 6; 161 162 private void close() { 163 // this sets mMetadataPtr to 0 164 nativeClose(); 165 mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final 166 } 167 168 private static int getTypeSize(int nativeType) { 169 switch(nativeType) { 170 case TYPE_BYTE: 171 return 1; 172 case TYPE_INT32: 173 case TYPE_FLOAT: 174 return 4; 175 case TYPE_INT64: 176 case TYPE_DOUBLE: 177 case TYPE_RATIONAL: 178 return 8; 179 } 180 181 throw new UnsupportedOperationException("Unknown type, can't get size " 182 + nativeType); 183 } 184 185 private static Class<?> getExpectedType(int nativeType) { 186 switch(nativeType) { 187 case TYPE_BYTE: 188 return Byte.TYPE; 189 case TYPE_INT32: 190 return Integer.TYPE; 191 case TYPE_FLOAT: 192 return Float.TYPE; 193 case TYPE_INT64: 194 return Long.TYPE; 195 case TYPE_DOUBLE: 196 return Double.TYPE; 197 case TYPE_RATIONAL: 198 return Rational.class; 199 } 200 201 throw new UnsupportedOperationException("Unknown type, can't map to Java type " 202 + nativeType); 203 } 204 205 @SuppressWarnings("unchecked") 206 private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type, 207 int nativeType, boolean sizeOnly) { 208 209 if (!sizeOnly) { 210 /** 211 * Rewrite types when the native type doesn't match the managed type 212 * - Boolean -> Byte 213 * - Integer -> Byte 214 */ 215 216 if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { 217 // Since a boolean can't be cast to byte, and we don't want to use putBoolean 218 boolean asBool = (Boolean) value; 219 byte asByte = (byte) (asBool ? 1 : 0); 220 value = (T) (Byte) asByte; 221 } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { 222 int asInt = (Integer) value; 223 byte asByte = (byte) asInt; 224 value = (T) (Byte) asByte; 225 } else if (type != getExpectedType(nativeType)) { 226 throw new UnsupportedOperationException("Tried to pack a type of " + type + 227 " but we expected the type to be " + getExpectedType(nativeType)); 228 } 229 230 if (nativeType == TYPE_BYTE) { 231 buffer.put((Byte) value); 232 } else if (nativeType == TYPE_INT32) { 233 buffer.putInt((Integer) value); 234 } else if (nativeType == TYPE_FLOAT) { 235 buffer.putFloat((Float) value); 236 } else if (nativeType == TYPE_INT64) { 237 buffer.putLong((Long) value); 238 } else if (nativeType == TYPE_DOUBLE) { 239 buffer.putDouble((Double) value); 240 } else if (nativeType == TYPE_RATIONAL) { 241 Rational r = (Rational) value; 242 buffer.putInt(r.getNumerator()); 243 buffer.putInt(r.getDenominator()); 244 } 245 246 } 247 248 return getTypeSize(nativeType); 249 } 250 251 @SuppressWarnings({"unchecked", "rawtypes"}) 252 private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType, 253 boolean sizeOnly) { 254 255 int size = 0; 256 257 if (type.isPrimitive() || type == Rational.class) { 258 size = packSingleNative(value, buffer, type, nativeType, sizeOnly); 259 } else if (type.isEnum()) { 260 size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly); 261 } else if (type.isArray()) { 262 size = packArray(value, buffer, type, nativeType, sizeOnly); 263 } else { 264 size = packClass(value, buffer, type, nativeType, sizeOnly); 265 } 266 267 return size; 268 } 269 270 private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type, 271 int nativeType, boolean sizeOnly) { 272 273 // TODO: add support for enums with their own values. 274 return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly); 275 } 276 277 @SuppressWarnings("unchecked") 278 private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType, 279 boolean sizeOnly) { 280 281 MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType); 282 if (marshaler == null) { 283 throw new IllegalArgumentException(String.format("Unknown Key type: %s", type)); 284 } 285 286 return marshaler.marshal(value, buffer, nativeType, sizeOnly); 287 } 288 289 private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType, 290 boolean sizeOnly) { 291 292 int size = 0; 293 int arrayLength = Array.getLength(value); 294 295 @SuppressWarnings("unchecked") 296 Class<Object> componentType = (Class<Object>)type.getComponentType(); 297 298 for (int i = 0; i < arrayLength; ++i) { 299 size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly); 300 } 301 302 return size; 303 } 304 305 @SuppressWarnings("unchecked") 306 private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) { 307 308 T val; 309 310 if (nativeType == TYPE_BYTE) { 311 val = (T) (Byte) buffer.get(); 312 } else if (nativeType == TYPE_INT32) { 313 val = (T) (Integer) buffer.getInt(); 314 } else if (nativeType == TYPE_FLOAT) { 315 val = (T) (Float) buffer.getFloat(); 316 } else if (nativeType == TYPE_INT64) { 317 val = (T) (Long) buffer.getLong(); 318 } else if (nativeType == TYPE_DOUBLE) { 319 val = (T) (Double) buffer.getDouble(); 320 } else if (nativeType == TYPE_RATIONAL) { 321 val = (T) new Rational(buffer.getInt(), buffer.getInt()); 322 } else { 323 throw new UnsupportedOperationException("Unknown type, can't unpack a native type " 324 + nativeType); 325 } 326 327 /** 328 * Rewrite types when the native type doesn't match the managed type 329 * - Byte -> Boolean 330 * - Byte -> Integer 331 */ 332 333 if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { 334 // Since a boolean can't be cast to byte, and we don't want to use getBoolean 335 byte asByte = (Byte) val; 336 boolean asBool = asByte != 0; 337 val = (T) (Boolean) asBool; 338 } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { 339 byte asByte = (Byte) val; 340 int asInt = asByte; 341 val = (T) (Integer) asInt; 342 } else if (type != getExpectedType(nativeType)) { 343 throw new UnsupportedOperationException("Tried to unpack a type of " + type + 344 " but we expected the type to be " + getExpectedType(nativeType)); 345 } 346 347 return val; 348 } 349 350 @SuppressWarnings({"unchecked", "rawtypes"}) 351 private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) { 352 353 if (type.isPrimitive() || type == Rational.class) { 354 return unpackSingleNative(buffer, type, nativeType); 355 } 356 357 if (type.isEnum()) { 358 return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType); 359 } 360 361 if (type.isArray()) { 362 return unpackArray(buffer, type, nativeType); 363 } 364 365 T instance = unpackClass(buffer, type, nativeType); 366 367 return instance; 368 } 369 370 private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type, 371 int nativeType) { 372 int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType); 373 return getEnumFromValue(type, ordinal); 374 } 375 376 private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) { 377 378 MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType); 379 if (marshaler == null) { 380 throw new IllegalArgumentException("Unknown class type: " + type); 381 } 382 383 return marshaler.unmarshal(buffer, nativeType); 384 } 385 386 @SuppressWarnings("unchecked") 387 private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) { 388 389 Class<?> componentType = type.getComponentType(); 390 Object array; 391 392 int elementSize = getTypeSize(nativeType); 393 394 MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType); 395 if (marshaler != null) { 396 elementSize = marshaler.getNativeSize(nativeType); 397 } 398 399 if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) { 400 int remaining = buffer.remaining(); 401 int arraySize = remaining / elementSize; 402 403 Log.v(TAG, 404 String.format( 405 "Attempting to unpack array (count = %d, element size = %d, bytes " + 406 "remaining = %d) for type %s", 407 arraySize, elementSize, remaining, type)); 408 409 array = Array.newInstance(componentType, arraySize); 410 for (int i = 0; i < arraySize; ++i) { 411 Object elem = unpackSingle(buffer, componentType, nativeType); 412 Array.set(array, i, elem); 413 } 414 } else { 415 // Dynamic size, use an array list. 416 ArrayList<Object> arrayList = new ArrayList<Object>(); 417 418 int primitiveSize = getTypeSize(nativeType); 419 while (buffer.remaining() >= primitiveSize) { 420 Object elem = unpackSingle(buffer, componentType, nativeType); 421 arrayList.add(elem); 422 } 423 424 array = arrayList.toArray((T[]) Array.newInstance(componentType, 0)); 425 } 426 427 if (buffer.remaining() != 0) { 428 Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " 429 + type); 430 } 431 432 return (T) array; 433 } 434 435 private long mMetadataPtr; // native CameraMetadata* 436 437 private native long nativeAllocate(); 438 private native long nativeAllocateCopy(CameraMetadataNative other) 439 throws NullPointerException; 440 441 private native synchronized void nativeWriteToParcel(Parcel dest); 442 private native synchronized void nativeReadFromParcel(Parcel source); 443 private native synchronized void nativeSwap(CameraMetadataNative other) 444 throws NullPointerException; 445 private native synchronized void nativeClose(); 446 private native synchronized boolean nativeIsEmpty(); 447 private native synchronized int nativeGetEntryCount(); 448 449 private native synchronized byte[] nativeReadValues(int tag); 450 private native synchronized void nativeWriteValues(int tag, byte[] src); 451 452 private static native int nativeGetTagFromKey(String keyName) 453 throws IllegalArgumentException; 454 private static native int nativeGetTypeFromTag(int tag) 455 throws IllegalArgumentException; 456 private static native void nativeClassInit(); 457 458 /** 459 * <p>Perform a 0-copy swap of the internal metadata with another object.</p> 460 * 461 * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p> 462 * 463 * @param other Metadata to swap with 464 * @throws NullPointerException if other was null 465 * @hide 466 */ 467 public void swap(CameraMetadataNative other) { 468 nativeSwap(other); 469 } 470 471 /** 472 * @hide 473 */ 474 public int getEntryCount() { 475 return nativeGetEntryCount(); 476 } 477 478 /** 479 * Does this metadata contain at least 1 entry? 480 * 481 * @hide 482 */ 483 public boolean isEmpty() { 484 return nativeIsEmpty(); 485 } 486 487 /** 488 * Convert a key string into the equivalent native tag. 489 * 490 * @throws IllegalArgumentException if the key was not recognized 491 * @throws NullPointerException if the key was null 492 * 493 * @hide 494 */ 495 public static int getTag(String key) { 496 return nativeGetTagFromKey(key); 497 } 498 499 /** 500 * Get the underlying native type for a tag. 501 * 502 * @param tag An integer tag, see e.g. {@link #getTag} 503 * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE} 504 * 505 * @hide 506 */ 507 public static int getNativeType(int tag) { 508 return nativeGetTypeFromTag(tag); 509 } 510 511 /** 512 * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing 513 * the entry if src was null.</p> 514 * 515 * <p>An empty array can be passed in to update the entry to 0 elements.</p> 516 * 517 * @param tag An integer tag, see e.g. {@link #getTag} 518 * @param src An array of bytes, or null to erase the entry 519 * 520 * @hide 521 */ 522 public void writeValues(int tag, byte[] src) { 523 nativeWriteValues(tag, src); 524 } 525 526 /** 527 * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize 528 * the data properly.</p> 529 * 530 * <p>An empty array can be returned to denote an existing entry with 0 elements.</p> 531 * 532 * @param tag An integer tag, see e.g. {@link #getTag} 533 * 534 * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise. 535 * @hide 536 */ 537 public byte[] readValues(int tag) { 538 // TODO: Optimization. Native code returns a ByteBuffer instead. 539 return nativeReadValues(tag); 540 } 541 542 @Override 543 protected void finalize() throws Throwable { 544 try { 545 close(); 546 } finally { 547 super.finalize(); 548 } 549 } 550 551 private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = 552 new HashMap<Class<? extends Enum>, int[]>(); 553 /** 554 * Register a non-sequential set of values to be used with the pack/unpack functions. 555 * This enables get/set to correctly marshal the enum into a value that is C-compatible. 556 * 557 * @param enumType The class for an enum 558 * @param values A list of values mapping to the ordinals of the enum 559 * 560 * @hide 561 */ 562 public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { 563 if (enumType.getEnumConstants().length != values.length) { 564 throw new IllegalArgumentException( 565 "Expected values array to be the same size as the enumTypes values " 566 + values.length + " for type " + enumType); 567 } 568 569 Log.v(TAG, "Registered enum values for type " + enumType + " values"); 570 571 sEnumValues.put(enumType, values); 572 } 573 574 /** 575 * Get the numeric value from an enum. This is usually the same as the ordinal value for 576 * enums that have fully sequential values, although for C-style enums the range of values 577 * may not map 1:1. 578 * 579 * @param enumValue Enum instance 580 * @return Int guaranteed to be ABI-compatible with the C enum equivalent 581 */ 582 private static <T extends Enum<T>> int getEnumValue(T enumValue) { 583 int[] values; 584 values = sEnumValues.get(enumValue.getClass()); 585 586 int ordinal = enumValue.ordinal(); 587 if (values != null) { 588 return values[ordinal]; 589 } 590 591 return ordinal; 592 } 593 594 /** 595 * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. 596 * 597 * @param enumType Class of the enum we want to find 598 * @param value The numeric value of the enum 599 * @return An instance of the enum 600 */ 601 private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { 602 int ordinal; 603 604 int[] registeredValues = sEnumValues.get(enumType); 605 if (registeredValues != null) { 606 ordinal = -1; 607 608 for (int i = 0; i < registeredValues.length; ++i) { 609 if (registeredValues[i] == value) { 610 ordinal = i; 611 break; 612 } 613 } 614 } else { 615 ordinal = value; 616 } 617 618 T[] values = enumType.getEnumConstants(); 619 620 if (ordinal < 0 || ordinal >= values.length) { 621 throw new IllegalArgumentException( 622 String.format( 623 "Argument 'value' (%d) was not a valid enum value for type %s " 624 + "(registered? %b)", 625 value, 626 enumType, (registeredValues != null))); 627 } 628 629 return values[ordinal]; 630 } 631 632 static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new 633 HashMap<Class<?>, MetadataMarshalClass<?>>(); 634 635 private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) { 636 sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler); 637 } 638 639 @SuppressWarnings("unchecked") 640 private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) { 641 MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type); 642 643 if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) { 644 throw new UnsupportedOperationException("Unsupported type " + nativeType + 645 " to be marshalled to/from a " + type); 646 } 647 648 return marshaler; 649 } 650 651 /** 652 * We use a class initializer to allow the native code to cache some field offsets 653 */ 654 static { 655 System.loadLibrary("media_jni"); 656 nativeClassInit(); 657 658 Log.v(TAG, "Shall register metadata marshalers"); 659 660 // load built-in marshallers 661 registerMarshaler(new MetadataMarshalRect()); 662 registerMarshaler(new MetadataMarshalSize()); 663 registerMarshaler(new MetadataMarshalString()); 664 665 Log.v(TAG, "Registered metadata marshalers"); 666 } 667 668} 669