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

Commit 6f0d16df authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "AudioTrack: Add Codec format change listener"

parents e68e6deb b7149a1d
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -24144,6 +24144,37 @@ package android.media {
    method public void onAudioFocusChange(int);
  }
  public final class AudioMetadata {
    method @NonNull public static android.media.AudioMetadata.Map createMap();
  }
  public static class AudioMetadata.Format {
    field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Boolean> KEY_ATMOS_PRESENT;
    field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_AUDIO_ENCODING;
    field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_BIT_RATE;
    field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_BIT_WIDTH;
    field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_CHANNEL_MASK;
    field @NonNull public static final android.media.AudioMetadata.Key<java.lang.String> KEY_MIME;
    field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_SAMPLE_RATE;
  }
  public static interface AudioMetadata.Key<T> {
    method @NonNull public String getName();
    method @NonNull public Class<T> getValueClass();
  }
  public static interface AudioMetadata.Map extends android.media.AudioMetadata.ReadMap {
    method @Nullable public <T> T remove(@NonNull android.media.AudioMetadata.Key<T>);
    method @Nullable public <T> T set(@NonNull android.media.AudioMetadata.Key<T>, @NonNull T);
  }
  public static interface AudioMetadata.ReadMap {
    method public <T> boolean containsKey(@NonNull android.media.AudioMetadata.Key<T>);
    method @NonNull public android.media.AudioMetadata.Map dup();
    method @Nullable public <T> T get(@NonNull android.media.AudioMetadata.Key<T>);
    method public int size();
  }
  public final class AudioPlaybackCaptureConfiguration {
    method @NonNull public int[] getExcludeUids();
    method @NonNull public int[] getExcludeUsages();
@@ -24329,6 +24360,7 @@ package android.media {
    ctor @Deprecated public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
    ctor @Deprecated public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
    ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
    method public void addOnCodecFormatChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioTrack.OnCodecFormatChangedListener);
    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
    method @Deprecated public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
    method public int attachAuxEffect(int);
@@ -24372,6 +24404,7 @@ package android.media {
    method public void registerStreamEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioTrack.StreamEventCallback);
    method public void release();
    method public int reloadStaticData();
    method public void removeOnCodecFormatChangedListener(@NonNull android.media.AudioTrack.OnCodecFormatChangedListener);
    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
    method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
    method public int setAuxEffectSendLevel(@FloatRange(from=0.0) float);
@@ -24445,6 +24478,10 @@ package android.media {
    field public static final String USAGE = "android.media.audiotrack.usage";
  }
  public static interface AudioTrack.OnCodecFormatChangedListener {
    method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadata.ReadMap);
  }
  public static interface AudioTrack.OnPlaybackPositionUpdateListener {
    method public void onMarkerReached(android.media.AudioTrack);
    method public void onPeriodicNotification(android.media.AudioTrack);
+406 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Pair;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
 * AudioMetadata class is used to manage typed key-value pairs for
 * configuration and capability requests within the Audio Framework.
 */
public final class AudioMetadata {
    /**
     * Key interface for the map.
     *
     * The presence of this {@code Key} interface on an object allows
     * it to be used to reference metadata in the Audio Framework.
     *
     * @param <T> type of value associated with {@code Key}.
     */
    // Conceivably metadata keys exposing multiple interfaces
    // could be eligible to work in multiple framework domains.
    public interface Key<T> {
        /**
         * Returns the internal name of the key.
         */
        @NonNull
        String getName();

        /**
         * Returns the class type of the associated value.
         */
        @NonNull
        Class<T> getValueClass();

        // TODO: consider adding bool isValid(@NonNull T value)

        /**
         * Do not allow non-framework apps to create their own keys
         * by implementing this interface; keep a method hidden.
         *
         * @hide
         */
        boolean isFromFramework();
    }

    /**
     * A read only {@code Map} interface of {@link Key} value pairs.
     *
     * Using a {@link Key} interface, look up the corresponding value.
     */
    public interface ReadMap {
        /**
         * Returns true if the key exists in the map.
         *
         * @param key interface for requesting the value.
         * @param <T> type of value.
         * @return true if key exists in the Map.
         */
        <T> boolean containsKey(@NonNull Key<T> key);

        /**
         * Returns a copy of the map.
         *
         * This is intended for safe conversion between a {@link ReadMap}
         * interface and a {@link Map} interface.
         * Currently only simple objects are used for key values which
         * means a shallow copy is sufficient.
         *
         * @return a Map copied from the existing map.
         */
        @NonNull
        Map dup(); // lint checker doesn't like clone().

        /**
         * Returns the value associated with the key.
         *
         * @param key interface for requesting the value.
         * @param <T> type of value.
         * @return returns the value of associated with key or null if it doesn't exist.
         */
        @Nullable
        <T> T get(@NonNull Key<T> key);

        /**
         * Returns a {@code Set} of keys associated with the map.
         * @hide
         */
        @NonNull
        Set<Key<?>> keySet();

        /**
         * Returns the number of elements in the map.
         */
        int size();
    }

    /**
     * A writeable {@link Map} interface of {@link Key} value pairs.
     * This interface is not guaranteed to be thread-safe
     * unless the supplier for the {@code Map} states it as thread safe.
     */
    // TODO: Create a wrapper like java.util.Collections.synchronizedMap?
    public interface Map extends ReadMap {
        /**
         * Removes the value associated with the key.
         * @param key interface for storing the value.
         * @param <T> type of value.
         * @return the value of the key, null if it doesn't exist.
         */
        @Nullable
        <T> T remove(@NonNull Key<T> key);

        /**
         * Sets a value for the key.
         *
         * @param key interface for storing the value.
         * @param <T> type of value.
         * @param value a non-null value of type T.
         * @return the previous value associated with key or null if it doesn't exist.
         */
        // See automatic Kotlin overloading for Java interoperability.
        // https://kotlinlang.org/docs/reference/java-interop.html#operators
        // See also Kotlin set for overloaded operator indexing.
        // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed
        // Also the Kotlin mutable-list set.
        // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html
        @Nullable
        <T> T set(@NonNull Key<T> key, @NonNull T value);
    }

    /**
     * Creates a {@link Map} suitable for adding keys.
     * @return an empty {@link Map} instance.
     */
    @NonNull
    public static Map createMap() {
        return new BaseMap();
    }

    /**
     * A container class for AudioMetadata Format keys.
     *
     * @see AudioTrack.OnCodecFormatChangedListener
     */
    public static class Format {
        // The key name strings used here must match that of the native framework, but are
        // allowed to change between API releases.  This due to the Java specification
        // on what is a compile time constant.
        //
        // Key<?> are final variables but not constant variables (per Java spec 4.12.4) because
        // the keys are not a primitive type nor a String initialized by a constant expression.
        // Hence (per Java spec 13.1.3), they are not resolved at compile time,
        // rather are picked up by applications at run time.
        //
        // So the contractual API behavior of AudioMetadata.Key<> are different than Strings
        // initialized by a constant expression (for example MediaFormat.KEY_*).

        // See MediaFormat
        /**
         * A key representing the bitrate of the encoded stream used in
         *
         * If the stream is variable bitrate, this is the average bitrate of the stream.
         * The unit is bits per second.
         *
         * An Integer value.
         *
         * @see MediaFormat#KEY_BIT_RATE
         */
        @NonNull public static final Key<Integer> KEY_BIT_RATE =
                createKey("bitrate", Integer.class);

        /**
         * A key representing the audio channel mask of the stream.
         *
         * An Integer value.
         *
         * @see AudioTrack#getChannelConfiguration()
         * @see MediaFormat#KEY_CHANNEL_MASK
         */
        @NonNull public static final Key<Integer> KEY_CHANNEL_MASK =
                createKey("channel-mask", Integer.class);


        /**
         * A key representing the codec mime string.
         *
         * A String value.
         *
         * @see MediaFormat#KEY_MIME
         */
        @NonNull public static final Key<String> KEY_MIME = createKey("mime", String.class);

        /**
         * A key representing the audio sample rate in Hz of the stream.
         *
         * An Integer value.
         *
         * @see AudioFormat#getSampleRate()
         * @see MediaFormat#KEY_SAMPLE_RATE
         */
        @NonNull public static final Key<Integer> KEY_SAMPLE_RATE =
                createKey("sample-rate", Integer.class);

        // Unique to Audio

        /**
         * A key representing the bit width of an element of decoded data.
         *
         * An Integer value.
         */
        @NonNull public static final Key<Integer> KEY_BIT_WIDTH =
                createKey("bit-width", Integer.class);

        /**
         * A key representing the presence of Atmos in an E-AC3 stream.
         *
         * A Boolean value which is true if Atmos is present in an E-AC3 stream.
         */
        @NonNull public static final Key<Boolean> KEY_ATMOS_PRESENT =
                createKey("atmos-present", Boolean.class);

        /**
         * A key representing the audio encoding used for the stream.
         * This is the same encoding used in {@link AudioFormat#getEncoding()}.
         *
         * An Integer value.
         *
         * @see AudioFormat#getEncoding()
         */
        @NonNull public static final Key<Integer> KEY_AUDIO_ENCODING =
                createKey("audio-encoding", Integer.class);

        private Format() {} // delete constructor
    }

    /////////////////////////////////////////////////////////////////////////
    // Hidden methods and functions.

    /**
     * Returns a Key object with the correct interface for the AudioMetadata.
     *
     * An interface with the same name and type will be treated as
     * identical for the purposes of value storage, even though
     * other methods or hidden parameters may return different values.
     *
     * @param name The name of the key.
     * @param type The class type of the value represented by the key.
     * @param <T> The type of value.
     * @return a new key interface.
     *
     * Creating keys is currently only allowed by the Framework.
     * @hide
     */
    @NonNull
    public static <T> Key<T> createKey(String name, Class<T> type) {
        // Implementation specific.
        return new Key<T>() {
            private final String mName = name;
            private final Class<T> mType = type;

            @Override
            @NonNull
            public String getName() {
                return mName;
            }

            @Override
            @NonNull
            public Class<T> getValueClass() {
                return mType;
            }

            // hidden interface method to prevent user class implements the of Key interface.
            @Override
            public boolean isFromFramework() {
                return true;
            }
        };
    }

    /**
     * @hide
     *
     * AudioMetadata is based on interfaces in order to allow multiple inheritance
     * and maximum flexibility in implementation.
     *
     * Here, we provide a simple implementation of {@link Map} interface;
     * Note that the Keys are not specific to this Map implementation.
     *
     * It is possible to require the keys to be of a certain class
     * before allowing a set or get operation.
     */
    public static class BaseMap implements Map {
        @Override
        public <T> boolean containsKey(@NonNull Key<T> key) {
            Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key));
            return valuePair != null;
        }

        @Override
        @NonNull
        public Map dup() {
            BaseMap map = new BaseMap();
            map.mHashMap.putAll(this.mHashMap);
            return map;
        }

        @Override
        @Nullable
        public <T> T get(@NonNull Key<T> key) {
            Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key));
            return (T) getValueFromValuePair(valuePair);
        }

        @Override
        @NonNull
        public Set<Key<?>> keySet() {
            HashSet<Key<?>> set = new HashSet();
            for (Pair<Key<?>, Object> pair : mHashMap.values()) {
                set.add(pair.first);
            }
            return set;
        }

        @Override
        @Nullable
        public <T> T remove(@NonNull Key<T> key) {
            Pair<Key<?>, Object> valuePair = mHashMap.remove(pairFromKey(key));
            return (T) getValueFromValuePair(valuePair);
        }

        @Override
        @Nullable
        public <T> T set(@NonNull Key<T> key, @NonNull T value) {
            Objects.requireNonNull(value);
            Pair<Key<?>, Object> valuePair = mHashMap
                    .put(pairFromKey(key), new Pair<Key<?>, Object>(key, value));
            return (T) getValueFromValuePair(valuePair);
        }

        @Override
        public int size() {
            return mHashMap.size();
        }

        /*
         * Implementation specific.
         *
         * To store the value in the HashMap we need to convert the Key interface
         * to a hashcode() / equals() compliant Pair.
         */
        @NonNull
        private static <T> Pair<String, Class<?>> pairFromKey(@NonNull Key<T> key) {
            Objects.requireNonNull(key);
            return new Pair<String, Class<?>>(key.getName(), key.getValueClass());
        }

        /*
         * Implementation specific.
         *
         * We store in a Pair (valuePair) the key along with the Object value.
         * This helper returns the Object value from the value pair.
         */
        @Nullable
        private static Object getValueFromValuePair(@Nullable Pair<Key<?>, Object> valuePair) {
            if (valuePair == null) {
                return null;
            }
            return valuePair.second;
        }

        /*
         * Implementation specific.
         *
         * We use a HashMap to back the AudioMetadata BaseMap object.
         * This is not locked, so concurrent reads are permitted if all threads
         * have a ReadMap; this is risky with a Map.
         */
        private final HashMap<Pair<String, Class<?>>, Pair<Key<?>, Object>> mHashMap =
                new HashMap();
    }

    // Delete the constructor as there is nothing to implement here.
    private AudioMetadata() {}
}
+79 −0
Original line number Diff line number Diff line
@@ -188,6 +188,10 @@ public class AudioTrack extends PlayerBase

    // Events:
    // to keep in sync with frameworks/av/include/media/AudioTrack.h
    // Note: To avoid collisions with other event constants,
    // do not define an event here that is the same value as
    // AudioSystem.NATIVE_EVENT_ROUTING_CHANGE.

    /**
     * Event id denotes when playback head has reached a previously set marker.
     */
