diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2ace06075e..b75cccc298 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -13,6 +13,10 @@ `DefaultTrackSelector.selectVideoTrack()` method. * Add `retryCount` parameter to `MediaSourceEventListener.onLoadStarted` and corresponding `MediaSourceEventListener.EventDispatcher` methods. + * Fix bug where playlist items or periods in multi-period DASH streams + with durations that don't match the actual content could cause frame + freezes at the end of the item + ([#1698](https://github.com/androidx/media/issues/1698)). * Transformer: * Update parameters of `VideoFrameProcessor.registerInputStream` and `VideoFrameProcessor.Listener.onInputStreamRegistered` to use `Format`. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index aadbe5d945..d8ae700301 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -1732,28 +1732,31 @@ public final class DefaultAudioSink implements AudioSink { long playoutDurationSinceLastCheckpointUs = positionUs - mediaPositionParameters.audioTrackPositionUs; + long estimatedMediaDurationSinceLastCheckpointUs = + Util.getMediaDurationForPlayoutDuration( + playoutDurationSinceLastCheckpointUs, mediaPositionParameters.playbackParameters.speed); if (mediaPositionParametersCheckpoints.isEmpty()) { - long mediaDurationSinceLastCheckpointUs = + long actualMediaDurationSinceLastCheckpointUs = audioProcessorChain.getMediaDuration(playoutDurationSinceLastCheckpointUs); - return mediaPositionParameters.mediaTimeUs + mediaDurationSinceLastCheckpointUs; + long currentMediaPositionUs = + mediaPositionParameters.mediaTimeUs + actualMediaDurationSinceLastCheckpointUs; + long mediaDurationEstimateDiffUs = + actualMediaDurationSinceLastCheckpointUs - estimatedMediaDurationSinceLastCheckpointUs; + if (Math.abs(mediaDurationEstimateDiffUs) > 10000) { + // Update current media position parameters if the estimate drifted from the actual + // media duration created by the audio processor chain. This ensures the estimate is always + // fairly accurate and we can rely on it once we enter the else-branch below. + mediaPositionParameters = + new MediaPositionParameters( + mediaPositionParameters.playbackParameters, currentMediaPositionUs, positionUs); + } + return currentMediaPositionUs; } else { // The processor chain has been configured with new parameters, but we're still playing audio // that was processed using previous parameters. We can't scale the playout duration using the // processor chain in this case, so we fall back to scaling using the previous parameters' - // target speed instead. Since the processor chain may not have achieved the target speed - // precisely, we scale the duration to the next checkpoint (which will always be small) rather - // than the duration from the previous checkpoint (which may be arbitrarily large). This - // limits the amount of error that can be introduced due to a difference between the target - // and actual speeds. - MediaPositionParameters nextMediaPositionParameters = - mediaPositionParametersCheckpoints.getFirst(); - long playoutDurationUntilNextCheckpointUs = - nextMediaPositionParameters.audioTrackPositionUs - positionUs; - long mediaDurationUntilNextCheckpointUs = - Util.getMediaDurationForPlayoutDuration( - playoutDurationUntilNextCheckpointUs, - mediaPositionParameters.playbackParameters.speed); - return nextMediaPositionParameters.mediaTimeUs - mediaDurationUntilNextCheckpointUs; + // target speed instead. + return mediaPositionParameters.mediaTimeUs + estimatedMediaDurationSinceLastCheckpointUs; } }