mirror of
https://github.com/androidx/media.git
synced 2025-05-04 22:20:47 +08:00
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 #
|
||||
|
||||
### 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
|
||||
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(
|
||||
oldFormat, newFormat, /* isNewFormatComplete= */ true)) {
|
||||
return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION;
|
||||
} else if (areCodecConfigurationCompatible(oldFormat, newFormat)) {
|
||||
} else if (canKeepCodecWithFlush(oldFormat, newFormat)) {
|
||||
return KEEP_CODEC_RESULT_YES_WITH_FLUSH;
|
||||
} else {
|
||||
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
|
||||
public MediaClock getMediaClock() {
|
||||
return this;
|
||||
@ -818,24 +839,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
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}
|
||||
* 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.
|
||||
* <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 height Height in pixels.
|
||||
* @param frameRate Optional frame rate in frames per second. Ignored if set to
|
||||
* {@link Format#NO_VALUE} or any value less than or equal to 0.
|
||||
* @param frameRate Optional frame rate in frames per second. Ignored if set to {@link
|
||||
* 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.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
|
Loading…
x
Reference in New Issue
Block a user