Commit 444d2156 by olly Committed by Oliver Woodman

Fix MediaCodecUtil regression.

On older devices getCapabilitiesForType calls fail for some
secondary codecs. We didn't used to call getCapabilitiesForType
on secondary codecs, and so avoided this issue.

This change suppresses any exceptions encountered when
querying secondary decoders on API levels prior to N, to
restore previous behavior when a failure occurs.

Issue: #1534
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=124010242
parent e684e429
...@@ -148,8 +148,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -148,8 +148,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
if (allowPassthrough(mimeType) && mediaCodecSelector.getPassthroughDecoderInfo() != null) { if (allowPassthrough(mimeType) && mediaCodecSelector.getPassthroughDecoderInfo() != null) {
return ADAPTIVE_NOT_SEAMLESS | FORMAT_HANDLED; return ADAPTIVE_NOT_SEAMLESS | FORMAT_HANDLED;
} }
// TODO[REFACTOR]: If requiresSecureDecryption then we should probably also check that the
// drmSession is able to make use of a secure decoder.
DecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType, DecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
format.requiresSecureDecryption); format.requiresSecureDecryption);
if (decoderInfo == null) { if (decoderInfo == null) {
......
...@@ -30,6 +30,7 @@ import android.media.MediaCodec.CryptoException; ...@@ -30,6 +30,7 @@ import android.media.MediaCodec.CryptoException;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -103,6 +104,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -103,6 +104,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
private static final String TAG = "MediaCodecRenderer";
/** /**
* If the {@link MediaCodec} is hotswapped (i.e. replaced during playback), this is the period of * If the {@link MediaCodec} is hotswapped (i.e. replaced during playback), this is the period of
* time during which {@link #isReady()} will report true regardless of whether the new codec has * time during which {@link #isReady()} will report true regardless of whether the new codec has
...@@ -262,7 +265,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -262,7 +265,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
String mimeType = format.sampleMimeType; String mimeType = format.sampleMimeType;
MediaCrypto mediaCrypto = null; MediaCrypto mediaCrypto = null;
boolean requiresSecureDecoder = false; boolean drmSessionRequiresSecureDecoder = false;
if (format.drmInitData != null) { if (format.drmInitData != null) {
if (drmSessionManager == null) { if (drmSessionManager == null) {
throw ExoPlaybackException.createForRenderer( throw ExoPlaybackException.createForRenderer(
...@@ -277,7 +280,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -277,7 +280,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} else if (drmSessionState == DrmSession.STATE_OPENED } else if (drmSessionState == DrmSession.STATE_OPENED
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) { || drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) {
mediaCrypto = drmSession.getMediaCrypto(); mediaCrypto = drmSession.getMediaCrypto();
requiresSecureDecoder = drmSession.requiresSecureDecoderComponent(mimeType); drmSessionRequiresSecureDecoder = drmSession.requiresSecureDecoderComponent(mimeType);
} else { } else {
// The drm session isn't open yet. // The drm session isn't open yet.
return; return;
...@@ -286,14 +289,26 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -286,14 +289,26 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
DecoderInfo decoderInfo = null; DecoderInfo decoderInfo = null;
try { try {
decoderInfo = getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder); decoderInfo = getDecoderInfo(mediaCodecSelector, format, drmSessionRequiresSecureDecoder);
if (decoderInfo == null && drmSessionRequiresSecureDecoder) {
// The drm session indicates that a secure decoder is required, but the device does not have
// one. Assuming that supportsFormat indicated support for the media being played, we know
// that it does not require a secure output path. Most CDM implementations allow playback to
// proceed with a non-secure decoder in this case, so we try our luck.
decoderInfo = getDecoderInfo(mediaCodecSelector, format, false);
if (decoderInfo != null) {
Log.w(TAG, "Drm session requires secure decoder for " + mimeType + ", but "
+ "no secure decoder available. Trying to proceed with " + decoderInfo.name + ".");
}
}
} catch (DecoderQueryException e) { } catch (DecoderQueryException e) {
throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder, throwDecoderInitError(new DecoderInitializationException(format, e,
DecoderInitializationException.DECODER_QUERY_ERROR)); drmSessionRequiresSecureDecoder, DecoderInitializationException.DECODER_QUERY_ERROR));
} }
if (decoderInfo == null) { if (decoderInfo == null) {
throwDecoderInitError(new DecoderInitializationException(format, null, requiresSecureDecoder, throwDecoderInitError(new DecoderInitializationException(format, null,
drmSessionRequiresSecureDecoder,
DecoderInitializationException.NO_SUITABLE_DECODER_ERROR)); DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
} }
...@@ -321,8 +336,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -321,8 +336,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
inputBuffers = codec.getInputBuffers(); inputBuffers = codec.getInputBuffers();
outputBuffers = codec.getOutputBuffers(); outputBuffers = codec.getOutputBuffers();
} catch (Exception e) { } catch (Exception e) {
throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder, throwDecoderInitError(new DecoderInitializationException(format, e,
codecName)); drmSessionRequiresSecureDecoder, codecName));
} }
codecHotswapDeadlineMs = getState() == TrackRenderer.STATE_STARTED codecHotswapDeadlineMs = getState() == TrackRenderer.STATE_STARTED
? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1; ? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1;
......
...@@ -153,6 +153,7 @@ public final class MediaCodecUtil { ...@@ -153,6 +153,7 @@ public final class MediaCodecUtil {
if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) { if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) {
for (String supportedType : codecInfo.getSupportedTypes()) { for (String supportedType : codecInfo.getSupportedTypes()) {
if (supportedType.equalsIgnoreCase(mimeType)) { if (supportedType.equalsIgnoreCase(mimeType)) {
try {
CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(supportedType); CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(supportedType);
boolean secure = mediaCodecList.isSecurePlaybackSupported(mimeType, capabilities); boolean secure = mediaCodecList.isSecurePlaybackSupported(mimeType, capabilities);
if ((secureDecodersExplicit && key.secure == secure) if ((secureDecodersExplicit && key.secure == secure)
...@@ -163,6 +164,14 @@ public final class MediaCodecUtil { ...@@ -163,6 +164,14 @@ public final class MediaCodecUtil {
// It only makes sense to have one synthesized secure decoder, return immediately. // It only makes sense to have one synthesized secure decoder, return immediately.
return decoderInfos; return decoderInfos;
} }
} catch (Exception e) {
if (Util.SDK_INT <= 23 && !decoderInfos.isEmpty()) {
// Suppress error querying secondary codec capabilities up to API level 23.
Log.e(TAG, "Skipping codec " + codecName + " (failed to query capabilities)");
} else {
throw e;
}
}
} }
} }
} }
......
...@@ -42,9 +42,6 @@ import java.nio.ByteBuffer; ...@@ -42,9 +42,6 @@ import java.nio.ByteBuffer;
public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
private static final String TAG = "MediaCodecVideoRenderer"; private static final String TAG = "MediaCodecVideoRenderer";
// TODO: Use MediaFormat constants if these get exposed through the API. See
// [Internal: b/14127601].
private static final String KEY_CROP_LEFT = "crop-left"; private static final String KEY_CROP_LEFT = "crop-left";
private static final String KEY_CROP_RIGHT = "crop-right"; private static final String KEY_CROP_RIGHT = "crop-right";
private static final String KEY_CROP_BOTTOM = "crop-bottom"; private static final String KEY_CROP_BOTTOM = "crop-bottom";
...@@ -176,8 +173,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -176,8 +173,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
if (!MimeTypes.isVideo(mimeType)) { if (!MimeTypes.isVideo(mimeType)) {
return FORMAT_UNSUPPORTED_TYPE; return FORMAT_UNSUPPORTED_TYPE;
} }
// TODO[REFACTOR]: If requiresSecureDecryption then we should probably also check that the
// drmSession is able to make use of a secure decoder.
DecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType, DecoderInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType,
format.requiresSecureDecryption); format.requiresSecureDecryption);
if (decoderInfo == null) { if (decoderInfo == null) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment