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(); + } +}