1 /* 2 * Copyright (C) 2022 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.IntDef; 20 import android.annotation.NonNull; 21 import android.icu.util.ULocale; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 import java.util.HashMap; 29 import java.util.Locale; 30 import java.util.Map; 31 import java.util.Objects; 32 33 34 /** 35 * The AudioPresentation class encapsulates the information that describes an audio presentation 36 * which is available in next generation audio content. 37 * 38 * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and 39 * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available 40 * presentations and to select one, respectively. 41 * 42 * A list of available audio presentations in a media source can be queried using 43 * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for 44 * selection. 45 * An AudioPresentation can be passed to an offloaded audio decoder via 46 * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected 47 * presentation. An audio stream may contain multiple presentations that differ by language, 48 * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have 49 * a set of description labels in different languages to help the user to make an informed 50 * selection. 51 * 52 * Applications that parse media streams and extract presentation information on their own 53 * can create instances of AudioPresentation by using {@link AudioPresentation.Builder} class. 54 */ 55 public final class AudioPresentation implements Parcelable { 56 private final int mPresentationId; 57 private final int mProgramId; 58 private final ULocale mLanguage; 59 60 /** 61 * The ContentClassifier int definitions represent the AudioPresentation content 62 * classifier (as per TS 103 190-1 v1.2.1 4.3.3.8.1) 63 * @hide 64 */ 65 @IntDef( 66 value = { 67 CONTENT_UNKNOWN, 68 CONTENT_MAIN, 69 CONTENT_MUSIC_AND_EFFECTS, 70 CONTENT_VISUALLY_IMPAIRED, 71 CONTENT_HEARING_IMPAIRED, 72 CONTENT_DIALOG, 73 CONTENT_COMMENTARY, 74 CONTENT_EMERGENCY, 75 CONTENT_VOICEOVER, 76 }) 77 @Retention(RetentionPolicy.SOURCE) 78 public @interface ContentClassifier {} 79 80 /** 81 * Audio presentation classifier: Unknown. 82 */ 83 public static final int CONTENT_UNKNOWN = -1; 84 /** 85 * Audio presentation classifier: Complete main. 86 */ 87 public static final int CONTENT_MAIN = 0; 88 /** 89 * Audio presentation content classifier: Music and effects. 90 */ 91 public static final int CONTENT_MUSIC_AND_EFFECTS = 1; 92 /** 93 * Audio presentation content classifier: Visually impaired. 94 */ 95 public static final int CONTENT_VISUALLY_IMPAIRED = 2; 96 /** 97 * Audio presentation content classifier: Hearing impaired. 98 */ 99 public static final int CONTENT_HEARING_IMPAIRED = 3; 100 /** 101 * Audio presentation content classifier: Dialog. 102 */ 103 public static final int CONTENT_DIALOG = 4; 104 /** 105 * Audio presentation content classifier: Commentary. 106 */ 107 public static final int CONTENT_COMMENTARY = 5; 108 /** 109 * Audio presentation content classifier: Emergency. 110 */ 111 public static final int CONTENT_EMERGENCY = 6; 112 /** 113 * Audio presentation content classifier: Voice over. 114 */ 115 public static final int CONTENT_VOICEOVER = 7; 116 117 /** @hide */ 118 @IntDef( 119 value = { 120 MASTERING_NOT_INDICATED, 121 MASTERED_FOR_STEREO, 122 MASTERED_FOR_SURROUND, 123 MASTERED_FOR_3D, 124 MASTERED_FOR_HEADPHONE, 125 }) 126 @Retention(RetentionPolicy.SOURCE) 127 public @interface MasteringIndicationType {} 128 private final @MasteringIndicationType int mMasteringIndication; 129 private final boolean mAudioDescriptionAvailable; 130 private final boolean mSpokenSubtitlesAvailable; 131 private final boolean mDialogueEnhancementAvailable; 132 private final HashMap<ULocale, String> mLabels; 133 134 /** 135 * No preferred reproduction channel layout. 136 * 137 * @see Builder#setMasteringIndication(int) 138 */ 139 public static final int MASTERING_NOT_INDICATED = 0; 140 /** 141 * Stereo speaker layout. 142 * 143 * @see Builder#setMasteringIndication(int) 144 */ 145 public static final int MASTERED_FOR_STEREO = 1; 146 /** 147 * Two-dimensional (e.g. 5.1) speaker layout. 148 * 149 * @see Builder#setMasteringIndication(int) 150 */ 151 public static final int MASTERED_FOR_SURROUND = 2; 152 /** 153 * Three-dimensional (e.g. 5.1.2) speaker layout. 154 * 155 * @see Builder#setMasteringIndication(int) 156 */ 157 public static final int MASTERED_FOR_3D = 3; 158 /** 159 * Prerendered for headphone playback. 160 * 161 * @see Builder#setMasteringIndication(int) 162 */ 163 public static final int MASTERED_FOR_HEADPHONE = 4; 164 165 /** 166 * Unknown audio presentation ID, this indicates audio presentation ID is not selected. 167 */ 168 public static final int PRESENTATION_ID_UNKNOWN = -1; 169 170 /** 171 * Unknown audio program ID, this indicates audio program ID is not selected. 172 */ 173 public static final int PROGRAM_ID_UNKNOWN = -1; 174 175 /** 176 * This allows an application developer to construct an AudioPresentation object with all the 177 * parameters. 178 * The IDs are all that is required for an 179 * {@link AudioTrack#setPresentation(AudioPresentation)} to be successful. 180 * The rest of the metadata is informative only so as to distinguish features 181 * of different presentations. 182 * @param presentationId Presentation ID to be decoded by a next generation audio decoder. 183 * @param programId Program ID to be decoded by a next generation audio decoder. 184 * @param language Locale corresponding to ISO 639-1/639-2 language code. 185 * @param masteringIndication One of {@link AudioPresentation#MASTERING_NOT_INDICATED}, 186 * {@link AudioPresentation#MASTERED_FOR_STEREO}, 187 * {@link AudioPresentation#MASTERED_FOR_SURROUND}, 188 * {@link AudioPresentation#MASTERED_FOR_3D}, 189 * {@link AudioPresentation#MASTERED_FOR_HEADPHONE}. 190 * @param audioDescriptionAvailable Audio description for the visually impaired. 191 * @param spokenSubtitlesAvailable Spoken subtitles for the visually impaired. 192 * @param dialogueEnhancementAvailable Dialogue enhancement. 193 * @param labels Text label indexed by its locale corresponding to the language code. 194 */ AudioPresentation(int presentationId, int programId, @NonNull ULocale language, @MasteringIndicationType int masteringIndication, boolean audioDescriptionAvailable, boolean spokenSubtitlesAvailable, boolean dialogueEnhancementAvailable, @NonNull Map<ULocale, String> labels)195 private AudioPresentation(int presentationId, 196 int programId, 197 @NonNull ULocale language, 198 @MasteringIndicationType int masteringIndication, 199 boolean audioDescriptionAvailable, 200 boolean spokenSubtitlesAvailable, 201 boolean dialogueEnhancementAvailable, 202 @NonNull Map<ULocale, String> labels) { 203 mPresentationId = presentationId; 204 mProgramId = programId; 205 mLanguage = language; 206 mMasteringIndication = masteringIndication; 207 mAudioDescriptionAvailable = audioDescriptionAvailable; 208 mSpokenSubtitlesAvailable = spokenSubtitlesAvailable; 209 mDialogueEnhancementAvailable = dialogueEnhancementAvailable; 210 mLabels = new HashMap<ULocale, String>(labels); 211 } 212 AudioPresentation(@onNull Parcel in)213 private AudioPresentation(@NonNull Parcel in) { 214 mPresentationId = in.readInt(); 215 mProgramId = in.readInt(); 216 mLanguage = in.readSerializable(ULocale.class.getClassLoader(), ULocale.class); 217 mMasteringIndication = in.readInt(); 218 mAudioDescriptionAvailable = in.readBoolean(); 219 mSpokenSubtitlesAvailable = in.readBoolean(); 220 mDialogueEnhancementAvailable = in.readBoolean(); 221 mLabels = in.readSerializable(HashMap.class.getClassLoader(), HashMap.class); 222 } 223 224 /** 225 * Returns presentation ID used by the framework to select an audio presentation rendered by a 226 * decoder. Presentation ID is typically sequential, but does not have to be. 227 */ getPresentationId()228 public int getPresentationId() { 229 return mPresentationId; 230 } 231 232 /** 233 * Returns program ID used by the framework to select an audio presentation rendered by a 234 * decoder. Program ID can be used to further uniquely identify the presentation to a decoder. 235 */ getProgramId()236 public int getProgramId() { 237 return mProgramId; 238 } 239 240 /** 241 * @return a map of available text labels for this presentation. Each label is indexed by its 242 * locale corresponding to the language code as specified by ISO 639-2. Either ISO 639-2/B 243 * or ISO 639-2/T could be used. 244 */ getLabels()245 public Map<Locale, String> getLabels() { 246 Map<Locale, String> localeLabels = new HashMap<Locale, String>(mLabels.size()); 247 for (Map.Entry<ULocale, String> entry : mLabels.entrySet()) { 248 localeLabels.put(entry.getKey().toLocale(), entry.getValue()); 249 } 250 return localeLabels; 251 } 252 getULabels()253 private Map<ULocale, String> getULabels() { 254 return mLabels; 255 } 256 257 /** 258 * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code. 259 */ getLocale()260 public Locale getLocale() { 261 return mLanguage.toLocale(); 262 } 263 getULocale()264 private ULocale getULocale() { 265 return mLanguage; 266 } 267 268 /** 269 * @return the mastering indication of the audio presentation. 270 * See {@link AudioPresentation#MASTERING_NOT_INDICATED}, 271 * {@link AudioPresentation#MASTERED_FOR_STEREO}, 272 * {@link AudioPresentation#MASTERED_FOR_SURROUND}, 273 * {@link AudioPresentation#MASTERED_FOR_3D}, 274 * {@link AudioPresentation#MASTERED_FOR_HEADPHONE} 275 */ 276 @MasteringIndicationType getMasteringIndication()277 public int getMasteringIndication() { 278 return mMasteringIndication; 279 } 280 281 /** 282 * Indicates whether an audio description for the visually impaired is available. 283 * @return {@code true} if audio description is available. 284 */ hasAudioDescription()285 public boolean hasAudioDescription() { 286 return mAudioDescriptionAvailable; 287 } 288 289 /** 290 * Indicates whether spoken subtitles for the visually impaired are available. 291 * @return {@code true} if spoken subtitles are available. 292 */ hasSpokenSubtitles()293 public boolean hasSpokenSubtitles() { 294 return mSpokenSubtitlesAvailable; 295 } 296 297 /** 298 * Indicates whether dialogue enhancement is available. 299 * @return {@code true} if dialogue enhancement is available. 300 */ hasDialogueEnhancement()301 public boolean hasDialogueEnhancement() { 302 return mDialogueEnhancementAvailable; 303 } 304 305 @Override equals(Object o)306 public boolean equals(Object o) { 307 if (this == o) { 308 return true; 309 } 310 if (!(o instanceof AudioPresentation)) { 311 return false; 312 } 313 AudioPresentation obj = (AudioPresentation) o; 314 return mPresentationId == obj.getPresentationId() 315 && mProgramId == obj.getProgramId() 316 && mLanguage.equals(obj.getULocale()) 317 && mMasteringIndication == obj.getMasteringIndication() 318 && mAudioDescriptionAvailable == obj.hasAudioDescription() 319 && mSpokenSubtitlesAvailable == obj.hasSpokenSubtitles() 320 && mDialogueEnhancementAvailable == obj.hasDialogueEnhancement() 321 && mLabels.equals(obj.getULabels()); 322 } 323 324 @Override hashCode()325 public int hashCode() { 326 return Objects.hash(mPresentationId, 327 mProgramId, 328 mLanguage.hashCode(), 329 mMasteringIndication, 330 mAudioDescriptionAvailable, 331 mSpokenSubtitlesAvailable, 332 mDialogueEnhancementAvailable, 333 mLabels.hashCode()); 334 } 335 336 @Override toString()337 public String toString() { 338 StringBuilder sb = new StringBuilder(); 339 sb.append(getClass().getSimpleName() + " "); 340 sb.append("{ presentation id=" + mPresentationId); 341 sb.append(", program id=" + mProgramId); 342 sb.append(", language=" + mLanguage); 343 sb.append(", labels=" + mLabels); 344 sb.append(", mastering indication=" + mMasteringIndication); 345 sb.append(", audio description=" + mAudioDescriptionAvailable); 346 sb.append(", spoken subtitles=" + mSpokenSubtitlesAvailable); 347 sb.append(", dialogue enhancement=" + mDialogueEnhancementAvailable); 348 sb.append(" }"); 349 return sb.toString(); 350 } 351 352 /** 353 * A builder class for creating {@link AudioPresentation} objects. 354 */ 355 public static final class Builder { 356 private final int mPresentationId; 357 private int mProgramId = PROGRAM_ID_UNKNOWN; 358 private ULocale mLanguage = new ULocale(""); 359 private int mMasteringIndication = MASTERING_NOT_INDICATED; 360 private boolean mAudioDescriptionAvailable = false; 361 private boolean mSpokenSubtitlesAvailable = false; 362 private boolean mDialogueEnhancementAvailable = false; 363 private HashMap<ULocale, String> mLabels = new HashMap<ULocale, String>(); 364 365 /** 366 * Create a {@link Builder}. Any field that should be included in the 367 * {@link AudioPresentation} must be added. 368 * 369 * @param presentationId The presentation ID of this audio presentation. 370 */ Builder(int presentationId)371 public Builder(int presentationId) { 372 mPresentationId = presentationId; 373 } 374 /** 375 * Sets the ProgramId to which this audio presentation refers. 376 * 377 * @param programId The program ID to be decoded. 378 */ setProgramId(int programId)379 public @NonNull Builder setProgramId(int programId) { 380 mProgramId = programId; 381 return this; 382 } 383 /** 384 * Sets the language information of the audio presentation. 385 * 386 * @param language Locale corresponding to ISO 639-1/639-2 language code. 387 */ setLocale(@onNull ULocale language)388 public @NonNull Builder setLocale(@NonNull ULocale language) { 389 mLanguage = language; 390 return this; 391 } 392 393 /** 394 * Sets the mastering indication. 395 * 396 * @param masteringIndication Input to set mastering indication. 397 * @throws IllegalArgumentException if the mastering indication is not any of 398 * {@link AudioPresentation#MASTERING_NOT_INDICATED}, 399 * {@link AudioPresentation#MASTERED_FOR_STEREO}, 400 * {@link AudioPresentation#MASTERED_FOR_SURROUND}, 401 * {@link AudioPresentation#MASTERED_FOR_3D}, 402 * and {@link AudioPresentation#MASTERED_FOR_HEADPHONE} 403 */ setMasteringIndication( @asteringIndicationType int masteringIndication)404 public @NonNull Builder setMasteringIndication( 405 @MasteringIndicationType int masteringIndication) { 406 if (masteringIndication != MASTERING_NOT_INDICATED 407 && masteringIndication != MASTERED_FOR_STEREO 408 && masteringIndication != MASTERED_FOR_SURROUND 409 && masteringIndication != MASTERED_FOR_3D 410 && masteringIndication != MASTERED_FOR_HEADPHONE) { 411 throw new IllegalArgumentException("Unknown mastering indication: " 412 + masteringIndication); 413 } 414 mMasteringIndication = masteringIndication; 415 return this; 416 } 417 418 /** 419 * Sets locale / text label pairs describing the presentation. 420 * 421 * @param labels Text label indexed by its locale corresponding to the language code. 422 */ setLabels(@onNull Map<ULocale, CharSequence> labels)423 public @NonNull Builder setLabels(@NonNull Map<ULocale, CharSequence> labels) { 424 mLabels.clear(); 425 for (Map.Entry<ULocale, CharSequence> entry : labels.entrySet()) { 426 mLabels.put(entry.getKey(), entry.getValue().toString()); 427 } 428 return this; 429 } 430 431 /** 432 * Indicate whether the presentation contains audio description for the visually impaired. 433 * 434 * @param audioDescriptionAvailable Audio description for the visually impaired. 435 */ setHasAudioDescription(boolean audioDescriptionAvailable)436 public @NonNull Builder setHasAudioDescription(boolean audioDescriptionAvailable) { 437 mAudioDescriptionAvailable = audioDescriptionAvailable; 438 return this; 439 } 440 441 /** 442 * Indicate whether the presentation contains spoken subtitles for the visually impaired. 443 * 444 * @param spokenSubtitlesAvailable Spoken subtitles for the visually impaired. 445 */ setHasSpokenSubtitles(boolean spokenSubtitlesAvailable)446 public @NonNull Builder setHasSpokenSubtitles(boolean spokenSubtitlesAvailable) { 447 mSpokenSubtitlesAvailable = spokenSubtitlesAvailable; 448 return this; 449 } 450 451 /** 452 * Indicate whether the presentation supports dialogue enhancement. 453 * 454 * @param dialogueEnhancementAvailable Dialogue enhancement. 455 */ setHasDialogueEnhancement(boolean dialogueEnhancementAvailable)456 public @NonNull Builder setHasDialogueEnhancement(boolean dialogueEnhancementAvailable) { 457 mDialogueEnhancementAvailable = dialogueEnhancementAvailable; 458 return this; 459 } 460 461 /** 462 * Creates a {@link AudioPresentation} instance with the specified fields. 463 * 464 * @return The new {@link AudioPresentation} instance 465 */ build()466 public @NonNull AudioPresentation build() { 467 return new AudioPresentation(mPresentationId, mProgramId, 468 mLanguage, mMasteringIndication, 469 mAudioDescriptionAvailable, mSpokenSubtitlesAvailable, 470 mDialogueEnhancementAvailable, mLabels); 471 } 472 } 473 474 @Override describeContents()475 public int describeContents() { 476 return 0; 477 } 478 479 @Override writeToParcel(@onNull Parcel dest, int flags)480 public void writeToParcel(@NonNull Parcel dest, int flags) { 481 dest.writeInt(getPresentationId()); 482 dest.writeInt(getProgramId()); 483 dest.writeSerializable(getULocale()); 484 dest.writeInt(getMasteringIndication()); 485 dest.writeBoolean(hasAudioDescription()); 486 dest.writeBoolean(hasSpokenSubtitles()); 487 dest.writeBoolean(hasDialogueEnhancement()); 488 dest.writeSerializable(mLabels); 489 } 490 491 @NonNull 492 public static final Parcelable.Creator<AudioPresentation> CREATOR = 493 new Parcelable.Creator<AudioPresentation>() { 494 @Override 495 public AudioPresentation createFromParcel(@NonNull Parcel in) { 496 return new AudioPresentation(in); 497 } 498 499 @Override 500 public AudioPresentation[] newArray(int size) { 501 return new AudioPresentation[size]; 502 } 503 }; 504 } 505