1 /* 2 * Copyright (C) 2020 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.annotation.SystemApi; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 25 import java.lang.annotation.Retention; 26 import java.lang.annotation.RetentionPolicy; 27 import java.util.Arrays; 28 import java.util.Objects; 29 import java.util.stream.Collectors; 30 31 /** 32 * An AudioProfile is specific to an audio format and lists supported sampling rates and 33 * channel masks for that format. An {@link AudioDeviceInfo} has a list of supported AudioProfiles. 34 * There can be multiple profiles whose encoding format is the same. This usually happens when 35 * an encoding format is only supported when it is encapsulated by some particular encapsulation 36 * types. If there are multiple encapsulation types that can carry this encoding format, they will 37 * be reported in different audio profiles. The application can choose any of the encapsulation 38 * types. 39 */ 40 public class AudioProfile implements Parcelable { 41 /** 42 * No encapsulation type is specified. 43 */ 44 public static final int AUDIO_ENCAPSULATION_TYPE_NONE = 0; 45 /** 46 * Encapsulation format is defined in standard IEC 61937. 47 */ 48 public static final int AUDIO_ENCAPSULATION_TYPE_IEC61937 = 1; 49 /** 50 * Encapsulation format is PCM, which can be used by other formats that can be wrapped in 51 * a PCM frame, such as DSD(Direct Stream Digital). 52 */ 53 public static final int AUDIO_ENCAPSULATION_TYPE_PCM = 2; 54 55 /** @hide */ 56 @IntDef({ 57 AUDIO_ENCAPSULATION_TYPE_NONE, 58 AUDIO_ENCAPSULATION_TYPE_IEC61937, 59 AUDIO_ENCAPSULATION_TYPE_PCM, 60 }) 61 @Retention(RetentionPolicy.SOURCE) 62 public @interface EncapsulationType {} 63 64 private final int mFormat; 65 private final int[] mSamplingRates; 66 private final int[] mChannelMasks; 67 private final int[] mChannelIndexMasks; 68 private final int mEncapsulationType; 69 70 /** 71 * @hide 72 * Constructor from format, sampling rates, channel masks, channel index masks and 73 * encapsulation type. 74 * @param format the audio format 75 * @param samplingRates the supported sampling rates 76 * @param channelMasks the supported channel masks 77 * @param channelIndexMasks the supported channel index masks 78 * @param encapsulationType the encapsulation type of the encoding format 79 */ 80 @SystemApi AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, @NonNull int[] channelIndexMasks, int encapsulationType)81 public AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, 82 @NonNull int[] channelIndexMasks, int encapsulationType) { 83 mFormat = format; 84 mSamplingRates = samplingRates; 85 mChannelMasks = channelMasks; 86 mChannelIndexMasks = channelIndexMasks; 87 mEncapsulationType = encapsulationType; 88 } 89 90 /** 91 * @return the encoding format for this AudioProfile. 92 */ getFormat()93 public @AudioFormat.Encoding int getFormat() { 94 return mFormat; 95 } 96 97 /** 98 * @return an array of channel position masks that are associated with the encoding format. 99 */ getChannelMasks()100 public @NonNull int[] getChannelMasks() { 101 return mChannelMasks; 102 } 103 104 /** 105 * @return an array of channel index masks that are associated with the encoding format. 106 */ getChannelIndexMasks()107 public @NonNull int[] getChannelIndexMasks() { 108 return mChannelIndexMasks; 109 } 110 111 /** 112 * @return an array of sample rates that are associated with the encoding format. 113 */ getSampleRates()114 public @NonNull int[] getSampleRates() { 115 return mSamplingRates; 116 } 117 118 /** 119 * The encapsulation type indicates what encapsulation type is required when the framework is 120 * using this format when playing to a device exposing this audio profile. 121 * When encapsulation is required, only playback with {@link android.media.AudioTrack} API is 122 * supported. But playback with {@link android.media.MediaPlayer} is not. 123 * When an encapsulation type is required, the {@link AudioFormat} encoding selected when 124 * creating the {@link AudioTrack} must match the encapsulation type, e.g 125 * AudioFormat.ENCODING_IEC61937 for AUDIO_ENCAPSULATION_TYPE_IEC61937. 126 * 127 * @return an integer representing the encapsulation type 128 * 129 * @see #AUDIO_ENCAPSULATION_TYPE_NONE 130 * @see #AUDIO_ENCAPSULATION_TYPE_IEC61937 131 * @see #AUDIO_ENCAPSULATION_TYPE_PCM 132 */ getEncapsulationType()133 public @EncapsulationType int getEncapsulationType() { 134 return mEncapsulationType; 135 } 136 137 @Override hashCode()138 public int hashCode() { 139 return Objects.hash(mFormat, Arrays.hashCode(mSamplingRates), 140 Arrays.hashCode(mChannelMasks), Arrays.hashCode(mChannelIndexMasks), 141 mEncapsulationType); 142 } 143 144 @Override equals(Object o)145 public boolean equals(Object o) { 146 if (this == o) return true; 147 if (o == null || getClass() != o.getClass()) return false; 148 149 AudioProfile that = (AudioProfile) o; 150 return ((mFormat == that.mFormat) 151 && (hasIdenticalElements(mSamplingRates, that.mSamplingRates)) 152 && (hasIdenticalElements(mChannelMasks, that.mChannelMasks)) 153 && (hasIdenticalElements(mChannelIndexMasks, that.mChannelIndexMasks)) 154 && (mEncapsulationType == that.mEncapsulationType)); 155 } 156 157 @Override toString()158 public String toString() { 159 StringBuilder sb = new StringBuilder("{"); 160 sb.append(AudioFormat.toLogFriendlyEncoding(mFormat)); 161 if (mSamplingRates != null && mSamplingRates.length > 0) { 162 sb.append(", sampling rates=").append(Arrays.toString(mSamplingRates)); 163 } 164 if (mChannelMasks != null && mChannelMasks.length > 0) { 165 sb.append(", channel masks=").append(toHexString(mChannelMasks)); 166 } 167 if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) { 168 sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks)); 169 } 170 sb.append(", encapsulation type=" + mEncapsulationType); 171 sb.append("}"); 172 return sb.toString(); 173 } 174 toHexString(int[] ints)175 private static String toHexString(int[] ints) { 176 if (ints == null || ints.length == 0) { 177 return ""; 178 } 179 return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X", anInt)) 180 .collect(Collectors.joining(", ")); 181 } 182 hasIdenticalElements(int[] array1, int[] array2)183 private static boolean hasIdenticalElements(int[] array1, int[] array2) { 184 int[] sortedArray1 = Arrays.copyOf(array1, array1.length); 185 Arrays.sort(sortedArray1); 186 int[] sortedArray2 = Arrays.copyOf(array2, array2.length); 187 Arrays.sort(sortedArray2); 188 return Arrays.equals(sortedArray1, sortedArray2); 189 } 190 191 @Override describeContents()192 public int describeContents() { 193 return 0; 194 } 195 196 @Override writeToParcel(@onNull Parcel dest, int flags)197 public void writeToParcel(@NonNull Parcel dest, int flags) { 198 dest.writeInt(mFormat); 199 dest.writeIntArray(mSamplingRates); 200 dest.writeIntArray(mChannelMasks); 201 dest.writeIntArray(mChannelIndexMasks); 202 dest.writeInt(mEncapsulationType); 203 } 204 AudioProfile(@onNull Parcel in)205 private AudioProfile(@NonNull Parcel in) { 206 mFormat = in.readInt(); 207 mSamplingRates = in.createIntArray(); 208 mChannelMasks = in.createIntArray(); 209 mChannelIndexMasks = in.createIntArray(); 210 mEncapsulationType = in.readInt(); 211 } 212 213 public static final @NonNull Parcelable.Creator<AudioProfile> CREATOR = 214 new Parcelable.Creator<AudioProfile>() { 215 /** 216 * Rebuilds an AudioProfile previously stored with writeToParcel(). 217 * @param p Parcel object to read the AudioProfile from 218 * @return a new AudioProfile created from the data in the parcel 219 */ 220 public AudioProfile createFromParcel(Parcel p) { 221 return new AudioProfile(p); 222 } 223 224 public AudioProfile[] newArray(int size) { 225 return new AudioProfile[size]; 226 } 227 }; 228 } 229