1 /* 2 * Copyright (C) 2020 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 android.media; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.util.Log; 23 import android.util.Pair; 24 25 import java.lang.reflect.ParameterizedType; 26 import java.nio.BufferUnderflowException; 27 import java.nio.ByteBuffer; 28 import java.nio.ByteOrder; 29 import java.nio.charset.Charset; 30 import java.nio.charset.StandardCharsets; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.Objects; 34 import java.util.Set; 35 36 /** 37 * AudioMetadata class is used to manage typed key-value pairs for 38 * configuration and capability requests within the Audio Framework. 39 */ 40 public final class AudioMetadata { 41 private static final String TAG = "AudioMetadata"; 42 43 /** 44 * Key interface for the {@code AudioMetadata} map. 45 * 46 * <p>The presence of this {@code Key} interface on an object allows 47 * it to reference metadata in the Audio Framework.</p> 48 * 49 * <p>Vendors are allowed to implement this {@code Key} interface for their debugging or 50 * private application use. To avoid name conflicts, vendor key names should be qualified by 51 * the vendor company name followed by a dot; for example, "vendorCompany.someVolume".</p> 52 * 53 * @param <T> type of value associated with {@code Key}. 54 */ 55 /* 56 * Internal details: 57 * Conceivably metadata keys exposing multiple interfaces 58 * could be eligible to work in multiple framework domains. 59 */ 60 public interface Key<T> { 61 /** 62 * Returns the internal name of the key. The name should be unique in the 63 * {@code AudioMetadata} namespace. Vendors should prefix their keys with 64 * the company name followed by a dot. 65 */ 66 @NonNull getName()67 String getName(); 68 69 /** 70 * Returns the class type {@code T} of the associated value. Valid class types for 71 * {@link android.os.Build.VERSION_CODES#R} are 72 * {@code Integer.class}, {@code Long.class}, {@code Float.class}, {@code Double.class}, 73 * {@code String.class}. 74 */ 75 @NonNull getValueClass()76 Class<T> getValueClass(); 77 78 // TODO: consider adding bool isValid(@NonNull T value) 79 } 80 81 /** 82 * Creates a {@link AudioMetadataMap} suitable for adding keys. 83 * @return an empty {@link AudioMetadataMap} instance. 84 */ 85 @NonNull createMap()86 public static AudioMetadataMap createMap() { 87 return new BaseMap(); 88 } 89 90 /** 91 * A container class for AudioMetadata Format keys. 92 * 93 * @see AudioTrack.OnCodecFormatChangedListener 94 */ 95 public static class Format { 96 // The key name strings used here must match that of the native framework, but are 97 // allowed to change between API releases. This due to the Java specification 98 // on what is a compile time constant. 99 // 100 // Key<?> are final variables but not constant variables (per Java spec 4.12.4) because 101 // the keys are not a primitive type nor a String initialized by a constant expression. 102 // Hence (per Java spec 13.1.3), they are not resolved at compile time, 103 // rather are picked up by applications at run time. 104 // 105 // So the contractual API behavior of AudioMetadata.Key<> are different than Strings 106 // initialized by a constant expression (for example MediaFormat.KEY_*). 107 108 // See MediaFormat 109 /** 110 * A key representing the bitrate of the encoded stream used in 111 * 112 * If the stream is variable bitrate, this is the average bitrate of the stream. 113 * The unit is bits per second. 114 * 115 * An Integer value. 116 * 117 * @see MediaFormat#KEY_BIT_RATE 118 */ 119 @NonNull public static final Key<Integer> KEY_BIT_RATE = 120 createKey("bitrate", Integer.class); 121 122 /** 123 * A key representing the audio channel mask of the stream. 124 * 125 * An Integer value. 126 * 127 * @see AudioTrack#getChannelConfiguration() 128 * @see MediaFormat#KEY_CHANNEL_MASK 129 */ 130 @NonNull public static final Key<Integer> KEY_CHANNEL_MASK = 131 createKey("channel-mask", Integer.class); 132 133 134 /** 135 * A key representing the codec mime string. 136 * 137 * A String value. 138 * 139 * @see MediaFormat#KEY_MIME 140 */ 141 @NonNull public static final Key<String> KEY_MIME = createKey("mime", String.class); 142 143 /** 144 * A key representing the audio sample rate in Hz of the stream. 145 * 146 * An Integer value. 147 * 148 * @see AudioFormat#getSampleRate() 149 * @see MediaFormat#KEY_SAMPLE_RATE 150 */ 151 @NonNull public static final Key<Integer> KEY_SAMPLE_RATE = 152 createKey("sample-rate", Integer.class); 153 154 // Unique to Audio 155 156 /** 157 * A key representing the bit width of an element of decoded data. 158 * 159 * An Integer value. 160 */ 161 @NonNull public static final Key<Integer> KEY_BIT_WIDTH = 162 createKey("bit-width", Integer.class); 163 164 /** 165 * A key representing the presence of Atmos in an E-AC3 stream. 166 * 167 * A Boolean value which is true if Atmos is present in an E-AC3 stream. 168 */ 169 170 // Since Boolean isn't handled by Parceling, we translate 171 // internally to KEY_HAS_ATMOS when sending through JNI. 172 // Consider deprecating this key for KEY_HAS_ATMOS in the future. 173 // 174 @NonNull public static final Key<Boolean> KEY_ATMOS_PRESENT = 175 createKey("atmos-present", Boolean.class); 176 177 /** 178 * A key representing the presence of Atmos in an E-AC3 stream. 179 * 180 * An Integer value which is nonzero if Atmos is present in an E-AC3 stream. 181 * The integer representation is used for communication to the native side. 182 * @hide 183 */ 184 @NonNull public static final Key<Integer> KEY_HAS_ATMOS = 185 createKey("has-atmos", Integer.class); 186 187 /** 188 * A key representing the audio encoding used for the stream. 189 * This is the same encoding used in {@link AudioFormat#getEncoding()}. 190 * 191 * An Integer value. 192 * 193 * @see AudioFormat#getEncoding() 194 */ 195 @NonNull public static final Key<Integer> KEY_AUDIO_ENCODING = 196 createKey("audio-encoding", Integer.class); 197 Format()198 private Format() {} // delete constructor 199 } 200 201 ///////////////////////////////////////////////////////////////////////// 202 // Hidden methods and functions. 203 204 /** 205 * Returns a Key object with the correct interface for the AudioMetadata. 206 * 207 * An interface with the same name and type will be treated as 208 * identical for the purposes of value storage, even though 209 * other methods or hidden parameters may return different values. 210 * 211 * @param name The name of the key. 212 * @param type The class type of the value represented by the key. 213 * @param <T> The type of value. 214 * @return a new key interface. 215 * 216 * Creating keys is currently only allowed by the Framework. 217 * @hide 218 */ 219 @NonNull createKey(@onNull String name, @NonNull Class<T> type)220 public static <T> Key<T> createKey(@NonNull String name, @NonNull Class<T> type) { 221 // Implementation specific. 222 return new Key<T>() { 223 private final String mName = name; 224 private final Class<T> mType = type; 225 226 @Override 227 @NonNull 228 public String getName() { 229 return mName; 230 } 231 232 @Override 233 @NonNull 234 public Class<T> getValueClass() { 235 return mType; 236 } 237 238 /** 239 * Return true if the name and the type of two objects are the same. 240 */ 241 @Override 242 public boolean equals(Object obj) { 243 if (obj == this) { 244 return true; 245 } 246 if (!(obj instanceof Key)) { 247 return false; 248 } 249 Key<?> other = (Key<?>) obj; 250 return mName.equals(other.getName()) && mType.equals(other.getValueClass()); 251 } 252 253 @Override 254 public int hashCode() { 255 return Objects.hash(mName, mType); 256 } 257 }; 258 } 259 260 /** 261 * @hide 262 * 263 * AudioMetadata is based on interfaces in order to allow multiple inheritance 264 * and maximum flexibility in implementation. 265 * 266 * Here, we provide a simple implementation of {@link Map} interface; 267 * Note that the Keys are not specific to this Map implementation. 268 * 269 * It is possible to require the keys to be of a certain class 270 * before allowing a set or get operation. 271 */ 272 public static class BaseMap implements AudioMetadataMap { 273 @Override containsKey(@onNull Key<T> key)274 public <T> boolean containsKey(@NonNull Key<T> key) { 275 Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key)); 276 return valuePair != null; 277 } 278 279 @Override 280 @NonNull dup()281 public AudioMetadataMap dup() { 282 BaseMap map = new BaseMap(); 283 map.mHashMap.putAll(this.mHashMap); 284 return map; 285 } 286 287 @Override 288 @Nullable get(@onNull Key<T> key)289 public <T> T get(@NonNull Key<T> key) { 290 Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key)); 291 return (T) getValueFromValuePair(valuePair); 292 } 293 294 @Override 295 @NonNull keySet()296 public Set<Key<?>> keySet() { 297 HashSet<Key<?>> set = new HashSet(); 298 for (Pair<Key<?>, Object> pair : mHashMap.values()) { 299 set.add(pair.first); 300 } 301 return set; 302 } 303 304 @Override 305 @Nullable remove(@onNull Key<T> key)306 public <T> T remove(@NonNull Key<T> key) { 307 Pair<Key<?>, Object> valuePair = mHashMap.remove(pairFromKey(key)); 308 return (T) getValueFromValuePair(valuePair); 309 } 310 311 @Override 312 @Nullable set(@onNull Key<T> key, @NonNull T value)313 public <T> T set(@NonNull Key<T> key, @NonNull T value) { 314 Objects.requireNonNull(value); 315 Pair<Key<?>, Object> valuePair = mHashMap 316 .put(pairFromKey(key), new Pair<Key<?>, Object>(key, value)); 317 return (T) getValueFromValuePair(valuePair); 318 } 319 320 @Override size()321 public int size() { 322 return mHashMap.size(); 323 } 324 325 /** 326 * Return true if the object is a BaseMap and the content from two BaseMap are the same. 327 * Note: Need to override the equals functions of Key<T> for HashMap comparison. 328 */ 329 @Override equals(Object obj)330 public boolean equals(Object obj) { 331 if (obj == this) { 332 return true; 333 } 334 if (!(obj instanceof BaseMap)) { 335 return false; 336 } 337 BaseMap other = (BaseMap) obj; 338 return mHashMap.equals(other.mHashMap); 339 } 340 341 @Override hashCode()342 public int hashCode() { 343 return Objects.hash(mHashMap); 344 } 345 346 /* 347 * Implementation specific. 348 * 349 * To store the value in the HashMap we need to convert the Key interface 350 * to a hashcode() / equals() compliant Pair. 351 */ 352 @NonNull pairFromKey(@onNull Key<T> key)353 private static <T> Pair<String, Class<?>> pairFromKey(@NonNull Key<T> key) { 354 Objects.requireNonNull(key); 355 return new Pair<String, Class<?>>(key.getName(), key.getValueClass()); 356 } 357 358 /* 359 * Implementation specific. 360 * 361 * We store in a Pair (valuePair) the key along with the Object value. 362 * This helper returns the Object value from the value pair. 363 */ 364 @Nullable getValueFromValuePair(@ullable Pair<Key<?>, Object> valuePair)365 private static Object getValueFromValuePair(@Nullable Pair<Key<?>, Object> valuePair) { 366 if (valuePair == null) { 367 return null; 368 } 369 return valuePair.second; 370 } 371 372 /* 373 * Implementation specific. 374 * 375 * We use a HashMap to back the AudioMetadata BaseMap object. 376 * This is not locked, so concurrent reads are permitted if all threads 377 * have a ReadMap; this is risky with a Map. 378 */ 379 private final HashMap<Pair<String, Class<?>>, Pair<Key<?>, Object>> mHashMap = 380 new HashMap(); 381 } 382 383 // The audio metadata object type index should be kept the same as 384 // the ones in audio_utils::metadata::metadata_types 385 private static final int AUDIO_METADATA_OBJ_TYPE_NONE = 0; 386 private static final int AUDIO_METADATA_OBJ_TYPE_INT = 1; 387 private static final int AUDIO_METADATA_OBJ_TYPE_LONG = 2; 388 private static final int AUDIO_METADATA_OBJ_TYPE_FLOAT = 3; 389 private static final int AUDIO_METADATA_OBJ_TYPE_DOUBLE = 4; 390 private static final int AUDIO_METADATA_OBJ_TYPE_STRING = 5; 391 // BaseMap is corresponding to audio_utils::metadata::Data 392 private static final int AUDIO_METADATA_OBJ_TYPE_BASEMAP = 6; 393 394 private static final HashMap<Class, Integer> AUDIO_METADATA_OBJ_TYPES = new HashMap<>() {{ 395 put(Integer.class, AUDIO_METADATA_OBJ_TYPE_INT); 396 put(Long.class, AUDIO_METADATA_OBJ_TYPE_LONG); 397 put(Float.class, AUDIO_METADATA_OBJ_TYPE_FLOAT); 398 put(Double.class, AUDIO_METADATA_OBJ_TYPE_DOUBLE); 399 put(String.class, AUDIO_METADATA_OBJ_TYPE_STRING); 400 put(BaseMap.class, AUDIO_METADATA_OBJ_TYPE_BASEMAP); 401 }}; 402 403 private static final Charset AUDIO_METADATA_CHARSET = StandardCharsets.UTF_8; 404 405 /** 406 * An auto growing byte buffer 407 */ 408 private static class AutoGrowByteBuffer { 409 private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE; 410 private static final int LONG_BYTE_COUNT = Long.SIZE / Byte.SIZE; 411 private static final int FLOAT_BYTE_COUNT = Float.SIZE / Byte.SIZE; 412 private static final int DOUBLE_BYTE_COUNT = Double.SIZE / Byte.SIZE; 413 414 private ByteBuffer mBuffer; 415 AutoGrowByteBuffer()416 AutoGrowByteBuffer() { 417 this(1024); 418 } 419 AutoGrowByteBuffer(@ntRangefrom = 0) int initialCapacity)420 AutoGrowByteBuffer(@IntRange(from = 0) int initialCapacity) { 421 mBuffer = ByteBuffer.allocateDirect(initialCapacity); 422 } 423 getRawByteBuffer()424 public ByteBuffer getRawByteBuffer() { 425 // Slice the buffer from 0 to position. 426 int limit = mBuffer.limit(); 427 int position = mBuffer.position(); 428 mBuffer.limit(position); 429 mBuffer.position(0); 430 ByteBuffer buffer = mBuffer.slice(); 431 432 // Restore position and limit. 433 mBuffer.limit(limit); 434 mBuffer.position(position); 435 return buffer; 436 } 437 order()438 public ByteOrder order() { 439 return mBuffer.order(); 440 } 441 position()442 public int position() { 443 return mBuffer.position(); 444 } 445 position(int newPosition)446 public AutoGrowByteBuffer position(int newPosition) { 447 mBuffer.position(newPosition); 448 return this; 449 } 450 order(ByteOrder order)451 public AutoGrowByteBuffer order(ByteOrder order) { 452 mBuffer.order(order); 453 return this; 454 } 455 putInt(int value)456 public AutoGrowByteBuffer putInt(int value) { 457 ensureCapacity(INTEGER_BYTE_COUNT); 458 mBuffer.putInt(value); 459 return this; 460 } 461 putLong(long value)462 public AutoGrowByteBuffer putLong(long value) { 463 ensureCapacity(LONG_BYTE_COUNT); 464 mBuffer.putLong(value); 465 return this; 466 } 467 putFloat(float value)468 public AutoGrowByteBuffer putFloat(float value) { 469 ensureCapacity(FLOAT_BYTE_COUNT); 470 mBuffer.putFloat(value); 471 return this; 472 } 473 putDouble(double value)474 public AutoGrowByteBuffer putDouble(double value) { 475 ensureCapacity(DOUBLE_BYTE_COUNT); 476 mBuffer.putDouble(value); 477 return this; 478 } 479 put(byte[] src)480 public AutoGrowByteBuffer put(byte[] src) { 481 ensureCapacity(src.length); 482 mBuffer.put(src); 483 return this; 484 } 485 486 /** 487 * Ensures capacity to append at least <code>count</code> values. 488 */ ensureCapacity(@ntRange int count)489 private void ensureCapacity(@IntRange int count) { 490 if (mBuffer.remaining() < count) { 491 int newCapacity = mBuffer.position() + count; 492 if (newCapacity > Integer.MAX_VALUE >> 1) { 493 throw new IllegalStateException( 494 "Item memory requirements too large: " + newCapacity); 495 } 496 newCapacity <<= 1; 497 ByteBuffer buffer = ByteBuffer.allocateDirect(newCapacity); 498 buffer.order(mBuffer.order()); 499 500 // Copy data from old buffer to new buffer 501 mBuffer.flip(); 502 buffer.put(mBuffer); 503 504 // Set buffer to new buffer 505 mBuffer = buffer; 506 } 507 } 508 } 509 510 /** 511 * @hide 512 * Describes a unpacking/packing contract of type {@code T} out of a {@link ByteBuffer} 513 * 514 * @param <T> the type being unpack 515 */ 516 private interface DataPackage<T> { 517 /** 518 * Read an item from a {@link ByteBuffer}. 519 * 520 * The parceling format is assumed the same as the one described in 521 * audio_utils::Metadata.h. Copied here as a reference. 522 * All values are native endian order. 523 * 524 * Datum = { (type_size_t) Type (the type index from type_as_value<T>.) 525 * (datum_size_t) Size (size of datum, including the size field) 526 * (byte string) Payload<Type> 527 * } 528 * 529 * Primitive types: 530 * Payload<Type> = { bytes in native endian order } 531 * 532 * Vector, Map, Container types: 533 * Payload<Type> = { (index_size_t) number of elements 534 * (byte string) Payload<Element_Type> * number 535 * } 536 * 537 * Pair container types: 538 * Payload<Type> = { (byte string) Payload<first>, 539 * (byte string) Payload<second> 540 * } 541 * 542 * @param buffer the byte buffer to read from 543 * @return an object, which types is given type for {@link DataPackage} 544 * @throws BufferUnderflowException when there is no enough data remaining 545 * in the buffer for unpacking. 546 */ 547 @Nullable unpack(ByteBuffer buffer)548 T unpack(ByteBuffer buffer); 549 550 /** 551 * Pack the item into a byte array. This is the reversed way of unpacking. 552 * 553 * @param output is the stream to which to write the data 554 * @param obj the item to pack 555 * @return true if packing successfully. Otherwise, return false. 556 */ pack(AutoGrowByteBuffer output, T obj)557 boolean pack(AutoGrowByteBuffer output, T obj); 558 559 /** 560 * Return what kind of data is contained in the package. 561 */ getMyType()562 default Class getMyType() { 563 return (Class) ((ParameterizedType) getClass().getGenericInterfaces()[0]) 564 .getActualTypeArguments()[0]; 565 } 566 } 567 568 /***************************************************************************************** 569 * Following class are common {@link DataPackage} implementations, which include types 570 * that are defined in audio_utils::metadata::metadata_types 571 * 572 * For Java 573 * int32_t corresponds to Integer 574 * int64_t corresponds to Long 575 * float corresponds to Float 576 * double corresponds to Double 577 * std::string corresponds to String 578 * Data corresponds to BaseMap 579 * Datum corresponds to Object 580 ****************************************************************************************/ 581 582 private static final HashMap<Integer, DataPackage<?>> DATA_PACKAGES = new HashMap<>() {{ 583 put(AUDIO_METADATA_OBJ_TYPE_INT, new DataPackage<Integer>() { 584 @Override 585 @Nullable 586 public Integer unpack(ByteBuffer buffer) { 587 return buffer.getInt(); 588 } 589 590 @Override 591 public boolean pack(AutoGrowByteBuffer output, Integer obj) { 592 output.putInt(obj); 593 return true; 594 } 595 }); 596 put(AUDIO_METADATA_OBJ_TYPE_LONG, new DataPackage<Long>() { 597 @Override 598 @Nullable 599 public Long unpack(ByteBuffer buffer) { 600 return buffer.getLong(); 601 } 602 603 @Override 604 public boolean pack(AutoGrowByteBuffer output, Long obj) { 605 output.putLong(obj); 606 return true; 607 } 608 }); 609 put(AUDIO_METADATA_OBJ_TYPE_FLOAT, new DataPackage<Float>() { 610 @Override 611 @Nullable 612 public Float unpack(ByteBuffer buffer) { 613 return buffer.getFloat(); 614 } 615 616 @Override 617 public boolean pack(AutoGrowByteBuffer output, Float obj) { 618 output.putFloat(obj); 619 return true; 620 } 621 }); 622 put(AUDIO_METADATA_OBJ_TYPE_DOUBLE, new DataPackage<Double>() { 623 @Override 624 @Nullable 625 public Double unpack(ByteBuffer buffer) { 626 return buffer.getDouble(); 627 } 628 629 @Override 630 public boolean pack(AutoGrowByteBuffer output, Double obj) { 631 output.putDouble(obj); 632 return true; 633 } 634 }); 635 put(AUDIO_METADATA_OBJ_TYPE_STRING, new DataPackage<String>() { 636 @Override 637 @Nullable 638 public String unpack(ByteBuffer buffer) { 639 int dataSize = buffer.getInt(); 640 if (buffer.position() + dataSize > buffer.limit()) { 641 return null; 642 } 643 byte[] valueArr = new byte[dataSize]; 644 buffer.get(valueArr); 645 String value = new String(valueArr, AUDIO_METADATA_CHARSET); 646 return value; 647 } 648 649 /** 650 * This is a reversed operation of unpack. It is needed to write the String 651 * at bytes encoded with AUDIO_METADATA_CHARSET. There should be an integer 652 * value representing the length of the bytes written before the bytes. 653 */ 654 @Override 655 public boolean pack(AutoGrowByteBuffer output, String obj) { 656 byte[] valueArr = obj.getBytes(AUDIO_METADATA_CHARSET); 657 output.putInt(valueArr.length); 658 output.put(valueArr); 659 return true; 660 } 661 }); 662 put(AUDIO_METADATA_OBJ_TYPE_BASEMAP, new BaseMapPackage()); 663 }}; 664 // ObjectPackage is a special case that it is expected to unpack audio_utils::metadata::Datum, 665 // which contains data type and data size besides the payload for the data. 666 private static final ObjectPackage OBJECT_PACKAGE = new ObjectPackage(); 667 668 private static class ObjectPackage implements DataPackage<Pair<Class, Object>> { 669 /** 670 * The {@link ObjectPackage} will unpack byte string for audio_utils::metadata::Datum. 671 * Since the Datum is a std::any, {@link Object} is used to carrying the data. The 672 * data type is stored in the data package header. In that case, a {@link Class} 673 * will also be returned to indicate the actual type for the object. 674 */ 675 @Override 676 @Nullable unpack(ByteBuffer buffer)677 public Pair<Class, Object> unpack(ByteBuffer buffer) { 678 int dataType = buffer.getInt(); 679 DataPackage dataPackage = DATA_PACKAGES.get(dataType); 680 if (dataPackage == null) { 681 Log.e(TAG, "Cannot find DataPackage for type:" + dataType); 682 return null; 683 } 684 int dataSize = buffer.getInt(); 685 int position = buffer.position(); 686 Object obj = dataPackage.unpack(buffer); 687 if (buffer.position() - position != dataSize) { 688 Log.e(TAG, "Broken data package"); 689 return null; 690 } 691 return new Pair<Class, Object>(dataPackage.getMyType(), obj); 692 } 693 694 @Override pack(AutoGrowByteBuffer output, Pair<Class, Object> obj)695 public boolean pack(AutoGrowByteBuffer output, Pair<Class, Object> obj) { 696 final Integer dataType = AUDIO_METADATA_OBJ_TYPES.get(obj.first); 697 if (dataType == null) { 698 Log.e(TAG, "Cannot find data type for " + obj.first); 699 return false; 700 } 701 DataPackage dataPackage = DATA_PACKAGES.get(dataType); 702 if (dataPackage == null) { 703 Log.e(TAG, "Cannot find DataPackage for type:" + dataType); 704 return false; 705 } 706 output.putInt(dataType); 707 int position = output.position(); // Keep current position. 708 output.putInt(0); // Keep a place for the size of payload. 709 int payloadIdx = output.position(); 710 if (!dataPackage.pack(output, obj.second)) { 711 Log.i(TAG, "Failed to pack object: " + obj.second); 712 return false; 713 } 714 // Put the actual payload size. 715 int currentPosition = output.position(); 716 output.position(position); 717 output.putInt(currentPosition - payloadIdx); 718 output.position(currentPosition); 719 return true; 720 } 721 } 722 723 /** 724 * BaseMap will be corresponding to audio_utils::metadata::Data. 725 */ 726 private static class BaseMapPackage implements DataPackage<BaseMap> { 727 @Override 728 @Nullable unpack(ByteBuffer buffer)729 public BaseMap unpack(ByteBuffer buffer) { 730 BaseMap ret = new BaseMap(); 731 int mapSize = buffer.getInt(); 732 DataPackage<String> strDataPackage = 733 (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING); 734 if (strDataPackage == null) { 735 Log.e(TAG, "Cannot find DataPackage for String"); 736 return null; 737 } 738 for (int i = 0; i < mapSize; i++) { 739 String key = strDataPackage.unpack(buffer); 740 if (key == null) { 741 Log.e(TAG, "Failed to unpack key for map"); 742 return null; 743 } 744 Pair<Class, Object> value = OBJECT_PACKAGE.unpack(buffer); 745 if (value == null) { 746 Log.e(TAG, "Failed to unpack value for map"); 747 return null; 748 } 749 750 // Special handling of KEY_ATMOS_PRESENT. 751 if (key.equals(Format.KEY_HAS_ATMOS.getName()) 752 && value.first == Format.KEY_HAS_ATMOS.getValueClass()) { 753 ret.set(Format.KEY_ATMOS_PRESENT, 754 (Boolean) ((int) value.second != 0)); // Translate Integer to Boolean 755 continue; // Should we store both keys in the java table? 756 } 757 758 ret.set(createKey(key, value.first), value.first.cast(value.second)); 759 } 760 return ret; 761 } 762 763 @Override pack(AutoGrowByteBuffer output, BaseMap obj)764 public boolean pack(AutoGrowByteBuffer output, BaseMap obj) { 765 output.putInt(obj.size()); 766 DataPackage<String> strDataPackage = 767 (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING); 768 if (strDataPackage == null) { 769 Log.e(TAG, "Cannot find DataPackage for String"); 770 return false; 771 } 772 for (Key<?> key : obj.keySet()) { 773 Object value = obj.get(key); 774 775 // Special handling of KEY_ATMOS_PRESENT. 776 if (key == Format.KEY_ATMOS_PRESENT) { 777 key = Format.KEY_HAS_ATMOS; 778 value = (Integer) ((boolean) value ? 1 : 0); // Translate Boolean to Integer 779 } 780 781 if (!strDataPackage.pack(output, key.getName())) { 782 Log.i(TAG, "Failed to pack key: " + key.getName()); 783 return false; 784 } 785 if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), value))) { 786 Log.i(TAG, "Failed to pack value: " + obj.get(key)); 787 return false; 788 } 789 } 790 return true; 791 } 792 } 793 794 /** 795 * @hide 796 * Extract a {@link BaseMap} from a given {@link ByteBuffer} 797 * @param buffer is a byte string that contains information to unpack. 798 * @return a {@link BaseMap} object if extracting successfully from given byte buffer. 799 * Otherwise, returns {@code null}. 800 */ 801 @Nullable fromByteBuffer(ByteBuffer buffer)802 public static BaseMap fromByteBuffer(ByteBuffer buffer) { 803 DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP); 804 if (dataPackage == null) { 805 Log.e(TAG, "Cannot find DataPackage for BaseMap"); 806 return null; 807 } 808 try { 809 return (BaseMap) dataPackage.unpack(buffer); 810 } catch (BufferUnderflowException e) { 811 Log.e(TAG, "No enough data to unpack"); 812 } 813 return null; 814 } 815 816 /** 817 * @hide 818 * Pack a {link BaseMap} to a {@link ByteBuffer} 819 * @param data is the object for packing 820 * @param order is the byte order 821 * @return a {@link ByteBuffer} if successfully packing the data. 822 * Otherwise, returns {@code null}; 823 */ 824 @Nullable toByteBuffer(BaseMap data, ByteOrder order)825 public static ByteBuffer toByteBuffer(BaseMap data, ByteOrder order) { 826 DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP); 827 if (dataPackage == null) { 828 Log.e(TAG, "Cannot find DataPackage for BaseMap"); 829 return null; 830 } 831 AutoGrowByteBuffer output = new AutoGrowByteBuffer(); 832 output.order(order); 833 if (dataPackage.pack(output, data)) { 834 return output.getRawByteBuffer(); 835 } 836 return null; 837 } 838 839 // Delete the constructor as there is nothing to implement here. AudioMetadata()840 private AudioMetadata() {} 841 } 842