diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
index 5b90a97905..ba0a94aa65 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
@@ -1954,6 +1954,48 @@ public final class Util {
.build();
}
+ /**
+ * Retrieves the API Level that {@link AudioFormat} introduced an encoding.
+ *
+ *
Method returns {@link Integer#MAX_VALUE} if the encoding is unknown.
+ *
+ * @param encoding for which to get the API level.
+ */
+ @UnstableApi
+ public static int getApiLevelThatAudioFormatIntroducedAudioEncoding(int encoding) {
+ switch (encoding) {
+ case C.ENCODING_PCM_16BIT:
+ case C.ENCODING_PCM_8BIT:
+ return 3;
+ case C.ENCODING_PCM_FLOAT:
+ case C.ENCODING_AC3:
+ case C.ENCODING_E_AC3:
+ return 21;
+ case C.ENCODING_DTS:
+ case C.ENCODING_DTS_HD:
+ return 23;
+ case C.ENCODING_DOLBY_TRUEHD:
+ return 25;
+ case C.ENCODING_MP3:
+ case C.ENCODING_AAC_LC:
+ case C.ENCODING_AAC_HE_V1:
+ case C.ENCODING_AAC_HE_V2:
+ case C.ENCODING_AAC_ELD:
+ case C.ENCODING_AAC_XHE:
+ case C.ENCODING_AC4:
+ case C.ENCODING_E_AC3_JOC:
+ return 28;
+ case C.ENCODING_OPUS:
+ return 30;
+ case C.ENCODING_PCM_32BIT:
+ return 31;
+ case C.ENCODING_DTS_UHD_P2:
+ return 34;
+ default:
+ return Integer.MAX_VALUE;
+ }
+ }
+
/**
* Returns the frame size for audio with {@code channelCount} channels in the specified encoding.
*
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java
index 2c26755157..d01c8eebd7 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java
@@ -378,8 +378,8 @@ public final class AudioCapabilities {
public static ImmutableList getDirectPlaybackSupportedEncodings() {
ImmutableList.Builder supportedEncodingsListBuilder = ImmutableList.builder();
for (int encoding : ALL_SURROUND_ENCODINGS_AND_MAX_CHANNELS.keySet()) {
- // AudioFormat.ENCODING_DTS_UHD_P2 is supported from API 34.
- if (Util.SDK_INT < 34 && encoding == C.ENCODING_DTS_UHD_P2) {
+ if (Util.SDK_INT < Util.getApiLevelThatAudioFormatIntroducedAudioEncoding(encoding)) {
+ // Example: AudioFormat.ENCODING_DTS_UHD_P2 is supported only from API 34.
continue;
}
if (AudioTrack.isDirectPlaybackSupported(
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioOffloadSupportProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioOffloadSupportProvider.java
index 55563e4415..5907380ac6 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioOffloadSupportProvider.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioOffloadSupportProvider.java
@@ -81,15 +81,12 @@ public final class DefaultAudioOffloadSupportProvider
@C.Encoding
int encoding = MimeTypes.getEncoding(checkNotNull(format.sampleMimeType), format.codecs);
- if (encoding == C.ENCODING_INVALID) {
- return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
- }
- // AudioFormat.ENCODING_DTS_UHD_P2 is defined from API 34 onwards. We return offload
- // unsupported to prevent crash in Util.getAudioFormat() below when it tries to create
- // an AudioFormat with ENCODING_DTS_UHD_P2.
- if ((Util.SDK_INT < 34) && (encoding == C.ENCODING_DTS_UHD_P2)) {
+ if (encoding == C.ENCODING_INVALID
+ || Util.SDK_INT < Util.getApiLevelThatAudioFormatIntroducedAudioEncoding(encoding)) {
+ // Example: AudioFormat.ENCODING_OPUS is supported only from API 30.
return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
}
+
int channelConfig = Util.getAudioTrackChannelConfig(format.channelCount);
if (channelConfig == AudioFormat.CHANNEL_INVALID) {
return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioOffloadSupportProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioOffloadSupportProviderTest.java
new file mode 100644
index 0000000000..8779ac32df
--- /dev/null
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioOffloadSupportProviderTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2023 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 androidx.media3.exoplayer.audio;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.media3.common.AudioAttributes;
+import androidx.media3.common.Format;
+import androidx.media3.common.MimeTypes;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Unit tests for {@link DefaultAudioOffloadSupportProvider}. */
+@RunWith(AndroidJUnit4.class)
+public final class DefaultAudioOffloadSupportProviderTest {
+
+ @Test
+ public void
+ getAudioOffloadSupport_withoutSampleRate_returnsAudioOffloadSupportDefaultUnsupported() {
+ Format formatWithoutSampleRate =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_MPEG).build();
+ DefaultAudioOffloadSupportProvider audioOffloadSupportProvider =
+ new DefaultAudioOffloadSupportProvider();
+
+ AudioOffloadSupport audioOffloadSupport =
+ audioOffloadSupportProvider.getAudioOffloadSupport(
+ formatWithoutSampleRate, AudioAttributes.DEFAULT);
+
+ assertThat(audioOffloadSupport.isFormatSupported).isFalse();
+ }
+
+ @Test
+ @Config(maxSdk = 29)
+ public void
+ getAudioOffloadSupport_withOpusAndSdkUnder30_returnsAudioOffloadSupportDefaultUnsupported() {
+ Format formatOpus =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_OPUS).setSampleRate(48_000).build();
+ DefaultAudioOffloadSupportProvider audioOffloadSupportProvider =
+ new DefaultAudioOffloadSupportProvider();
+
+ AudioOffloadSupport audioOffloadSupport =
+ audioOffloadSupportProvider.getAudioOffloadSupport(formatOpus, AudioAttributes.DEFAULT);
+
+ assertThat(audioOffloadSupport.isFormatSupported).isFalse();
+ }
+
+ @Test
+ @Config(maxSdk = 33)
+ public void
+ getAudioOffloadSupport_withDtsXAndSdkUnder34_returnsAudioOffloadSupportDefaultUnsupported() {
+ Format formatDtsX =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_DTS_X).setSampleRate(48_000).build();
+ DefaultAudioOffloadSupportProvider audioOffloadSupportProvider =
+ new DefaultAudioOffloadSupportProvider();
+
+ AudioOffloadSupport audioOffloadSupport =
+ audioOffloadSupportProvider.getAudioOffloadSupport(formatDtsX, AudioAttributes.DEFAULT);
+
+ assertThat(audioOffloadSupport.isFormatSupported).isFalse();
+ }
+}