1 /*
2  * Copyright (C) 2018 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 
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.util.HashMap;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.Objects;
29 
30 
31 /**
32  * The AudioPresentation class encapsulates the information that describes an audio presentation
33  * which is available in next generation audio content.
34  *
35  * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and
36  * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available
37  * presentations and to select one, respectively.
38  *
39  * A list of available audio presentations in a media source can be queried using
40  * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for
41  * selection.
42  * An AudioPresentation can be passed to an offloaded audio decoder via
43  * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected
44  * presentation. An audio stream may contain multiple presentations that differ by language,
45  * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have
46  * a set of description labels in different languages to help the user to make an informed
47  * selection.
48  *
49  * Applications that parse media streams and extract presentation information on their own
50  * can create instances of AudioPresentation by using {@link AudioPresentation.Builder} class.
51  */
52 public final class AudioPresentation {
53     private final int mPresentationId;
54     private final int mProgramId;
55     private final ULocale mLanguage;
56 
57     /** @hide */
58     @IntDef(
59         value = {
60             MASTERING_NOT_INDICATED,
61             MASTERED_FOR_STEREO,
62             MASTERED_FOR_SURROUND,
63             MASTERED_FOR_3D,
64             MASTERED_FOR_HEADPHONE,
65     })
66     @Retention(RetentionPolicy.SOURCE)
67     public @interface MasteringIndicationType {}
68     private final @MasteringIndicationType int mMasteringIndication;
69     private final boolean mAudioDescriptionAvailable;
70     private final boolean mSpokenSubtitlesAvailable;
71     private final boolean mDialogueEnhancementAvailable;
72     private final Map<ULocale, CharSequence> mLabels;
73 
74     /**
75      * No preferred reproduction channel layout.
76      *
77      * @see Builder#setMasteringIndication(int)
78      */
79     public static final int MASTERING_NOT_INDICATED         = 0;
80     /**
81      * Stereo speaker layout.
82      *
83      * @see Builder#setMasteringIndication(int)
84      */
85     public static final int MASTERED_FOR_STEREO             = 1;
86     /**
87      * Two-dimensional (e.g. 5.1) speaker layout.
88      *
89      * @see Builder#setMasteringIndication(int)
90      */
91     public static final int MASTERED_FOR_SURROUND           = 2;
92     /**
93      * Three-dimensional (e.g. 5.1.2) speaker layout.
94      *
95      * @see Builder#setMasteringIndication(int)
96      */
97     public static final int MASTERED_FOR_3D                 = 3;
98     /**
99      * Prerendered for headphone playback.
100      *
101      * @see Builder#setMasteringIndication(int)
102      */
103     public static final int MASTERED_FOR_HEADPHONE          = 4;
104 
105     /**
106      * This ID is reserved. No items can be explicitly assigned this ID.
107      */
108     private static final int UNKNOWN_ID = -1;
109 
110     /**
111      * This allows an application developer to construct an AudioPresentation object with all the
112      * parameters.
113      * The IDs are all that is required for an
114      * {@link AudioTrack#setPresentation(AudioPresentation)} to be successful.
115      * The rest of the metadata is informative only so as to distinguish features
116      * of different presentations.
117      * @param presentationId Presentation ID to be decoded by a next generation audio decoder.
118      * @param programId Program ID to be decoded by a next generation audio decoder.
119      * @param language Locale corresponding to ISO 639-1/639-2 language code.
120      * @param masteringIndication One of {@link AudioPresentation#MASTERING_NOT_INDICATED},
121      *     {@link AudioPresentation#MASTERED_FOR_STEREO},
122      *     {@link AudioPresentation#MASTERED_FOR_SURROUND},
123      *     {@link AudioPresentation#MASTERED_FOR_3D},
124      *     {@link AudioPresentation#MASTERED_FOR_HEADPHONE}.
125      * @param audioDescriptionAvailable Audio description for the visually impaired.
126      * @param spokenSubtitlesAvailable Spoken subtitles for the visually impaired.
127      * @param dialogueEnhancementAvailable Dialogue enhancement.
128      * @param labels Text label indexed by its locale corresponding to the language code.
129      */
AudioPresentation(int presentationId, int programId, @NonNull ULocale language, @MasteringIndicationType int masteringIndication, boolean audioDescriptionAvailable, boolean spokenSubtitlesAvailable, boolean dialogueEnhancementAvailable, @NonNull Map<ULocale, CharSequence> labels)130     private AudioPresentation(int presentationId,
131                              int programId,
132                              @NonNull ULocale language,
133                              @MasteringIndicationType int masteringIndication,
134                              boolean audioDescriptionAvailable,
135                              boolean spokenSubtitlesAvailable,
136                              boolean dialogueEnhancementAvailable,
137                              @NonNull Map<ULocale, CharSequence> labels) {
138         mPresentationId = presentationId;
139         mProgramId = programId;
140         mLanguage = language;
141         mMasteringIndication = masteringIndication;
142         mAudioDescriptionAvailable = audioDescriptionAvailable;
143         mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
144         mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
145         mLabels = new HashMap<ULocale, CharSequence>(labels);
146     }
147 
148     /**
149      * Returns presentation ID used by the framework to select an audio presentation rendered by a
150      * decoder. Presentation ID is typically sequential, but does not have to be.
151      */
getPresentationId()152     public int getPresentationId() {
153         return mPresentationId;
154     }
155 
156     /**
157      * Returns program ID used by the framework to select an audio presentation rendered by a
158      * decoder. Program ID can be used to further uniquely identify the presentation to a decoder.
159      */
getProgramId()160     public int getProgramId() {
161         return mProgramId;
162     }
163 
164     /**
165      * @return a map of available text labels for this presentation. Each label is indexed by its
166      * locale corresponding to the language code as specified by ISO 639-2. Either ISO 639-2/B
167      * or ISO 639-2/T could be used.
168      */
getLabels()169     public Map<Locale, String> getLabels() {
170         Map<Locale, String> localeLabels = new HashMap<Locale, String>(mLabels.size());
171         for (Map.Entry<ULocale, CharSequence> entry : mLabels.entrySet()) {
172             localeLabels.put(entry.getKey().toLocale(), entry.getValue().toString());
173         }
174         return localeLabels;
175     }
176 
getULabels()177     private Map<ULocale, CharSequence> getULabels() {
178         return mLabels;
179     }
180 
181     /**
182      * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
183      */
getLocale()184     public Locale getLocale() {
185         return mLanguage.toLocale();
186     }
187 
getULocale()188     private ULocale getULocale() {
189         return mLanguage;
190     }
191 
192     /**
193      * @return the mastering indication of the audio presentation.
194      * See {@link AudioPresentation#MASTERING_NOT_INDICATED},
195      *     {@link AudioPresentation#MASTERED_FOR_STEREO},
196      *     {@link AudioPresentation#MASTERED_FOR_SURROUND},
197      *     {@link AudioPresentation#MASTERED_FOR_3D},
198      *     {@link AudioPresentation#MASTERED_FOR_HEADPHONE}
199      */
200     @MasteringIndicationType
getMasteringIndication()201     public int getMasteringIndication() {
202         return mMasteringIndication;
203     }
204 
205     /**
206      * Indicates whether an audio description for the visually impaired is available.
207      * @return {@code true} if audio description is available.
208      */
hasAudioDescription()209     public boolean hasAudioDescription() {
210         return mAudioDescriptionAvailable;
211     }
212 
213     /**
214      * Indicates whether spoken subtitles for the visually impaired are available.
215      * @return {@code true} if spoken subtitles are available.
216      */
hasSpokenSubtitles()217     public boolean hasSpokenSubtitles() {
218         return mSpokenSubtitlesAvailable;
219     }
220 
221     /**
222      * Indicates whether dialogue enhancement is available.
223      * @return {@code true} if dialogue enhancement is available.
224      */
hasDialogueEnhancement()225     public boolean hasDialogueEnhancement() {
226         return mDialogueEnhancementAvailable;
227     }
228 
229     @Override
equals(Object o)230     public boolean equals(Object o) {
231         if (this == o) {
232             return true;
233         }
234         if (!(o instanceof AudioPresentation)) {
235             return false;
236         }
237         AudioPresentation obj = (AudioPresentation) o;
238         return mPresentationId == obj.getPresentationId()
239                 && mProgramId == obj.getProgramId()
240                 && mLanguage.equals(obj.getULocale())
241                 && mMasteringIndication == obj.getMasteringIndication()
242                 && mAudioDescriptionAvailable == obj.hasAudioDescription()
243                 && mSpokenSubtitlesAvailable == obj.hasSpokenSubtitles()
244                 && mDialogueEnhancementAvailable == obj.hasDialogueEnhancement()
245                 && mLabels.equals(obj.getULabels());
246     }
247 
248     @Override
hashCode()249     public int hashCode() {
250         return Objects.hash(mPresentationId,
251                 mProgramId,
252                 mLanguage.hashCode(),
253                 mMasteringIndication,
254                 mAudioDescriptionAvailable,
255                 mSpokenSubtitlesAvailable,
256                 mDialogueEnhancementAvailable,
257                 mLabels.hashCode());
258     }
259 
260     @Override
toString()261     public String toString() {
262         StringBuilder sb = new StringBuilder();
263         sb.append(getClass().getSimpleName() + " ");
264         sb.append("{ presentation id=" + mPresentationId);
265         sb.append(", program id=" + mProgramId);
266         sb.append(", language=" + mLanguage);
267         sb.append(", labels=" + mLabels);
268         sb.append(", mastering indication=" + mMasteringIndication);
269         sb.append(", audio description=" + mAudioDescriptionAvailable);
270         sb.append(", spoken subtitles=" + mSpokenSubtitlesAvailable);
271         sb.append(", dialogue enhancement=" + mDialogueEnhancementAvailable);
272         sb.append(" }");
273         return sb.toString();
274     }
275 
276     /**
277      * A builder class for creating {@link AudioPresentation} objects.
278      */
279     public static final class Builder {
280         private final int mPresentationId;
281         private int mProgramId = UNKNOWN_ID;
282         private ULocale mLanguage = new ULocale("");
283         private int mMasteringIndication = MASTERING_NOT_INDICATED;
284         private boolean mAudioDescriptionAvailable = false;
285         private boolean mSpokenSubtitlesAvailable = false;
286         private boolean mDialogueEnhancementAvailable = false;
287         private Map<ULocale, CharSequence> mLabels = new HashMap<ULocale, CharSequence>();
288 
289         /**
290          * Create a {@link Builder}. Any field that should be included in the
291          * {@link AudioPresentation} must be added.
292          *
293          * @param presentationId The presentation ID of this audio presentation.
294          */
Builder(int presentationId)295         public Builder(int presentationId) {
296             mPresentationId = presentationId;
297         }
298         /**
299          * Sets the ProgramId to which this audio presentation refers.
300          *
301          * @param programId The program ID to be decoded.
302          */
setProgramId(int programId)303         public @NonNull Builder setProgramId(int programId) {
304             mProgramId = programId;
305             return this;
306         }
307         /**
308          * Sets the language information of the audio presentation.
309          *
310          * @param language Locale corresponding to ISO 639-1/639-2 language code.
311          */
setLocale(@onNull ULocale language)312         public @NonNull Builder setLocale(@NonNull ULocale language) {
313             mLanguage = language;
314             return this;
315         }
316 
317         /**
318          * Sets the mastering indication.
319          *
320          * @param masteringIndication Input to set mastering indication.
321          * @throws IllegalArgumentException if the mastering indication is not any of
322          * {@link AudioPresentation#MASTERING_NOT_INDICATED},
323          * {@link AudioPresentation#MASTERED_FOR_STEREO},
324          * {@link AudioPresentation#MASTERED_FOR_SURROUND},
325          * {@link AudioPresentation#MASTERED_FOR_3D},
326          * and {@link AudioPresentation#MASTERED_FOR_HEADPHONE}
327          */
setMasteringIndication( @asteringIndicationType int masteringIndication)328         public @NonNull Builder setMasteringIndication(
329                 @MasteringIndicationType int masteringIndication) {
330             if (masteringIndication != MASTERING_NOT_INDICATED
331                     && masteringIndication != MASTERED_FOR_STEREO
332                     && masteringIndication != MASTERED_FOR_SURROUND
333                     && masteringIndication != MASTERED_FOR_3D
334                     && masteringIndication != MASTERED_FOR_HEADPHONE) {
335                 throw new IllegalArgumentException("Unknown mastering indication: "
336                                                         + masteringIndication);
337             }
338             mMasteringIndication = masteringIndication;
339             return this;
340         }
341 
342         /**
343          * Sets locale / text label pairs describing the presentation.
344          *
345          * @param labels Text label indexed by its locale corresponding to the language code.
346          */
setLabels(@onNull Map<ULocale, CharSequence> labels)347         public @NonNull Builder setLabels(@NonNull Map<ULocale, CharSequence> labels) {
348             mLabels = new HashMap<ULocale, CharSequence>(labels);
349             return this;
350         }
351 
352         /**
353          * Indicate whether the presentation contains audio description for the visually impaired.
354          *
355          * @param audioDescriptionAvailable Audio description for the visually impaired.
356          */
setHasAudioDescription(boolean audioDescriptionAvailable)357         public @NonNull Builder setHasAudioDescription(boolean audioDescriptionAvailable) {
358             mAudioDescriptionAvailable = audioDescriptionAvailable;
359             return this;
360         }
361 
362         /**
363          * Indicate whether the presentation contains spoken subtitles for the visually impaired.
364          *
365          * @param spokenSubtitlesAvailable Spoken subtitles for the visually impaired.
366          */
setHasSpokenSubtitles(boolean spokenSubtitlesAvailable)367         public @NonNull Builder setHasSpokenSubtitles(boolean spokenSubtitlesAvailable) {
368             mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
369             return this;
370         }
371 
372         /**
373          * Indicate whether the presentation supports dialogue enhancement.
374          *
375          * @param dialogueEnhancementAvailable Dialogue enhancement.
376          */
setHasDialogueEnhancement(boolean dialogueEnhancementAvailable)377         public @NonNull Builder setHasDialogueEnhancement(boolean dialogueEnhancementAvailable) {
378             mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
379             return this;
380         }
381 
382         /**
383          * Creates a {@link AudioPresentation} instance with the specified fields.
384          *
385          * @return The new {@link AudioPresentation} instance
386          */
build()387         public @NonNull AudioPresentation build() {
388             return new AudioPresentation(mPresentationId, mProgramId,
389                                            mLanguage, mMasteringIndication,
390                                            mAudioDescriptionAvailable, mSpokenSubtitlesAvailable,
391                                            mDialogueEnhancementAvailable, mLabels);
392         }
393     }
394 }
395