Recreate Opus decoder for stream change
The framework opus decoder discards some samples after a call to flush(). Because we flush a decoder that is being retained across an input format change, this means that the start of audio gets truncated when transitioning to a new opus stream. See also https://android.googlesource.com/platform/frameworks/av/+/refs/heads/android10-release/media/libstagefright/codecs/opus/dec/SoftOpus.cpp. Avoid this by recreating opus decoders instead of flushing them. It seems fine to do this for all opus decoders as reinitialization should be cheap, OEM-provided implementations may also discard samples and playback shouldn't be interrupted on reinitialization due to the downstream AudioTrack buffer. PiperOrigin-RevId: 277458759
This commit is contained in:
parent
acb84396d5
commit
f4b9042bf0
@ -1,6 +1,11 @@
|
|||||||
# Release notes #
|
# Release notes #
|
||||||
|
|
||||||
### 2.10.6 (2019-10-18) ###
|
### 2.10.7 ###
|
||||||
|
|
||||||
|
* Fix the start of audio getting truncated when transitioning to a new
|
||||||
|
item in a playlist of opus streams.
|
||||||
|
|
||||||
|
### 2.10.6 (2019-10-17) ###
|
||||||
|
|
||||||
* Add `Player.onPlaybackSuppressionReasonChanged` to allow listeners to
|
* Add `Player.onPlaybackSuppressionReasonChanged` to allow listeners to
|
||||||
detect playbacks suppressions (e.g. transient audio focus loss) directly
|
detect playbacks suppressions (e.g. transient audio focus loss) directly
|
||||||
|
@ -434,13 +434,34 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
} else if (codecInfo.isSeamlessAdaptationSupported(
|
} else if (codecInfo.isSeamlessAdaptationSupported(
|
||||||
oldFormat, newFormat, /* isNewFormatComplete= */ true)) {
|
oldFormat, newFormat, /* isNewFormatComplete= */ true)) {
|
||||||
return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION;
|
return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION;
|
||||||
} else if (areCodecConfigurationCompatible(oldFormat, newFormat)) {
|
} else if (canKeepCodecWithFlush(oldFormat, newFormat)) {
|
||||||
return KEEP_CODEC_RESULT_YES_WITH_FLUSH;
|
return KEEP_CODEC_RESULT_YES_WITH_FLUSH;
|
||||||
} else {
|
} else {
|
||||||
return KEEP_CODEC_RESULT_NO;
|
return KEEP_CODEC_RESULT_NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the codec can be flushed and reused when switching to a new format. Reuse is
|
||||||
|
* generally possible when the codec would be configured in an identical way after the format
|
||||||
|
* change (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 codec can be flushed and reused when switching to a new format.
|
||||||
|
*/
|
||||||
|
protected boolean canKeepCodecWithFlush(Format oldFormat, Format newFormat) {
|
||||||
|
// Flush and reuse the codec if the audio format and initialization data matches. For Opus, we
|
||||||
|
// don't flush and reuse the codec because the decoder may discard samples after flushing, which
|
||||||
|
// would result in audio being dropped just after a stream change (see [Internal: b/143450854]).
|
||||||
|
return Util.areEqual(oldFormat.sampleMimeType, newFormat.sampleMimeType)
|
||||||
|
&& oldFormat.channelCount == newFormat.channelCount
|
||||||
|
&& oldFormat.sampleRate == newFormat.sampleRate
|
||||||
|
&& oldFormat.initializationDataEquals(newFormat)
|
||||||
|
&& !MimeTypes.AUDIO_OPUS.equals(oldFormat.sampleMimeType);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaClock getMediaClock() {
|
public MediaClock getMediaClock() {
|
||||||
return this;
|
return this;
|
||||||
@ -818,24 +839,6 @@ 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.
|
||||||
|
@ -328,13 +328,13 @@ public final class MediaCodecInfo {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the decoder supports video with a given width, height and frame rate.
|
* Whether the decoder supports video with a given width, height and frame rate.
|
||||||
* <p>
|
*
|
||||||
* Must not be called if the device SDK version is less than 21.
|
* <p>Must not be called if the device SDK version is less than 21.
|
||||||
*
|
*
|
||||||
* @param width Width in pixels.
|
* @param width Width in pixels.
|
||||||
* @param height Height in pixels.
|
* @param height Height in pixels.
|
||||||
* @param frameRate Optional frame rate in frames per second. Ignored if set to
|
* @param frameRate Optional frame rate in frames per second. Ignored if set to {@link
|
||||||
* {@link Format#NO_VALUE} or any value less than or equal to 0.
|
* Format#NO_VALUE} or any value less than or equal to 0.
|
||||||
* @return Whether the decoder supports video with the given width, height and frame rate.
|
* @return Whether the decoder supports video with the given width, height and frame rate.
|
||||||
*/
|
*/
|
||||||
@TargetApi(21)
|
@TargetApi(21)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user