diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 5add56ca08..49127a1099 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,31 @@ # Release notes # +### 2.11.1 (2019-12-20) ### + +* UI: Exclude `DefaultTimeBar` region from system gesture detection + ([#6685](https://github.com/google/ExoPlayer/issues/6685)). +* ProGuard fixes: + * Ensure `Libgav1VideoRenderer` constructor is kept for use by + `DefaultRenderersFactory` + ([#6773](https://github.com/google/ExoPlayer/issues/6773)). + * Ensure `VideoDecoderOutputBuffer` and its members are kept for use by video + decoder extensions. + * Ensure raw resources used with `RawResourceDataSource` are kept. + * Suppress spurious warnings about the `javax.annotation` package, and + restructure use of `IntDef` annotations to remove spurious warnings about + `SsaStyle$SsaAlignment` + ([#6771](https://github.com/google/ExoPlayer/issues/6771)). +* Fix `CacheDataSource` to correctly propagate `DataSpec.httpRequestHeaders`. +* Fix issue with `DefaultDownloadIndex` that could result in an + `IllegalStateException` being thrown from + `DefaultDownloadIndex.getDownloadForCurrentRow` + ([#6785](https://github.com/google/ExoPlayer/issues/6785)). +* Fix `IndexOutOfBoundsException` in `SinglePeriodTimeline.getWindow` + ([#6776](https://github.com/google/ExoPlayer/issues/6776)). +* Add missing `@Nullable` to `MediaCodecAudioRenderer.getMediaClock` and + `SimpleDecoderAudioRenderer.getMediaClock` + ([#6792](https://github.com/google/ExoPlayer/issues/6792)). + ### 2.11.0 (2019-12-11) ### * Core library: @@ -30,6 +56,10 @@ * Fix issue where player errors are thrown too early at playlist transitions ([#5407](https://github.com/google/ExoPlayer/issues/5407)). * Add `Format` and renderer support flags to renderer `ExoPlaybackException`s. + * Where there are multiple platform decoders for a given MIME type, prefer to + use one that advertises support for the profile and level of the media being + played over one that does not, even if it does not come first in the + `MediaCodecList`. * DRM: * Inject `DrmSessionManager` into the `MediaSources` instead of `Renderers`. This allows each `MediaSource` in a `ConcatenatingMediaSource` to use a diff --git a/constants.gradle b/constants.gradle index 599af54dde..392c623455 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.11.0' - releaseVersionCode = 2011000 + releaseVersion = '2.11.1' + releaseVersionCode = 2011001 minSdkVersion = 16 appTargetSdkVersion = 29 targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved diff --git a/extensions/av1/README.md b/extensions/av1/README.md index 276daae4e2..54e27a3b87 100644 --- a/extensions/av1/README.md +++ b/extensions/av1/README.md @@ -96,6 +96,14 @@ a custom track selector the choice of `Renderer` is up to your implementation. You need to make sure you are passing a `Libgav1VideoRenderer` to the player and then you need to implement your own logic to use the renderer for a given track. +## Using the extension in the demo application ## + +To try out playback using the extension in the [demo application][], see +[enabling extension decoders][]. + +[demo application]: https://exoplayer.dev/demo-application.html +[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders + ## Rendering options ## There are two possibilities for rendering the output `Libgav1VideoRenderer` diff --git a/extensions/ffmpeg/README.md b/extensions/ffmpeg/README.md index fe4aca772a..1b2db8f0f4 100644 --- a/extensions/ffmpeg/README.md +++ b/extensions/ffmpeg/README.md @@ -106,9 +106,19 @@ then implement your own logic to use the renderer for a given track. [#2781]: https://github.com/google/ExoPlayer/issues/2781 [Supported formats]: https://exoplayer.dev/supported-formats.html#ffmpeg-extension +## Using the extension in the demo application ## + +To try out playback using the extension in the [demo application][], see +[enabling extension decoders][]. + +[demo application]: https://exoplayer.dev/demo-application.html +[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders + ## Links ## +* [Troubleshooting using extensions][] * [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.ffmpeg.*` belong to this module. +[Troubleshooting using extensions]: https://exoplayer.dev/troubleshooting.html#how-can-i-get-a-decoding-extension-to-load-and-be-used-for-playback [Javadoc]: https://exoplayer.dev/doc/reference/index.html diff --git a/extensions/flac/README.md b/extensions/flac/README.md index 84a92f9586..a9d4c3094e 100644 --- a/extensions/flac/README.md +++ b/extensions/flac/README.md @@ -97,6 +97,14 @@ a custom track selector the choice of `Renderer` is up to your implementation, so you need to make sure you are passing an `LibflacAudioRenderer` to the player, then implement your own logic to use the renderer for a given track. +## Using the extension in the demo application ## + +To try out playback using the extension in the [demo application][], see +[enabling extension decoders][]. + +[demo application]: https://exoplayer.dev/demo-application.html +[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders + ## Links ## * [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.flac.*` diff --git a/extensions/flac/src/main/jni/flac_parser.cc b/extensions/flac/src/main/jni/flac_parser.cc index b920560f3a..f39e4bd1f7 100644 --- a/extensions/flac/src/main/jni/flac_parser.cc +++ b/extensions/flac/src/main/jni/flac_parser.cc @@ -462,8 +462,9 @@ bool FLACParser::getSeekPositions(int64_t timeUs, if (sampleNumber <= targetSampleNumber) { result[0] = (sampleNumber * 1000000LL) / sampleRate; result[1] = firstFrameOffset + points[i - 1].stream_offset; - if (sampleNumber == targetSampleNumber || i >= length) { - // exact seek, or no following seek point. + if (sampleNumber == targetSampleNumber || i >= length || + points[i].sample_number == -1) { // placeholder + // exact seek, or no following non-placeholder seek point result[2] = result[0]; result[3] = result[1]; } else { diff --git a/extensions/opus/README.md b/extensions/opus/README.md index 05448f2073..d3691b07bd 100644 --- a/extensions/opus/README.md +++ b/extensions/opus/README.md @@ -101,6 +101,14 @@ a custom track selector the choice of `Renderer` is up to your implementation, so you need to make sure you are passing an `LibopusAudioRenderer` to the player, then implement your own logic to use the renderer for a given track. +## Using the extension in the demo application ## + +To try out playback using the extension in the [demo application][], see +[enabling extension decoders][]. + +[demo application]: https://exoplayer.dev/demo-application.html +[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders + ## Links ## * [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.opus.*` diff --git a/extensions/vp9/README.md b/extensions/vp9/README.md index 71241d9a4f..fd0836648a 100644 --- a/extensions/vp9/README.md +++ b/extensions/vp9/README.md @@ -114,6 +114,14 @@ a custom track selector the choice of `Renderer` is up to your implementation, so you need to make sure you are passing an `LibvpxVideoRenderer` to the player, then implement your own logic to use the renderer for a given track. +## Using the extension in the demo application ## + +To try out playback using the extension in the [demo application][], see +[enabling extension decoders][]. + +[demo application]: https://exoplayer.dev/demo-application.html +[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders + ## Rendering options ## There are two possibilities for rendering the output `LibvpxVideoRenderer` diff --git a/library/core/proguard-rules.txt b/library/core/proguard-rules.txt index 67646be956..fd4e196945 100644 --- a/library/core/proguard-rules.txt +++ b/library/core/proguard-rules.txt @@ -1,10 +1,24 @@ # Proguard rules specific to the core module. +# Constant folding for resource integers may mean that a resource passed to this method appears to be unused. Keep the method to prevent this from happening. +-keep class com.google.android.exoplayer2.upstream.RawResourceDataSource { + public static android.net.Uri buildRawResourceUri(int); +} + +# Some members of this class are being accessed from native methods. Keep them unobfuscated. +-keep class com.google.android.exoplayer2.video.VideoDecoderOutputBuffer { + *; +} + # Constructors accessed via reflection in DefaultRenderersFactory -dontnote com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer -keepclassmembers class com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer { (long, android.os.Handler, com.google.android.exoplayer2.video.VideoRendererEventListener, int); } +-dontnote com.google.android.exoplayer2.ext.av1.Libgav1VideoRenderer +-keepclassmembers class com.google.android.exoplayer2.ext.av1.Libgav1VideoRenderer { + (long, android.os.Handler, com.google.android.exoplayer2.video.VideoRendererEventListener, int); +} -dontnote com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer -keepclassmembers class com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer { (android.os.Handler, com.google.android.exoplayer2.audio.AudioRendererEventListener, com.google.android.exoplayer2.audio.AudioProcessor[]); @@ -61,8 +75,4 @@ # Don't warn about checkerframework and Kotlin annotations -dontwarn org.checkerframework.** -dontwarn kotlin.annotations.jvm.** - -# Some members of this class are being accessed from native methods. Keep them unobfuscated. --keep class com.google.android.exoplayer2.ext.video.VideoDecoderOutputBuffer { - *; -} +-dontwarn javax.annotation.** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 249ef7e44e..217f580e7b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo { /** The version of the library expressed as a string, for example "1.2.3". */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa. - public static final String VERSION = "2.11.0"; + public static final String VERSION = "2.11.1"; /** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.0"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.1"; /** * The version of the library expressed as an integer, for example 1002003. @@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final int VERSION_INT = 2011000; + public static final int VERSION_INT = 2011001; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java index 5a57844c83..dc1089eca0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java @@ -804,9 +804,13 @@ public class AnalyticsCollector /** Updates the queue with a newly created media period. */ public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) { - boolean isInTimeline = timeline.getIndexOfPeriod(mediaPeriodId.periodUid) != C.INDEX_UNSET; + int periodIndex = timeline.getIndexOfPeriod(mediaPeriodId.periodUid); + boolean isInTimeline = periodIndex != C.INDEX_UNSET; MediaPeriodInfo mediaPeriodInfo = - new MediaPeriodInfo(mediaPeriodId, isInTimeline ? timeline : Timeline.EMPTY, windowIndex); + new MediaPeriodInfo( + mediaPeriodId, + isInTimeline ? timeline : Timeline.EMPTY, + isInTimeline ? timeline.getPeriod(periodIndex, period).windowIndex : windowIndex); mediaPeriodInfoQueue.add(mediaPeriodInfo); mediaPeriodIdToInfo.put(mediaPeriodId, mediaPeriodInfo); lastPlayingMediaPeriod = mediaPeriodInfoQueue.get(0); 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 ae50d14728..096f4ccd1f 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 @@ -520,6 +520,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } @Override + @Nullable public MediaClock getMediaClock() { return this; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java index 5ccbf04c5c..60870204cc 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java @@ -218,6 +218,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements } @Override + @Nullable public MediaClock getMediaClock() { return this; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index 403f6c3d41..b30fbf105e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -1635,6 +1635,16 @@ public class MatroskaExtractor implements Extractor { sizes[cuePointsSize - 1] = (int) (segmentContentPosition + segmentContentSize - offsets[cuePointsSize - 1]); durationsUs[cuePointsSize - 1] = durationUs - timesUs[cuePointsSize - 1]; + + long lastDurationUs = durationsUs[cuePointsSize - 1]; + if (lastDurationUs <= 0) { + Log.w(TAG, "Discarding last cue point with unexpected duration: " + lastDurationUs); + sizes = Arrays.copyOf(sizes, sizes.length - 1); + offsets = Arrays.copyOf(offsets, offsets.length - 1); + durationsUs = Arrays.copyOf(durationsUs, durationsUs.length - 1); + timesUs = Arrays.copyOf(timesUs, timesUs.length - 1); + } + cueTimesUs = null; cueClusterPositions = null; return new ChunkIndex(sizes, offsets, durationsUs, timesUs); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java index 95193785c0..dac74bfe2b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java @@ -118,10 +118,6 @@ import java.io.IOException; } } - if (inputLength != C.LENGTH_UNSET && bytesSearched + atomSize > inputLength) { - // The file is invalid because the atom extends past the end of the file. - return false; - } if (atomSize < headerSize) { // The file is invalid because the atom size is too small for its header. return false; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 820f9f003e..6c405f7ced 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -59,9 +59,7 @@ import java.util.List; */ public abstract class MediaCodecRenderer extends BaseRenderer { - /** - * Thrown when a failure occurs instantiating a decoder. - */ + /** Thrown when a failure occurs instantiating a decoder. */ public static class DecoderInitializationException extends Exception { private static final int CUSTOM_ERROR_CODE_BASE = -50000; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java index 7ed1eb095f..f1c897813f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java @@ -26,6 +26,8 @@ import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.database.DatabaseIOException; import com.google.android.exoplayer2.database.DatabaseProvider; import com.google.android.exoplayer2.database.VersionTable; +import com.google.android.exoplayer2.offline.Download.FailureReason; +import com.google.android.exoplayer2.offline.Download.State; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.util.ArrayList; @@ -239,6 +241,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { try { ContentValues values = new ContentValues(); values.put(COLUMN_STATE, Download.STATE_REMOVING); + // Only downloads in STATE_FAILED are allowed a failure reason, so we need to clear it here in + // case we're moving downloads from STATE_FAILED to STATE_REMOVING. + values.put(COLUMN_FAILURE_REASON, Download.FAILURE_REASON_NONE); SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase(); writableDatabase.update(tableName, values, /* whereClause= */ null, /* whereArgs= */ null); } catch (SQLException e) { @@ -351,14 +356,22 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { DownloadProgress downloadProgress = new DownloadProgress(); downloadProgress.bytesDownloaded = cursor.getLong(COLUMN_INDEX_BYTES_DOWNLOADED); downloadProgress.percentDownloaded = cursor.getFloat(COLUMN_INDEX_PERCENT_DOWNLOADED); + @State int state = cursor.getInt(COLUMN_INDEX_STATE); + // It's possible the database contains failure reasons for non-failed downloads, which is + // invalid. Clear them here. See https://github.com/google/ExoPlayer/issues/6785. + @FailureReason + int failureReason = + state == Download.STATE_FAILED + ? cursor.getInt(COLUMN_INDEX_FAILURE_REASON) + : Download.FAILURE_REASON_NONE; return new Download( request, - /* state= */ cursor.getInt(COLUMN_INDEX_STATE), + state, /* startTimeMs= */ cursor.getLong(COLUMN_INDEX_START_TIME_MS), /* updateTimeMs= */ cursor.getLong(COLUMN_INDEX_UPDATE_TIME_MS), /* contentLength= */ cursor.getLong(COLUMN_INDEX_CONTENT_LENGTH), /* stopReason= */ cursor.getInt(COLUMN_INDEX_STOP_REASON), - /* failureReason= */ cursor.getInt(COLUMN_INDEX_FAILURE_REASON), + failureReason, downloadProgress); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/Download.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/Download.java index 97dff8394e..da46120b29 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/Download.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/Download.java @@ -130,9 +130,9 @@ public final class Download { @FailureReason int failureReason, DownloadProgress progress) { Assertions.checkNotNull(progress); - Assertions.checkState((failureReason == FAILURE_REASON_NONE) == (state != STATE_FAILED)); + Assertions.checkArgument((failureReason == FAILURE_REASON_NONE) == (state != STATE_FAILED)); if (stopReason != 0) { - Assertions.checkState(state != STATE_DOWNLOADING && state != STATE_QUEUED); + Assertions.checkArgument(state != STATE_DOWNLOADING && state != STATE_QUEUED); } this.request = request; this.state = state; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java index 917ac8e36e..eef9d2eec1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java @@ -300,12 +300,12 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { float screenWidth, float screenHeight) { @SsaStyle.SsaAlignment int alignment; - if (styleOverrides.alignment != SsaStyle.SsaAlignment.UNKNOWN) { + if (styleOverrides.alignment != SsaStyle.SSA_ALIGNMENT_UNKNOWN) { alignment = styleOverrides.alignment; } else if (style != null) { alignment = style.alignment; } else { - alignment = SsaStyle.SsaAlignment.UNKNOWN; + alignment = SsaStyle.SSA_ALIGNMENT_UNKNOWN; } @Cue.AnchorType int positionAnchor = toPositionAnchor(alignment); @Cue.AnchorType int lineAnchor = toLineAnchor(alignment); @@ -337,19 +337,19 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { @Nullable private static Layout.Alignment toTextAlignment(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { - case SsaStyle.SsaAlignment.BOTTOM_LEFT: - case SsaStyle.SsaAlignment.MIDDLE_LEFT: - case SsaStyle.SsaAlignment.TOP_LEFT: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT: + case SsaStyle.SSA_ALIGNMENT_TOP_LEFT: return Layout.Alignment.ALIGN_NORMAL; - case SsaStyle.SsaAlignment.BOTTOM_CENTER: - case SsaStyle.SsaAlignment.MIDDLE_CENTER: - case SsaStyle.SsaAlignment.TOP_CENTER: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_CENTER: + case SsaStyle.SSA_ALIGNMENT_TOP_CENTER: return Layout.Alignment.ALIGN_CENTER; - case SsaStyle.SsaAlignment.BOTTOM_RIGHT: - case SsaStyle.SsaAlignment.MIDDLE_RIGHT: - case SsaStyle.SsaAlignment.TOP_RIGHT: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_RIGHT: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_RIGHT: + case SsaStyle.SSA_ALIGNMENT_TOP_RIGHT: return Layout.Alignment.ALIGN_OPPOSITE; - case SsaStyle.SsaAlignment.UNKNOWN: + case SsaStyle.SSA_ALIGNMENT_UNKNOWN: return null; default: Log.w(TAG, "Unknown alignment: " + alignment); @@ -360,19 +360,19 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { @Cue.AnchorType private static int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { - case SsaStyle.SsaAlignment.BOTTOM_LEFT: - case SsaStyle.SsaAlignment.BOTTOM_CENTER: - case SsaStyle.SsaAlignment.BOTTOM_RIGHT: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_RIGHT: return Cue.ANCHOR_TYPE_END; - case SsaStyle.SsaAlignment.MIDDLE_LEFT: - case SsaStyle.SsaAlignment.MIDDLE_CENTER: - case SsaStyle.SsaAlignment.MIDDLE_RIGHT: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_CENTER: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_RIGHT: return Cue.ANCHOR_TYPE_MIDDLE; - case SsaStyle.SsaAlignment.TOP_LEFT: - case SsaStyle.SsaAlignment.TOP_CENTER: - case SsaStyle.SsaAlignment.TOP_RIGHT: + case SsaStyle.SSA_ALIGNMENT_TOP_LEFT: + case SsaStyle.SSA_ALIGNMENT_TOP_CENTER: + case SsaStyle.SSA_ALIGNMENT_TOP_RIGHT: return Cue.ANCHOR_TYPE_START; - case SsaStyle.SsaAlignment.UNKNOWN: + case SsaStyle.SSA_ALIGNMENT_UNKNOWN: return Cue.TYPE_UNSET; default: Log.w(TAG, "Unknown alignment: " + alignment); @@ -383,19 +383,19 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { @Cue.AnchorType private static int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { - case SsaStyle.SsaAlignment.BOTTOM_LEFT: - case SsaStyle.SsaAlignment.MIDDLE_LEFT: - case SsaStyle.SsaAlignment.TOP_LEFT: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT: + case SsaStyle.SSA_ALIGNMENT_TOP_LEFT: return Cue.ANCHOR_TYPE_START; - case SsaStyle.SsaAlignment.BOTTOM_CENTER: - case SsaStyle.SsaAlignment.MIDDLE_CENTER: - case SsaStyle.SsaAlignment.TOP_CENTER: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_CENTER: + case SsaStyle.SSA_ALIGNMENT_TOP_CENTER: return Cue.ANCHOR_TYPE_MIDDLE; - case SsaStyle.SsaAlignment.BOTTOM_RIGHT: - case SsaStyle.SsaAlignment.MIDDLE_RIGHT: - case SsaStyle.SsaAlignment.TOP_RIGHT: + case SsaStyle.SSA_ALIGNMENT_BOTTOM_RIGHT: + case SsaStyle.SSA_ALIGNMENT_MIDDLE_RIGHT: + case SsaStyle.SSA_ALIGNMENT_TOP_RIGHT: return Cue.ANCHOR_TYPE_END; - case SsaStyle.SsaAlignment.UNKNOWN: + case SsaStyle.SSA_ALIGNMENT_UNKNOWN: return Cue.TYPE_UNSET; default: Log.w(TAG, "Unknown alignment: " + alignment); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java index e8070976e7..fd2cb036b7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java @@ -37,6 +37,52 @@ import java.util.regex.Pattern; private static final String TAG = "SsaStyle"; + /** + * The SSA/ASS alignments. + * + *

