From 103bd4199c2e08d918aeefd6a5dfac7b937002ce Mon Sep 17 00:00:00 2001 From: kimvde Date: Tue, 28 Jan 2020 09:48:00 +0000 Subject: [PATCH] Add IndexSeeker to MP3 extractor This seeker is only seeking to frames that have already been read by the extractor for playback. Seeking to not-yet-read frames will be implemented in another change. Issue: #6787 PiperOrigin-RevId: 291888899 --- .../exoplayer2/extractor/mp3/IndexSeeker.java | 95 ++++ .../extractor/mp3/Mp3Extractor.java | 51 +- .../src/test/assets/mp3/bear-vbr.mp3 | Bin 0 -> 38397 bytes .../src/test/assets/mp3/bear-vbr.mp3.0.dump | 499 ++++++++++++++++++ .../src/test/assets/mp3/bear-vbr.mp3.1.dump | 359 +++++++++++++ .../src/test/assets/mp3/bear-vbr.mp3.2.dump | 199 +++++++ .../src/test/assets/mp3/bear-vbr.mp3.3.dump | 39 ++ .../extractor/mp3/Mp3ExtractorTest.java | 6 + 8 files changed, 1241 insertions(+), 7 deletions(-) create mode 100644 library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java create mode 100644 library/extractor/src/test/assets/mp3/bear-vbr.mp3 create mode 100644 library/extractor/src/test/assets/mp3/bear-vbr.mp3.0.dump create mode 100644 library/extractor/src/test/assets/mp3/bear-vbr.mp3.1.dump create mode 100644 library/extractor/src/test/assets/mp3/bear-vbr.mp3.2.dump create mode 100644 library/extractor/src/test/assets/mp3/bear-vbr.mp3.3.dump diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java new file mode 100644 index 0000000000..102716b3f6 --- /dev/null +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 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.extractor.mp3; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.extractor.SeekPoint; +import com.google.android.exoplayer2.util.LongArray; +import com.google.android.exoplayer2.util.Util; + +/** MP3 seeker that builds a time-to-byte mapping as the stream is read. */ +/* package */ final class IndexSeeker implements Seeker { + + private static final long MIN_TIME_BETWEEN_POINTS_US = C.MICROS_PER_SECOND / 10; + + private final long durationUs; + private final long dataEndPosition; + private final LongArray timesUs; + private final LongArray positions; + + public IndexSeeker(long durationUs, long dataStartPosition, long dataEndPosition) { + this.durationUs = durationUs; + this.dataEndPosition = dataEndPosition; + timesUs = new LongArray(); + positions = new LongArray(); + timesUs.add(0L); + positions.add(dataStartPosition); + } + + @Override + public long getTimeUs(long position) { + int targetIndex = + Util.binarySearchFloor( + positions, position, /* inclusive= */ true, /* stayInBounds= */ true); + return timesUs.get(targetIndex); + } + + @Override + public long getDataEndPosition() { + return dataEndPosition; + } + + @Override + public boolean isSeekable() { + return true; + } + + @Override + public long getDurationUs() { + return durationUs; + } + + @Override + public SeekPoints getSeekPoints(long timeUs) { + int targetIndex = + Util.binarySearchFloor(timesUs, timeUs, /* inclusive= */ true, /* stayInBounds= */ true); + SeekPoint seekPoint = new SeekPoint(timesUs.get(targetIndex), positions.get(targetIndex)); + if (seekPoint.timeUs >= timeUs || targetIndex == timesUs.size() - 1) { + return new SeekPoints(seekPoint); + } else { + SeekPoint nextSeekPoint = + new SeekPoint(timesUs.get(targetIndex + 1), positions.get(targetIndex + 1)); + return new SeekPoints(seekPoint, nextSeekPoint); + } + } + + /** + * Adds a seek point to the index if it is sufficiently distant from the other points. + * + *

