Skip to content

Commit 72a1aa9

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 6536929 commit 72a1aa9

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,6 +2276,52 @@ public static boolean isEncodingHighResolutionPcm(@C.PcmEncoding int encoding) {
22762276
|| encoding == C.ENCODING_PCM_FLOAT;
22772277
}
22782278

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

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

Lines changed: 42 additions & 12 deletions
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 (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)