Prioritize decoders with format support
PiperOrigin-RevId: 244167456
This commit is contained in:
parent
a985ca93c5
commit
2347bd2c99
@ -2,6 +2,9 @@
|
||||
|
||||
### dev-v2 (not yet released) ###
|
||||
|
||||
* Decoders: prefer codecs that advertise format support over ones that do not,
|
||||
even if they are listed lower in the `MediaCodecList`.
|
||||
|
||||
### 2.10.0 ###
|
||||
|
||||
* Core library:
|
||||
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaFormatUtil;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
@ -290,8 +291,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
}
|
||||
}
|
||||
List<MediaCodecInfo> decoderInfos =
|
||||
mediaCodecSelector.getDecoderInfos(
|
||||
format.sampleMimeType, requiresSecureDecryption, /* requiresTunnelingDecoder= */ false);
|
||||
getDecoderInfos(mediaCodecSelector, format, requiresSecureDecryption);
|
||||
if (decoderInfos.isEmpty()) {
|
||||
return requiresSecureDecryption
|
||||
&& !mediaCodecSelector
|
||||
@ -327,8 +327,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
return Collections.singletonList(passthroughDecoderInfo);
|
||||
}
|
||||
}
|
||||
return mediaCodecSelector.getDecoderInfos(
|
||||
List<MediaCodecInfo> decoderInfos =
|
||||
mediaCodecSelector.getDecoderInfos(
|
||||
format.sampleMimeType, requiresSecureDecoder, /* requiresTunnelingDecoder= */ false);
|
||||
decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format);
|
||||
return Collections.unmodifiableList(decoderInfos);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,16 +20,17 @@ import android.annotation.TargetApi;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaCodecInfo.CodecProfileLevel;
|
||||
import android.media.MediaCodecList;
|
||||
import androidx.annotation.CheckResult;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseIntArray;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -185,6 +186,26 @@ public final class MediaCodecUtil {
|
||||
return unmodifiableDecoderInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the provided decoder list sorted such that decoders with format support are
|
||||
* listed first. The returned list is modifiable for convenience.
|
||||
*/
|
||||
@CheckResult
|
||||
public static List<MediaCodecInfo> getDecoderInfosSortedByFormatSupport(
|
||||
List<MediaCodecInfo> decoderInfos, Format format) {
|
||||
decoderInfos = new ArrayList<>(decoderInfos);
|
||||
sortByScore(
|
||||
decoderInfos,
|
||||
decoderInfo -> {
|
||||
try {
|
||||
return decoderInfo.isFormatSupported(format) ? 1 : 0;
|
||||
} catch (DecoderQueryException e) {
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
return decoderInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum frame size supported by the default H264 decoder.
|
||||
*
|
||||
@ -484,7 +505,22 @@ public final class MediaCodecUtil {
|
||||
*/
|
||||
private static void applyWorkarounds(String mimeType, List<MediaCodecInfo> decoderInfos) {
|
||||
if (MimeTypes.AUDIO_RAW.equals(mimeType)) {
|
||||
Collections.sort(decoderInfos, new RawAudioCodecComparator());
|
||||
// Work around inconsistent raw audio decoding behavior across different devices.
|
||||
sortByScore(
|
||||
decoderInfos,
|
||||
decoderInfo -> {
|
||||
String name = decoderInfo.name;
|
||||
if (name.startsWith("OMX.google") || name.startsWith("c2.android")) {
|
||||
// Prefer generic decoders over ones provided by the device.
|
||||
return 1;
|
||||
}
|
||||
if (Util.SDK_INT < 26 && name.equals("OMX.MTK.AUDIO.DECODER.RAW")) {
|
||||
// This decoder may modify the audio, so any other compatible decoders take
|
||||
// precedence. See [Internal: b/62337687].
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
} else if (Util.SDK_INT < 21 && decoderInfos.size() > 1) {
|
||||
String firstCodecName = decoderInfos.get(0).name;
|
||||
if ("OMX.SEC.mp3.dec".equals(firstCodecName)
|
||||
@ -494,7 +530,7 @@ public final class MediaCodecUtil {
|
||||
// OMX.brcm.audio.mp3.decoder on older devices. See:
|
||||
// https://github.com/google/ExoPlayer/issues/398 and
|
||||
// https://github.com/google/ExoPlayer/issues/4519.
|
||||
Collections.sort(decoderInfos, new PreferOmxGoogleCodecComparator());
|
||||
sortByScore(decoderInfos, decoderInfo -> decoderInfo.name.startsWith("OMX.google") ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -676,6 +712,17 @@ public final class MediaCodecUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Stably sorts the provided {@code list} in-place, in order of decreasing score. */
|
||||
private static <T> void sortByScore(List<T> list, ScoreProvider<T> scoreProvider) {
|
||||
Collections.sort(list, (a, b) -> scoreProvider.getScore(b) - scoreProvider.getScore(a));
|
||||
}
|
||||
|
||||
/** Interface for providers of item scores. */
|
||||
private interface ScoreProvider<T> {
|
||||
/** Returns the score of the provided item. */
|
||||
int getScore(T t);
|
||||
}
|
||||
|
||||
private interface MediaCodecListCompat {
|
||||
|
||||
/**
|
||||
@ -826,44 +873,6 @@ public final class MediaCodecUtil {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator for ordering media codecs that handle {@link MimeTypes#AUDIO_RAW} to work around
|
||||
* possible inconsistent behavior across different devices. A list sorted with this comparator has
|
||||
* more preferred codecs first.
|
||||
*/
|
||||
private static final class RawAudioCodecComparator implements Comparator<MediaCodecInfo> {
|
||||
@Override
|
||||
public int compare(MediaCodecInfo a, MediaCodecInfo b) {
|
||||
return scoreMediaCodecInfo(a) - scoreMediaCodecInfo(b);
|
||||
}
|
||||
|
||||
private static int scoreMediaCodecInfo(MediaCodecInfo mediaCodecInfo) {
|
||||
String name = mediaCodecInfo.name;
|
||||
if (name.startsWith("OMX.google") || name.startsWith("c2.android")) {
|
||||
// Prefer generic decoders over ones provided by the device.
|
||||
return -1;
|
||||
}
|
||||
if (Util.SDK_INT < 26 && name.equals("OMX.MTK.AUDIO.DECODER.RAW")) {
|
||||
// This decoder may modify the audio, so any other compatible decoders take precedence. See
|
||||
// [Internal: b/62337687].
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Comparator for preferring OMX.google media codecs. */
|
||||
private static final class PreferOmxGoogleCodecComparator implements Comparator<MediaCodecInfo> {
|
||||
@Override
|
||||
public int compare(MediaCodecInfo a, MediaCodecInfo b) {
|
||||
return scoreMediaCodecInfo(a) - scoreMediaCodecInfo(b);
|
||||
}
|
||||
|
||||
private static int scoreMediaCodecInfo(MediaCodecInfo mediaCodecInfo) {
|
||||
return mediaCodecInfo.name.startsWith("OMX.google") ? -1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
AVC_PROFILE_NUMBER_TO_CONST = new SparseIntArray();
|
||||
AVC_PROFILE_NUMBER_TO_CONST.put(66, CodecProfileLevel.AVCProfileBaseline);
|
||||
|
@ -306,12 +306,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
}
|
||||
List<MediaCodecInfo> decoderInfos =
|
||||
getDecoderInfos(mediaCodecSelector, format, requiresSecureDecryption);
|
||||
getDecoderInfos(
|
||||
mediaCodecSelector,
|
||||
format,
|
||||
requiresSecureDecryption,
|
||||
/* requiresTunnelingDecoder= */ false);
|
||||
if (decoderInfos.isEmpty()) {
|
||||
return requiresSecureDecryption
|
||||
&& !mediaCodecSelector
|
||||
.getDecoderInfos(
|
||||
format.sampleMimeType,
|
||||
&& !getDecoderInfos(
|
||||
mediaCodecSelector,
|
||||
format,
|
||||
/* requiresSecureDecoder= */ false,
|
||||
/* requiresTunnelingDecoder= */ false)
|
||||
.isEmpty()
|
||||
@ -331,8 +335,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
int tunnelingSupport = TUNNELING_NOT_SUPPORTED;
|
||||
if (isFormatSupported) {
|
||||
List<MediaCodecInfo> tunnelingDecoderInfos =
|
||||
mediaCodecSelector.getDecoderInfos(
|
||||
format.sampleMimeType,
|
||||
getDecoderInfos(
|
||||
mediaCodecSelector,
|
||||
format,
|
||||
requiresSecureDecryption,
|
||||
/* requiresTunnelingDecoder= */ true);
|
||||
if (!tunnelingDecoderInfos.isEmpty()) {
|
||||
@ -351,8 +356,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
protected List<MediaCodecInfo> getDecoderInfos(
|
||||
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
|
||||
throws DecoderQueryException {
|
||||
return getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder, tunneling);
|
||||
}
|
||||
|
||||
private static List<MediaCodecInfo> getDecoderInfos(
|
||||
MediaCodecSelector mediaCodecSelector,
|
||||
Format format,
|
||||
boolean requiresSecureDecoder,
|
||||
boolean requiresTunnelingDecoder)
|
||||
throws DecoderQueryException {
|
||||
List<MediaCodecInfo> decoderInfos =
|
||||
mediaCodecSelector.getDecoderInfos(format.sampleMimeType, requiresSecureDecoder, tunneling);
|
||||
mediaCodecSelector.getDecoderInfos(
|
||||
format.sampleMimeType, requiresSecureDecoder, requiresTunnelingDecoder);
|
||||
decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format);
|
||||
return Collections.unmodifiableList(decoderInfos);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user