Allowed values: + * + *

    + *
  • {@link #SSA_ALIGNMENT_UNKNOWN} + *
  • {@link #SSA_ALIGNMENT_BOTTOM_LEFT} + *
  • {@link #SSA_ALIGNMENT_BOTTOM_CENTER} + *
  • {@link #SSA_ALIGNMENT_BOTTOM_RIGHT} + *
  • {@link #SSA_ALIGNMENT_MIDDLE_LEFT} + *
  • {@link #SSA_ALIGNMENT_MIDDLE_CENTER} + *
  • {@link #SSA_ALIGNMENT_MIDDLE_RIGHT} + *
  • {@link #SSA_ALIGNMENT_TOP_LEFT} + *
  • {@link #SSA_ALIGNMENT_TOP_CENTER} + *
  • {@link #SSA_ALIGNMENT_TOP_RIGHT} + *
+ */ + @IntDef({ + SSA_ALIGNMENT_UNKNOWN, + SSA_ALIGNMENT_BOTTOM_LEFT, + SSA_ALIGNMENT_BOTTOM_CENTER, + SSA_ALIGNMENT_BOTTOM_RIGHT, + SSA_ALIGNMENT_MIDDLE_LEFT, + SSA_ALIGNMENT_MIDDLE_CENTER, + SSA_ALIGNMENT_MIDDLE_RIGHT, + SSA_ALIGNMENT_TOP_LEFT, + SSA_ALIGNMENT_TOP_CENTER, + SSA_ALIGNMENT_TOP_RIGHT, + }) + @Documented + @Retention(SOURCE) + public @interface SsaAlignment {} + + // The numbering follows the ASS (v4+) spec (i.e. the points on the number pad). + public static final int SSA_ALIGNMENT_UNKNOWN = -1; + public static final int SSA_ALIGNMENT_BOTTOM_LEFT = 1; + public static final int SSA_ALIGNMENT_BOTTOM_CENTER = 2; + public static final int SSA_ALIGNMENT_BOTTOM_RIGHT = 3; + public static final int SSA_ALIGNMENT_MIDDLE_LEFT = 4; + public static final int SSA_ALIGNMENT_MIDDLE_CENTER = 5; + public static final int SSA_ALIGNMENT_MIDDLE_RIGHT = 6; + public static final int SSA_ALIGNMENT_TOP_LEFT = 7; + public static final int SSA_ALIGNMENT_TOP_CENTER = 8; + public static final int SSA_ALIGNMENT_TOP_RIGHT = 9; + public final String name; @SsaAlignment public final int alignment; @@ -77,22 +123,22 @@ import java.util.regex.Pattern; // Swallow the exception and return UNKNOWN below. } Log.w(TAG, "Ignoring unknown alignment: " + alignmentStr); - return SsaAlignment.UNKNOWN; + return SSA_ALIGNMENT_UNKNOWN; } private static boolean isValidAlignment(@SsaAlignment int alignment) { switch (alignment) { - case SsaAlignment.BOTTOM_CENTER: - case SsaAlignment.BOTTOM_LEFT: - case SsaAlignment.BOTTOM_RIGHT: - case SsaAlignment.MIDDLE_CENTER: - case SsaAlignment.MIDDLE_LEFT: - case SsaAlignment.MIDDLE_RIGHT: - case SsaAlignment.TOP_CENTER: - case SsaAlignment.TOP_LEFT: - case SsaAlignment.TOP_RIGHT: + case SSA_ALIGNMENT_BOTTOM_CENTER: + case SSA_ALIGNMENT_BOTTOM_LEFT: + case SSA_ALIGNMENT_BOTTOM_RIGHT: + case SSA_ALIGNMENT_MIDDLE_CENTER: + case SSA_ALIGNMENT_MIDDLE_LEFT: + case SSA_ALIGNMENT_MIDDLE_RIGHT: + case SSA_ALIGNMENT_TOP_CENTER: + case SSA_ALIGNMENT_TOP_LEFT: + case SSA_ALIGNMENT_TOP_RIGHT: return true; - case SsaAlignment.UNKNOWN: + case SSA_ALIGNMENT_UNKNOWN: default: return false; } @@ -177,7 +223,7 @@ import java.util.regex.Pattern; } public static Overrides parseFromDialogue(String text) { - @SsaAlignment int alignment = SsaAlignment.UNKNOWN; + @SsaAlignment int alignment = SSA_ALIGNMENT_UNKNOWN; PointF position = null; Matcher matcher = BRACES_PATTERN.matcher(text); while (matcher.find()) { @@ -192,7 +238,7 @@ import java.util.regex.Pattern; } try { @SsaAlignment int parsedAlignment = parseAlignmentOverride(braceContents); - if (parsedAlignment != SsaAlignment.UNKNOWN) { + if (parsedAlignment != SSA_ALIGNMENT_UNKNOWN) { alignment = parsedAlignment; } } catch (RuntimeException e) { @@ -249,36 +295,7 @@ import java.util.regex.Pattern; @SsaAlignment private static int parseAlignmentOverride(String braceContents) { Matcher matcher = ALIGNMENT_OVERRIDE_PATTERN.matcher(braceContents); - return matcher.find() ? parseAlignment(matcher.group(1)) : SsaAlignment.UNKNOWN; + return matcher.find() ? parseAlignment(matcher.group(1)) : SSA_ALIGNMENT_UNKNOWN; } } - - /** The SSA/ASS alignments. */ - @IntDef({ - SsaAlignment.UNKNOWN, - SsaAlignment.BOTTOM_LEFT, - SsaAlignment.BOTTOM_CENTER, - SsaAlignment.BOTTOM_RIGHT, - SsaAlignment.MIDDLE_LEFT, - SsaAlignment.MIDDLE_CENTER, - SsaAlignment.MIDDLE_RIGHT, - SsaAlignment.TOP_LEFT, - SsaAlignment.TOP_CENTER, - SsaAlignment.TOP_RIGHT, - }) - @Documented - @Retention(SOURCE) - /* package */ @interface SsaAlignment { - // The numbering follows the ASS (v4+) spec (i.e. the points on the number pad). - int UNKNOWN = -1; - int BOTTOM_LEFT = 1; - int BOTTOM_CENTER = 2; - int BOTTOM_RIGHT = 3; - int MIDDLE_LEFT = 4; - int MIDDLE_CENTER = 5; - int MIDDLE_RIGHT = 6; - int TOP_LEFT = 7; - int TOP_CENTER = 8; - int TOP_RIGHT = 9; - } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java index bfa067e322..55e568efa1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java @@ -77,39 +77,40 @@ public final class WebvttCue extends Cue { @Documented @Retention(SOURCE) @IntDef({ - TextAlignment.START, - TextAlignment.CENTER, - TextAlignment.END, - TextAlignment.LEFT, - TextAlignment.RIGHT + TEXT_ALIGNMENT_START, + TEXT_ALIGNMENT_CENTER, + TEXT_ALIGNMENT_END, + TEXT_ALIGNMENT_LEFT, + TEXT_ALIGNMENT_RIGHT }) - public @interface TextAlignment { - /** - * See WebVTT's align:start. - */ - int START = 1; - /** - * See WebVTT's align:center. - */ - int CENTER = 2; - /** - * See WebVTT's align:end. - */ - int END = 3; - /** - * See WebVTT's align:left. - */ - int LEFT = 4; - /** - * See WebVTT's align:right. - */ - int RIGHT = 5; - } + public @interface TextAlignment {} + /** + * See WebVTT's align:start. + */ + public static final int TEXT_ALIGNMENT_START = 1; + + /** + * See WebVTT's align:center. + */ + public static final int TEXT_ALIGNMENT_CENTER = 2; + + /** + * See WebVTT's align:end. + */ + public static final int TEXT_ALIGNMENT_END = 3; + + /** + * See WebVTT's align:left. + */ + public static final int TEXT_ALIGNMENT_LEFT = 4; + + /** + * See WebVTT's align:right. + */ + public static final int TEXT_ALIGNMENT_RIGHT = 5; private static final String TAG = "WebvttCueBuilder"; @@ -140,7 +141,7 @@ public final class WebvttCue extends Cue { endTime = 0; text = null; // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment - textAlignment = TextAlignment.CENTER; + textAlignment = TEXT_ALIGNMENT_CENTER; line = Cue.DIMEN_UNSET; // Defaults to NUMBER (true): https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag lineType = Cue.LINE_TYPE_NUMBER; @@ -251,13 +252,13 @@ public final class WebvttCue extends Cue { // https://www.w3.org/TR/webvtt1/#webvtt-cue-position private static float derivePosition(@TextAlignment int textAlignment) { switch (textAlignment) { - case TextAlignment.LEFT: + case TEXT_ALIGNMENT_LEFT: return 0.0f; - case TextAlignment.RIGHT: + case TEXT_ALIGNMENT_RIGHT: return 1.0f; - case TextAlignment.START: - case TextAlignment.CENTER: - case TextAlignment.END: + case TEXT_ALIGNMENT_START: + case TEXT_ALIGNMENT_CENTER: + case TEXT_ALIGNMENT_END: default: return DEFAULT_POSITION; } @@ -267,13 +268,13 @@ public final class WebvttCue extends Cue { @AnchorType private static int derivePositionAnchor(@TextAlignment int textAlignment) { switch (textAlignment) { - case TextAlignment.LEFT: - case TextAlignment.START: + case TEXT_ALIGNMENT_LEFT: + case TEXT_ALIGNMENT_START: return Cue.ANCHOR_TYPE_START; - case TextAlignment.RIGHT: - case TextAlignment.END: + case TEXT_ALIGNMENT_RIGHT: + case TEXT_ALIGNMENT_END: return Cue.ANCHOR_TYPE_END; - case TextAlignment.CENTER: + case TEXT_ALIGNMENT_CENTER: default: return Cue.ANCHOR_TYPE_MIDDLE; } @@ -282,13 +283,13 @@ public final class WebvttCue extends Cue { @Nullable private static Alignment convertTextAlignment(@TextAlignment int textAlignment) { switch (textAlignment) { - case TextAlignment.START: - case TextAlignment.LEFT: + case TEXT_ALIGNMENT_START: + case TEXT_ALIGNMENT_LEFT: return Alignment.ALIGN_NORMAL; - case TextAlignment.CENTER: + case TEXT_ALIGNMENT_CENTER: return Alignment.ALIGN_CENTER; - case TextAlignment.END: - case TextAlignment.RIGHT: + case TEXT_ALIGNMENT_END: + case TEXT_ALIGNMENT_RIGHT: return Alignment.ALIGN_OPPOSITE; default: Log.w(TAG, "Unknown textAlignment: " + textAlignment); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java index 6e5bd31b4b..b6ddf89dc3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java @@ -308,20 +308,20 @@ public final class WebvttCueParser { private static int parseTextAlignment(String s) { switch (s) { case "start": - return WebvttCue.Builder.TextAlignment.START; + return WebvttCue.Builder.TEXT_ALIGNMENT_START; case "left": - return WebvttCue.Builder.TextAlignment.LEFT; + return WebvttCue.Builder.TEXT_ALIGNMENT_LEFT; case "center": case "middle": - return WebvttCue.Builder.TextAlignment.CENTER; + return WebvttCue.Builder.TEXT_ALIGNMENT_CENTER; case "end": - return WebvttCue.Builder.TextAlignment.END; + return WebvttCue.Builder.TEXT_ALIGNMENT_END; case "right": - return WebvttCue.Builder.TextAlignment.RIGHT; + return WebvttCue.Builder.TEXT_ALIGNMENT_RIGHT; default: Log.w(TAG, "Invalid alignment value: " + s); // Default value: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment - return WebvttCue.Builder.TextAlignment.CENTER; + return WebvttCue.Builder.TEXT_ALIGNMENT_CENTER; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java index fb74bd9d54..c2fbeb6e2d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java @@ -75,7 +75,7 @@ import com.google.android.exoplayer2.util.Assertions; * the two are tightly bound together. It may only be possible to play a certain combination tracks * if the renderers are configured in a particular way. Equally, it may only be possible to * configure renderers in a particular way if certain tracks are selected. Hence it makes sense to - * determined the track selection and corresponding renderer configurations in a single step. + * determine the track selection and corresponding renderer configurations in a single step. * *

Threading model

* diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java index 307652f456..435f4bf578 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java @@ -71,6 +71,7 @@ public class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPolicy { int responseCode = ((InvalidResponseCodeException) exception).responseCode; return responseCode == 404 // HTTP 404 Not Found. || responseCode == 410 // HTTP 410 Gone. + || responseCode == 416 // HTTP 416 Range Not Satisfiable. ? DEFAULT_TRACK_BLACKLIST_MS : C.TIME_UNSET; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java index 541c3b2d9d..94ec2c6dff 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java @@ -138,7 +138,8 @@ public final class CacheDataSource implements DataSource { @Nullable private Uri actualUri; @HttpMethod private int httpMethod; @Nullable private byte[] httpBody; - private int flags; + private Map httpRequestHeaders = Collections.emptyMap(); + @DataSpec.Flags private int flags; @Nullable private String key; private long readPosition; private long bytesRemaining; @@ -263,6 +264,7 @@ public final class CacheDataSource implements DataSource { actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri); httpMethod = dataSpec.httpMethod; httpBody = dataSpec.httpBody; + httpRequestHeaders = dataSpec.httpRequestHeaders; flags = dataSpec.flags; readPosition = dataSpec.position; @@ -353,6 +355,10 @@ public final class CacheDataSource implements DataSource { actualUri = null; httpMethod = DataSpec.HTTP_METHOD_GET; httpBody = null; + httpRequestHeaders = Collections.emptyMap(); + flags = 0; + readPosition = 0; + key = null; notifyBytesRead(); try { closeCurrentSource(); @@ -399,7 +405,15 @@ public final class CacheDataSource implements DataSource { nextDataSource = upstreamDataSource; nextDataSpec = new DataSpec( - uri, httpMethod, httpBody, readPosition, readPosition, bytesRemaining, key, flags); + uri, + httpMethod, + httpBody, + readPosition, + readPosition, + bytesRemaining, + key, + flags, + httpRequestHeaders); } else if (nextSpan.isCached) { // Data is cached, read from cache. Uri fileUri = Uri.fromFile(nextSpan.file); @@ -408,6 +422,8 @@ public final class CacheDataSource implements DataSource { if (bytesRemaining != C.LENGTH_UNSET) { length = Math.min(length, bytesRemaining); } + // Deliberately skip the HTTP-related parameters since we're reading from the cache, not + // making an HTTP request. nextDataSpec = new DataSpec(fileUri, readPosition, filePosition, length, key, flags); nextDataSource = cacheReadDataSource; } else { @@ -422,7 +438,16 @@ public final class CacheDataSource implements DataSource { } } nextDataSpec = - new DataSpec(uri, httpMethod, httpBody, readPosition, readPosition, length, key, flags); + new DataSpec( + uri, + httpMethod, + httpBody, + readPosition, + readPosition, + length, + key, + flags, + httpRequestHeaders); if (cacheWriteDataSource != null) { nextDataSource = cacheWriteDataSource; } else { diff --git a/library/core/src/test/assets/mkv/sample.mkv.0.dump b/library/core/src/test/assets/mkv/sample.mkv.0.dump index 847799396d..d91f845199 100644 --- a/library/core/src/test/assets/mkv/sample.mkv.0.dump +++ b/library/core/src/test/assets/mkv/sample.mkv.0.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = 1072000 + duration = 1104000 getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: diff --git a/library/core/src/test/assets/mkv/sample.mkv.1.dump b/library/core/src/test/assets/mkv/sample.mkv.1.dump index 5caa638437..d9013a762e 100644 --- a/library/core/src/test/assets/mkv/sample.mkv.1.dump +++ b/library/core/src/test/assets/mkv/sample.mkv.1.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = 1072000 + duration = 1104000 getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: @@ -27,93 +27,85 @@ track 1: initializationData: data = length 30, hash F6F3D010 data = length 10, hash 7A0D0F2B - total output bytes = 30995 - sample count = 22 + total output bytes = 29422 + sample count = 20 sample 0: - time = 334000 - flags = 0 - data = length 953, hash 7160C661 - sample 1: - time = 300000 - flags = 0 - data = length 620, hash 7A7AE07C - sample 2: time = 367000 flags = 0 data = length 405, hash 5CC7F4E7 - sample 3: + sample 1: time = 500000 flags = 0 data = length 4852, hash 9DB6979D - sample 4: + sample 2: time = 467000 flags = 0 data = length 547, hash E31A6979 - sample 5: + sample 3: time = 434000 flags = 0 data = length 570, hash FEC40D00 - sample 6: + sample 4: time = 634000 flags = 0 data = length 5525, hash 7C478F7E - sample 7: + sample 5: time = 567000 flags = 0 data = length 1082, hash DA07059A - sample 8: + sample 6: time = 534000 flags = 0 data = length 807, hash 93478E6B - sample 9: + sample 7: time = 600000 flags = 0 data = length 744, hash 9A8E6026 - sample 10: + sample 8: time = 767000 flags = 0 data = length 4732, hash C73B23C0 - sample 11: + sample 9: time = 700000 flags = 0 data = length 1004, hash 8A19A228 - sample 12: + sample 10: time = 667000 flags = 0 data = length 794, hash 8126022C - sample 13: + sample 11: time = 734000 flags = 0 data = length 645, hash F08300E5 - sample 14: + sample 12: time = 900000 flags = 0 data = length 2684, hash 727FE378 - sample 15: + sample 13: time = 834000 flags = 0 data = length 787, hash 419A7821 - sample 16: + sample 14: time = 800000 flags = 0 data = length 649, hash 5C159346 - sample 17: + sample 15: time = 867000 flags = 0 data = length 509, hash F912D655 - sample 18: + sample 16: time = 1034000 flags = 0 data = length 1226, hash 29815C21 - sample 19: + sample 17: time = 967000 flags = 0 data = length 898, hash D997AD0A - sample 20: + sample 18: time = 934000 flags = 0 data = length 476, hash A0423645 - sample 21: + sample 19: time = 1000000 flags = 0 data = length 486, hash DDF32CBB diff --git a/library/core/src/test/assets/mkv/sample.mkv.2.dump b/library/core/src/test/assets/mkv/sample.mkv.2.dump index de4e2a58bf..b0f943e2f2 100644 --- a/library/core/src/test/assets/mkv/sample.mkv.2.dump +++ b/library/core/src/test/assets/mkv/sample.mkv.2.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = 1072000 + duration = 1104000 getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: @@ -27,49 +27,41 @@ track 1: initializationData: data = length 30, hash F6F3D010 data = length 10, hash 7A0D0F2B - total output bytes = 10158 - sample count = 11 + total output bytes = 8360 + sample count = 9 sample 0: - time = 700000 - flags = 0 - data = length 1004, hash 8A19A228 - sample 1: - time = 667000 - flags = 0 - data = length 794, hash 8126022C - sample 2: time = 734000 flags = 0 data = length 645, hash F08300E5 - sample 3: + sample 1: time = 900000 flags = 0 data = length 2684, hash 727FE378 - sample 4: + sample 2: time = 834000 flags = 0 data = length 787, hash 419A7821 - sample 5: + sample 3: time = 800000 flags = 0 data = length 649, hash 5C159346 - sample 6: + sample 4: time = 867000 flags = 0 data = length 509, hash F912D655 - sample 7: + sample 5: time = 1034000 flags = 0 data = length 1226, hash 29815C21 - sample 8: + sample 6: time = 967000 flags = 0 data = length 898, hash D997AD0A - sample 9: + sample 7: time = 934000 flags = 0 data = length 476, hash A0423645 - sample 10: + sample 8: time = 1000000 flags = 0 data = length 486, hash DDF32CBB diff --git a/library/core/src/test/assets/mkv/sample.mkv.3.dump b/library/core/src/test/assets/mkv/sample.mkv.3.dump index 6034c54dec..460aca0e90 100644 --- a/library/core/src/test/assets/mkv/sample.mkv.3.dump +++ b/library/core/src/test/assets/mkv/sample.mkv.3.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = 1072000 + duration = 1104000 getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: diff --git a/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4 b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4 new file mode 100644 index 0000000000..f50d4f49de Binary files /dev/null and b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4 differ diff --git a/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.0.dump b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.0.dump new file mode 100644 index 0000000000..4bc30a8bef --- /dev/null +++ b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.0.dump @@ -0,0 +1,357 @@ +seekMap: + isSeekable = true + duration = 1024000 + getPosition(0) = [[timeUs=0, position=2192]] +numberOfTracks = 2 +track 0: + format: + bitrate = -1 + id = 1 + containerMimeType = null + sampleMimeType = video/avc + maxInputSize = 36722 + width = 1080 + height = 720 + frameRate = 29.970028 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = -1 + sampleRate = -1 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B + total output bytes = 89876 + sample count = 30 + sample 0: + time = 0 + flags = 1 + data = length 36692, hash D216076E + sample 1: + time = 66733 + flags = 0 + data = length 5312, hash D45D3CA0 + sample 2: + time = 33366 + flags = 0 + data = length 599, hash 1BE7812D + sample 3: + time = 200200 + flags = 0 + data = length 7735, hash 4490F110 + sample 4: + time = 133466 + flags = 0 + data = length 987, hash 560B5036 + sample 5: + time = 100100 + flags = 0 + data = length 673, hash ED7CD8C7 + sample 6: + time = 166833 + flags = 0 + data = length 523, hash 3020DF50 + sample 7: + time = 333666 + flags = 0 + data = length 6061, hash 736C72B2 + sample 8: + time = 266933 + flags = 0 + data = length 992, hash FE132F23 + sample 9: + time = 233566 + flags = 0 + data = length 623, hash 5B2C1816 + sample 10: + time = 300300 + flags = 0 + data = length 421, hash 742E69C1 + sample 11: + time = 433766 + flags = 0 + data = length 4899, hash F72F86A1 + sample 12: + time = 400400 + flags = 0 + data = length 568, hash 519A8E50 + sample 13: + time = 367033 + flags = 0 + data = length 620, hash 3990AA39 + sample 14: + time = 567233 + flags = 0 + data = length 5450, hash F06EC4AA + sample 15: + time = 500500 + flags = 0 + data = length 1051, hash 92DFA63A + sample 16: + time = 467133 + flags = 0 + data = length 874, hash 69587FB4 + sample 17: + time = 533866 + flags = 0 + data = length 781, hash 36BE495B + sample 18: + time = 700700 + flags = 0 + data = length 4725, hash AC0C8CD3 + sample 19: + time = 633966 + flags = 0 + data = length 1022, hash 5D8BFF34 + sample 20: + time = 600600 + flags = 0 + data = length 790, hash 99413A99 + sample 21: + time = 667333 + flags = 0 + data = length 610, hash 5E129290 + sample 22: + time = 834166 + flags = 0 + data = length 2751, hash 769974CB + sample 23: + time = 767433 + flags = 0 + data = length 745, hash B78A477A + sample 24: + time = 734066 + flags = 0 + data = length 621, hash CF741E7A + sample 25: + time = 800800 + flags = 0 + data = length 505, hash 1DB4894E + sample 26: + time = 967633 + flags = 0 + data = length 1268, hash C15348DC + sample 27: + time = 900900 + flags = 0 + data = length 880, hash C2DE85D0 + sample 28: + time = 867533 + flags = 0 + data = length 530, hash C98BC6A8 + sample 29: + time = 934266 + flags = 536870912 + data = length 568, hash 4FE5C8EA +track 1: + format: + bitrate = -1 + id = 2 + containerMimeType = null + sampleMimeType = audio/mp4a-latm + maxInputSize = 294 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 1 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = und + drmInitData = - + initializationData: + data = length 2, hash 5F7 + total output bytes = 9529 + sample count = 45 + sample 0: + time = 44000 + flags = 1 + data = length 23, hash 47DE9131 + sample 1: + time = 67219 + flags = 1 + data = length 6, hash 31EC5206 + sample 2: + time = 90439 + flags = 1 + data = length 148, hash 894A176B + sample 3: + time = 113659 + flags = 1 + data = length 189, hash CEF235A1 + sample 4: + time = 136879 + flags = 1 + data = length 205, hash BBF5F7B0 + sample 5: + time = 160099 + flags = 1 + data = length 210, hash F278B193 + sample 6: + time = 183319 + flags = 1 + data = length 210, hash 82DA1589 + sample 7: + time = 206539 + flags = 1 + data = length 207, hash 5BE231DF + sample 8: + time = 229759 + flags = 1 + data = length 225, hash 18819EE1 + sample 9: + time = 252979 + flags = 1 + data = length 215, hash CA7FA67B + sample 10: + time = 276199 + flags = 1 + data = length 211, hash 581A1C18 + sample 11: + time = 299419 + flags = 1 + data = length 216, hash ADB88187 + sample 12: + time = 322639 + flags = 1 + data = length 229, hash 2E8BA4DC + sample 13: + time = 345859 + flags = 1 + data = length 232, hash 22F0C510 + sample 14: + time = 369079 + flags = 1 + data = length 235, hash 867AD0DC + sample 15: + time = 392299 + flags = 1 + data = length 231, hash 84E823A8 + sample 16: + time = 415519 + flags = 1 + data = length 226, hash 1BEF3A95 + sample 17: + time = 438739 + flags = 1 + data = length 216, hash EAA345AE + sample 18: + time = 461959 + flags = 1 + data = length 229, hash 6957411F + sample 19: + time = 485179 + flags = 1 + data = length 219, hash 41275022 + sample 20: + time = 508399 + flags = 1 + data = length 241, hash 6495DF96 + sample 21: + time = 531619 + flags = 1 + data = length 228, hash 63D95906 + sample 22: + time = 554839 + flags = 1 + data = length 238, hash 34F676F9 + sample 23: + time = 578058 + flags = 1 + data = length 234, hash E5CBC045 + sample 24: + time = 601278 + flags = 1 + data = length 231, hash 5FC43661 + sample 25: + time = 624498 + flags = 1 + data = length 217, hash 682708ED + sample 26: + time = 647718 + flags = 1 + data = length 239, hash D43780FC + sample 27: + time = 670938 + flags = 1 + data = length 243, hash C5E17980 + sample 28: + time = 694158 + flags = 1 + data = length 231, hash AC5837BA + sample 29: + time = 717378 + flags = 1 + data = length 230, hash 169EE895 + sample 30: + time = 740598 + flags = 1 + data = length 238, hash C48FF3F1 + sample 31: + time = 763818 + flags = 1 + data = length 225, hash 531E4599 + sample 32: + time = 787038 + flags = 1 + data = length 232, hash CB3C6B8D + sample 33: + time = 810258 + flags = 1 + data = length 243, hash F8C94C7 + sample 34: + time = 833478 + flags = 1 + data = length 232, hash A646A7D0 + sample 35: + time = 856698 + flags = 1 + data = length 237, hash E8B787A5 + sample 36: + time = 879918 + flags = 1 + data = length 228, hash 3FA7A29F + sample 37: + time = 903138 + flags = 1 + data = length 235, hash B9B33B0A + sample 38: + time = 926358 + flags = 1 + data = length 264, hash 71A4869E + sample 39: + time = 949578 + flags = 1 + data = length 257, hash D049B54C + sample 40: + time = 972798 + flags = 1 + data = length 227, hash 66757231 + sample 41: + time = 996018 + flags = 1 + data = length 227, hash BD374F1B + sample 42: + time = 1019238 + flags = 1 + data = length 235, hash 999477F6 + sample 43: + time = 1042458 + flags = 1 + data = length 229, hash FFF98DF0 + sample 44: + time = 1065678 + flags = 536870913 + data = length 6, hash 31B22286 +tracksEnded = true diff --git a/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.1.dump b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.1.dump new file mode 100644 index 0000000000..3b4906f063 --- /dev/null +++ b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.1.dump @@ -0,0 +1,309 @@ +seekMap: + isSeekable = true + duration = 1024000 + getPosition(0) = [[timeUs=0, position=2192]] +numberOfTracks = 2 +track 0: + format: + bitrate = -1 + id = 1 + containerMimeType = null + sampleMimeType = video/avc + maxInputSize = 36722 + width = 1080 + height = 720 + frameRate = 29.970028 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = -1 + sampleRate = -1 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B + total output bytes = 89876 + sample count = 30 + sample 0: + time = 0 + flags = 1 + data = length 36692, hash D216076E + sample 1: + time = 66733 + flags = 0 + data = length 5312, hash D45D3CA0 + sample 2: + time = 33366 + flags = 0 + data = length 599, hash 1BE7812D + sample 3: + time = 200200 + flags = 0 + data = length 7735, hash 4490F110 + sample 4: + time = 133466 + flags = 0 + data = length 987, hash 560B5036 + sample 5: + time = 100100 + flags = 0 + data = length 673, hash ED7CD8C7 + sample 6: + time = 166833 + flags = 0 + data = length 523, hash 3020DF50 + sample 7: + time = 333666 + flags = 0 + data = length 6061, hash 736C72B2 + sample 8: + time = 266933 + flags = 0 + data = length 992, hash FE132F23 + sample 9: + time = 233566 + flags = 0 + data = length 623, hash 5B2C1816 + sample 10: + time = 300300 + flags = 0 + data = length 421, hash 742E69C1 + sample 11: + time = 433766 + flags = 0 + data = length 4899, hash F72F86A1 + sample 12: + time = 400400 + flags = 0 + data = length 568, hash 519A8E50 + sample 13: + time = 367033 + flags = 0 + data = length 620, hash 3990AA39 + sample 14: + time = 567233 + flags = 0 + data = length 5450, hash F06EC4AA + sample 15: + time = 500500 + flags = 0 + data = length 1051, hash 92DFA63A + sample 16: + time = 467133 + flags = 0 + data = length 874, hash 69587FB4 + sample 17: + time = 533866 + flags = 0 + data = length 781, hash 36BE495B + sample 18: + time = 700700 + flags = 0 + data = length 4725, hash AC0C8CD3 + sample 19: + time = 633966 + flags = 0 + data = length 1022, hash 5D8BFF34 + sample 20: + time = 600600 + flags = 0 + data = length 790, hash 99413A99 + sample 21: + time = 667333 + flags = 0 + data = length 610, hash 5E129290 + sample 22: + time = 834166 + flags = 0 + data = length 2751, hash 769974CB + sample 23: + time = 767433 + flags = 0 + data = length 745, hash B78A477A + sample 24: + time = 734066 + flags = 0 + data = length 621, hash CF741E7A + sample 25: + time = 800800 + flags = 0 + data = length 505, hash 1DB4894E + sample 26: + time = 967633 + flags = 0 + data = length 1268, hash C15348DC + sample 27: + time = 900900 + flags = 0 + data = length 880, hash C2DE85D0 + sample 28: + time = 867533 + flags = 0 + data = length 530, hash C98BC6A8 + sample 29: + time = 934266 + flags = 536870912 + data = length 568, hash 4FE5C8EA +track 1: + format: + bitrate = -1 + id = 2 + containerMimeType = null + sampleMimeType = audio/mp4a-latm + maxInputSize = 294 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 1 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = und + drmInitData = - + initializationData: + data = length 2, hash 5F7 + total output bytes = 7464 + sample count = 33 + sample 0: + time = 322639 + flags = 1 + data = length 229, hash 2E8BA4DC + sample 1: + time = 345859 + flags = 1 + data = length 232, hash 22F0C510 + sample 2: + time = 369079 + flags = 1 + data = length 235, hash 867AD0DC + sample 3: + time = 392299 + flags = 1 + data = length 231, hash 84E823A8 + sample 4: + time = 415519 + flags = 1 + data = length 226, hash 1BEF3A95 + sample 5: + time = 438739 + flags = 1 + data = length 216, hash EAA345AE + sample 6: + time = 461959 + flags = 1 + data = length 229, hash 6957411F + sample 7: + time = 485179 + flags = 1 + data = length 219, hash 41275022 + sample 8: + time = 508399 + flags = 1 + data = length 241, hash 6495DF96 + sample 9: + time = 531619 + flags = 1 + data = length 228, hash 63D95906 + sample 10: + time = 554839 + flags = 1 + data = length 238, hash 34F676F9 + sample 11: + time = 578058 + flags = 1 + data = length 234, hash E5CBC045 + sample 12: + time = 601278 + flags = 1 + data = length 231, hash 5FC43661 + sample 13: + time = 624498 + flags = 1 + data = length 217, hash 682708ED + sample 14: + time = 647718 + flags = 1 + data = length 239, hash D43780FC + sample 15: + time = 670938 + flags = 1 + data = length 243, hash C5E17980 + sample 16: + time = 694158 + flags = 1 + data = length 231, hash AC5837BA + sample 17: + time = 717378 + flags = 1 + data = length 230, hash 169EE895 + sample 18: + time = 740598 + flags = 1 + data = length 238, hash C48FF3F1 + sample 19: + time = 763818 + flags = 1 + data = length 225, hash 531E4599 + sample 20: + time = 787038 + flags = 1 + data = length 232, hash CB3C6B8D + sample 21: + time = 810258 + flags = 1 + data = length 243, hash F8C94C7 + sample 22: + time = 833478 + flags = 1 + data = length 232, hash A646A7D0 + sample 23: + time = 856698 + flags = 1 + data = length 237, hash E8B787A5 + sample 24: + time = 879918 + flags = 1 + data = length 228, hash 3FA7A29F + sample 25: + time = 903138 + flags = 1 + data = length 235, hash B9B33B0A + sample 26: + time = 926358 + flags = 1 + data = length 264, hash 71A4869E + sample 27: + time = 949578 + flags = 1 + data = length 257, hash D049B54C + sample 28: + time = 972798 + flags = 1 + data = length 227, hash 66757231 + sample 29: + time = 996018 + flags = 1 + data = length 227, hash BD374F1B + sample 30: + time = 1019238 + flags = 1 + data = length 235, hash 999477F6 + sample 31: + time = 1042458 + flags = 1 + data = length 229, hash FFF98DF0 + sample 32: + time = 1065678 + flags = 536870913 + data = length 6, hash 31B22286 +tracksEnded = true diff --git a/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.2.dump b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.2.dump new file mode 100644 index 0000000000..b4db32c20c --- /dev/null +++ b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.2.dump @@ -0,0 +1,249 @@ +seekMap: + isSeekable = true + duration = 1024000 + getPosition(0) = [[timeUs=0, position=2192]] +numberOfTracks = 2 +track 0: + format: + bitrate = -1 + id = 1 + containerMimeType = null + sampleMimeType = video/avc + maxInputSize = 36722 + width = 1080 + height = 720 + frameRate = 29.970028 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = -1 + sampleRate = -1 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B + total output bytes = 89876 + sample count = 30 + sample 0: + time = 0 + flags = 1 + data = length 36692, hash D216076E + sample 1: + time = 66733 + flags = 0 + data = length 5312, hash D45D3CA0 + sample 2: + time = 33366 + flags = 0 + data = length 599, hash 1BE7812D + sample 3: + time = 200200 + flags = 0 + data = length 7735, hash 4490F110 + sample 4: + time = 133466 + flags = 0 + data = length 987, hash 560B5036 + sample 5: + time = 100100 + flags = 0 + data = length 673, hash ED7CD8C7 + sample 6: + time = 166833 + flags = 0 + data = length 523, hash 3020DF50 + sample 7: + time = 333666 + flags = 0 + data = length 6061, hash 736C72B2 + sample 8: + time = 266933 + flags = 0 + data = length 992, hash FE132F23 + sample 9: + time = 233566 + flags = 0 + data = length 623, hash 5B2C1816 + sample 10: + time = 300300 + flags = 0 + data = length 421, hash 742E69C1 + sample 11: + time = 433766 + flags = 0 + data = length 4899, hash F72F86A1 + sample 12: + time = 400400 + flags = 0 + data = length 568, hash 519A8E50 + sample 13: + time = 367033 + flags = 0 + data = length 620, hash 3990AA39 + sample 14: + time = 567233 + flags = 0 + data = length 5450, hash F06EC4AA + sample 15: + time = 500500 + flags = 0 + data = length 1051, hash 92DFA63A + sample 16: + time = 467133 + flags = 0 + data = length 874, hash 69587FB4 + sample 17: + time = 533866 + flags = 0 + data = length 781, hash 36BE495B + sample 18: + time = 700700 + flags = 0 + data = length 4725, hash AC0C8CD3 + sample 19: + time = 633966 + flags = 0 + data = length 1022, hash 5D8BFF34 + sample 20: + time = 600600 + flags = 0 + data = length 790, hash 99413A99 + sample 21: + time = 667333 + flags = 0 + data = length 610, hash 5E129290 + sample 22: + time = 834166 + flags = 0 + data = length 2751, hash 769974CB + sample 23: + time = 767433 + flags = 0 + data = length 745, hash B78A477A + sample 24: + time = 734066 + flags = 0 + data = length 621, hash CF741E7A + sample 25: + time = 800800 + flags = 0 + data = length 505, hash 1DB4894E + sample 26: + time = 967633 + flags = 0 + data = length 1268, hash C15348DC + sample 27: + time = 900900 + flags = 0 + data = length 880, hash C2DE85D0 + sample 28: + time = 867533 + flags = 0 + data = length 530, hash C98BC6A8 + sample 29: + time = 934266 + flags = 536870912 + data = length 568, hash 4FE5C8EA +track 1: + format: + bitrate = -1 + id = 2 + containerMimeType = null + sampleMimeType = audio/mp4a-latm + maxInputSize = 294 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 1 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = und + drmInitData = - + initializationData: + data = length 2, hash 5F7 + total output bytes = 4019 + sample count = 18 + sample 0: + time = 670938 + flags = 1 + data = length 243, hash C5E17980 + sample 1: + time = 694158 + flags = 1 + data = length 231, hash AC5837BA + sample 2: + time = 717378 + flags = 1 + data = length 230, hash 169EE895 + sample 3: + time = 740598 + flags = 1 + data = length 238, hash C48FF3F1 + sample 4: + time = 763818 + flags = 1 + data = length 225, hash 531E4599 + sample 5: + time = 787038 + flags = 1 + data = length 232, hash CB3C6B8D + sample 6: + time = 810258 + flags = 1 + data = length 243, hash F8C94C7 + sample 7: + time = 833478 + flags = 1 + data = length 232, hash A646A7D0 + sample 8: + time = 856698 + flags = 1 + data = length 237, hash E8B787A5 + sample 9: + time = 879918 + flags = 1 + data = length 228, hash 3FA7A29F + sample 10: + time = 903138 + flags = 1 + data = length 235, hash B9B33B0A + sample 11: + time = 926358 + flags = 1 + data = length 264, hash 71A4869E + sample 12: + time = 949578 + flags = 1 + data = length 257, hash D049B54C + sample 13: + time = 972798 + flags = 1 + data = length 227, hash 66757231 + sample 14: + time = 996018 + flags = 1 + data = length 227, hash BD374F1B + sample 15: + time = 1019238 + flags = 1 + data = length 235, hash 999477F6 + sample 16: + time = 1042458 + flags = 1 + data = length 229, hash FFF98DF0 + sample 17: + time = 1065678 + flags = 536870913 + data = length 6, hash 31B22286 +tracksEnded = true diff --git a/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.3.dump b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.3.dump new file mode 100644 index 0000000000..fd58d5042e --- /dev/null +++ b/library/core/src/test/assets/mp4/sample_mdat_too_long.mp4.3.dump @@ -0,0 +1,189 @@ +seekMap: + isSeekable = true + duration = 1024000 + getPosition(0) = [[timeUs=0, position=2192]] +numberOfTracks = 2 +track 0: + format: + bitrate = -1 + id = 1 + containerMimeType = null + sampleMimeType = video/avc + maxInputSize = 36722 + width = 1080 + height = 720 + frameRate = 29.970028 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = -1 + sampleRate = -1 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B + total output bytes = 89876 + sample count = 30 + sample 0: + time = 0 + flags = 1 + data = length 36692, hash D216076E + sample 1: + time = 66733 + flags = 0 + data = length 5312, hash D45D3CA0 + sample 2: + time = 33366 + flags = 0 + data = length 599, hash 1BE7812D + sample 3: + time = 200200 + flags = 0 + data = length 7735, hash 4490F110 + sample 4: + time = 133466 + flags = 0 + data = length 987, hash 560B5036 + sample 5: + time = 100100 + flags = 0 + data = length 673, hash ED7CD8C7 + sample 6: + time = 166833 + flags = 0 + data = length 523, hash 3020DF50 + sample 7: + time = 333666 + flags = 0 + data = length 6061, hash 736C72B2 + sample 8: + time = 266933 + flags = 0 + data = length 992, hash FE132F23 + sample 9: + time = 233566 + flags = 0 + data = length 623, hash 5B2C1816 + sample 10: + time = 300300 + flags = 0 + data = length 421, hash 742E69C1 + sample 11: + time = 433766 + flags = 0 + data = length 4899, hash F72F86A1 + sample 12: + time = 400400 + flags = 0 + data = length 568, hash 519A8E50 + sample 13: + time = 367033 + flags = 0 + data = length 620, hash 3990AA39 + sample 14: + time = 567233 + flags = 0 + data = length 5450, hash F06EC4AA + sample 15: + time = 500500 + flags = 0 + data = length 1051, hash 92DFA63A + sample 16: + time = 467133 + flags = 0 + data = length 874, hash 69587FB4 + sample 17: + time = 533866 + flags = 0 + data = length 781, hash 36BE495B + sample 18: + time = 700700 + flags = 0 + data = length 4725, hash AC0C8CD3 + sample 19: + time = 633966 + flags = 0 + data = length 1022, hash 5D8BFF34 + sample 20: + time = 600600 + flags = 0 + data = length 790, hash 99413A99 + sample 21: + time = 667333 + flags = 0 + data = length 610, hash 5E129290 + sample 22: + time = 834166 + flags = 0 + data = length 2751, hash 769974CB + sample 23: + time = 767433 + flags = 0 + data = length 745, hash B78A477A + sample 24: + time = 734066 + flags = 0 + data = length 621, hash CF741E7A + sample 25: + time = 800800 + flags = 0 + data = length 505, hash 1DB4894E + sample 26: + time = 967633 + flags = 0 + data = length 1268, hash C15348DC + sample 27: + time = 900900 + flags = 0 + data = length 880, hash C2DE85D0 + sample 28: + time = 867533 + flags = 0 + data = length 530, hash C98BC6A8 + sample 29: + time = 934266 + flags = 536870912 + data = length 568, hash 4FE5C8EA +track 1: + format: + bitrate = -1 + id = 2 + containerMimeType = null + sampleMimeType = audio/mp4a-latm + maxInputSize = 294 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 1 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = 0 + encoderPadding = 0 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = und + drmInitData = - + initializationData: + data = length 2, hash 5F7 + total output bytes = 470 + sample count = 3 + sample 0: + time = 1019238 + flags = 1 + data = length 235, hash 999477F6 + sample 1: + time = 1042458 + flags = 1 + data = length 229, hash FFF98DF0 + sample 2: + time = 1065678 + flags = 536870913 + data = length 6, hash 31B22286 +tracksEnded = true diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java index 981ee17e92..9c769f4127 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java @@ -28,4 +28,13 @@ public final class Mp4ExtractorTest { public void testMp4Sample() throws Exception { ExtractorAsserts.assertBehavior(Mp4Extractor::new, "mp4/sample.mp4"); } + + /** + * Test case for https://github.com/google/ExoPlayer/issues/6774. The sample file contains an mdat + * atom whose size indicates that it extends 8 bytes beyond the end of the file. + */ + @Test + public void testMp4SampleWithMdatTooLong() throws Exception { + ExtractorAsserts.assertBehavior(Mp4Extractor::new, "mp4/sample_mdat_too_long.mp4"); + } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java index f42a1c6086..d7664e21ca 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java @@ -248,6 +248,23 @@ public class DefaultDownloadIndexTest { assertEqual(readDownload, download); } + @Test + public void setStatesToRemoving_setsStateAndClearsFailureReason() throws Exception { + String id = "id"; + DownloadBuilder downloadBuilder = + new DownloadBuilder(id) + .setState(Download.STATE_FAILED) + .setFailureReason(Download.FAILURE_REASON_UNKNOWN); + Download download = downloadBuilder.build(); + downloadIndex.putDownload(download); + + downloadIndex.setStatesToRemoving(); + + download = downloadIndex.getDownload(id); + assertThat(download.state).isEqualTo(Download.STATE_REMOVING); + assertThat(download.failureReason).isEqualTo(Download.FAILURE_REASON_NONE); + } + @Test public void setSingleDownloadStopReason_setReasonToNone() throws Exception { String id = "id"; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java index 83104119ad..27438fcac3 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceTest.java @@ -34,6 +34,8 @@ import com.google.android.exoplayer2.util.Util; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.NavigableSet; import org.junit.After; import org.junit.Before; @@ -48,20 +50,27 @@ public final class CacheDataSourceTest { private static final int CACHE_FRAGMENT_SIZE = 3; private static final String DATASPEC_KEY = "dataSpecKey"; + // Test data private Uri testDataUri; + private Map httpRequestHeaders; private DataSpec unboundedDataSpec; private DataSpec boundedDataSpec; private DataSpec unboundedDataSpecWithKey; private DataSpec boundedDataSpecWithKey; private String defaultCacheKey; private String customCacheKey; + + // Dependencies of SUT private CacheKeyFactory cacheKeyFactory; private File tempFolder; private SimpleCache cache; + private FakeDataSource upstreamDataSource; @Before public void setUp() throws Exception { testDataUri = Uri.parse("https://www.test.com/data"); + httpRequestHeaders = new HashMap<>(); + httpRequestHeaders.put("Test-key", "Test-val"); unboundedDataSpec = buildDataSpec(/* unbounded= */ true, /* key= */ null); boundedDataSpec = buildDataSpec(/* unbounded= */ false, /* key= */ null); unboundedDataSpecWithKey = buildDataSpec(/* unbounded= */ true, DATASPEC_KEY); @@ -69,9 +78,11 @@ public final class CacheDataSourceTest { defaultCacheKey = CacheUtil.DEFAULT_CACHE_KEY_FACTORY.buildCacheKey(unboundedDataSpec); customCacheKey = "customKey." + defaultCacheKey; cacheKeyFactory = dataSpec -> customCacheKey; + tempFolder = Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest"); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); + upstreamDataSource = new FakeDataSource(); } @After @@ -111,6 +122,19 @@ public final class CacheDataSourceTest { assertCacheAndRead(boundedDataSpec, /* unknownLength= */ false); } + @Test + public void testPropagatesHttpHeadersUpstream() throws Exception { + CacheDataSource cacheDataSource = + createCacheDataSource(/* setReadException= */ false, /* unknownLength= */ false); + DataSpec dataSpec = buildDataSpec(/* position= */ 2, /* length= */ 5); + cacheDataSource.open(dataSpec); + + DataSpec[] upstreamDataSpecs = upstreamDataSource.getAndClearOpenedDataSpecs(); + + assertThat(upstreamDataSpecs).hasLength(1); + assertThat(upstreamDataSpecs[0].httpRequestHeaders).isEqualTo(this.httpRequestHeaders); + } + @Test public void testUnsatisfiableRange() throws Exception { // Bounded request but the content length is unknown. This forces all data to be cached but not @@ -572,9 +596,8 @@ public final class CacheDataSourceTest { @CacheDataSource.Flags int flags, CacheDataSink cacheWriteDataSink, CacheKeyFactory cacheKeyFactory) { - FakeDataSource upstream = new FakeDataSource(); FakeData fakeData = - upstream + upstreamDataSource .getDataSet() .newDefaultData() .setSimulateUnknownLength(unknownLength) @@ -584,7 +607,7 @@ public final class CacheDataSourceTest { } return new CacheDataSource( cache, - upstream, + upstreamDataSource, new FileDataSource(), cacheWriteDataSink, flags, @@ -602,6 +625,11 @@ public final class CacheDataSourceTest { private DataSpec buildDataSpec(long position, long length, @Nullable String key) { return new DataSpec( - testDataUri, position, length, key, DataSpec.FLAG_ALLOW_CACHE_FRAGMENTATION); + testDataUri, + position, + length, + key, + DataSpec.FLAG_ALLOW_CACHE_FRAGMENTATION, + httpRequestHeaders); } } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index 352131d70a..3f179d0e7e 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -1010,8 +1010,13 @@ public final class DashMediaSource extends BaseMediaSource { windowDurationUs / 2); } } - long windowStartTimeMs = manifest.availabilityStartTimeMs - + manifest.getPeriod(0).startMs + C.usToMs(currentStartTimeUs); + long windowStartTimeMs = C.TIME_UNSET; + if (manifest.availabilityStartTimeMs != C.TIME_UNSET) { + windowStartTimeMs = + manifest.availabilityStartTimeMs + + manifest.getPeriod(0).startMs + + C.usToMs(currentStartTimeUs); + } DashTimeline timeline = new DashTimeline( manifest.availabilityStartTimeMs, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index f74d9b0b0c..3b723af435 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -75,7 +75,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper private final TimestampAdjusterProvider timestampAdjusterProvider; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final boolean allowChunklessPreparation; - private final @HlsMetadataType int metadataType; + private final @HlsMediaSource.MetadataType int metadataType; private final boolean useSessionKeys; @Nullable private Callback callback; @@ -118,7 +118,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper Allocator allocator, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, boolean allowChunklessPreparation, - @HlsMetadataType int metadataType, + @HlsMediaSource.MetadataType int metadataType, boolean useSessionKeys) { this.extractorFactory = extractorFactory; this.playlistTracker = playlistTracker; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 4f6a0405f2..db52fa1c02 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -15,8 +15,11 @@ */ package com.google.android.exoplayer2.source.hls; +import static java.lang.annotation.RetentionPolicy.SOURCE; + import android.net.Uri; import android.os.Handler; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayerLibraryInfo; @@ -47,6 +50,8 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; import java.util.List; /** An HLS {@link MediaSource}. */ @@ -57,6 +62,28 @@ public final class HlsMediaSource extends BaseMediaSource ExoPlayerLibraryInfo.registerModule("goog.exo.hls"); } + /** + * The types of metadata that can be extracted from HLS streams. + * + *

Allowed values: + * + *

    + *
  • {@link #METADATA_TYPE_ID3} + *
  • {@link #METADATA_TYPE_EMSG} + *
+ * + *

See {@link Factory#setMetadataType(int)}. + */ + @Documented + @Retention(SOURCE) + @IntDef({METADATA_TYPE_ID3, METADATA_TYPE_EMSG}) + public @interface MetadataType {} + + /** Type for ID3 metadata in HLS streams. */ + public static final int METADATA_TYPE_ID3 = 1; + /** Type for ESMG metadata in HLS streams. */ + public static final int METADATA_TYPE_EMSG = 3; + /** Factory for {@link HlsMediaSource}s. */ public static final class Factory implements MediaSourceFactory { @@ -70,7 +97,7 @@ public final class HlsMediaSource extends BaseMediaSource private DrmSessionManager drmSessionManager; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private boolean allowChunklessPreparation; - @HlsMetadataType private int metadataType; + @MetadataType private int metadataType; private boolean useSessionKeys; private boolean isCreateCalled; @Nullable private Object tag; @@ -100,7 +127,7 @@ public final class HlsMediaSource extends BaseMediaSource drmSessionManager = DrmSessionManager.getDummyDrmSessionManager(); loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy(); compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); - metadataType = HlsMetadataType.ID3; + metadataType = METADATA_TYPE_ID3; } /** @@ -246,24 +273,24 @@ public final class HlsMediaSource extends BaseMediaSource /** * Sets the type of metadata to extract from the HLS source (defaults to {@link - * HlsMetadataType#ID3}). + * #METADATA_TYPE_ID3}). * *

HLS supports in-band ID3 in both TS and fMP4 streams, but in the fMP4 case the data is * wrapped in an EMSG box [spec]. * - *

If this is set to {@link HlsMetadataType#ID3} then raw ID3 metadata of will be extracted + *

If this is set to {@link #METADATA_TYPE_ID3} then raw ID3 metadata of will be extracted * from TS sources. From fMP4 streams EMSGs containing metadata of this type (in the variant * stream only) will be unwrapped to expose the inner data. All other in-band metadata will be * dropped. * - *

If this is set to {@link HlsMetadataType#EMSG} then all EMSG data from the fMP4 variant + *

If this is set to {@link #METADATA_TYPE_EMSG} then all EMSG data from the fMP4 variant * stream will be extracted. No metadata will be extracted from TS streams, since they don't * support EMSG. * * @param metadataType The type of metadata to extract. * @return This factory, for convenience. */ - public Factory setMetadataType(@HlsMetadataType int metadataType) { + public Factory setMetadataType(@MetadataType int metadataType) { Assertions.checkState(!isCreateCalled); this.metadataType = metadataType; return this; @@ -347,7 +374,7 @@ public final class HlsMediaSource extends BaseMediaSource private final DrmSessionManager drmSessionManager; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final boolean allowChunklessPreparation; - private final @HlsMetadataType int metadataType; + private final @MetadataType int metadataType; private final boolean useSessionKeys; private final HlsPlaylistTracker playlistTracker; @Nullable private final Object tag; @@ -363,7 +390,7 @@ public final class HlsMediaSource extends BaseMediaSource LoadErrorHandlingPolicy loadErrorHandlingPolicy, HlsPlaylistTracker playlistTracker, boolean allowChunklessPreparation, - @HlsMetadataType int metadataType, + @MetadataType int metadataType, boolean useSessionKeys, @Nullable Object tag) { this.manifestUri = manifestUri; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMetadataType.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMetadataType.java deleted file mode 100644 index 8fb6c220cf..0000000000 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMetadataType.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.google.android.exoplayer2.source.hls; - -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import androidx.annotation.IntDef; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -/** - * The types of metadata that can be extracted from HLS streams. - * - *

See {@link HlsMediaSource.Factory#setMetadataType(int)}. - */ -@Documented -@Retention(SOURCE) -@IntDef({HlsMetadataType.ID3, HlsMetadataType.EMSG}) -public @interface HlsMetadataType { - /** Type for ID3 metadata in HLS streams. */ - int ID3 = 1; - /** Type for ESMG metadata in HLS streams. */ - int EMSG = 3; -} diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 18465bcaf7..77242ea0fa 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -116,7 +116,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final Loader loader; private final EventDispatcher eventDispatcher; - private final @HlsMetadataType int metadataType; + private final @HlsMediaSource.MetadataType int metadataType; private final HlsChunkSource.HlsChunkHolder nextChunkHolder; private final ArrayList mediaChunks; private final List readOnlyMediaChunks; @@ -190,7 +190,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; DrmSessionManager drmSessionManager, LoadErrorHandlingPolicy loadErrorHandlingPolicy, EventDispatcher eventDispatcher, - @HlsMetadataType int metadataType) { + @HlsMediaSource.MetadataType int metadataType) { this.trackType = trackType; this.callback = callback; this.chunkSource = chunkSource; @@ -1362,14 +1362,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private byte[] buffer; private int bufferPosition; - public EmsgUnwrappingTrackOutput(TrackOutput delegate, @HlsMetadataType int metadataType) { + public EmsgUnwrappingTrackOutput( + TrackOutput delegate, @HlsMediaSource.MetadataType int metadataType) { this.emsgDecoder = new EventMessageDecoder(); this.delegate = delegate; switch (metadataType) { - case HlsMetadataType.ID3: + case HlsMediaSource.METADATA_TYPE_ID3: delegateFormat = ID3_FORMAT; break; - case HlsMetadataType.EMSG: + case HlsMediaSource.METADATA_TYPE_EMSG: delegateFormat = EMSG_FORMAT; break; default: diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java index 73ef11bda9..820c39c197 100644 --- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java @@ -92,7 +92,7 @@ public final class HlsMediaPeriodTest { mock(Allocator.class), mock(CompositeSequenceableLoaderFactory.class), /* allowChunklessPreparation =*/ true, - HlsMetadataType.ID3, + HlsMediaSource.METADATA_TYPE_ID3, /* useSessionKeys= */ false); }; diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index 8b737bc006..89bcaf84bc 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -36,12 +36,15 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import androidx.annotation.ColorInt; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; +import java.util.Collections; import java.util.Formatter; import java.util.Locale; import java.util.concurrent.CopyOnWriteArraySet; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * A time bar that shows a current position, buffered position, duration and ad markers. @@ -199,6 +202,7 @@ public class DefaultTimeBar extends View implements TimeBar { private int keyCountIncrement; private long keyTimeIncrement; private int lastCoarseScrubXPosition; + @MonotonicNonNull private Rect lastExclusionRectangle; private boolean scrubbing; private long scrubPosition; @@ -604,6 +608,9 @@ public class DefaultTimeBar extends View implements TimeBar { seekBounds.set(seekLeft, barY, seekRight, barY + touchTargetHeight); progressBar.set(seekBounds.left + scrubberPadding, progressY, seekBounds.right - scrubberPadding, progressY + barHeight); + if (Util.SDK_INT >= 29) { + setSystemGestureExclusionRectsV29(width, height); + } update(); } @@ -834,6 +841,18 @@ public class DefaultTimeBar extends View implements TimeBar { } } + @RequiresApi(29) + private void setSystemGestureExclusionRectsV29(int width, int height) { + if (lastExclusionRectangle != null + && lastExclusionRectangle.width() == width + && lastExclusionRectangle.height() == height) { + // Allocating inside onLayout is considered a DrawAllocation lint error, so avoid if possible. + return; + } + lastExclusionRectangle = new Rect(/* left= */ 0, /* top= */ 0, width, height); + setSystemGestureExclusionRects(Collections.singletonList(lastExclusionRectangle)); + } + private String getProgressText() { return Util.getStringForTime(formatBuilder, formatter, position); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java index 4022a0ccc1..0502707682 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.util.SparseArray; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.SeekMap; import java.io.File; @@ -67,6 +68,19 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab @Override public void seekMap(SeekMap seekMap) { + if (seekMap.isSeekable()) { + SeekMap.SeekPoints seekPoints = seekMap.getSeekPoints(0); + if (!seekPoints.first.equals(seekPoints.second)) { + throw new IllegalStateException("SeekMap defines two seek points for t=0"); + } + long durationUs = seekMap.getDurationUs(); + if (durationUs != C.TIME_UNSET) { + seekPoints = seekMap.getSeekPoints(durationUs); + if (!seekPoints.first.equals(seekPoints.second)) { + throw new IllegalStateException("SeekMap defines two seek points for t=durationUs"); + } + } + } this.seekMap = seekMap; } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java index 4dd00557ae..f78e835b48 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java @@ -93,6 +93,12 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable { @Override public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, CryptoData cryptoData) { + if (format == null) { + throw new IllegalStateException("TrackOutput must receive format before sampleMetadata"); + } + if (format.maxInputSize != Format.NO_VALUE && size > format.maxInputSize) { + throw new IllegalStateException("Sample size exceeds Format.maxInputSize"); + } sampleTimesUs.add(timeUs); sampleFlags.add(flags); sampleStartOffsets.add(sampleData.length - offset - size);