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