diff options
| -rwxr-xr-x | api/current.txt | 14 | ||||
| -rw-r--r-- | api/test-current.txt | 6 | ||||
| -rw-r--r-- | media/java/android/media/AudioPresentation.java | 254 | ||||
| -rw-r--r-- | media/jni/android_media_AudioPresentation.h | 45 |
4 files changed, 259 insertions, 60 deletions
diff --git a/api/current.txt b/api/current.txt index 80ee52577ac9..b98ef685ecaf 100755 --- a/api/current.txt +++ b/api/current.txt @@ -22927,6 +22927,8 @@ package android.media { method public java.util.Map<java.util.Locale, java.lang.String> getLabels(); method public java.util.Locale getLocale(); method public int getMasteringIndication(); + method public int getPresentationId(); + method public int getProgramId(); method public boolean hasAudioDescription(); method public boolean hasDialogueEnhancement(); method public boolean hasSpokenSubtitles(); @@ -22937,6 +22939,18 @@ package android.media { field public static final int MASTERING_NOT_INDICATED = 0; // 0x0 } + public static class AudioPresentation.Builder { + ctor public AudioPresentation.Builder(int); + method public android.media.AudioPresentation build(); + method public android.media.AudioPresentation.Builder setHasAudioDescription(boolean); + method public android.media.AudioPresentation.Builder setHasDialogueEnhancement(boolean); + method public android.media.AudioPresentation.Builder setHasSpokenSubtitles(boolean); + method public android.media.AudioPresentation.Builder setLabels(java.util.Map<android.icu.util.ULocale, java.lang.String>); + method public android.media.AudioPresentation.Builder setLocale(android.icu.util.ULocale); + method public android.media.AudioPresentation.Builder setMasteringIndication(int); + method public android.media.AudioPresentation.Builder setProgramId(int); + } + public class AudioRecord implements android.media.AudioRouting { ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException; method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); diff --git a/api/test-current.txt b/api/test-current.txt index d453395fe9cd..3f26ab094ce9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -578,12 +578,6 @@ package android.media { method public static boolean isEncodingLinearPcm(int); } - public final class AudioPresentation { - ctor public AudioPresentation(int, int, java.util.Map<java.lang.String, java.lang.String>, java.lang.String, int, boolean, boolean, boolean); - method public int getPresentationId(); - method public int getProgramId(); - } - public final class BufferingParams implements android.os.Parcelable { method public int describeContents(); method public int getInitialMarkMs(); diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java index ce71436b9bb3..1cc650bf2702 100644 --- a/media/java/android/media/AudioPresentation.java +++ b/media/java/android/media/AudioPresentation.java @@ -19,13 +19,14 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; +import android.icu.util.ULocale; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; /** @@ -34,7 +35,7 @@ import java.util.Map; * * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available - * presentations and to select one. + * presentations and to select one, respectively. * * A list of available audio presentations in a media source can be queried using * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for @@ -49,8 +50,7 @@ import java.util.Map; public final class AudioPresentation { private final int mPresentationId; private final int mProgramId; - private final Map<String, String> mLabels; - private final String mLanguage; + private final ULocale mLanguage; /** @hide */ @IntDef( @@ -63,72 +63,98 @@ public final class AudioPresentation { }) @Retention(RetentionPolicy.SOURCE) public @interface MasteringIndicationType {} - private final @MasteringIndicationType int mMasteringIndication; private final boolean mAudioDescriptionAvailable; private final boolean mSpokenSubtitlesAvailable; private final boolean mDialogueEnhancementAvailable; + private final Map<ULocale, String> mLabels; /** * No preferred reproduction channel layout. + * + * @see Builder#setMasteringIndication(int) */ public static final int MASTERING_NOT_INDICATED = 0; /** * Stereo speaker layout. + * + * @see Builder#setMasteringIndication(int) */ public static final int MASTERED_FOR_STEREO = 1; /** * Two-dimensional (e.g. 5.1) speaker layout. + * + * @see Builder#setMasteringIndication(int) */ public static final int MASTERED_FOR_SURROUND = 2; /** * Three-dimensional (e.g. 5.1.2) speaker layout. + * + * @see Builder#setMasteringIndication(int) */ public static final int MASTERED_FOR_3D = 3; /** * Prerendered for headphone playback. + * + * @see Builder#setMasteringIndication(int) */ public static final int MASTERED_FOR_HEADPHONE = 4; /** - * @hide - */ - @TestApi - public AudioPresentation(int presentationId, - int programId, - @NonNull Map<String, String> labels, - @NonNull String language, - @MasteringIndicationType int masteringIndication, - boolean audioDescriptionAvailable, - boolean spokenSubtitlesAvailable, - boolean dialogueEnhancementAvailable) { - this.mPresentationId = presentationId; - this.mProgramId = programId; - this.mLanguage = language; - this.mMasteringIndication = masteringIndication; - this.mAudioDescriptionAvailable = audioDescriptionAvailable; - this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable; - this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable; - - this.mLabels = new HashMap<String, String>(labels); + * This ID is reserved. No items can be explicitly assigned this ID. + */ + private static final int UNKNOWN_ID = -1; + + /** + * This allows an application developer to construct an AudioPresentation object with all the + * parameters. + * The IDs are all that is required for an + * {@link AudioTrack#setPresentation(AudioPresentation)} to be successful. + * The rest of the metadata is informative only so as to distinguish features + * of different presentations. + * @param presentationId Presentation ID to be decoded by a next generation audio decoder. + * @param programId Program ID to be decoded by a next generation audio decoder. + * @param language Locale corresponding to ISO 639-1/639-2 language code. + * @param masteringIndication One of {@link AudioPresentation#MASTERING_NOT_INDICATED}, + * {@link AudioPresentation#MASTERED_FOR_STEREO}, + * {@link AudioPresentation#MASTERED_FOR_SURROUND}, + * {@link AudioPresentation#MASTERED_FOR_3D}, + * {@link AudioPresentation#MASTERED_FOR_HEADPHONE}. + * @param audioDescriptionAvailable Audio description for the visually impaired. + * @param spokenSubtitlesAvailable Spoken subtitles for the visually impaired. + * @param dialogueEnhancementAvailable Dialogue enhancement. + * @param labels Text label indexed by its locale corresponding to the language code. + */ + private AudioPresentation(int presentationId, + int programId, + @NonNull ULocale language, + @MasteringIndicationType int masteringIndication, + boolean audioDescriptionAvailable, + boolean spokenSubtitlesAvailable, + boolean dialogueEnhancementAvailable, + @NonNull Map<ULocale, String> labels) { + mPresentationId = presentationId; + mProgramId = programId; + mLanguage = language; + mMasteringIndication = masteringIndication; + mAudioDescriptionAvailable = audioDescriptionAvailable; + mSpokenSubtitlesAvailable = spokenSubtitlesAvailable; + mDialogueEnhancementAvailable = dialogueEnhancementAvailable; + mLabels = new HashMap<ULocale, String>(labels); } /** - * The framework uses this presentation id to select an audio presentation rendered by a - * decoder. Presentation id is typically sequential, but does not have to be. - * @hide + * Returns presentation ID used by the framework to select an audio presentation rendered by a + * decoder. Presentation ID is typically sequential, but does not have to be. */ - @TestApi public int getPresentationId() { return mPresentationId; } /** - * The framework uses this program id to select an audio presentation rendered by a decoder. - * Program id can be used to further uniquely identify the presentation to a decoder. - * @hide + * Returns program ID used by the framework to select an audio presentation rendered by a + * decoder. Program ID can be used to further uniquely identify the presentation to a decoder. */ - @TestApi public int getProgramId() { return mProgramId; } @@ -139,9 +165,9 @@ public final class AudioPresentation { * or ISO 639-2/T could be used. */ public Map<Locale, String> getLabels() { - Map<Locale, String> localeLabels = new HashMap<>(); - for (Map.Entry<String, String> entry : mLabels.entrySet()) { - localeLabels.put(new Locale(entry.getKey()), entry.getValue()); + Map<Locale, String> localeLabels = new HashMap<Locale, String>(); + for (Map.Entry<ULocale, String> entry : mLabels.entrySet()) { + localeLabels.put(entry.getKey().toLocale(), entry.getValue()); } return localeLabels; } @@ -150,13 +176,20 @@ public final class AudioPresentation { * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code. */ public Locale getLocale() { - return new Locale(mLanguage); + return mLanguage.toLocale(); + } + + private ULocale getULocale() { + return mLanguage; } /** * @return the mastering indication of the audio presentation. - * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO}, - * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE} + * See {@link AudioPresentation#MASTERING_NOT_INDICATED}, + * {@link AudioPresentation#MASTERED_FOR_STEREO}, + * {@link AudioPresentation#MASTERED_FOR_SURROUND}, + * {@link AudioPresentation#MASTERED_FOR_3D}, + * {@link AudioPresentation#MASTERED_FOR_HEADPHONE} */ @MasteringIndicationType public int getMasteringIndication() { @@ -186,4 +219,147 @@ public final class AudioPresentation { public boolean hasDialogueEnhancement() { return mDialogueEnhancementAvailable; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AudioPresentation)) { + return false; + } + AudioPresentation obj = (AudioPresentation) o; + return mPresentationId == obj.getPresentationId() + && mProgramId == obj.getProgramId() + && mLanguage == obj.getULocale() + && mMasteringIndication == obj.getMasteringIndication() + && mAudioDescriptionAvailable == obj.hasAudioDescription() + && mSpokenSubtitlesAvailable == obj.hasSpokenSubtitles() + && mDialogueEnhancementAvailable == obj.hasDialogueEnhancement() + && mLabels.equals(obj.getLabels()); + } + + @Override + public int hashCode() { + return Objects.hashCode(mPresentationId); + } + + /** + * A builder class for creating {@link AudioPresentation} objects. + */ + public static class Builder { + private final int mPresentationId; + private int mProgramId = UNKNOWN_ID; + private ULocale mLanguage = new ULocale(""); + private int mMasteringIndication = MASTERING_NOT_INDICATED; + private boolean mAudioDescriptionAvailable = false; + private boolean mSpokenSubtitlesAvailable = false; + private boolean mDialogueEnhancementAvailable = false; + private Map<ULocale, String> mLabels = new HashMap<ULocale, String>(); + + /** + * Create a {@link Builder}. Any field that should be included in the + * {@link AudioPresentation} must be added. + * + * @param presentationId the presentation ID of this audio presentation + */ + public Builder(int presentationId) { + mPresentationId = presentationId; + } + /** + * Sets the ProgramId to which this audio presentation refers. + * + * @param programId + */ + public @NonNull Builder setProgramId(int programId) { + mProgramId = programId; + return this; + } + /** + * Sets the language information of the audio presentation. + * + * @param language code + */ + public @NonNull Builder setLocale(ULocale language) { + mLanguage = language; + return this; + } + + /** + * Sets the mastering indication. + * + * @param masteringIndication Input to set mastering indication. + * @throws IllegalArgumentException if the mastering indication is not any of + * {@link AudioPresentation#MASTERING_NOT_INDICATED}, + * {@link AudioPresentation#MASTERED_FOR_STEREO}, + * {@link AudioPresentation#MASTERED_FOR_SURROUND}, + * {@link AudioPresentation#MASTERED_FOR_3D}, + * and {@link AudioPresentation#MASTERED_FOR_HEADPHONE} + */ + public @NonNull Builder setMasteringIndication( + @MasteringIndicationType int masteringIndication) { + if (masteringIndication != MASTERING_NOT_INDICATED + && masteringIndication != MASTERED_FOR_STEREO + && masteringIndication != MASTERED_FOR_SURROUND + && masteringIndication != MASTERED_FOR_3D + && masteringIndication != MASTERED_FOR_HEADPHONE) { + throw new IllegalArgumentException("Unknown mastering indication: " + + masteringIndication); + } + mMasteringIndication = masteringIndication; + return this; + } + + /** + * Sets locale / text label pairs describing the presentation. + * + * @param labels + */ + public @NonNull Builder setLabels(@NonNull Map<ULocale, String> labels) { + mLabels = new HashMap<ULocale, String>(labels); + return this; + } + + /** + * Indicate whether the presentation contains audio description for the visually impaired. + * + * @param audioDescriptionAvailable + */ + public @NonNull Builder setHasAudioDescription(boolean audioDescriptionAvailable) { + mAudioDescriptionAvailable = audioDescriptionAvailable; + return this; + } + + /** + * Indicate whether the presentation contains spoken subtitles for the visually impaired. + * + * @param spokenSubtitlesAvailable + */ + public @NonNull Builder setHasSpokenSubtitles(boolean spokenSubtitlesAvailable) { + mSpokenSubtitlesAvailable = spokenSubtitlesAvailable; + return this; + } + + /** + * Indicate whether the presentation supports dialogue enhancement. + * + * @param dialogueEnhancementAvailable + */ + public @NonNull Builder setHasDialogueEnhancement(boolean dialogueEnhancementAvailable) { + mDialogueEnhancementAvailable = dialogueEnhancementAvailable; + return this; + } + + /** + * Creates a {@link AudioPresentation} instance with the specified fields. + * + * @return The new {@link AudioPresentation} instance + */ + public @NonNull AudioPresentation build() { + return new AudioPresentation(mPresentationId, mProgramId, + mLanguage, mMasteringIndication, + mAudioDescriptionAvailable, mSpokenSubtitlesAvailable, + mDialogueEnhancementAvailable, mLabels); + } + } } diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h index 71b8dacfbdfa..5306de6f8580 100644 --- a/media/jni/android_media_AudioPresentation.h +++ b/media/jni/android_media_AudioPresentation.h @@ -49,7 +49,7 @@ struct JAudioPresentationInfo { } constructID = env->GetMethodID(clazz, "<init>", - "(IILjava/util/Map;Ljava/lang/String;IZZZ)V"); + "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V"); env->DeleteLocalRef(lclazz); // list objects @@ -104,21 +104,26 @@ struct JAudioPresentationInfo { // don't expose private keys (starting with android._) continue; } - jobject valueObj = NULL; - AString val; CHECK(msg->findString(key, &val)); - valueObj = env->NewStringUTF(val.c_str()); - if (valueObj != NULL) { - jstring keyObj = env->NewStringUTF(key); - - env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj); - - env->DeleteLocalRef(keyObj); keyObj = NULL; + ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale")); + if (localeClazz.get() == NULL) { + return -EINVAL; + } + jmethodID localeConstructID = + env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V"); + if (localeConstructID == NULL) { + return -EINVAL; + } + jstring jLanguage = env->NewStringUTF(key); + jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage); + env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj); + env->DeleteLocalRef(jLocale); jLocale = NULL; env->DeleteLocalRef(valueObj); valueObj = NULL; + env->DeleteLocalRef(jLanguage); jLanguage = NULL; } } @@ -142,26 +147,36 @@ struct JAudioPresentationInfo { if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) { return NULL; } - jstring jLanguage = env->NewStringUTF(ap->mLanguage.string()); - + ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale")); + if (localeClazz.get() == NULL) { + return NULL; + } + jmethodID localeConstructID = + env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V"); + if (localeConstructID == NULL) { + return NULL; + } + jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str()); + jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage); jobject jValueObj = env->NewObject(fields.clazz, fields.constructID, static_cast<jint>(ap->mPresentationId), static_cast<jint>(ap->mProgramId), - jLabelObject, - jLanguage, + jLocale, static_cast<jint>(ap->mMasteringIndication), static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ? 1 : 0), static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ? 1 : 0), static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ? - 1 : 0)); + 1 : 0), + jLabelObject); if (jValueObj == NULL) { env->DeleteLocalRef(jLanguage); jLanguage = NULL; return NULL; } env->CallBooleanMethod(list, fields.listAddId, jValueObj); + env->DeleteLocalRef(jLocale); jLocale = NULL; env->DeleteLocalRef(jValueObj); jValueObj = NULL; env->DeleteLocalRef(jLanguage); jLanguage = NULL; } |