From 8ef4af135823a466e0319cd48c34fbd21d244a01 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 14 Mar 2016 05:37:52 -0700 Subject: [PATCH] Fix incorrect position calculation in VBRI seeker. Issue: #1197 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=117125081 --- .../exoplayer/extractor/mp3/Mp3Extractor.java | 2 +- .../exoplayer/extractor/mp3/VbriSeeker.java | 51 +++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java index 0d11da8dab..c3c31e0603 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java @@ -290,7 +290,7 @@ public final class Mp3Extractor implements Extractor { frame.setPosition(36); // MPEG audio header (4 bytes) + 32 bytes. headerData = frame.readInt(); if (headerData == VBRI_HEADER) { - seeker = VbriSeeker.create(synchronizedHeader, frame, position); + seeker = VbriSeeker.create(synchronizedHeader, frame, position, length); input.skipFully(synchronizedHeader.frameSize); } } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java index 170a944409..8d0c0000d2 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java @@ -34,11 +34,12 @@ import com.google.android.exoplayer.util.Util; * @param frame The data in this audio frame, with its position set to immediately after the * 'VBRI' tag. * @param position The position (byte offset) of the start of this frame in the stream. + * @param inputLength The length of the stream in bytes. * @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required * information is not present. */ public static VbriSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame, - long position) { + long position, long inputLength) { frame.skipBytes(10); int numFrames = frame.readInt(); if (numFrames <= 0) { @@ -47,53 +48,52 @@ import com.google.android.exoplayer.util.Util; int sampleRate = mpegAudioHeader.sampleRate; long durationUs = Util.scaleLargeTimestamp(numFrames, C.MICROS_PER_SECOND * (sampleRate >= 32000 ? 1152 : 576), sampleRate); - int numEntries = frame.readUnsignedShort(); + int entryCount = frame.readUnsignedShort(); int scale = frame.readUnsignedShort(); int entrySize = frame.readUnsignedShort(); + frame.skipBytes(2); - // Read entries in the VBRI header. - long[] timesUs = new long[numEntries]; - long[] offsets = new long[numEntries]; - long segmentDurationUs = durationUs / numEntries; - long now = 0; - int segmentIndex = 0; - while (segmentIndex < numEntries) { - int numBytes; + // Skip the frame containing the VBRI header. + position += mpegAudioHeader.frameSize; + + // Read table of contents entries. + long[] timesUs = new long[entryCount + 1]; + long[] positions = new long[entryCount + 1]; + timesUs[0] = 0L; + positions[0] = position; + for (int index = 1; index < timesUs.length; index++) { + int segmentSize; switch (entrySize) { case 1: - numBytes = frame.readUnsignedByte(); + segmentSize = frame.readUnsignedByte(); break; case 2: - numBytes = frame.readUnsignedShort(); + segmentSize = frame.readUnsignedShort(); break; case 3: - numBytes = frame.readUnsignedInt24(); + segmentSize = frame.readUnsignedInt24(); break; case 4: - numBytes = frame.readUnsignedIntToInt(); + segmentSize = frame.readUnsignedIntToInt(); break; default: return null; } - now += segmentDurationUs; - timesUs[segmentIndex] = now; - position += numBytes * scale; - offsets[segmentIndex] = position; - - segmentIndex++; + position += segmentSize * scale; + timesUs[index] = index * durationUs / entryCount; + positions[index] = + inputLength == C.LENGTH_UNBOUNDED ? position : Math.min(inputLength, position); } - return new VbriSeeker(timesUs, offsets, position + mpegAudioHeader.frameSize, durationUs); + return new VbriSeeker(timesUs, positions, durationUs); } private final long[] timesUs; private final long[] positions; - private final long basePosition; private final long durationUs; - private VbriSeeker(long[] timesUs, long[] positions, long basePosition, long durationUs) { + private VbriSeeker(long[] timesUs, long[] positions, long durationUs) { this.timesUs = timesUs; this.positions = positions; - this.basePosition = basePosition; this.durationUs = durationUs; } @@ -104,8 +104,7 @@ import com.google.android.exoplayer.util.Util; @Override public long getPosition(long timeUs) { - int index = Util.binarySearchFloor(timesUs, timeUs, false, false); - return basePosition + (index == -1 ? 0L : positions[index]); + return positions[Util.binarySearchFloor(timesUs, timeUs, true, true)]; } @Override