1 /* 2 * Copyright (C) 2016 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 package com.google.android.exoplayer2.audio; 17 18 import android.annotation.SuppressLint; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.media.AudioFormat; 23 import android.media.AudioManager; 24 import android.net.Uri; 25 import android.provider.Settings.Global; 26 import androidx.annotation.Nullable; 27 import com.google.android.exoplayer2.C; 28 import com.google.android.exoplayer2.util.Util; 29 import java.util.Arrays; 30 31 /** Represents the set of audio formats that a device is capable of playing. */ 32 public final class AudioCapabilities { 33 34 private static final int DEFAULT_MAX_CHANNEL_COUNT = 8; 35 36 /** The minimum audio capabilities supported by all devices. */ 37 public static final AudioCapabilities DEFAULT_AUDIO_CAPABILITIES = 38 new AudioCapabilities(new int[] {AudioFormat.ENCODING_PCM_16BIT}, DEFAULT_MAX_CHANNEL_COUNT); 39 40 /** Audio capabilities when the device specifies external surround sound. */ 41 private static final AudioCapabilities EXTERNAL_SURROUND_SOUND_CAPABILITIES = 42 new AudioCapabilities( 43 new int[] { 44 AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_E_AC3 45 }, 46 DEFAULT_MAX_CHANNEL_COUNT); 47 48 /** Global settings key for devices that can specify external surround sound. */ 49 private static final String EXTERNAL_SURROUND_SOUND_KEY = "external_surround_sound_enabled"; 50 51 /** 52 * Returns the current audio capabilities for the device. 53 * 54 * @param context A context for obtaining the current audio capabilities. 55 * @return The current audio capabilities for the device. 56 */ 57 @SuppressWarnings("InlinedApi") getCapabilities(Context context)58 public static AudioCapabilities getCapabilities(Context context) { 59 Intent intent = 60 context.registerReceiver( 61 /* receiver= */ null, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG)); 62 return getCapabilities(context, intent); 63 } 64 65 @SuppressLint("InlinedApi") getCapabilities(Context context, @Nullable Intent intent)66 /* package */ static AudioCapabilities getCapabilities(Context context, @Nullable Intent intent) { 67 if (deviceMaySetExternalSurroundSoundGlobalSetting() 68 && Global.getInt(context.getContentResolver(), EXTERNAL_SURROUND_SOUND_KEY, 0) == 1) { 69 return EXTERNAL_SURROUND_SOUND_CAPABILITIES; 70 } 71 if (intent == null || intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) == 0) { 72 return DEFAULT_AUDIO_CAPABILITIES; 73 } 74 return new AudioCapabilities( 75 intent.getIntArrayExtra(AudioManager.EXTRA_ENCODINGS), 76 intent.getIntExtra( 77 AudioManager.EXTRA_MAX_CHANNEL_COUNT, /* defaultValue= */ DEFAULT_MAX_CHANNEL_COUNT)); 78 } 79 80 /** 81 * Returns the global settings {@link Uri} used by the device to specify external surround sound, 82 * or null if the device does not support this functionality. 83 */ 84 @Nullable getExternalSurroundSoundGlobalSettingUri()85 /* package */ static Uri getExternalSurroundSoundGlobalSettingUri() { 86 return deviceMaySetExternalSurroundSoundGlobalSetting() 87 ? Global.getUriFor(EXTERNAL_SURROUND_SOUND_KEY) 88 : null; 89 } 90 91 private final int[] supportedEncodings; 92 private final int maxChannelCount; 93 94 /** 95 * Constructs new audio capabilities based on a set of supported encodings and a maximum channel 96 * count. 97 * 98 * <p>Applications should generally call {@link #getCapabilities(Context)} to obtain an instance 99 * based on the capabilities advertised by the platform, rather than calling this constructor. 100 * 101 * @param supportedEncodings Supported audio encodings from {@link android.media.AudioFormat}'s 102 * {@code ENCODING_*} constants. Passing {@code null} indicates that no encodings are 103 * supported. 104 * @param maxChannelCount The maximum number of audio channels that can be played simultaneously. 105 */ AudioCapabilities(@ullable int[] supportedEncodings, int maxChannelCount)106 public AudioCapabilities(@Nullable int[] supportedEncodings, int maxChannelCount) { 107 if (supportedEncodings != null) { 108 this.supportedEncodings = Arrays.copyOf(supportedEncodings, supportedEncodings.length); 109 Arrays.sort(this.supportedEncodings); 110 } else { 111 this.supportedEncodings = new int[0]; 112 } 113 this.maxChannelCount = maxChannelCount; 114 } 115 116 /** 117 * Returns whether this device supports playback of the specified audio {@code encoding}. 118 * 119 * @param encoding One of {@link C.Encoding}'s {@code ENCODING_*} constants. 120 * @return Whether this device supports playback the specified audio {@code encoding}. 121 */ supportsEncoding(@.Encoding int encoding)122 public boolean supportsEncoding(@C.Encoding int encoding) { 123 return Arrays.binarySearch(supportedEncodings, encoding) >= 0; 124 } 125 126 /** 127 * Returns the maximum number of channels the device can play at the same time. 128 */ getMaxChannelCount()129 public int getMaxChannelCount() { 130 return maxChannelCount; 131 } 132 133 @Override equals(@ullable Object other)134 public boolean equals(@Nullable Object other) { 135 if (this == other) { 136 return true; 137 } 138 if (!(other instanceof AudioCapabilities)) { 139 return false; 140 } 141 AudioCapabilities audioCapabilities = (AudioCapabilities) other; 142 return Arrays.equals(supportedEncodings, audioCapabilities.supportedEncodings) 143 && maxChannelCount == audioCapabilities.maxChannelCount; 144 } 145 146 @Override hashCode()147 public int hashCode() { 148 return maxChannelCount + 31 * Arrays.hashCode(supportedEncodings); 149 } 150 151 @Override toString()152 public String toString() { 153 return "AudioCapabilities[maxChannelCount=" + maxChannelCount 154 + ", supportedEncodings=" + Arrays.toString(supportedEncodings) + "]"; 155 } 156 deviceMaySetExternalSurroundSoundGlobalSetting()157 private static boolean deviceMaySetExternalSurroundSoundGlobalSetting() { 158 return Util.SDK_INT >= 17 && "Amazon".equals(Util.MANUFACTURER); 159 } 160 } 161