Prevent native crash in raw decoder

Playback will still fail if an input sample is larger
than 32K, but will now fail gracefully.

Issue: #4057

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=193951955
This commit is contained in:
olly 2018-04-23 11:04:39 -07:00 committed by Oliver Woodman
parent 895ac660a8
commit f7c5e475a7
5 changed files with 166 additions and 77 deletions

View File

@ -268,6 +268,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
ArrayList<Renderer> out) { ArrayList<Renderer> out) {
out.add( out.add(
new MediaCodecAudioRenderer( new MediaCodecAudioRenderer(
context,
MediaCodecSelector.DEFAULT, MediaCodecSelector.DEFAULT,
drmSessionManager, drmSessionManager,
/* playClearSamplesWithoutKeys= */ false, /* playClearSamplesWithoutKeys= */ false,

View File

@ -17,6 +17,8 @@ package com.google.android.exoplayer2.audio;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
@ -61,6 +63,7 @@ import java.nio.ByteBuffer;
@TargetApi(16) @TargetApi(16)
public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock { public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock {
private final Context context;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final AudioSink audioSink; private final AudioSink audioSink;
@ -78,16 +81,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private boolean allowPositionDiscontinuity; private boolean allowPositionDiscontinuity;
/** /**
* @param context A context.
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
*/ */
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector) { public MediaCodecAudioRenderer(Context context, MediaCodecSelector mediaCodecSelector) {
this( this(
context,
mediaCodecSelector, mediaCodecSelector,
/* drmSessionManager= */ null, /* drmSessionManager= */ null,
/* playClearSamplesWithoutKeys= */ false); /* playClearSamplesWithoutKeys= */ false);
} }
/** /**
* @param context A context.
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted * @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required. * content is not required.
@ -97,10 +103,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* permitted to play clear regions of encrypted media files before {@code drmSessionManager} * permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media. * has obtained the keys necessary to decrypt encrypted regions of the media.
*/ */
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioRenderer(
Context context,
MediaCodecSelector mediaCodecSelector,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys) { boolean playClearSamplesWithoutKeys) {
this( this(
context,
mediaCodecSelector, mediaCodecSelector,
drmSessionManager, drmSessionManager,
playClearSamplesWithoutKeys, playClearSamplesWithoutKeys,
@ -109,14 +118,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
/** /**
* @param context A context.
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required. * null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required.
*/ */
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioRenderer(
@Nullable Handler eventHandler, @Nullable AudioRendererEventListener eventListener) { Context context,
MediaCodecSelector mediaCodecSelector,
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener) {
this( this(
context,
mediaCodecSelector, mediaCodecSelector,
/* drmSessionManager= */ null, /* drmSessionManager= */ null,
/* playClearSamplesWithoutKeys= */ false, /* playClearSamplesWithoutKeys= */ false,
@ -125,6 +139,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
/** /**
* @param context A context.
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted * @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required. * content is not required.
@ -137,15 +152,25 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* null if delivery of events is not required. * null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required.
*/ */
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioRenderer(
Context context,
MediaCodecSelector mediaCodecSelector,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler, boolean playClearSamplesWithoutKeys,
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener) { @Nullable AudioRendererEventListener eventListener) {
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, this(
eventListener, (AudioCapabilities) null); context,
mediaCodecSelector,
drmSessionManager,
playClearSamplesWithoutKeys,
eventHandler,
eventListener,
(AudioCapabilities) null);
} }
/** /**
* @param context A context.
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted * @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required. * content is not required.
@ -162,16 +187,27 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* @param audioProcessors Optional {@link AudioProcessor}s that will process PCM audio before * @param audioProcessors Optional {@link AudioProcessor}s that will process PCM audio before
* output. * output.
*/ */
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioRenderer(
Context context,
MediaCodecSelector mediaCodecSelector,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler, boolean playClearSamplesWithoutKeys,
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener, @Nullable AudioRendererEventListener eventListener,
@Nullable AudioCapabilities audioCapabilities, AudioProcessor... audioProcessors) { @Nullable AudioCapabilities audioCapabilities,
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, AudioProcessor... audioProcessors) {
eventHandler, eventListener, new DefaultAudioSink(audioCapabilities, audioProcessors)); this(
context,
mediaCodecSelector,
drmSessionManager,
playClearSamplesWithoutKeys,
eventHandler,
eventListener,
new DefaultAudioSink(audioCapabilities, audioProcessors));
} }
/** /**
* @param context A context.
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted * @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required. * content is not required.
@ -185,13 +221,18 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* @param eventListener A listener of events. May be null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioSink The sink to which audio will be output. * @param audioSink The sink to which audio will be output.
*/ */
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioRenderer(
Context context,
MediaCodecSelector mediaCodecSelector,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler, boolean playClearSamplesWithoutKeys,
@Nullable AudioRendererEventListener eventListener, AudioSink audioSink) { @Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioSink audioSink) {
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
eventDispatcher = new EventDispatcher(eventHandler, eventListener); this.context = context.getApplicationContext();
this.audioSink = audioSink; this.audioSink = audioSink;
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
audioSink.setListener(new AudioSinkListener()); audioSink.setListener(new AudioSinkListener());
} }
@ -246,11 +287,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
if (allowPassthrough(format.sampleMimeType)) { if (allowPassthrough(format.sampleMimeType)) {
MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo(); MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
if (passthroughDecoderInfo != null) { if (passthroughDecoderInfo != null) {
passthroughEnabled = true;
return passthroughDecoderInfo; return passthroughDecoderInfo;
} }
} }
passthroughEnabled = false;
return super.getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder); return super.getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
} }
@ -270,8 +309,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format, protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format,
MediaCrypto crypto) { MediaCrypto crypto) {
codecMaxInputSize = getCodecMaxInputSize(format, getStreamFormats()); codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats());
codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name); codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name);
passthroughEnabled = codecInfo.passthrough;
String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType; String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType;
MediaFormat mediaFormat = getMediaFormat(format, codecMimeType, codecMaxInputSize); MediaFormat mediaFormat = getMediaFormat(format, codecMimeType, codecMaxInputSize);
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0); codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
@ -286,8 +326,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected @KeepCodecResult int canKeepCodec( protected @KeepCodecResult int canKeepCodec(
MediaCodec codec, boolean codecIsAdaptive, Format oldFormat, Format newFormat) { MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
return newFormat.maxInputSize <= codecMaxInputSize return getCodecMaxInputSize(codecInfo, newFormat) <= codecMaxInputSize
&& areAdaptationCompatible(oldFormat, newFormat) && areAdaptationCompatible(oldFormat, newFormat)
? KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION ? KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION
: KEEP_CODEC_RESULT_NO; : KEEP_CODEC_RESULT_NO;
@ -523,12 +563,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* Returns a maximum input size suitable for configuring a codec for {@code format} in a way that * Returns a maximum input size suitable for configuring a codec for {@code format} in a way that
* will allow possible adaptation to other compatible formats in {@code streamFormats}. * will allow possible adaptation to other compatible formats in {@code streamFormats}.
* *
* @param codecInfo A {@link MediaCodecInfo} describing the decoder.
* @param format The format for which the codec is being configured. * @param format The format for which the codec is being configured.
* @param streamFormats The possible stream formats. * @param streamFormats The possible stream formats.
* @return A suitable maximum input size. * @return A suitable maximum input size.
*/ */
protected int getCodecMaxInputSize(Format format, Format[] streamFormats) { protected int getCodecMaxInputSize(
int maxInputSize = format.maxInputSize; MediaCodecInfo codecInfo, Format format, Format[] streamFormats) {
int maxInputSize = getCodecMaxInputSize(codecInfo, format);
if (streamFormats.length == 1) { if (streamFormats.length == 1) {
// The single entry in streamFormats must correspond to the format for which the codec is // The single entry in streamFormats must correspond to the format for which the codec is
// being configured. // being configured.
@ -536,12 +578,42 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
for (Format streamFormat : streamFormats) { for (Format streamFormat : streamFormats) {
if (areAdaptationCompatible(format, streamFormat)) { if (areAdaptationCompatible(format, streamFormat)) {
maxInputSize = Math.max(maxInputSize, streamFormat.maxInputSize); maxInputSize = Math.max(maxInputSize, getCodecMaxInputSize(codecInfo, streamFormat));
} }
} }
return maxInputSize; return maxInputSize;
} }
/**
* Returns a maximum input buffer size for a given format.
*
* @param codecInfo A {@link MediaCodecInfo} describing the decoder.
* @param format The format.
* @return A maximum input buffer size in bytes, or {@link Format#NO_VALUE} if a maximum could not
* be determined.
*/
private int getCodecMaxInputSize(MediaCodecInfo codecInfo, Format format) {
if (Util.SDK_INT < 24 && "OMX.google.raw.decoder".equals(codecInfo.name)) {
// OMX.google.raw.decoder didn't resize its output buffers correctly prior to N, so there's no
// point requesting a non-default input size. Doing so may cause a native crash, where-as not
// doing so will cause a more controlled failure when attempting to fill an input buffer. See:
// https://github.com/google/ExoPlayer/issues/4057.
boolean needsRawDecoderWorkaround = true;
if (Util.SDK_INT == 23) {
PackageManager packageManager = context.getPackageManager();
if (packageManager != null
&& packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
// The workaround is not required for AndroidTV devices running M.
needsRawDecoderWorkaround = false;
}
}
if (needsRawDecoderWorkaround) {
return Format.NO_VALUE;
}
}
return format.maxInputSize;
}
/** /**
* Returns the framework {@link MediaFormat} that can be used to configure a {@link MediaCodec} * Returns the framework {@link MediaFormat} that can be used to configure a {@link MediaCodec}
* for decoding the given {@link Format} for playback. * for decoding the given {@link Format} for playback.

View File

@ -85,6 +85,9 @@ public final class MediaCodecInfo {
*/ */
public final boolean secure; public final boolean secure;
/** Whether this instance describes a passthrough codec. */
public final boolean passthrough;
/** /**
* Creates an instance representing an audio passthrough decoder. * Creates an instance representing an audio passthrough decoder.
* *
@ -96,6 +99,7 @@ public final class MediaCodecInfo {
name, name,
/* mimeType= */ null, /* mimeType= */ null,
/* capabilities= */ null, /* capabilities= */ null,
/* passthrough= */ true,
/* forceDisableAdaptive= */ false, /* forceDisableAdaptive= */ false,
/* forceSecure= */ false); /* forceSecure= */ false);
} }
@ -111,7 +115,12 @@ public final class MediaCodecInfo {
public static MediaCodecInfo newInstance(String name, String mimeType, public static MediaCodecInfo newInstance(String name, String mimeType,
CodecCapabilities capabilities) { CodecCapabilities capabilities) {
return new MediaCodecInfo( return new MediaCodecInfo(
name, mimeType, capabilities, /* forceDisableAdaptive= */ false, /* forceSecure= */ false); name,
mimeType,
capabilities,
/* passthrough= */ false,
/* forceDisableAdaptive= */ false,
/* forceSecure= */ false);
} }
/** /**
@ -130,18 +139,21 @@ public final class MediaCodecInfo {
CodecCapabilities capabilities, CodecCapabilities capabilities,
boolean forceDisableAdaptive, boolean forceDisableAdaptive,
boolean forceSecure) { boolean forceSecure) {
return new MediaCodecInfo(name, mimeType, capabilities, forceDisableAdaptive, forceSecure); return new MediaCodecInfo(
name, mimeType, capabilities, /* passthrough= */ false, forceDisableAdaptive, forceSecure);
} }
private MediaCodecInfo( private MediaCodecInfo(
String name, String name,
@Nullable String mimeType, @Nullable String mimeType,
@Nullable CodecCapabilities capabilities, @Nullable CodecCapabilities capabilities,
boolean passthrough,
boolean forceDisableAdaptive, boolean forceDisableAdaptive,
boolean forceSecure) { boolean forceSecure) {
this.name = Assertions.checkNotNull(name); this.name = Assertions.checkNotNull(name);
this.mimeType = mimeType; this.mimeType = mimeType;
this.capabilities = capabilities; this.capabilities = capabilities;
this.passthrough = passthrough;
adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities); adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities);
tunneling = capabilities != null && isTunneling(capabilities); tunneling = capabilities != null && isTunneling(capabilities);
secure = forceSecure || (capabilities != null && isSecure(capabilities)); secure = forceSecure || (capabilities != null && isSecure(capabilities));

