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 00000000000..102716b3f6d --- /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 a1e0e34e0e9..e8c4633c906 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 @@ private int readSample(ExtractorInput extractorInput) throws IOException, Interr } } 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 @@ private int readSample(ExtractorInput extractorInput) throws IOException, Interr 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 @@ private Seeker computeSeeker(ExtractorInput input) throws IOException, Interrupt } @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 00000000000..8b32dcdd58e Binary files /dev/null and b/library/extractor/src/test/assets/mp3/bear-vbr.mp3 differ 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 00000000000..6a3e36695cb --- /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 00000000000..dabe8be921b --- /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 00000000000..2d5af88b153 --- /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 00000000000..1dc7fa92589 --- /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 727af17401c..ed7484bdb17 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 void testMp3SampleWithCbrSeeker() throws Exception { 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");