@@ -210,6 +214,14 @@ public class AudioTrack extends PlayerBase
     * back (after stop is called) for an offloaded track.
     */
    private static final int NATIVE_EVENT_STREAM_END = 7;
    /**
     * Event id denotes when the codec format changes.
     *
     * Note: Similar to a device routing change (AudioSystem.NATIVE_EVENT_ROUTING_CHANGE),
     * this event comes from the AudioFlinger Thread / Output Stream management
     * (not from buffer indications as above).
     */
    private static final int NATIVE_EVENT_CODEC_FORMAT_CHANGE = 100;

    private final static String TAG = "android.media.AudioTrack";

@@ -3409,6 +3421,67 @@ public class AudioTrack extends PlayerBase
        }
    }

    //--------------------------------------------------------------------------
    // Codec notifications
    //--------------------

    // OnCodecFormatChangedListener notifications uses an instance
    // of ListenerList to manage its listeners.

    private final Utils.ListenerList<AudioMetadata.ReadMap> mCodecFormatChangedListeners =
            new Utils.ListenerList();

    /**
     * Interface definition for a listener for codec format changes.
     */
    public interface OnCodecFormatChangedListener {
        /**
         * Called when the compressed codec format changes.
         *
         * @param audioTrack is the {@code AudioTrack} instance associated with the codec.
         * @param info is a {@link AudioMetadata.ReadMap} of values which contains decoded format
         *     changes reported by the codec.  Not all hardware
         *     codecs indicate codec format changes. Acceptable keys are taken from
         *     {@code AudioMetadata.Format.KEY_*} range, with the associated value type.
         */
        void onCodecFormatChanged(
                @NonNull AudioTrack audioTrack, @Nullable AudioMetadata.ReadMap info);
    }

    /**
     * Adds an {@link OnCodecFormatChangedListener} to receive notifications of
     * codec format change events on this {@code AudioTrack}.
     *
     * @param executor  Specifies the {@link Executor} object to control execution.
     *
     * @param listener The {@link OnCodecFormatChangedListener} interface to receive
     *     notifications of codec events.
     */
    public void addOnCodecFormatChangedListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull OnCodecFormatChangedListener listener) { // NPE checks done by ListenerList.
        mCodecFormatChangedListeners.add(
                listener, /* key for removal */
                executor,
                (int eventCode, AudioMetadata.ReadMap readMap) -> {
                    // eventCode is unused by this implementation.
                    listener.onCodecFormatChanged(this, readMap);
                }
        );
    }

    /**
     * Removes an {@link OnCodecFormatChangedListener} which has been previously added
     * to receive codec format change events.
     *
     * @param listener The previously added {@link OnCodecFormatChangedListener} interface
     * to remove.
     */
    public void removeOnCodecFormatChangedListener(
            @NonNull OnCodecFormatChangedListener listener) {
        mCodecFormatChangedListeners.remove(listener);  // NPE checks done by ListenerList.
    }

    //---------------------------------------------------------
    // Interface definitions
    //--------------------
@@ -3745,6 +3818,12 @@ public class AudioTrack extends PlayerBase
            return;
        }

        if (what == NATIVE_EVENT_CODEC_FORMAT_CHANGE) {
            track.mCodecFormatChangedListeners.notify(
                    0 /* eventCode, unused */, (AudioMetadata.ReadMap) obj);
            return;
        }

        if (what == NATIVE_EVENT_CAN_WRITE_MORE_DATA
                || what == NATIVE_EVENT_NEW_IAUDIOTRACK
                || what == NATIVE_EVENT_STREAM_END) {
+280 −2

File changed.

Preview size limit exceeded, changes collapsed.