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 198 199 /** 200 * A key representing the audio presentation id being decoded by a next generation 201 * audio decoder. 202 * 203 * An Integer value representing presentation id. 204 * 205 * @see AudioPresentation#getPresentationId() 206 */ 207 @NonNull public static final Key<Integer> KEY_PRESENTATION_ID = 208 createKey("presentation-id", Integer.class); 209 210 /** 211 * A key representing the audio program id being decoded by a next generation 212 * audio decoder. 213 * 214 * An Integer value representing program id. 215 * 216 * @see AudioPresentation#getProgramId() 217 */ 218 @NonNull public static final Key<Integer> KEY_PROGRAM_ID = 219 createKey("program-id", Integer.class); 220 221 222 /** 223 * A key representing the audio presentation content classifier being rendered 224 * by a next generation audio decoder. 225 * 226 * An Integer value representing presentation content classifier. 227 * 228 * @see AudioPresentation.ContentClassifier 229 * One of {@link AudioPresentation#CONTENT_UNKNOWN}, 230 * {@link AudioPresentation#CONTENT_MAIN}, 231 * {@link AudioPresentation#CONTENT_MUSIC_AND_EFFECTS}, 232 * {@link AudioPresentation#CONTENT_VISUALLY_IMPAIRED}, 233 * {@link AudioPresentation#CONTENT_HEARING_IMPAIRED}, 234 * {@link AudioPresentation#CONTENT_DIALOG}, 235 * {@link AudioPresentation#CONTENT_COMMENTARY}, 236 * {@link AudioPresentation#CONTENT_EMERGENCY}, 237 * {@link 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 HashMap<Class, Integer> AUDIO_METADATA_OBJ_TYPES = new HashMap<>() {{ 450 put(Integer.class, AUDIO_METADATA_OBJ_TYPE_INT); 451 put(Long.class, AUDIO_METADATA_OBJ_TYPE_LONG); 452 put(Float.class, AUDIO_METADATA_OBJ_TYPE_FLOAT); 453 put(Double.class, AUDIO_METADATA_OBJ_TYPE_DOUBLE); 454 put(String.class, AUDIO_METADATA_OBJ_TYPE_STRING); 455 put(BaseMap.class, AUDIO_METADATA_OBJ_TYPE_BASEMAP); 456 }}; 457 458 private static final Charset AUDIO_METADATA_CHARSET = StandardCharsets.UTF_8; 459 460 /** 461 * An auto growing byte buffer 462 */ 463 private static class AutoGrowByteBuffer { 464 private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE; 465 private static final int LONG_BYTE_COUNT = Long.SIZE / Byte.SIZE; 466 private static final int FLOAT_BYTE_COUNT = Float.SIZE / Byte.SIZE; 467 private static final int DOUBLE_BYTE_COUNT = Double.SIZE / Byte.SIZE; 468 469 private ByteBuffer mBuffer; 470 AutoGrowByteBuffer()471 AutoGrowByteBuffer() { 472 this(1024); 473 } 474 AutoGrowByteBuffer(@ntRangefrom = 0) int initialCapacity)475 AutoGrowByteBuffer(@IntRange(from = 0) int initialCapacity) { 476 mBuffer = ByteBuffer.allocateDirect(initialCapacity); 477 } 478 getRawByteBuffer()479 public ByteBuffer getRawByteBuffer() { 480 // Slice the buffer from 0 to position. 481 int limit = mBuffer.limit(); 482 int position = mBuffer.position(); 483 mBuffer.limit(position); 484 mBuffer.position(0); 485 ByteBuffer buffer = mBuffer.slice(); 486 487 // Restore position and limit. 488 mBuffer.limit(limit); 489 mBuffer.position(position); 490 return buffer; 491 } 492 order()493 public ByteOrder order() { 494 return mBuffer.order(); 495 } 496 position()497 public int position() { 498 return mBuffer.position(); 499 } 500 position(int newPosition)501 public AutoGrowByteBuffer position(int newPosition) { 502 mBuffer.position(newPosition); 503 return this; 504 } 505 order(ByteOrder order)506 public AutoGrowByteBuffer order(ByteOrder order) { 507 mBuffer.order(order); 508 return this; 509 } 510 putInt(int value)511 public AutoGrowByteBuffer putInt(int value) { 512 ensureCapacity(INTEGER_BYTE_COUNT); 513 mBuffer.putInt(value); 514 return this; 515 } 516 putLong(long value)517 public AutoGrowByteBuffer putLong(long value) { 518 ensureCapacity(LONG_BYTE_COUNT); 519 mBuffer.putLong(value); 520 return this; 521 } 522 putFloat(float value)523 public AutoGrowByteBuffer putFloat(float value) { 524 ensureCapacity(FLOAT_BYTE_COUNT); 525 mBuffer.putFloat(value); 526 return this; 527 } 528 putDouble(double value)529 public AutoGrowByteBuffer putDouble(double value) { 530 ensureCapacity(DOUBLE_BYTE_COUNT); 531 mBuffer.putDouble(value); 532 return this; 533 } 534 put(byte[] src)535 public AutoGrowByteBuffer put(byte[] src) { 536 ensureCapacity(src.length); 537 mBuffer.put(src); 538 return this; 539 } 540 541 /** 542 * Ensures capacity to append at least <code>count</code> values. 543 */ ensureCapacity(@ntRange int count)544 private void ensureCapacity(@IntRange int count) { 545 if (mBuffer.remaining() < count) { 546 int newCapacity = mBuffer.position() + count; 547 if (newCapacity > Integer.MAX_VALUE >> 1) { 548 throw new IllegalStateException( 549 "Item memory requirements too large: " + newCapacity); 550 } 551 newCapacity <<= 1; 552 ByteBuffer buffer = ByteBuffer.allocateDirect(newCapacity); 553 buffer.order(mBuffer.order()); 554 555 // Copy data from old buffer to new buffer 556 mBuffer.flip(); 557 buffer.put(mBuffer); 558 559 // Set buffer to new buffer 560 mBuffer = buffer; 561 } 562 } 563 } 564 565 /** 566 * @hide 567 * Describes a unpacking/packing contract of type {@code T} out of a {@link ByteBuffer} 568 * 569 * @param <T> the type being unpack 570 */ 571 private interface DataPackage<T> { 572 /** 573 * Read an item from a {@link ByteBuffer}. 574 * 575 * The parceling format is assumed the same as the one described in 576 * audio_utils::Metadata.h. Copied here as a reference. 577 * All values are native endian order. 578 * 579 * Datum = { (type_size_t) Type (the type index from type_as_value<T>.) 580 * (datum_size_t) Size (size of datum, including the size field) 581 * (byte string) Payload<Type> 582 * } 583 * 584 * Primitive types: 585 * Payload<Type> = { bytes in native endian order } 586 * 587 * Vector, Map, Container types: 588 * Payload<Type> = { (index_size_t) number of elements 589 * (byte string) Payload<Element_Type> * number 590 * } 591 * 592 * Pair container types: 593 * Payload<Type> = { (byte string) Payload<first>, 594 * (byte string) Payload<second> 595 * } 596 * 597 * @param buffer the byte buffer to read from 598 * @return an object, which types is given type for {@link DataPackage} 599 * @throws BufferUnderflowException when there is no enough data remaining 600 * in the buffer for unpacking. 601 */ 602 @Nullable unpack(ByteBuffer buffer)603 T unpack(ByteBuffer buffer); 604 605 /** 606 * Pack the item into a byte array. This is the reversed way of unpacking. 607 * 608 * @param output is the stream to which to write the data 609 * @param obj the item to pack 610 * @return true if packing successfully. Otherwise, return false. 611 */ pack(AutoGrowByteBuffer output, T obj)612 boolean pack(AutoGrowByteBuffer output, T obj); 613 614 /** 615 * Return what kind of data is contained in the package. 616 */ getMyType()617 default Class getMyType() { 618 return (Class) ((ParameterizedType) getClass().getGenericInterfaces()[0]) 619 .getActualTypeArguments()[0]; 620 } 621 } 622 623 /***************************************************************************************** 624 * Following class are common {@link DataPackage} implementations, which include types 625 * that are defined in audio_utils::metadata::metadata_types 626 * 627 * For Java 628 * int32_t corresponds to Integer 629 * int64_t corresponds to Long 630 * float corresponds to Float 631 * double corresponds to Double 632 * std::string corresponds to String 633 * Data corresponds to BaseMap 634 * Datum corresponds to Object 635 ****************************************************************************************/ 636 637 private static final HashMap<Integer, DataPackage<?>> DATA_PACKAGES = new HashMap<>() {{ 638 put(AUDIO_METADATA_OBJ_TYPE_INT, new DataPackage<Integer>() { 639 @Override 640 @Nullable 641 public Integer unpack(ByteBuffer buffer) { 642 return buffer.getInt(); 643 } 644 645 @Override 646 public boolean pack(AutoGrowByteBuffer output, Integer obj) { 647 output.putInt(obj); 648 return true; 649 } 650 }); 651 put(AUDIO_METADATA_OBJ_TYPE_LONG, new DataPackage<Long>() { 652 @Override 653 @Nullable 654 public Long unpack(ByteBuffer buffer) { 655 return buffer.getLong(); 656 } 657 658 @Override 659 public boolean pack(AutoGrowByteBuffer output, Long obj) { 660 output.putLong(obj); 661 return true; 662 } 663 }); 664 put(AUDIO_METADATA_OBJ_TYPE_FLOAT, new DataPackage<Float>() { 665 @Override 666 @Nullable 667 public Float unpack(ByteBuffer buffer) { 668 return buffer.getFloat(); 669 } 670 671 @Override 672 public boolean pack(AutoGrowByteBuffer output, Float obj) { 673 output.putFloat(obj); 674 return true; 675 } 676 }); 677 put(AUDIO_METADATA_OBJ_TYPE_DOUBLE, new DataPackage<Double>() { 678 @Override 679 @Nullable 680 public Double unpack(ByteBuffer buffer) { 681 return buffer.getDouble(); 682 } 683 684 @Override 685 public boolean pack(AutoGrowByteBuffer output, Double obj) { 686 output.putDouble(obj); 687 return true; 688 } 689 }); 690 put(AUDIO_METADATA_OBJ_TYPE_STRING, new DataPackage<String>() { 691 @Override 692 @Nullable 693 public String unpack(ByteBuffer buffer) { 694 int dataSize = buffer.getInt(); 695 if (buffer.position() + dataSize > buffer.limit()) { 696 return null; 697 } 698 byte[] valueArr = new byte[dataSize]; 699 buffer.get(valueArr); 700 String value = new String(valueArr, AUDIO_METADATA_CHARSET); 701 return value; 702 } 703 704 /** 705 * This is a reversed operation of unpack. It is needed to write the String 706 * at bytes encoded with AUDIO_METADATA_CHARSET. There should be an integer 707 * value representing the length of the bytes written before the bytes. 708 */ 709 @Override 710 public boolean pack(AutoGrowByteBuffer output, String obj) { 711 byte[] valueArr = obj.getBytes(AUDIO_METADATA_CHARSET); 712 output.putInt(valueArr.length); 713 output.put(valueArr); 714 return true; 715 } 716 }); 717 put(AUDIO_METADATA_OBJ_TYPE_BASEMAP, new BaseMapPackage()); 718 }}; 719 // ObjectPackage is a special case that it is expected to unpack audio_utils::metadata::Datum, 720 // which contains data type and data size besides the payload for the data. 721 private static final ObjectPackage OBJECT_PACKAGE = new ObjectPackage(); 722 723 private static class ObjectPackage implements DataPackage<Pair<Class, Object>> { 724 /** 725 * The {@link ObjectPackage} will unpack byte string for audio_utils::metadata::Datum. 726 * Since the Datum is a std::any, {@link Object} is used to carrying the data. The 727 * data type is stored in the data package header. In that case, a {@link Class} 728 * will also be returned to indicate the actual type for the object. 729 */ 730 @Override 731 @Nullable unpack(ByteBuffer buffer)732 public Pair<Class, Object> unpack(ByteBuffer buffer) { 733 int dataType = buffer.getInt(); 734 DataPackage dataPackage = DATA_PACKAGES.get(dataType); 735 if (dataPackage == null) { 736 Log.e(TAG, "Cannot find DataPackage for type:" + dataType); 737 return null; 738 } 739 int dataSize = buffer.getInt(); 740 int position = buffer.position(); 741 Object obj = dataPackage.unpack(buffer); 742 if (buffer.position() - position != dataSize) { 743 Log.e(TAG, "Broken data package"); 744 return null; 745 } 746 return new Pair<Class, Object>(dataPackage.getMyType(), obj); 747 } 748 749 @Override pack(AutoGrowByteBuffer output, Pair<Class, Object> obj)750 public boolean pack(AutoGrowByteBuffer output, Pair<Class, Object> obj) { 751 final Integer dataType = AUDIO_METADATA_OBJ_TYPES.get(obj.first); 752 if (dataType == null) { 753 Log.e(TAG, "Cannot find data type for " + obj.first); 754 return false; 755 } 756 DataPackage dataPackage = DATA_PACKAGES.get(dataType); 757 if (dataPackage == null) { 758 Log.e(TAG, "Cannot find DataPackage for type:" + dataType); 759 return false; 760 } 761 output.putInt(dataType); 762 int position = output.position(); // Keep current position. 763 output.putInt(0); // Keep a place for the size of payload. 764 int payloadIdx = output.position(); 765 if (!dataPackage.pack(output, obj.second)) { 766 Log.i(TAG, "Failed to pack object: " + obj.second); 767 return false; 768 } 769 // Put the actual payload size. 770 int currentPosition = output.position(); 771 output.position(position); 772 output.putInt(currentPosition - payloadIdx); 773 output.position(currentPosition); 774 return true; 775 } 776 } 777 778 /** 779 * BaseMap will be corresponding to audio_utils::metadata::Data. 780 */ 781 private static class BaseMapPackage implements DataPackage<BaseMap> { 782 @Override 783 @Nullable unpack(ByteBuffer buffer)784 public BaseMap unpack(ByteBuffer buffer) { 785 BaseMap ret = new BaseMap(); 786 int mapSize = buffer.getInt(); 787 DataPackage<String> strDataPackage = 788 (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING); 789 if (strDataPackage == null) { 790 Log.e(TAG, "Cannot find DataPackage for String"); 791 return null; 792 } 793 for (int i = 0; i < mapSize; i++) { 794 String key = strDataPackage.unpack(buffer); 795 if (key == null) { 796 Log.e(TAG, "Failed to unpack key for map"); 797 return null; 798 } 799 Pair<Class, Object> value = OBJECT_PACKAGE.unpack(buffer); 800 if (value == null) { 801 Log.e(TAG, "Failed to unpack value for map"); 802 return null; 803 } 804 805 // Special handling of KEY_ATMOS_PRESENT. 806 if (key.equals(Format.KEY_HAS_ATMOS.getName()) 807 && value.first == Format.KEY_HAS_ATMOS.getValueClass()) { 808 ret.set(Format.KEY_ATMOS_PRESENT, 809 (Boolean) ((int) value.second != 0)); // Translate Integer to Boolean 810 continue; // Should we store both keys in the java table? 811 } 812 813 ret.set(createKey(key, value.first), value.first.cast(value.second)); 814 } 815 return ret; 816 } 817 818 @Override pack(AutoGrowByteBuffer output, BaseMap obj)819 public boolean pack(AutoGrowByteBuffer output, BaseMap obj) { 820 output.putInt(obj.size()); 821 DataPackage<String> strDataPackage = 822 (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING); 823 if (strDataPackage == null) { 824 Log.e(TAG, "Cannot find DataPackage for String"); 825 return false; 826 } 827 for (Key<?> key : obj.keySet()) { 828 Object value = obj.get(key); 829 830 // Special handling of KEY_ATMOS_PRESENT. 831 if (key == Format.KEY_ATMOS_PRESENT) { 832 key = Format.KEY_HAS_ATMOS; 833 value = (Integer) ((boolean) value ? 1 : 0); // Translate Boolean to Integer 834 } 835 836 if (!strDataPackage.pack(output, key.getName())) { 837 Log.i(TAG, "Failed to pack key: " + key.getName()); 838 return false; 839 } 840 if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), value))) { 841 Log.i(TAG, "Failed to pack value: " + obj.get(key)); 842 return false; 843 } 844 } 845 return true; 846 } 847 } 848 849 /** 850 * @hide 851 * Extract a {@link BaseMap} from a given {@link ByteBuffer} 852 * @param buffer is a byte string that contains information to unpack. 853 * @return a {@link BaseMap} object if extracting successfully from given byte buffer. 854 * Otherwise, returns {@code null}. 855 */ 856 @Nullable fromByteBuffer(ByteBuffer buffer)857 public static BaseMap fromByteBuffer(ByteBuffer buffer) { 858 DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP); 859 if (dataPackage == null) { 860 Log.e(TAG, "Cannot find DataPackage for BaseMap"); 861 return null; 862 } 863 try { 864 return (BaseMap) dataPackage.unpack(buffer); 865 } catch (BufferUnderflowException e) { 866 Log.e(TAG, "No enough data to unpack"); 867 } 868 return null; 869 } 870 871 /** 872 * @hide 873 * Pack a {link BaseMap} to a {@link ByteBuffer} 874 * @param data is the object for packing 875 * @param order is the byte order 876 * @return a {@link ByteBuffer} if successfully packing the data. 877 * Otherwise, returns {@code null}; 878 */ 879 @Nullable toByteBuffer(BaseMap data, ByteOrder order)880 public static ByteBuffer toByteBuffer(BaseMap data, ByteOrder order) { 881 DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP); 882 if (dataPackage == null) { 883 Log.e(TAG, "Cannot find DataPackage for BaseMap"); 884 return null; 885 } 886 AutoGrowByteBuffer output = new AutoGrowByteBuffer(); 887 output.order(order); 888 if (dataPackage.pack(output, data)) { 889 return output.getRawByteBuffer(); 890 } 891 return null; 892 } 893 894 // Delete the constructor as there is nothing to implement here. AudioMetadata()895 private AudioMetadata() {} 896 } 897