Skip to content

Commit ccaff20

Browse files
committed
Try input format PCM encoding first in MediaCodecAudioRenderer
...and remove hard requirement for 16-bit PCM support in the audio sink even if that's not needed to playback. If the audio file is using 24-bit or 32-bit (int) PCM, MediaCodecAudioRenderer tries to output float, and if that is not supported, falls back to 16-bit PCM. This commit changes this behavior to first trying the media file format, then any close high-resolution audio formats (such as float) and then fall back to 16-bit PCM. The behaviour with DefaultAudioSink does not change, but if an audio sink supports any more optimal formats, they will now be used with MediaCodecAudioRenderer. Issue: #1931
1 parent 16c0711 commit ccaff20

File tree

2 files changed

+88
-12
lines changed

2 files changed

+88
-12
lines changed

libraries/common/src/main/java/androidx/media3/common/util/Util.java

+46
Original file line numberDiff line numberDiff line change
@@ -2268,6 +2268,52 @@ public static boolean isEncodingHighResolutionPcm(@C.PcmEncoding int encoding) {
22682268
|| encoding == C.ENCODING_PCM_FLOAT;
22692269
}
22702270

2271+
/**
2272+
* Returns the closest platform PCM encodings supported on the current API level for
2273+
* {@code encoding}. The results never include 16-bit PCM, which should be used if none of the
2274+
* suggested formats can be used for playback.
2275+
*
2276+
* @param encoding The encoding of the audio data.
2277+
* @return The closest platform encodings, sorted by descending order of preference. May be empty.
2278+
*/
2279+
@UnstableApi
2280+
public static int[] getClosestPlatformPcmEncodings(@C.PcmEncoding int encoding) {
2281+
switch (encoding) {
2282+
case C.ENCODING_PCM_FLOAT:
2283+
return Build.VERSION.SDK_INT >= 31
2284+
? new int[] {
2285+
AudioFormat.ENCODING_PCM_FLOAT,
2286+
AudioFormat.ENCODING_PCM_32BIT,
2287+
AudioFormat.ENCODING_PCM_24BIT_PACKED
2288+
}
2289+
: new int[] {AudioFormat.ENCODING_PCM_FLOAT};
2290+
case C.ENCODING_PCM_32BIT:
2291+
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
2292+
return Build.VERSION.SDK_INT >= 31
2293+
? new int[] {
2294+
AudioFormat.ENCODING_PCM_32BIT,
2295+
AudioFormat.ENCODING_PCM_FLOAT,
2296+
AudioFormat.ENCODING_PCM_24BIT_PACKED
2297+
}
2298+
: new int[] {AudioFormat.ENCODING_PCM_FLOAT};
2299+
case C.ENCODING_PCM_24BIT:
2300+
case C.ENCODING_PCM_24BIT_BIG_ENDIAN:
2301+
return Build.VERSION.SDK_INT >= 31
2302+
? new int[] {
2303+
AudioFormat.ENCODING_PCM_24BIT_PACKED,
2304+
AudioFormat.ENCODING_PCM_32BIT,
2305+
AudioFormat.ENCODING_PCM_FLOAT
2306+
}
2307+
: new int[] {AudioFormat.ENCODING_PCM_FLOAT};
2308+
case C.ENCODING_PCM_8BIT:
2309+
return new int[] {AudioFormat.ENCODING_PCM_8BIT};
2310+
case C.ENCODING_PCM_16BIT:
2311+
case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
2312+
default:
2313+
return new int[] {};
2314+
}
2315+
}
2316+
22712317
/**
22722318
* Returns the audio track channel configuration for the given channel count, or {@link
22732319
* AudioFormat#CHANNEL_INVALID} if output is not possible.

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java

+42-12
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import android.annotation.SuppressLint;
2727
import android.content.Context;
2828
import android.media.AudioDeviceInfo;
29-
import android.media.AudioFormat;
3029
import android.media.MediaCodec;
3130
import android.media.MediaCrypto;
3231
import android.media.MediaFormat;
@@ -338,14 +337,38 @@ public String getName() {
338337
}
339338
// If the input is PCM then it will be passed directly to the sink. Hence the sink must support
340339
// the input format directly.
341-
if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType) && !audioSink.supportsFormat(format)) {
342-
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
343-
}
344-
// For all other input formats, we expect the decoder to output 16-bit PCM.
345-
if (!audioSink.supportsFormat(
346-
Util.getPcmFormat(C.ENCODING_PCM_16BIT, format.channelCount, format.sampleRate))) {
347-
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
340+
if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)) {
341+
if (!audioSink.supportsFormat(format)) {
342+
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
343+
}
344+
} else {
345+
// For all other input formats, MediaCodec can do some conversions for us. Check if the sink
346+
// supports any of the formats we can get out of MediaCodec.
347+
boolean pcmEncodingSupported = false;
348+
// On API levels before 24, any non-PCM input format is decoded to 16-bit PCM.
349+
if (Util.SDK_INT >= 24) {
350+
int[] platformEncodings = Util.getClosestPlatformPcmEncodings(format.pcmEncoding);
351+
for (int platformEncoding : platformEncodings) {
352+
if (audioSink.getFormatSupport(
353+
Util.getPcmFormat(platformEncoding, format.channelCount, format.sampleRate))
354+
== AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY) {
355+
pcmEncodingSupported = true;
356+
break;
357+
}
358+
}
359+
}
360+
// If none of the suggested encodings are supported, fall back to MediaCodec's default value
361+
// of 16-bit PCM.
362+
if (!pcmEncodingSupported
363+
&& audioSink.supportsFormat(
364+
Util.getPcmFormat(C.ENCODING_PCM_16BIT, format.channelCount, format.sampleRate))) {
365+
pcmEncodingSupported = true;
366+
}
367+
if (!pcmEncodingSupported) {
368+
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
369+
}
348370
}
371+
349372
List<MediaCodecInfo> decoderInfos =
350373
getDecoderInfos(mediaCodecSelector, format, /* requiresSecureDecoder= */ false, audioSink);
351374
if (decoderInfos.isEmpty()) {
@@ -1025,11 +1048,18 @@ protected MediaFormat getMediaFormat(
10251048
mediaFormat.setInteger("ac4-is-sync", 1);
10261049
}
10271050
}
1028-
if (SDK_INT >= 24
1029-
&& audioSink.getFormatSupport(
1030-
Util.getPcmFormat(C.ENCODING_PCM_FLOAT, format.channelCount, format.sampleRate))
1051+
if (SDK_INT >= 24) {
1052+
int[] platformEncodings = Util.getClosestPlatformPcmEncodings(format.pcmEncoding);
1053+
for (int platformEncoding : platformEncodings) {
1054+
if (audioSink.getFormatSupport(
1055+
Util.getPcmFormat(platformEncoding, format.channelCount, format.sampleRate))
10311056
== AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY) {
1032-
mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_FLOAT);
1057+
mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, platformEncoding);
1058+
break;
1059+
}
1060+
}
1061+
// If none of the suggested encodings are supported, fall back to MediaCodec's default value
1062+
// of 16-bit PCM.
10331063
}
10341064
if (SDK_INT >= 32) {
10351065
mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99);

0 commit comments

Comments
 (0)