Seek points must be added in order. + * + * @param timeUs The time corresponding to the seek point to add in microseconds. + * @param position The position corresponding to the seek point to add in bytes. + */ + public void maybeAddSeekPoint(long timeUs, long position) { + long lastTimeUs = timesUs.get(timesUs.size() - 1); + if (timeUs - lastTimeUs < MIN_TIME_BETWEEN_POINTS_US) { + return; + } + timesUs.add(timeUs); + positions.add(position); + } +} diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java index a1e0e34e0e..e8c4633c90 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java @@ -56,24 +56,35 @@ public final class Mp3Extractor implements Extractor { /** * Flags controlling the behavior of the extractor. Possible flag values are {@link - * #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING} and {@link #FLAG_DISABLE_ID3_METADATA}. + * #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}, {@link #FLAG_ENABLE_INDEX_SEEKING} and {@link + * #FLAG_DISABLE_ID3_METADATA}. */ @Documented @Retention(RetentionPolicy.SOURCE) @IntDef( flag = true, - value = {FLAG_ENABLE_CONSTANT_BITRATE_SEEKING, FLAG_DISABLE_ID3_METADATA}) + value = { + FLAG_ENABLE_CONSTANT_BITRATE_SEEKING, + FLAG_ENABLE_INDEX_SEEKING, + FLAG_DISABLE_ID3_METADATA + }) public @interface Flags {} /** * Flag to force enable seeking using a constant bitrate assumption in cases where seeking would * otherwise not be possible. + * + *

This flag is ignored if {@link #FLAG_ENABLE_INDEX_SEEKING} is set. */ public static final int FLAG_ENABLE_CONSTANT_BITRATE_SEEKING = 1; + /** + * Flag to force index seeking, consisting in building a time-to-byte mapping as the file is read. + */ + public static final int FLAG_ENABLE_INDEX_SEEKING = 1 << 1; /** * Flag to disable parsing of ID3 metadata. Can be set to save memory if ID3 metadata is not * required. */ - public static final int FLAG_DISABLE_ID3_METADATA = 2; + public static final int FLAG_DISABLE_ID3_METADATA = 1 << 2; /** Predicate that matches ID3 frames containing only required gapless/seeking metadata. */ private static final FramePredicate REQUIRED_ID3_FRAME_PREDICATE = @@ -256,6 +267,9 @@ public final class Mp3Extractor implements Extractor { } } sampleBytesRemaining = synchronizedHeader.frameSize; + maybeAddSeekPointToIndexSeeker( + computeTimeUs(samplesRead + synchronizedHeader.samplesPerFrame), + extractorInput.getPosition() + synchronizedHeader.frameSize); } int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true); if (bytesAppended == C.RESULT_END_OF_INPUT) { @@ -265,14 +279,24 @@ public final class Mp3Extractor implements Extractor { if (sampleBytesRemaining > 0) { return RESULT_CONTINUE; } - long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate); - trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0, - null); + trackOutput.sampleMetadata( + computeTimeUs(samplesRead), C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0, null); samplesRead += synchronizedHeader.samplesPerFrame; sampleBytesRemaining = 0; return RESULT_CONTINUE; } + private long computeTimeUs(long samplesRead) { + return basisTimeUs + samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate; + } + + private void maybeAddSeekPointToIndexSeeker(long timeUs, long position) { + if (!(seeker instanceof IndexSeeker)) { + return; + } + ((IndexSeeker) seeker).maybeAddSeekPoint(timeUs, position); + } + private boolean synchronize(ExtractorInput input, boolean sniffing) throws IOException, InterruptedException { int validFrameCount = 0; @@ -379,7 +403,20 @@ public final class Mp3Extractor implements Extractor { } @Nullable Seeker resultSeeker = null; - if (metadataSeeker != null) { + if ((flags & FLAG_ENABLE_INDEX_SEEKING) != 0) { + long durationUs = C.TIME_UNSET; + long dataEndPosition = C.POSITION_UNSET; + if (metadataSeeker != null) { + durationUs = metadataSeeker.getDurationUs(); + dataEndPosition = metadataSeeker.getDataEndPosition(); + } else if (seekFrameSeeker != null) { + durationUs = seekFrameSeeker.getDurationUs(); + dataEndPosition = seekFrameSeeker.getDataEndPosition(); + } + resultSeeker = + new IndexSeeker( + durationUs, /* dataStartPosition= */ input.getPosition(), dataEndPosition); + } else if (metadataSeeker != null) { resultSeeker = metadataSeeker; } else if (seekFrameSeeker != null) { resultSeeker = seekFrameSeeker; diff --git a/library/extractor/src/test/assets/mp3/bear-vbr.mp3 b/library/extractor/src/test/assets/mp3/bear-vbr.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8b32dcdd58ea93eca5615d5decb82472648ba0ca GIT binary patch literal 38397 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 hB+80D-<I{z{{(t#*}MP% literal 0 HcmV?d00001 diff --git a/library/extractor/src/test/assets/mp3/bear-vbr.mp3.0.dump b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.0.dump new file mode 100644 index 0000000000..6a3e36695c --- /dev/null +++ b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.0.dump @@ -0,0 +1,499 @@ +seekMap: + isSeekable = true + duration = 2808000 + getPosition(0) = [[timeUs=0, position=237]] +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = -1 + encoderDelay = 576 + encoderPadding = 576 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + metadata = entries=[TSSE: description=null: value=Lavf58.29.100] + initializationData: + total output bytes = 38160 + sample count = 117 + sample 0: + time = 0 + flags = 1 + data = length 96, hash 1F161542 + sample 1: + time = 24000 + flags = 1 + data = length 768, hash CD1DC50F + sample 2: + time = 48000 + flags = 1 + data = length 336, hash 3F64124B + sample 3: + time = 72000 + flags = 1 + data = length 336, hash 8FFED94E + sample 4: + time = 96000 + flags = 1 + data = length 288, hash 9CD77D47 + sample 5: + time = 120000 + flags = 1 + data = length 384, hash 24607BB5 + sample 6: + time = 144000 + flags = 1 + data = length 480, hash 4937EBAB + sample 7: + time = 168000 + flags = 1 + data = length 336, hash 546342B1 + sample 8: + time = 192000 + flags = 1 + data = length 336, hash 79E0923F + sample 9: + time = 216000 + flags = 1 + data = length 336, hash AB1F3948 + sample 10: + time = 240000 + flags = 1 + data = length 336, hash C3A4D888 + sample 11: + time = 264000 + flags = 1 + data = length 288, hash 7867DA45 + sample 12: + time = 288000 + flags = 1 + data = length 336, hash B1240B73 + sample 13: + time = 312000 + flags = 1 + data = length 336, hash 94CFCD35 + sample 14: + time = 336000 + flags = 1 + data = length 288, hash 94F412C + sample 15: + time = 360000 + flags = 1 + data = length 336, hash A1D9FF41 + sample 16: + time = 384000 + flags = 1 + data = length 288, hash 2A8DA21B + sample 17: + time = 408000 + flags = 1 + data = length 336, hash 6A429CE + sample 18: + time = 432000 + flags = 1 + data = length 336, hash 68853982 + sample 19: + time = 456000 + flags = 1 + data = length 384, hash 1D6F779C + sample 20: + time = 480000 + flags = 1 + data = length 480, hash 6B31EBEE + sample 21: + time = 504000 + flags = 1 + data = length 336, hash 888335BE + sample 22: + time = 528000 + flags = 1 + data = length 336, hash 6072AC8B + sample 23: + time = 552000 + flags = 1 + data = length 336, hash C9D24234 + sample 24: + time = 576000 + flags = 1 + data = length 288, hash 52BF4D1E + sample 25: + time = 600000 + flags = 1 + data = length 336, hash F93F4F0 + sample 26: + time = 624000 + flags = 1 + data = length 336, hash 8617688A + sample 27: + time = 648000 + flags = 1 + data = length 480, hash FAB0D31B + sample 28: + time = 672000 + flags = 1 + data = length 384, hash FA4B53E2 + sample 29: + time = 696000 + flags = 1 + data = length 336, hash 8C435F6A + sample 30: + time = 720000 + flags = 1 + data = length 336, hash 60D3F80C + sample 31: + time = 744000 + flags = 1 + data = length 336, hash DC15B68B + sample 32: + time = 768000 + flags = 1 + data = length 288, hash FF3DF141 + sample 33: + time = 792000 + flags = 1 + data = length 336, hash A64B3042 + sample 34: + time = 816000 + flags = 1 + data = length 336, hash ACA622A1 + sample 35: + time = 840000 + flags = 1 + data = length 288, hash 3E34B8D4 + sample 36: + time = 864000 + flags = 1 + data = length 288, hash 9B96F72A + sample 37: + time = 888000 + flags = 1 + data = length 336, hash E917C122 + sample 38: + time = 912000 + flags = 1 + data = length 336, hash 10ED1470 + sample 39: + time = 936000 + flags = 1 + data = length 288, hash 706B8A7C + sample 40: + time = 960000 + flags = 1 + data = length 336, hash 71FFE4A0 + sample 41: + time = 984000 + flags = 1 + data = length 336, hash D4160463 + sample 42: + time = 1008000 + flags = 1 + data = length 336, hash EC557B14 + sample 43: + time = 1032000 + flags = 1 + data = length 288, hash 5598CF8B + sample 44: + time = 1056000 + flags = 1 + data = length 336, hash 7E0AB41 + sample 45: + time = 1080000 + flags = 1 + data = length 336, hash 1C585FEF + sample 46: + time = 1104000 + flags = 1 + data = length 336, hash A4A4855E + sample 47: + time = 1128000 + flags = 1 + data = length 336, hash CECA51D3 + sample 48: + time = 1152000 + flags = 1 + data = length 288, hash 2D362DC5 + sample 49: + time = 1176000 + flags = 1 + data = length 336, hash 9EB2609D + sample 50: + time = 1200000 + flags = 1 + data = length 336, hash 28FFB3FE + sample 51: + time = 1224000 + flags = 1 + data = length 288, hash 2AA2D216 + sample 52: + time = 1248000 + flags = 1 + data = length 336, hash CDBC7032 + sample 53: + time = 1272000 + flags = 1 + data = length 336, hash 25B13FE7 + sample 54: + time = 1296000 + flags = 1 + data = length 336, hash DB6BB1E + sample 55: + time = 1320000 + flags = 1 + data = length 336, hash EBE951F4 + sample 56: + time = 1344000 + flags = 1 + data = length 288, hash 9E2EBFF7 + sample 57: + time = 1368000 + flags = 1 + data = length 336, hash 36A7D455 + sample 58: + time = 1392000 + flags = 1 + data = length 336, hash 84545F8C + sample 59: + time = 1416000 + flags = 1 + data = length 336, hash F66F3045 + sample 60: + time = 1440000 + flags = 1 + data = length 576, hash 5AB089EA + sample 61: + time = 1464000 + flags = 1 + data = length 336, hash 8868086 + sample 62: + time = 1488000 + flags = 1 + data = length 336, hash D5EB6D63 + sample 63: + time = 1512000 + flags = 1 + data = length 288, hash 7A5374B7 + sample 64: + time = 1536000 + flags = 1 + data = length 336, hash BEB27A75 + sample 65: + time = 1560000 + flags = 1 + data = length 336, hash E251E0FD + sample 66: + time = 1584000 + flags = 1 + data = length 288, hash D54C970 + sample 67: + time = 1608000 + flags = 1 + data = length 336, hash 52C473B9 + sample 68: + time = 1632000 + flags = 1 + data = length 336, hash F5F13334 + sample 69: + time = 1656000 + flags = 1 + data = length 480, hash A5F1E987 + sample 70: + time = 1680000 + flags = 1 + data = length 288, hash 453A1267 + sample 71: + time = 1704000 + flags = 1 + data = length 288, hash 7C6C2EA9 + sample 72: + time = 1728000 + flags = 1 + data = length 336, hash F4BFECA4 + sample 73: + time = 1752000 + flags = 1 + data = length 336, hash 751A395A + sample 74: + time = 1776000 + flags = 1 + data = length 336, hash EE38DB02 + sample 75: + time = 1800000 + flags = 1 + data = length 336, hash F18837E2 + sample 76: + time = 1824000 + flags = 1 + data = length 336, hash ED36B78E + sample 77: + time = 1848000 + flags = 1 + data = length 336, hash B3D28289 + sample 78: + time = 1872000 + flags = 1 + data = length 288, hash 8BDE28E1 + sample 79: + time = 1896000 + flags = 1 + data = length 336, hash CFD5E966 + sample 80: + time = 1920000 + flags = 1 + data = length 288, hash DC08E267 + sample 81: + time = 1944000 + flags = 1 + data = length 336, hash 6530CB78 + sample 82: + time = 1968000 + flags = 1 + data = length 336, hash 6CC6636E + sample 83: + time = 1992000 + flags = 1 + data = length 336, hash 613047C1 + sample 84: + time = 2016000 + flags = 1 + data = length 288, hash CDC747BF + sample 85: + time = 2040000 + flags = 1 + data = length 336, hash AF22AA74 + sample 86: + time = 2064000 + flags = 1 + data = length 384, hash 82F326AA + sample 87: + time = 2088000 + flags = 1 + data = length 384, hash EDA26C4D + sample 88: + time = 2112000 + flags = 1 + data = length 336, hash 94C643DC + sample 89: + time = 2136000 + flags = 1 + data = length 288, hash CB5D9C40 + sample 90: + time = 2160000 + flags = 1 + data = length 336, hash 1E69DE3F + sample 91: + time = 2184000 + flags = 1 + data = length 336, hash 7E472219 + sample 92: + time = 2208000 + flags = 1 + data = length 336, hash DA47B9FA + sample 93: + time = 2232000 + flags = 1 + data = length 336, hash DD0ABB7C + sample 94: + time = 2256000 + flags = 1 + data = length 288, hash DBF93FAC + sample 95: + time = 2280000 + flags = 1 + data = length 336, hash 243F4B2 + sample 96: + time = 2304000 + flags = 1 + data = length 336, hash 2E881490 + sample 97: + time = 2328000 + flags = 1 + data = length 288, hash 1C28C8BE + sample 98: + time = 2352000 + flags = 1 + data = length 336, hash C73E5D30 + sample 99: + time = 2376000 + flags = 1 + data = length 288, hash 98B5BFF6 + sample 100: + time = 2400000 + flags = 1 + data = length 336, hash E0135533 + sample 101: + time = 2424000 + flags = 1 + data = length 336, hash D13C9DBC + sample 102: + time = 2448000 + flags = 1 + data = length 336, hash 63D524CA + sample 103: + time = 2472000 + flags = 1 + data = length 288, hash A28514C3 + sample 104: + time = 2496000 + flags = 1 + data = length 336, hash 72B647FF + sample 105: + time = 2520000 + flags = 1 + data = length 336, hash 8F740AB1 + sample 106: + time = 2544000 + flags = 1 + data = length 336, hash 5E3C7E93 + sample 107: + time = 2568000 + flags = 1 + data = length 336, hash 121B913B + sample 108: + time = 2592000 + flags = 1 + data = length 336, hash 578FCCF2 + sample 109: + time = 2616000 + flags = 1 + data = length 336, hash 5B5823DE + sample 110: + time = 2640000 + flags = 1 + data = length 384, hash D8B83F78 + sample 111: + time = 2664000 + flags = 1 + data = length 240, hash E649682F + sample 112: + time = 2688000 + flags = 1 + data = length 96, hash C559A6F4 + sample 113: + time = 2712000 + flags = 1 + data = length 96, hash 792796BC + sample 114: + time = 2736000 + flags = 1 + data = length 120, hash 8172CD0E + sample 115: + time = 2760000 + flags = 1 + data = length 120, hash F562B52F + sample 116: + time = 2784000 + flags = 1 + data = length 96, hash FF8D5B98 +tracksEnded = true diff --git a/library/extractor/src/test/assets/mp3/bear-vbr.mp3.1.dump b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.1.dump new file mode 100644 index 0000000000..dabe8be921 --- /dev/null +++ b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.1.dump @@ -0,0 +1,359 @@ +seekMap: + isSeekable = true + duration = 2808000 + getPosition(0) = [[timeUs=0, position=237]] +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = -1 + encoderDelay = 576 + encoderPadding = 576 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + metadata = entries=[TSSE: description=null: value=Lavf58.29.100] + initializationData: + total output bytes = 25920 + sample count = 82 + sample 0: + time = 840000 + flags = 1 + data = length 288, hash 3E34B8D4 + sample 1: + time = 864000 + flags = 1 + data = length 288, hash 9B96F72A + sample 2: + time = 888000 + flags = 1 + data = length 336, hash E917C122 + sample 3: + time = 912000 + flags = 1 + data = length 336, hash 10ED1470 + sample 4: + time = 936000 + flags = 1 + data = length 288, hash 706B8A7C + sample 5: + time = 960000 + flags = 1 + data = length 336, hash 71FFE4A0 + sample 6: + time = 984000 + flags = 1 + data = length 336, hash D4160463 + sample 7: + time = 1008000 + flags = 1 + data = length 336, hash EC557B14 + sample 8: + time = 1032000 + flags = 1 + data = length 288, hash 5598CF8B + sample 9: + time = 1056000 + flags = 1 + data = length 336, hash 7E0AB41 + sample 10: + time = 1080000 + flags = 1 + data = length 336, hash 1C585FEF + sample 11: + time = 1104000 + flags = 1 + data = length 336, hash A4A4855E + sample 12: + time = 1128000 + flags = 1 + data = length 336, hash CECA51D3 + sample 13: + time = 1152000 + flags = 1 + data = length 288, hash 2D362DC5 + sample 14: + time = 1176000 + flags = 1 + data = length 336, hash 9EB2609D + sample 15: + time = 1200000 + flags = 1 + data = length 336, hash 28FFB3FE + sample 16: + time = 1224000 + flags = 1 + data = length 288, hash 2AA2D216 + sample 17: + time = 1248000 + flags = 1 + data = length 336, hash CDBC7032 + sample 18: + time = 1272000 + flags = 1 + data = length 336, hash 25B13FE7 + sample 19: + time = 1296000 + flags = 1 + data = length 336, hash DB6BB1E + sample 20: + time = 1320000 + flags = 1 + data = length 336, hash EBE951F4 + sample 21: + time = 1344000 + flags = 1 + data = length 288, hash 9E2EBFF7 + sample 22: + time = 1368000 + flags = 1 + data = length 336, hash 36A7D455 + sample 23: + time = 1392000 + flags = 1 + data = length 336, hash 84545F8C + sample 24: + time = 1416000 + flags = 1 + data = length 336, hash F66F3045 + sample 25: + time = 1440000 + flags = 1 + data = length 576, hash 5AB089EA + sample 26: + time = 1464000 + flags = 1 + data = length 336, hash 8868086 + sample 27: + time = 1488000 + flags = 1 + data = length 336, hash D5EB6D63 + sample 28: + time = 1512000 + flags = 1 + data = length 288, hash 7A5374B7 + sample 29: + time = 1536000 + flags = 1 + data = length 336, hash BEB27A75 + sample 30: + time = 1560000 + flags = 1 + data = length 336, hash E251E0FD + sample 31: + time = 1584000 + flags = 1 + data = length 288, hash D54C970 + sample 32: + time = 1608000 + flags = 1 + data = length 336, hash 52C473B9 + sample 33: + time = 1632000 + flags = 1 + data = length 336, hash F5F13334 + sample 34: + time = 1656000 + flags = 1 + data = length 480, hash A5F1E987 + sample 35: + time = 1680000 + flags = 1 + data = length 288, hash 453A1267 + sample 36: + time = 1704000 + flags = 1 + data = length 288, hash 7C6C2EA9 + sample 37: + time = 1728000 + flags = 1 + data = length 336, hash F4BFECA4 + sample 38: + time = 1752000 + flags = 1 + data = length 336, hash 751A395A + sample 39: + time = 1776000 + flags = 1 + data = length 336, hash EE38DB02 + sample 40: + time = 1800000 + flags = 1 + data = length 336, hash F18837E2 + sample 41: + time = 1824000 + flags = 1 + data = length 336, hash ED36B78E + sample 42: + time = 1848000 + flags = 1 + data = length 336, hash B3D28289 + sample 43: + time = 1872000 + flags = 1 + data = length 288, hash 8BDE28E1 + sample 44: + time = 1896000 + flags = 1 + data = length 336, hash CFD5E966 + sample 45: + time = 1920000 + flags = 1 + data = length 288, hash DC08E267 + sample 46: + time = 1944000 + flags = 1 + data = length 336, hash 6530CB78 + sample 47: + time = 1968000 + flags = 1 + data = length 336, hash 6CC6636E + sample 48: + time = 1992000 + flags = 1 + data = length 336, hash 613047C1 + sample 49: + time = 2016000 + flags = 1 + data = length 288, hash CDC747BF + sample 50: + time = 2040000 + flags = 1 + data = length 336, hash AF22AA74 + sample 51: + time = 2064000 + flags = 1 + data = length 384, hash 82F326AA + sample 52: + time = 2088000 + flags = 1 + data = length 384, hash EDA26C4D + sample 53: + time = 2112000 + flags = 1 + data = length 336, hash 94C643DC + sample 54: + time = 2136000 + flags = 1 + data = length 288, hash CB5D9C40 + sample 55: + time = 2160000 + flags = 1 + data = length 336, hash 1E69DE3F + sample 56: + time = 2184000 + flags = 1 + data = length 336, hash 7E472219 + sample 57: + time = 2208000 + flags = 1 + data = length 336, hash DA47B9FA + sample 58: + time = 2232000 + flags = 1 + data = length 336, hash DD0ABB7C + sample 59: + time = 2256000 + flags = 1 + data = length 288, hash DBF93FAC + sample 60: + time = 2280000 + flags = 1 + data = length 336, hash 243F4B2 + sample 61: + time = 2304000 + flags = 1 + data = length 336, hash 2E881490 + sample 62: + time = 2328000 + flags = 1 + data = length 288, hash 1C28C8BE + sample 63: + time = 2352000 + flags = 1 + data = length 336, hash C73E5D30 + sample 64: + time = 2376000 + flags = 1 + data = length 288, hash 98B5BFF6 + sample 65: + time = 2400000 + flags = 1 + data = length 336, hash E0135533 + sample 66: + time = 2424000 + flags = 1 + data = length 336, hash D13C9DBC + sample 67: + time = 2448000 + flags = 1 + data = length 336, hash 63D524CA + sample 68: + time = 2472000 + flags = 1 + data = length 288, hash A28514C3 + sample 69: + time = 2496000 + flags = 1 + data = length 336, hash 72B647FF + sample 70: + time = 2520000 + flags = 1 + data = length 336, hash 8F740AB1 + sample 71: + time = 2544000 + flags = 1 + data = length 336, hash 5E3C7E93 + sample 72: + time = 2568000 + flags = 1 + data = length 336, hash 121B913B + sample 73: + time = 2592000 + flags = 1 + data = length 336, hash 578FCCF2 + sample 74: + time = 2616000 + flags = 1 + data = length 336, hash 5B5823DE + sample 75: + time = 2640000 + flags = 1 + data = length 384, hash D8B83F78 + sample 76: + time = 2664000 + flags = 1 + data = length 240, hash E649682F + sample 77: + time = 2688000 + flags = 1 + data = length 96, hash C559A6F4 + sample 78: + time = 2712000 + flags = 1 + data = length 96, hash 792796BC + sample 79: + time = 2736000 + flags = 1 + data = length 120, hash 8172CD0E + sample 80: + time = 2760000 + flags = 1 + data = length 120, hash F562B52F + sample 81: + time = 2784000 + flags = 1 + data = length 96, hash FF8D5B98 +tracksEnded = true diff --git a/library/extractor/src/test/assets/mp3/bear-vbr.mp3.2.dump b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.2.dump new file mode 100644 index 0000000000..2d5af88b15 --- /dev/null +++ b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.2.dump @@ -0,0 +1,199 @@ +seekMap: + isSeekable = true + duration = 2808000 + getPosition(0) = [[timeUs=0, position=237]] +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = -1 + encoderDelay = 576 + encoderPadding = 576 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + metadata = entries=[TSSE: description=null: value=Lavf58.29.100] + initializationData: + total output bytes = 12624 + sample count = 42 + sample 0: + time = 1800000 + flags = 1 + data = length 336, hash F18837E2 + sample 1: + time = 1824000 + flags = 1 + data = length 336, hash ED36B78E + sample 2: + time = 1848000 + flags = 1 + data = length 336, hash B3D28289 + sample 3: + time = 1872000 + flags = 1 + data = length 288, hash 8BDE28E1 + sample 4: + time = 1896000 + flags = 1 + data = length 336, hash CFD5E966 + sample 5: + time = 1920000 + flags = 1 + data = length 288, hash DC08E267 + sample 6: + time = 1944000 + flags = 1 + data = length 336, hash 6530CB78 + sample 7: + time = 1968000 + flags = 1 + data = length 336, hash 6CC6636E + sample 8: + time = 1992000 + flags = 1 + data = length 336, hash 613047C1 + sample 9: + time = 2016000 + flags = 1 + data = length 288, hash CDC747BF + sample 10: + time = 2040000 + flags = 1 + data = length 336, hash AF22AA74 + sample 11: + time = 2064000 + flags = 1 + data = length 384, hash 82F326AA + sample 12: + time = 2088000 + flags = 1 + data = length 384, hash EDA26C4D + sample 13: + time = 2112000 + flags = 1 + data = length 336, hash 94C643DC + sample 14: + time = 2136000 + flags = 1 + data = length 288, hash CB5D9C40 + sample 15: + time = 2160000 + flags = 1 + data = length 336, hash 1E69DE3F + sample 16: + time = 2184000 + flags = 1 + data = length 336, hash 7E472219 + sample 17: + time = 2208000 + flags = 1 + data = length 336, hash DA47B9FA + sample 18: + time = 2232000 + flags = 1 + data = length 336, hash DD0ABB7C + sample 19: + time = 2256000 + flags = 1 + data = length 288, hash DBF93FAC + sample 20: + time = 2280000 + flags = 1 + data = length 336, hash 243F4B2 + sample 21: + time = 2304000 + flags = 1 + data = length 336, hash 2E881490 + sample 22: + time = 2328000 + flags = 1 + data = length 288, hash 1C28C8BE + sample 23: + time = 2352000 + flags = 1 + data = length 336, hash C73E5D30 + sample 24: + time = 2376000 + flags = 1 + data = length 288, hash 98B5BFF6 + sample 25: + time = 2400000 + flags = 1 + data = length 336, hash E0135533 + sample 26: + time = 2424000 + flags = 1 + data = length 336, hash D13C9DBC + sample 27: + time = 2448000 + flags = 1 + data = length 336, hash 63D524CA + sample 28: + time = 2472000 + flags = 1 + data = length 288, hash A28514C3 + sample 29: + time = 2496000 + flags = 1 + data = length 336, hash 72B647FF + sample 30: + time = 2520000 + flags = 1 + data = length 336, hash 8F740AB1 + sample 31: + time = 2544000 + flags = 1 + data = length 336, hash 5E3C7E93 + sample 32: + time = 2568000 + flags = 1 + data = length 336, hash 121B913B + sample 33: + time = 2592000 + flags = 1 + data = length 336, hash 578FCCF2 + sample 34: + time = 2616000 + flags = 1 + data = length 336, hash 5B5823DE + sample 35: + time = 2640000 + flags = 1 + data = length 384, hash D8B83F78 + sample 36: + time = 2664000 + flags = 1 + data = length 240, hash E649682F + sample 37: + time = 2688000 + flags = 1 + data = length 96, hash C559A6F4 + sample 38: + time = 2712000 + flags = 1 + data = length 96, hash 792796BC + sample 39: + time = 2736000 + flags = 1 + data = length 120, hash 8172CD0E + sample 40: + time = 2760000 + flags = 1 + data = length 120, hash F562B52F + sample 41: + time = 2784000 + flags = 1 + data = length 96, hash FF8D5B98 +tracksEnded = true diff --git a/library/extractor/src/test/assets/mp3/bear-vbr.mp3.3.dump b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.3.dump new file mode 100644 index 0000000000..1dc7fa9258 --- /dev/null +++ b/library/extractor/src/test/assets/mp3/bear-vbr.mp3.3.dump @@ -0,0 +1,39 @@ +seekMap: + isSeekable = true + duration = 2808000 + getPosition(0) = [[timeUs=0, position=237]] +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = 0 + pixelWidthHeightRatio = 1.0 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = -1 + encoderDelay = 576 + encoderPadding = 576 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + metadata = entries=[TSSE: description=null: value=Lavf58.29.100] + initializationData: + total output bytes = 216 + sample count = 2 + sample 0: + time = 2760000 + flags = 1 + data = length 120, hash F562B52F + sample 1: + time = 2784000 + flags = 1 + data = length 96, hash FF8D5B98 +tracksEnded = true diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java index 727af17401..ed7484bdb1 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java @@ -35,6 +35,12 @@ public final class Mp3ExtractorTest { Mp3Extractor::new, "mp3/bear-cbr-variable-frame-size-no-seek-table.mp3"); } + @Test + public void testMp3SampleWithIndexSeeker() throws Exception { + ExtractorAsserts.assertBehavior( + () -> new Mp3Extractor(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING), "mp3/bear-vbr.mp3"); + } + @Test public void testTrimmedMp3Sample() throws Exception { ExtractorAsserts.assertBehavior(Mp3Extractor::new, "mp3/play-trimmed.mp3");