Skip to content

Commit 478efac

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.
1 parent 8702554 commit 478efac

File tree

2 files changed

+87
-12
lines changed

2 files changed

+87
-12
lines changed

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

+46
Original file line numberDiff line numberDiff line change
@@ -2241,6 +2241,52 @@ public static boolean isEncodingHighResolutionPcm(@C.PcmEncoding int encoding) {
22412241
|| encoding == C.ENCODING_PCM_FLOAT;
22422242
}
22432243

2244+
/**
2245+
* Returns the closest platform PCM encodings supported on the current API level for
2246+
* {@code encoding}. The results never include 16-bit PCM, which should be used if none of the
2247+
* suggested formats can be used for playback.
2248+
*
2249+
* @param encoding The encoding of the audio data.
2250+
* @return The closest platform encodings, sorted by descending order of preference. May be empty.
2251+
*/
2252+
@UnstableApi
2253+
public static int[] getClosestPlatformPcmEncodings(@C.PcmEncoding int encoding) {
2254+
switch (encoding) {
2255+
case C.ENCODING_PCM_FLOAT:
2256+
return Util.SDK_INT >= 31
2257+
? new int[] {
2258+
AudioFormat.ENCODING_PCM_FLOAT,
2259+
AudioFormat.ENCODING_PCM_32BIT,
2260+
AudioFormat.ENCODING_PCM_24BIT_PACKED
2261+
}
2262+
: new int[] {AudioFormat.ENCODING_PCM_FLOAT};
2263+
case C.ENCODING_PCM_32BIT:
2264+
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
2265+
return Util.SDK_INT >= 31
2266+
? new int[] {
2267+
AudioFormat.ENCODING_PCM_32BIT,
2268+
AudioFormat.ENCODING_PCM_FLOAT,
2269+
AudioFormat.ENCODING_PCM_24BIT_PACKED
2270+
}
2271+
: new int[] {AudioFormat.ENCODING_PCM_FLOAT};
2272+
case C.ENCODING_PCM_24BIT:
2273+
case C.ENCODING_PCM_24BIT_BIG_ENDIAN:
2274+
return Util.SDK_INT >= 31
2275+
? new int[] {
2276+
AudioFormat.ENCODING_PCM_24BIT_PACKED,
2277+
AudioFormat.ENCODING_PCM_32BIT,
2278+
AudioFormat.ENCODING_PCM_FLOAT
2279+
}
2280+
: new int[] {AudioFormat.ENCODING_PCM_FLOAT};
2281+
case C.ENCODING_PCM_8BIT:
2282+
return new int[] {AudioFormat.ENCODING_PCM_8BIT};
2283+
case C.ENCODING_PCM_16BIT:
2284+
case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
2285+
default:
2286+
return new int[] {};
2287+
}
2288+
}
2289+
22442290
/**
22452291
* Returns the audio track channel configuration for the given channel count, or {@link
22462292
* AudioFormat#CHANNEL_INVALID} if output is not possible.

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

+41-12
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import android.annotation.SuppressLint;
2525
import android.content.Context;
2626
import android.media.AudioDeviceInfo;
27-
import android.media.AudioFormat;
2827
import android.media.MediaCodec;
2928
import android.media.MediaCrypto;
3029
import android.media.MediaFormat;
@@ -334,14 +333,37 @@ public String getName() {
334333
}
335334
// If the input is PCM then it will be passed directly to the sink. Hence the sink must support
336335
// the input format directly.
337-
if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType) && !audioSink.supportsFormat(format)) {
338-
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
339-
}
340-
// For all other input formats, we expect the decoder to output 16-bit PCM.
341-
if (!audioSink.supportsFormat(
342-
Util.getPcmFormat(C.ENCODING_PCM_16BIT, format.channelCount, format.sampleRate))) {
343-
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
336+
if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)) {
337+
if (!audioSink.supportsFormat(format)) {
338+
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
339+
}
340+
} else {
341+
// For all other input formats, MediaCodec can do some conversions for us. Check if the sink
342+
// supports any of the formats we can get out of MediaCodec.
343+
boolean pcmEncodingSupported = false;
344+
// On API levels before 24, any non-PCM input format is decoded to 16-bit PCM.
345+
if (Util.SDK_INT >= 24) {
346+
int[] platformEncodings = Util.getClosestPlatformPcmEncodings(format.pcmEncoding);
347+
for (int platformEncoding : platformEncodings) {
348+
if (audioSink.supportsFormat(
349+
Util.getPcmFormat(platformEncoding, format.channelCount, format.sampleRate))) {
350+
pcmEncodingSupported = true;
351+
break;
352+
}
353+
}
354+
}
355+
// If none of the suggested encodings are supported, fall back to MediaCodec's default value
356+
// of 16-bit PCM.
357+
if (!pcmEncodingSupported
358+
&& audioSink.supportsFormat(
359+
Util.getPcmFormat(C.ENCODING_PCM_16BIT, format.channelCount, format.sampleRate))) {
360+
pcmEncodingSupported = true;
361+
}
362+
if (!pcmEncodingSupported) {
363+
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
364+
}
344365
}
366+
345367
List<MediaCodecInfo> decoderInfos =
346368
getDecoderInfos(mediaCodecSelector, format, /* requiresSecureDecoder= */ false, audioSink);
347369
if (decoderInfos.isEmpty()) {
@@ -1005,11 +1027,18 @@ protected MediaFormat getMediaFormat(
10051027
// not sync frames. Set a format key to override this.
10061028
mediaFormat.setInteger("ac4-is-sync", 1);
10071029
}
1008-
if (Util.SDK_INT >= 24
1009-
&& audioSink.getFormatSupport(
1010-
Util.getPcmFormat(C.ENCODING_PCM_FLOAT, format.channelCount, format.sampleRate))
1030+
if (Util.SDK_INT >= 24) {
1031+
int[] platformEncodings = Util.getClosestPlatformPcmEncodings(format.pcmEncoding);
1032+
for (int platformEncoding : platformEncodings) {
1033+
if (audioSink.getFormatSupport(
1034+
Util.getPcmFormat(platformEncoding, format.channelCount, format.sampleRate))
10111035
== AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY) {
1012-
mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_FLOAT);
1036+
mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, platformEncoding);
1037+
break;
1038+
}
1039+
}
1040+
// If none of the suggested encodings are supported, fall back to MediaCodec's default value
1041+
// of 16-bit PCM.
10131042
}
10141043
if (Util.SDK_INT >= 32) {
10151044
mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99);

0 commit comments

Comments
 (0)