From 3e35b6d0161530d49c74a7804b97aea274189755 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 6 Nov 2018 00:35:42 -0800 Subject: [PATCH] Work around non-empty EoS buffers with timestamp 0 Issue: #5045 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=220237752 --- RELEASENOTES.md | 9 ++++-- .../audio/MediaCodecAudioRenderer.java | 30 ++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3d4c3cd63ed..18d42c0c9b2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -35,13 +35,16 @@ * Fix issue where the buffered position was not updated correctly when transitioning between periods ([#4899](https://github.com/google/ExoPlayer/issues/4899)). +* Fix issue where a `NullPointerException` is thrown when removing an unprepared + media source from a `ConcatenatingMediaSource` with the `useLazyPreparation` + option enabled ([#4986](https://github.com/google/ExoPlayer/issues/4986)). +* Work around an issue where a non-empty end-of-stream audio buffer would be + output with timestamp zero, causing the player position to jump backwards + ([#5045](https://github.com/google/ExoPlayer/issues/5045)). * Suppress a spurious assertion failure on some Samsung devices ([#4532](https://github.com/google/ExoPlayer/issues/4532)). * Suppress spurious "references unknown class member" shrinking warning ([#4890](https://github.com/google/ExoPlayer/issues/4890)). -* Fix issue where a `NullPointerException` is thrown when removing an unprepared - media source from a `ConcatenatingMediaSource` with the `useLazyPreparation` - option enabled ([#4986](https://github.com/google/ExoPlayer/issues/4986)). * Swap recommended order for google() and jcenter() in gradle config ([#4997](https://github.com/google/ExoPlayer/issues/4997)). diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java index 624e698ad63..7fdce350988 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java @@ -24,6 +24,7 @@ import android.media.MediaFormat; import android.media.audiofx.Virtualizer; import android.os.Handler; +import android.support.annotation.CallSuper; import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -86,6 +87,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media private int codecMaxInputSize; private boolean passthroughEnabled; private boolean codecNeedsDiscardChannelsWorkaround; + private boolean codecNeedsEosBufferTimestampWorkaround; private android.media.MediaFormat passthroughMediaFormat; private @C.Encoding int pcmEncoding; private int channelCount; @@ -345,6 +347,7 @@ protected void configureCodec( float codecOperatingRate) { codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats()); codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name); + codecNeedsEosBufferTimestampWorkaround = codecNeedsEosBufferTimestampWorkaround(codecInfo.name); passthroughEnabled = codecInfo.passthrough; String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType; MediaFormat mediaFormat = @@ -583,9 +586,9 @@ protected void onQueueInputBuffer(DecoderInputBuffer buffer) { lastInputTimeUs = Math.max(buffer.timeUs, lastInputTimeUs); } + @CallSuper @Override protected void onProcessedOutputBuffer(long presentationTimeUs) { - super.onProcessedOutputBuffer(presentationTimeUs); while (pendingStreamChangeCount != 0 && presentationTimeUs >= pendingStreamChangeTimesUs[0]) { audioSink.handleDiscontinuity(); pendingStreamChangeCount--; @@ -610,6 +613,13 @@ protected boolean processOutputBuffer( boolean shouldSkip, Format format) throws ExoPlaybackException { + if (codecNeedsEosBufferTimestampWorkaround + && bufferPresentationTimeUs == 0 + && (bufferFlags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 + && lastInputTimeUs != C.TIME_UNSET) { + bufferPresentationTimeUs = lastInputTimeUs; + } + if (passthroughEnabled && (bufferFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { // Discard output buffers from the passthrough (raw) decoder containing codec specific data. codec.releaseOutputBuffer(bufferIndex, false); @@ -777,6 +787,24 @@ private static boolean codecNeedsDiscardChannelsWorkaround(String codecName) { || Util.DEVICE.startsWith("heroqlte")); } + /** + * Returns whether the decoder may output a non-empty buffer with timestamp 0 as the end of stream + * buffer. + * + *

See GitHub issue #5045. + */ + private static boolean codecNeedsEosBufferTimestampWorkaround(String codecName) { + return Util.SDK_INT < 21 + && "OMX.SEC.mp3.dec".equals(codecName) + && "samsung".equals(Util.MANUFACTURER) + && (Util.DEVICE.startsWith("baffin") + || Util.DEVICE.startsWith("grand") + || Util.DEVICE.startsWith("fortuna") + || Util.DEVICE.startsWith("gprimelte") + || Util.DEVICE.startsWith("j2y18lte") + || Util.DEVICE.startsWith("ms01")); + } + private final class AudioSinkListener implements AudioSink.Listener { @Override