diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 41052a0063..7d1191199c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -9,6 +9,9 @@ * Audio: * Fix `SimpleExoPlayer` reporting audio session ID as 0 in some cases ([#8585](https://github.com/google/ExoPlayer/issues/8585)). + * Report unexpected discontinuities in + `AnalyticsListener.onAudioSinkError` + ([#6384](https://github.com/google/ExoPlayer/issues/6384)). * Analytics: * Add `onAudioCodecError` and `onVideoCodecError` to `AnalyticsListener`. * Library restructuring: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java index bf0c8b5d22..a68e2f7fa2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java @@ -854,8 +854,9 @@ public interface AnalyticsListener { * wishes to do so. * * @param eventTime The event time. - * @param audioSinkError Either a {@link AudioSink.InitializationException} or a {@link - * AudioSink.WriteException} describing the error. + * @param audioSinkError The error that occurred. Typically an {@link + * AudioSink.InitializationException}, a {@link AudioSink.WriteException}, or an {@link + * AudioSink.UnexpectedDiscontinuityException}. */ default void onAudioSinkError(EventTime eventTime, Exception audioSinkError) {} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioRendererEventListener.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioRendererEventListener.java index cfd476a421..69803ceef6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioRendererEventListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioRendererEventListener.java @@ -144,7 +144,8 @@ public interface AudioRendererEventListener { * wishes to do so. * * @param audioSinkError The error that occurred. Typically an {@link - * AudioSink.InitializationException} or a {@link AudioSink.WriteException}. + * AudioSink.InitializationException}, a {@link AudioSink.WriteException}, or an {@link + * AudioSink.UnexpectedDiscontinuityException}. */ default void onAudioSinkError(Exception audioSinkError) {} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java index 463461916f..e761924586 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java @@ -126,8 +126,8 @@ public interface AudioSink { *

Fatal errors that cannot be recovered will be reported wrapped in a {@link * ExoPlaybackException} by {@link Player.EventListener#onPlayerError(ExoPlaybackException)}. * - * @param audioSinkError Either an {@link AudioSink.InitializationException} or a {@link - * AudioSink.WriteException} describing the error. + * @param audioSinkError The error that occurred. Typically an {@link InitializationException}, + * a {@link WriteException}, or an {@link UnexpectedDiscontinuityException}. */ default void onAudioSinkError(Exception audioSinkError) {} } @@ -226,6 +226,27 @@ public interface AudioSink { } + /** Thrown when the sink encounters an unexpected timestamp discontinuity. */ + final class UnexpectedDiscontinuityException extends Exception { + /** The actual presentation time of a sample, in microseconds. */ + public final long actualPresentationTimeUs; + /** The expected presentation time of a sample, in microseconds. */ + public final long expectedPresentationTimeUs; + + /** + * Creates an instance. + * + * @param actualPresentationTimeUs The actual presentation time of a sample, in microseconds. + * @param expectedPresentationTimeUs The expected presentation time of a sample, in + * microseconds. + */ + public UnexpectedDiscontinuityException( + long actualPresentationTimeUs, long expectedPresentationTimeUs) { + this.actualPresentationTimeUs = actualPresentationTimeUs; + this.expectedPresentationTimeUs = expectedPresentationTimeUs; + } + } + /** * The level of support the sink provides for a format. One of {@link * #SINK_FORMAT_SUPPORTED_DIRECTLY}, {@link #SINK_FORMAT_SUPPORTED_WITH_TRANSCODING} or {@link diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index 826839b3d2..919870c24e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -762,13 +762,9 @@ public final class DefaultAudioSink implements AudioSink { getSubmittedFrames() - trimmingAudioProcessor.getTrimmedFrameCount()); if (!startMediaTimeUsNeedsSync && Math.abs(expectedPresentationTimeUs - presentationTimeUs) > 200000) { - Log.e( - TAG, - "Discontinuity detected [expected " - + expectedPresentationTimeUs - + ", got " - + presentationTimeUs - + "]"); + listener.onAudioSinkError( + new AudioSink.UnexpectedDiscontinuityException( + presentationTimeUs, expectedPresentationTimeUs)); startMediaTimeUsNeedsSync = true; } if (startMediaTimeUsNeedsSync) {