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:
parent
895ac660a8
commit
f7c5e475a7
@ -268,6 +268,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
|
||||
ArrayList<Renderer> out) {
|
||||
out.add(
|
||||
new MediaCodecAudioRenderer(
|
||||
context,
|
||||
MediaCodecSelector.DEFAULT,
|
||||
drmSessionManager,
|
||||
/* playClearSamplesWithoutKeys= */ false,
|
||||
|
@ -17,6 +17,8 @@ package com.google.android.exoplayer2.audio;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCrypto;
|
||||
import android.media.MediaFormat;
|
||||
@ -61,6 +63,7 @@ import java.nio.ByteBuffer;
|
||||
@TargetApi(16)
|
||||
public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock {
|
||||
|
||||
private final Context context;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final AudioSink audioSink;
|
||||
|
||||
@ -78,16 +81,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
private boolean allowPositionDiscontinuity;
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
*/
|
||||
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector) {
|
||||
public MediaCodecAudioRenderer(Context context, MediaCodecSelector mediaCodecSelector) {
|
||||
this(
|
||||
context,
|
||||
mediaCodecSelector,
|
||||
/* drmSessionManager= */ null,
|
||||
/* playClearSamplesWithoutKeys= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* 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}
|
||||
* 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,
|
||||
boolean playClearSamplesWithoutKeys) {
|
||||
this(
|
||||
context,
|
||||
mediaCodecSelector,
|
||||
drmSessionManager,
|
||||
playClearSamplesWithoutKeys,
|
||||
@ -109,14 +118,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. 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,
|
||||
@Nullable Handler eventHandler, @Nullable AudioRendererEventListener eventListener) {
|
||||
public MediaCodecAudioRenderer(
|
||||
Context context,
|
||||
MediaCodecSelector mediaCodecSelector,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable AudioRendererEventListener eventListener) {
|
||||
this(
|
||||
context,
|
||||
mediaCodecSelector,
|
||||
/* drmSessionManager= */ null,
|
||||
/* playClearSamplesWithoutKeys= */ false,
|
||||
@ -125,6 +139,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* content is not required.
|
||||
@ -137,15 +152,25 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
* 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,
|
||||
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler,
|
||||
boolean playClearSamplesWithoutKeys,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable AudioRendererEventListener eventListener) {
|
||||
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
||||
eventListener, (AudioCapabilities) null);
|
||||
this(
|
||||
context,
|
||||
mediaCodecSelector,
|
||||
drmSessionManager,
|
||||
playClearSamplesWithoutKeys,
|
||||
eventHandler,
|
||||
eventListener,
|
||||
(AudioCapabilities) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* 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
|
||||
* output.
|
||||
*/
|
||||
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector,
|
||||
public MediaCodecAudioRenderer(
|
||||
Context context,
|
||||
MediaCodecSelector mediaCodecSelector,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
||||
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler,
|
||||
boolean playClearSamplesWithoutKeys,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable AudioRendererEventListener eventListener,
|
||||
@Nullable AudioCapabilities audioCapabilities, AudioProcessor... audioProcessors) {
|
||||
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys,
|
||||
eventHandler, eventListener, new DefaultAudioSink(audioCapabilities, audioProcessors));
|
||||
@Nullable AudioCapabilities audioCapabilities,
|
||||
AudioProcessor... audioProcessors) {
|
||||
this(
|
||||
context,
|
||||
mediaCodecSelector,
|
||||
drmSessionManager,
|
||||
playClearSamplesWithoutKeys,
|
||||
eventHandler,
|
||||
eventListener,
|
||||
new DefaultAudioSink(audioCapabilities, audioProcessors));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* 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 audioSink The sink to which audio will be output.
|
||||
*/
|
||||
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector,
|
||||
public MediaCodecAudioRenderer(
|
||||
Context context,
|
||||
MediaCodecSelector mediaCodecSelector,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
||||
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler,
|
||||
@Nullable AudioRendererEventListener eventListener, AudioSink audioSink) {
|
||||
boolean playClearSamplesWithoutKeys,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable AudioRendererEventListener eventListener,
|
||||
AudioSink audioSink) {
|
||||
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
this.context = context.getApplicationContext();
|
||||
this.audioSink = audioSink;
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
audioSink.setListener(new AudioSinkListener());
|
||||
}
|
||||
|
||||
@ -246,11 +287,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
if (allowPassthrough(format.sampleMimeType)) {
|
||||
MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
|
||||
if (passthroughDecoderInfo != null) {
|
||||
passthroughEnabled = true;
|
||||
return passthroughDecoderInfo;
|
||||
}
|
||||
}
|
||||
passthroughEnabled = false;
|
||||
return super.getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
|
||||
}
|
||||
|
||||
@ -270,8 +309,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
@Override
|
||||
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format,
|
||||
MediaCrypto crypto) {
|
||||
codecMaxInputSize = getCodecMaxInputSize(format, getStreamFormats());
|
||||
codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats());
|
||||
codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name);
|
||||
passthroughEnabled = codecInfo.passthrough;
|
||||
String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType;
|
||||
MediaFormat mediaFormat = getMediaFormat(format, codecMimeType, codecMaxInputSize);
|
||||
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
|
||||
@ -286,8 +326,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
|
||||
@Override
|
||||
protected @KeepCodecResult int canKeepCodec(
|
||||
MediaCodec codec, boolean codecIsAdaptive, Format oldFormat, Format newFormat) {
|
||||
return newFormat.maxInputSize <= codecMaxInputSize
|
||||
MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
|
||||
return getCodecMaxInputSize(codecInfo, newFormat) <= codecMaxInputSize
|
||||
&& areAdaptationCompatible(oldFormat, newFormat)
|
||||
? KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION
|
||||
: 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
|
||||
* 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 streamFormats The possible stream formats.
|
||||
* @return A suitable maximum input size.
|
||||
*/
|
||||
protected int getCodecMaxInputSize(Format format, Format[] streamFormats) {
|
||||
int maxInputSize = format.maxInputSize;
|
||||
protected int getCodecMaxInputSize(
|
||||
MediaCodecInfo codecInfo, Format format, Format[] streamFormats) {
|
||||
int maxInputSize = getCodecMaxInputSize(codecInfo, format);
|
||||
if (streamFormats.length == 1) {
|
||||
// The single entry in streamFormats must correspond to the format for which the codec is
|
||||
// being configured.
|
||||
@ -536,12 +578,42 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
}
|
||||
for (Format streamFormat : streamFormats) {
|
||||
if (areAdaptationCompatible(format, streamFormat)) {
|
||||
maxInputSize = Math.max(maxInputSize, streamFormat.maxInputSize);
|
||||
maxInputSize = Math.max(maxInputSize, getCodecMaxInputSize(codecInfo, streamFormat));
|
||||
}
|
||||
}
|
||||
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}
|
||||
* for decoding the given {@link Format} for playback.
|
||||
|
@ -85,6 +85,9 @@ public final class MediaCodecInfo {
|
||||
*/
|
||||
public final boolean secure;
|
||||
|
||||
/** Whether this instance describes a passthrough codec. */
|
||||
public final boolean passthrough;
|
||||
|
||||
/**
|
||||
* Creates an instance representing an audio passthrough decoder.
|
||||
*
|
||||
@ -96,6 +99,7 @@ public final class MediaCodecInfo {
|
||||
name,
|
||||
/* mimeType= */ null,
|
||||
/* capabilities= */ null,
|
||||
/* passthrough= */ true,
|
||||
/* forceDisableAdaptive= */ false,
|
||||
/* forceSecure= */ false);
|
||||
}
|
||||
@ -111,7 +115,12 @@ public final class MediaCodecInfo {
|
||||
public static MediaCodecInfo newInstance(String name, String mimeType,
|
||||
CodecCapabilities capabilities) {
|
||||
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,
|
||||
boolean forceDisableAdaptive,
|
||||
boolean forceSecure) {
|
||||
return new MediaCodecInfo(name, mimeType, capabilities, forceDisableAdaptive, forceSecure);
|
||||
return new MediaCodecInfo(
|
||||
name, mimeType, capabilities, /* passthrough= */ false, forceDisableAdaptive, forceSecure);
|
||||
}
|
||||
|
||||
private MediaCodecInfo(
|
||||
String name,
|
||||
@Nullable String mimeType,
|
||||
@Nullable CodecCapabilities capabilities,
|
||||
boolean passthrough,
|
||||
boolean forceDisableAdaptive,
|
||||
boolean forceSecure) {
|
||||
this.name = Assertions.checkNotNull(name);
|
||||
this.mimeType = mimeType;
|
||||
this.capabilities = capabilities;
|
||||
this.passthrough = passthrough;
|
||||
adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities);
|
||||
tunneling = capabilities != null && isTunneling(capabilities);
|
||||
secure = forceSecure || (capabilities != null && isSecure(capabilities));
|
||||
|
@ -129,7 +129,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
*/
|
||||
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)
|
||||
@IntDef({
|
||||
KEEP_CODEC_RESULT_NO,
|
||||
@ -885,7 +888,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
|
||||
boolean keepingCodec = false;
|
||||
if (pendingDrmSession == drmSession && codec != null) {
|
||||
switch (canKeepCodec(codec, codecInfo.adaptive, oldFormat, format)) {
|
||||
switch (canKeepCodec(codec, codecInfo, oldFormat, format)) {
|
||||
case KEEP_CODEC_RESULT_NO:
|
||||
// Do nothing.
|
||||
break;
|
||||
@ -962,13 +965,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
* <p>The default implementation returns {@link #KEEP_CODEC_RESULT_NO}.
|
||||
*
|
||||
* @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 newFormat The new format.
|
||||
* @return Whether the instance can be kept, and if it can whether it requires reconfiguration.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -455,8 +455,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
|
||||
@Override
|
||||
protected @KeepCodecResult int canKeepCodec(
|
||||
MediaCodec codec, boolean codecIsAdaptive, Format oldFormat, Format newFormat) {
|
||||
if (areAdaptationCompatible(codecIsAdaptive, oldFormat, newFormat)
|
||||
MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
|
||||
if (areAdaptationCompatible(codecInfo.adaptive, oldFormat, newFormat)
|
||||
&& newFormat.width <= codecMaxValues.width
|
||||
&& newFormat.height <= codecMaxValues.height
|
||||
&& getMaxInputSize(newFormat) <= codecMaxValues.inputSize) {
|
||||
@ -921,50 +921,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
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.
|
||||
*
|
||||
@ -1010,6 +966,51 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
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
|
||||
* that will allow possible adaptation to other compatible formats that are expected to have the
|
||||
|
Loading…
x
Reference in New Issue
Block a user