Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5272e68c authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

Allow apps to construct AudioPresentation object

Added a Builder class to gather necessary parameters
for creating an AudioPresentation object.

Unhidden two getters--getPresentationId/getProgramId to keep API
symmetry.

Bug: 63901775
Test: android.media.cts.AudioPresentationTest
      android.media.cts.AudioTrackTest#testSetPresentationDefaultTrack
Change-Id: I5f4878973fcfd5e79494d2f530776f096713ac5f
parent 469b9d6a
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -22830,6 +22830,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();
@@ -22840,6 +22842,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);
+0 −6
Original line number Diff line number Diff line
@@ -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();
+215 −39
Original line number Diff line number Diff line
@@ -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
     * This ID is reserved. No items can be explicitly assigned this ID.
     */
    @TestApi
    public AudioPresentation(int presentationId,
    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 Map<String, String> labels,
                        @NonNull String language,
                             @NonNull ULocale 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);
                             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);
        }
    }
}
+30 −15
Original line number Diff line number Diff line
@@ -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;
        }