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) {
|
ArrayList<Renderer> out) {
|
||||||
out.add(
|
out.add(
|
||||||
new MediaCodecAudioRenderer(
|
new MediaCodecAudioRenderer(
|
||||||
|
context,
|
||||||
MediaCodecSelector.DEFAULT,
|
MediaCodecSelector.DEFAULT,
|
||||||
drmSessionManager,
|
drmSessionManager,
|
||||||
/* playClearSamplesWithoutKeys= */ false,
|
/* playClearSamplesWithoutKeys= */ false,
|
||||||
|
@ -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.
|
||||||
|
@ -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));
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user