mirror of
https://github.com/androidx/media.git
synced 2025-05-06 07:00:19 +08:00
Don't recreate the audio decoder if Format is unchanged
More specifically, if the parts of the Format that are used for decoder configuration are unchanged. Issue: #2826 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=214791234
This commit is contained in:
parent
1dd0533af6
commit
028f3e6438
@ -346,7 +346,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats());
|
codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats());
|
||||||
codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name);
|
codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name);
|
||||||
passthroughEnabled = codecInfo.passthrough;
|
passthroughEnabled = codecInfo.passthrough;
|
||||||
String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType;
|
String codecMimeType = passthroughEnabled ? MimeTypes.AUDIO_RAW : codecInfo.mimeType;
|
||||||
MediaFormat mediaFormat =
|
MediaFormat mediaFormat =
|
||||||
getMediaFormat(format, codecMimeType, codecMaxInputSize, codecOperatingRate);
|
getMediaFormat(format, codecMimeType, codecMaxInputSize, codecOperatingRate);
|
||||||
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
|
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
|
||||||
@ -362,14 +362,22 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
@Override
|
@Override
|
||||||
protected @KeepCodecResult int canKeepCodec(
|
protected @KeepCodecResult int canKeepCodec(
|
||||||
MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
|
MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
|
||||||
if (getCodecMaxInputSize(codecInfo, newFormat) <= codecMaxInputSize
|
// TODO: We currently rely on recreating the codec when encoder delay or padding is non-zero.
|
||||||
&& codecInfo.isSeamlessAdaptationSupported(
|
// Re-creating the codec is necessary to guarantee that onOutputFormatChanged is called, which
|
||||||
oldFormat, newFormat, /* isNewFormatComplete= */ true)
|
// is where encoder delay and padding are propagated to the sink. We should find a better way to
|
||||||
&& oldFormat.encoderDelay == 0
|
// propagate these values, and then allow the codec to be re-used in cases where this would
|
||||||
&& oldFormat.encoderPadding == 0
|
// otherwise be possible.
|
||||||
&& newFormat.encoderDelay == 0
|
if (getCodecMaxInputSize(codecInfo, newFormat) > codecMaxInputSize
|
||||||
&& newFormat.encoderPadding == 0) {
|
|| oldFormat.encoderDelay != 0
|
||||||
|
|| oldFormat.encoderPadding != 0
|
||||||
|
|| newFormat.encoderDelay != 0
|
||||||
|
|| newFormat.encoderPadding != 0) {
|
||||||
|
return KEEP_CODEC_RESULT_NO;
|
||||||
|
} else if (codecInfo.isSeamlessAdaptationSupported(
|
||||||
|
oldFormat, newFormat, /* isNewFormatComplete= */ true)) {
|
||||||
return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION;
|
return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION;
|
||||||
|
} else if (areCodecConfigurationCompatible(oldFormat, newFormat)) {
|
||||||
|
return KEEP_CODEC_RESULT_YES_WITH_FLUSH;
|
||||||
} else {
|
} else {
|
||||||
return KEEP_CODEC_RESULT_NO;
|
return KEEP_CODEC_RESULT_NO;
|
||||||
}
|
}
|
||||||
@ -720,6 +728,24 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
return format.maxInputSize;
|
return format.maxInputSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether two {@link Format}s will cause the same codec to be configured in an identical
|
||||||
|
* way, excluding {@link MediaFormat#KEY_MAX_INPUT_SIZE} and configuration that does not come from
|
||||||
|
* the {@link Format}.
|
||||||
|
*
|
||||||
|
* @param oldFormat The first format.
|
||||||
|
* @param newFormat The second format.
|
||||||
|
* @return Whether the two formats will cause a codec to be configured in an identical way,
|
||||||
|
* excluding {@link MediaFormat#KEY_MAX_INPUT_SIZE} and configuration that does not come from
|
||||||
|
* the {@link Format}.
|
||||||
|
*/
|
||||||
|
protected boolean areCodecConfigurationCompatible(Format oldFormat, Format newFormat) {
|
||||||
|
return Util.areEqual(oldFormat.sampleMimeType, newFormat.sampleMimeType)
|
||||||
|
&& oldFormat.channelCount == newFormat.channelCount
|
||||||
|
&& oldFormat.sampleRate == newFormat.sampleRate
|
||||||
|
&& oldFormat.initializationDataEquals(newFormat);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
@ -185,19 +185,22 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@IntDef({
|
@IntDef({
|
||||||
KEEP_CODEC_RESULT_NO,
|
KEEP_CODEC_RESULT_NO,
|
||||||
KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION,
|
KEEP_CODEC_RESULT_YES_WITH_FLUSH,
|
||||||
KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION
|
KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION,
|
||||||
|
KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION
|
||||||
})
|
})
|
||||||
protected @interface KeepCodecResult {}
|
protected @interface KeepCodecResult {}
|
||||||
/** The codec cannot be kept. */
|
/** The codec cannot be kept. */
|
||||||
protected static final int KEEP_CODEC_RESULT_NO = 0;
|
protected static final int KEEP_CODEC_RESULT_NO = 0;
|
||||||
/** The codec can be kept. No reconfiguration is required. */
|
/** The codec can be kept, but must be flushed. */
|
||||||
protected static final int KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION = 1;
|
protected static final int KEEP_CODEC_RESULT_YES_WITH_FLUSH = 1;
|
||||||
/**
|
/**
|
||||||
* The codec can be kept, but must be reconfigured by prefixing the next input buffer with the new
|
* The codec can be kept. It does not need to be flushed, but must be reconfigured by prefixing
|
||||||
* format's configuration data.
|
* the next input buffer with the new format's configuration data.
|
||||||
*/
|
*/
|
||||||
protected static final int KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION = 3;
|
protected static final int KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION = 2;
|
||||||
|
/** The codec can be kept. It does not need to be flushed and no reconfiguration is required. */
|
||||||
|
protected static final int KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION = 3;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@IntDef({RECONFIGURATION_STATE_NONE, RECONFIGURATION_STATE_WRITE_PENDING,
|
@IntDef({RECONFIGURATION_STATE_NONE, RECONFIGURATION_STATE_WRITE_PENDING,
|
||||||
@ -309,6 +312,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
private boolean codecReconfigured;
|
private boolean codecReconfigured;
|
||||||
private @ReconfigurationState int codecReconfigurationState;
|
private @ReconfigurationState int codecReconfigurationState;
|
||||||
private @ReinitializationState int codecReinitializationState;
|
private @ReinitializationState int codecReinitializationState;
|
||||||
|
private boolean codecReinitializationIsRelease;
|
||||||
private boolean codecReceivedBuffers;
|
private boolean codecReceivedBuffers;
|
||||||
private boolean codecReceivedEos;
|
private boolean codecReceivedEos;
|
||||||
|
|
||||||
@ -587,6 +591,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
codecReceivedEos = false;
|
codecReceivedEos = false;
|
||||||
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
||||||
codecReinitializationState = REINITIALIZATION_STATE_NONE;
|
codecReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||||
|
codecReinitializationIsRelease = false;
|
||||||
codecConfiguredWithOperatingRate = false;
|
codecConfiguredWithOperatingRate = false;
|
||||||
if (codec != null) {
|
if (codec != null) {
|
||||||
decoderCounters.decoderReleaseCount++;
|
decoderCounters.decoderReleaseCount++;
|
||||||
@ -682,10 +687,16 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
releaseCodec();
|
releaseCodec();
|
||||||
maybeInitCodec();
|
maybeInitCodec();
|
||||||
} else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) {
|
} else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) {
|
||||||
// We're already waiting to release and re-initialize the codec. Since we're now flushing,
|
// We're already waiting to re-initialize the codec. Since we're now flushing, there's no need
|
||||||
// there's no need to wait any longer.
|
// to wait any longer.
|
||||||
|
if (codecReinitializationIsRelease) {
|
||||||
releaseCodec();
|
releaseCodec();
|
||||||
maybeInitCodec();
|
maybeInitCodec();
|
||||||
|
} else {
|
||||||
|
codec.flush();
|
||||||
|
codecReceivedBuffers = false;
|
||||||
|
codecReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// We can flush and re-use the existing decoder.
|
// We can flush and re-use the existing decoder.
|
||||||
codec.flush();
|
codec.flush();
|
||||||
@ -865,7 +876,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
private boolean feedInputBuffer() throws ExoPlaybackException {
|
private boolean feedInputBuffer() throws ExoPlaybackException {
|
||||||
if (codec == null || codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM
|
if (codec == null || codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM
|
||||||
|| inputStreamEnded) {
|
|| inputStreamEnded) {
|
||||||
// We need to reinitialize the codec or the input stream has ended.
|
// We need to re-initialize the codec or the input stream has ended.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1043,7 +1054,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
* Called when a new format is read from the upstream {@link MediaPeriod}.
|
* Called when a new format is read from the upstream {@link MediaPeriod}.
|
||||||
*
|
*
|
||||||
* @param newFormat The new format.
|
* @param newFormat The new format.
|
||||||
* @throws ExoPlaybackException If an error occurs reinitializing the {@link MediaCodec}.
|
* @throws ExoPlaybackException If an error occurs re-initializing the {@link MediaCodec}.
|
||||||
*/
|
*/
|
||||||
protected void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
protected void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
|
||||||
Format oldFormat = format;
|
Format oldFormat = format;
|
||||||
@ -1067,18 +1078,28 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean keepingCodec = false;
|
if (codec == null) {
|
||||||
if (pendingDrmSession == drmSession && codec != null) {
|
maybeInitCodec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have an existing codec that we may need to reconfigure or re-initialize. If the existing
|
||||||
|
// codec instance is being kept then its operating rate may need to be updated.
|
||||||
|
if (pendingDrmSession != drmSession) {
|
||||||
|
reinitializeCodec(/* release= */ true);
|
||||||
|
} else {
|
||||||
switch (canKeepCodec(codec, codecInfo, oldFormat, format)) {
|
switch (canKeepCodec(codec, codecInfo, oldFormat, format)) {
|
||||||
case KEEP_CODEC_RESULT_NO:
|
case KEEP_CODEC_RESULT_NO:
|
||||||
// Do nothing.
|
reinitializeCodec(/* release= */ true);
|
||||||
break;
|
break;
|
||||||
case KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION:
|
case KEEP_CODEC_RESULT_YES_WITH_FLUSH:
|
||||||
keepingCodec = true;
|
reinitializeCodec(/* release= */ false);
|
||||||
|
updateCodecOperatingRate();
|
||||||
break;
|
break;
|
||||||
case KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION:
|
case KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION:
|
||||||
if (!codecNeedsReconfigureWorkaround) {
|
if (codecNeedsReconfigureWorkaround) {
|
||||||
keepingCodec = true;
|
reinitializeCodec(/* release= */ true);
|
||||||
|
} else {
|
||||||
codecReconfigured = true;
|
codecReconfigured = true;
|
||||||
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
|
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
|
||||||
codecNeedsAdaptationWorkaroundBuffer =
|
codecNeedsAdaptationWorkaroundBuffer =
|
||||||
@ -1086,18 +1107,16 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
|| (codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION
|
|| (codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION
|
||||||
&& format.width == oldFormat.width
|
&& format.width == oldFormat.width
|
||||||
&& format.height == oldFormat.height);
|
&& format.height == oldFormat.height);
|
||||||
|
updateCodecOperatingRate();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION:
|
||||||
|
updateCodecOperatingRate();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException(); // Never happens.
|
throw new IllegalStateException(); // Never happens.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keepingCodec) {
|
|
||||||
reinitializeCodec();
|
|
||||||
} else {
|
|
||||||
updateCodecOperatingRate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1211,13 +1230,15 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.codecOperatingRate = codecOperatingRate;
|
this.codecOperatingRate = codecOperatingRate;
|
||||||
if (codec == null || codecReinitializationState != REINITIALIZATION_STATE_NONE) {
|
if (codec == null
|
||||||
// Either no codec, or it's about to be reinitialized anyway.
|
|| (codecReinitializationState != REINITIALIZATION_STATE_NONE
|
||||||
|
&& codecReinitializationIsRelease)) {
|
||||||
|
// Either no codec, or it's about to be released due to re-initialization anyway.
|
||||||
} else if (codecOperatingRate == CODEC_OPERATING_RATE_UNSET
|
} else if (codecOperatingRate == CODEC_OPERATING_RATE_UNSET
|
||||||
&& codecConfiguredWithOperatingRate) {
|
&& codecConfiguredWithOperatingRate) {
|
||||||
// We need to clear the operating rate. The only way to do so is to instantiate a new codec
|
// We need to clear the operating rate. The only way to do so is to instantiate a new codec
|
||||||
// instance. See [Internal ref: b/71987865].
|
// instance. See [Internal ref: b/71987865].
|
||||||
reinitializeCodec();
|
reinitializeCodec(/* release= */ true);
|
||||||
} else if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET
|
} else if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET
|
||||||
&& (codecConfiguredWithOperatingRate
|
&& (codecConfiguredWithOperatingRate
|
||||||
|| codecOperatingRate > assumedMinimumCodecOperatingRate)) {
|
|| codecOperatingRate > assumedMinimumCodecOperatingRate)) {
|
||||||
@ -1231,18 +1252,26 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the process of releasing the existing codec and initializing a new one. This may occur
|
* Starts the process of re-initializing the codec. This may occur immediately, or be deferred
|
||||||
* immediately, or be deferred until any final output buffers have been dequeued.
|
* until any final output buffers have been dequeued.
|
||||||
*
|
*
|
||||||
|
* @param release Whether re-initialization requires fully releasing the codec and instantiating a
|
||||||
|
* new instance, as opposed to flushing and reusing the current instance.
|
||||||
* @throws ExoPlaybackException If an error occurs releasing or initializing a codec.
|
* @throws ExoPlaybackException If an error occurs releasing or initializing a codec.
|
||||||
*/
|
*/
|
||||||
private void reinitializeCodec() throws ExoPlaybackException {
|
private void reinitializeCodec(boolean release) throws ExoPlaybackException {
|
||||||
availableCodecInfos = null;
|
availableCodecInfos = null;
|
||||||
if (codecReceivedBuffers) {
|
if (codecReceivedBuffers) {
|
||||||
// Signal end of stream and wait for any final output buffers before re-initialization.
|
// Signal end of stream and wait for any final output buffers before re-initialization.
|
||||||
codecReinitializationState = REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM;
|
codecReinitializationState = REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM;
|
||||||
} else {
|
codecReinitializationIsRelease = release;
|
||||||
// There aren't any final output buffers, so perform re-initialization immediately.
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing has been queued to the decoder. If we need to fully release the codec and instantiate
|
||||||
|
// a new instance, do so immediately. If only a flush is required then we can do nothing, since
|
||||||
|
// flushing will be a no-op.
|
||||||
|
if (release) {
|
||||||
releaseCodec();
|
releaseCodec();
|
||||||
maybeInitCodec();
|
maybeInitCodec();
|
||||||
}
|
}
|
||||||
@ -1449,8 +1478,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
private void processEndOfStream() throws ExoPlaybackException {
|
private void processEndOfStream() throws ExoPlaybackException {
|
||||||
if (codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) {
|
if (codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) {
|
||||||
// We're waiting to re-initialize the codec, and have now processed all final buffers.
|
// We're waiting to re-initialize the codec, and have now processed all final buffers.
|
||||||
|
if (codecReinitializationIsRelease) {
|
||||||
releaseCodec();
|
releaseCodec();
|
||||||
maybeInitCodec();
|
maybeInitCodec();
|
||||||
|
} else {
|
||||||
|
flushCodec();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
outputStreamEnded = true;
|
outputStreamEnded = true;
|
||||||
renderToEndOfStream();
|
renderToEndOfStream();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user