View File

@ -129,7 +129,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
*/ */
private static final long MAX_CODEC_HOTSWAP_TIME_MS = 1000; private static final long MAX_CODEC_HOTSWAP_TIME_MS = 1000;
/** The possible return values for {@link #canKeepCodec(MediaCodec, boolean, Format, Format)}. */ /**
* The possible return values for {@link #canKeepCodec(MediaCodec, MediaCodecInfo, Format,
* Format)}.
*/
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({ @IntDef({
KEEP_CODEC_RESULT_NO, KEEP_CODEC_RESULT_NO,
@ -885,7 +888,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
boolean keepingCodec = false; boolean keepingCodec = false;
if (pendingDrmSession == drmSession && codec != null) { if (pendingDrmSession == drmSession && codec != null) {
switch (canKeepCodec(codec, codecInfo.adaptive, oldFormat, format)) { switch (canKeepCodec(codec, codecInfo, oldFormat, format)) {
case KEEP_CODEC_RESULT_NO: case KEEP_CODEC_RESULT_NO:
// Do nothing. // Do nothing.
break; break;
@ -962,13 +965,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* <p>The default implementation returns {@link #KEEP_CODEC_RESULT_NO}. * <p>The default implementation returns {@link #KEEP_CODEC_RESULT_NO}.
* *
* @param codec The existing {@link MediaCodec} instance. * @param codec The existing {@link MediaCodec} instance.
* @param codecIsAdaptive Whether the codec is adaptive. * @param codecInfo A {@link MediaCodecInfo} describing the decoder.
* @param oldFormat The format for which the existing instance is configured. * @param oldFormat The format for which the existing instance is configured.
* @param newFormat The new format. * @param newFormat The new format.
* @return Whether the instance can be kept, and if it can whether it requires reconfiguration. * @return Whether the instance can be kept, and if it can whether it requires reconfiguration.
*/ */
protected @KeepCodecResult int canKeepCodec( protected @KeepCodecResult int canKeepCodec(
MediaCodec codec, boolean codecIsAdaptive, Format oldFormat, Format newFormat) { MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
return KEEP_CODEC_RESULT_NO; return KEEP_CODEC_RESULT_NO;
} }

View File

@ -455,8 +455,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override @Override
protected @KeepCodecResult int canKeepCodec( protected @KeepCodecResult int canKeepCodec(
MediaCodec codec, boolean codecIsAdaptive, Format oldFormat, Format newFormat) { MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
if (areAdaptationCompatible(codecIsAdaptive, oldFormat, newFormat) if (areAdaptationCompatible(codecInfo.adaptive, oldFormat, newFormat)
&& newFormat.width <= codecMaxValues.width && newFormat.width <= codecMaxValues.width
&& newFormat.height <= codecMaxValues.height && newFormat.height <= codecMaxValues.height
&& getMaxInputSize(newFormat) <= codecMaxValues.inputSize) { && getMaxInputSize(newFormat) <= codecMaxValues.inputSize) {
@ -921,50 +921,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
mediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelingAudioSessionId); mediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelingAudioSessionId);
} }
/**
* Returns {@link CodecMaxValues} suitable for configuring a codec for {@code format} in a way
* that will allow possible adaptation to other compatible formats in {@code streamFormats}.
*
* @param codecInfo Information about the {@link MediaCodec} being configured.
* @param format The format for which the codec is being configured.
* @param streamFormats The possible stream formats.
* @return Suitable {@link CodecMaxValues}.
* @throws DecoderQueryException If an error occurs querying {@code codecInfo}.
*/
protected CodecMaxValues getCodecMaxValues(MediaCodecInfo codecInfo, Format format,
Format[] streamFormats) throws DecoderQueryException {
int maxWidth = format.width;
int maxHeight = format.height;
int maxInputSize = getMaxInputSize(format);
if (streamFormats.length == 1) {
// The single entry in streamFormats must correspond to the format for which the codec is
// being configured.
return new CodecMaxValues(maxWidth, maxHeight, maxInputSize);
}
boolean haveUnknownDimensions = false;
for (Format streamFormat : streamFormats) {
if (areAdaptationCompatible(codecInfo.adaptive, format, streamFormat)) {
haveUnknownDimensions |= (streamFormat.width == Format.NO_VALUE
|| streamFormat.height == Format.NO_VALUE);
maxWidth = Math.max(maxWidth, streamFormat.width);
maxHeight = Math.max(maxHeight, streamFormat.height);
maxInputSize = Math.max(maxInputSize, getMaxInputSize(streamFormat));
}
}
if (haveUnknownDimensions) {
Log.w(TAG, "Resolutions unknown. Codec max resolution: " + maxWidth + "x" + maxHeight);
Point codecMaxSize = getCodecMaxSize(codecInfo, format);
if (codecMaxSize != null) {
maxWidth = Math.max(maxWidth, codecMaxSize.x);
maxHeight = Math.max(maxHeight, codecMaxSize.y);
maxInputSize = Math.max(maxInputSize,
getMaxInputSize(format.sampleMimeType, maxWidth, maxHeight));
Log.w(TAG, "Codec max resolution adjusted to: " + maxWidth + "x" + maxHeight);
}
}
return new CodecMaxValues(maxWidth, maxHeight, maxInputSize);
}
/** /**
* Returns the framework {@link MediaFormat} that should be used to configure the decoder. * Returns the framework {@link MediaFormat} that should be used to configure the decoder.
* *
@ -1010,6 +966,51 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return mediaFormat; return mediaFormat;
} }
/**
* Returns {@link CodecMaxValues} suitable for configuring a codec for {@code format} in a way
* that will allow possible adaptation to other compatible formats in {@code streamFormats}.
*
* @param codecInfo Information about the {@link MediaCodec} being configured.
* @param format The format for which the codec is being configured.
* @param streamFormats The possible stream formats.
* @return Suitable {@link CodecMaxValues}.
* @throws DecoderQueryException If an error occurs querying {@code codecInfo}.
*/
protected CodecMaxValues getCodecMaxValues(
MediaCodecInfo codecInfo, Format format, Format[] streamFormats)
throws DecoderQueryException {
int maxWidth = format.width;
int maxHeight = format.height;
int maxInputSize = getMaxInputSize(format);
if (streamFormats.length == 1) {
// The single entry in streamFormats must correspond to the format for which the codec is
// being configured.
return new CodecMaxValues(maxWidth, maxHeight, maxInputSize);
}
boolean haveUnknownDimensions = false;
for (Format streamFormat : streamFormats) {
if (areAdaptationCompatible(codecInfo.adaptive, format, streamFormat)) {
haveUnknownDimensions |=
(streamFormat.width == Format.NO_VALUE || streamFormat.height == Format.NO_VALUE);
maxWidth = Math.max(maxWidth, streamFormat.width);
maxHeight = Math.max(maxHeight, streamFormat.height);
maxInputSize = Math.max(maxInputSize, getMaxInputSize(streamFormat));
}
}
if (haveUnknownDimensions) {
Log.w(TAG, "Resolutions unknown. Codec max resolution: " + maxWidth + "x" + maxHeight);
Point codecMaxSize = getCodecMaxSize(codecInfo, format);
if (codecMaxSize != null) {
maxWidth = Math.max(maxWidth, codecMaxSize.x);
maxHeight = Math.max(maxHeight, codecMaxSize.y);
maxInputSize =
Math.max(maxInputSize, getMaxInputSize(format.sampleMimeType, maxWidth, maxHeight));
Log.w(TAG, "Codec max resolution adjusted to: " + maxWidth + "x" + maxHeight);
}
}
return new CodecMaxValues(maxWidth, maxHeight, maxInputSize);
}
/** /**
* Returns a maximum video size to use when configuring a codec for {@code format} in a way * Returns a maximum video size to use when configuring a codec for {@code format} in a way
* that will allow possible adaptation to other compatible formats that are expected to have the * that will allow possible adaptation to other compatible formats that are expected to have the