From 5b3066f380a433ccfaa3afbbf7c1e1e283bf4b7a Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 22 May 2024 06:56:35 -0700 Subject: [PATCH] MP3: Derive duration and bitrate from frame count in `Info` header `Info` header is used for CBR files, but in some cases not **every** frame in these files is the same size. This change stops using the single frame after the `Info` frame as the 'template' (and assuming all subsequent frames are the same size/bitrate), and instead derives the bitrate from fields in the `Info` header. This works for files which are 'almost' constant bitrate, like the one in Issue: androidx/media#1376 where every frame is either 1044 or 1045 bytes except the one immediately after the `Info` frame which is 104 bytes (32kbps), resulting in a wildly incorrect duration calculation. PiperOrigin-RevId: 636151605 --- RELEASENOTES.md | 10 ++ .../extractor/mp3/ConstantBitrateSeeker.java | 20 ++- .../media3/extractor/mp3/Mp3Extractor.java | 101 +++++++++++---- .../media3/extractor/mp3/XingFrame.java | 17 +++ .../media3/extractor/mp3/XingSeeker.java | 22 +--- .../media3/extractor/mp3/XingSeekerTest.java | 3 - ...test-cbr-info-header-pcut-frame.mp3.0.dump | 10 +- ...test-cbr-info-header-pcut-frame.mp3.1.dump | 64 +++++----- ...test-cbr-info-header-pcut-frame.mp3.2.dump | 36 +++--- ...test-cbr-info-header-pcut-frame.mp3.3.dump | 10 +- ...-header-pcut-frame.mp3.unknown_length.dump | 10 +- .../mp3/test-cbr-info-header.mp3.0.dump | 6 +- .../mp3/test-cbr-info-header.mp3.1.dump | 116 +++++++++--------- .../mp3/test-cbr-info-header.mp3.2.dump | 64 +++++----- .../mp3/test-cbr-info-header.mp3.3.dump | 14 ++- ...st-cbr-info-header.mp3.unknown_length.dump | 6 +- 16 files changed, 289 insertions(+), 220 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2880a89dfd..f5509a07ec 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -58,6 +58,16 @@ HLS streams([#1150](https://github.com/androidx/media/issues/1150)) and H.262 HLS streams ([#1126](https://github.com/androidx/media/issues/1126)). + * MP3: Prefer the data size from an `Info` frame over the size reported by + the underlying stream (e.g. file size, or HTTP `Content-Length` header). + This helps to exclude non-playable trailer data (e.g. album artwork) + from constant bitrate seeking calculations, making seeks more accurate + ([#1376](https://github.com/androidx/media/issues/1376)). + * MP3: Use the frame count and other data in an `Info` frame (if present) + to compute an average bitrate for constant bitrate seeking, rather than + extrapolating from the bitrate of the frame after the `Info` frame, + which may be artificially small, e.g. `PCUT` frame + ([#1376](https://github.com/androidx/media/issues/1376)). * Audio: * Fix DTS:X Profile 2 encoding attributes for passthrough playback ([#1299](https://github.com/androidx/media/pull/1299)). diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/ConstantBitrateSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/ConstantBitrateSeeker.java index 179e718dd7..e167d4bbe8 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/ConstantBitrateSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/ConstantBitrateSeeker.java @@ -27,6 +27,8 @@ import androidx.media3.extractor.MpegAudioUtil; private final int bitrate; /** + * Constructs an instance. + * * @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown. * @param firstFramePosition The position of the first frame in the stream. * @param mpegAudioHeader The MPEG audio header associated with the first frame. @@ -39,15 +41,25 @@ import androidx.media3.extractor.MpegAudioUtil; MpegAudioUtil.Header mpegAudioHeader, boolean allowSeeksIfLengthUnknown) { // Set the seeker frame size to the size of the first frame (even though some constant bitrate - // streams have variable frame sizes) to avoid the need to re-synchronize for constant frame - // size streams. - super( + // streams have variable frame sizes due to padding) to avoid the need to re-synchronize for + // constant frame size streams. + this( inputLength, firstFramePosition, mpegAudioHeader.bitrate, mpegAudioHeader.frameSize, allowSeeksIfLengthUnknown); - bitrate = mpegAudioHeader.bitrate; + } + + /** See {@link ConstantBitrateSeekMap#ConstantBitrateSeekMap(long, long, int, int, boolean)}. */ + public ConstantBitrateSeeker( + long inputLength, + long firstFramePosition, + int bitrate, + int frameSize, + boolean allowSeeksIfLengthUnknown) { + super(inputLength, firstFramePosition, bitrate, frameSize, allowSeeksIfLengthUnknown); + this.bitrate = bitrate; } @Override diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java index 337e69b817..eafb68c8e3 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java @@ -27,6 +27,7 @@ import androidx.media3.common.ParserException; import androidx.media3.common.PlaybackException; import androidx.media3.common.Player; import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Log; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; @@ -45,11 +46,14 @@ import androidx.media3.extractor.metadata.id3.Id3Decoder.FramePredicate; import androidx.media3.extractor.metadata.id3.MlltFrame; import androidx.media3.extractor.metadata.id3.TextInformationFrame; import androidx.media3.extractor.mp3.Seeker.UnseekableSeeker; +import com.google.common.math.LongMath; +import com.google.common.primitives.Ints; import java.io.EOFException; import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.math.RoundingMode; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -122,6 +126,8 @@ public final class Mp3Extractor implements Extractor { */ public static final int FLAG_DISABLE_ID3_METADATA = 1 << 3; + private static final String TAG = "Mp3Extractor"; + /** Predicate that matches ID3 frames containing only required gapless/seeking metadata. */ private static final FramePredicate REQUIRED_ID3_FRAME_PREDICATE = (majorVersion, id0, id1, id2, id3) -> @@ -528,22 +534,26 @@ public final class Mp3Extractor implements Extractor { gaplessInfoHolder.encoderPadding = xingFrame.encoderPadding; } long startPosition = input.getPosition(); + if (input.getLength() != C.LENGTH_UNSET + && xingFrame.dataSize != C.LENGTH_UNSET + && input.getLength() != startPosition + xingFrame.dataSize) { + Log.i( + TAG, + "Data size mismatch between stream (" + + input.getLength() + + ") and Xing frame (" + + (startPosition + xingFrame.dataSize) + + "), using Xing value."); + } input.skipFully(synchronizedHeader.frameSize); // An Xing frame indicates the file is VBR (so we have to use the seek header for seeking) // while an Info header indicates the file is CBR, in which case ConstantBitrateSeeker will // give more accurate seeking than the low-resolution seek table in the Info header. We can // still use the length from the Info frame if we don't know the stream length directly. if (seekHeader == SEEK_HEADER_XING) { - seeker = XingSeeker.create(input.getLength(), xingFrame, startPosition); + seeker = XingSeeker.create(xingFrame, startPosition); } else { // seekHeader == SEEK_HEADER_INFO - long streamLength = - xingFrame.dataSize != C.LENGTH_UNSET - ? startPosition + xingFrame.dataSize - : C.LENGTH_UNSET; - // TODO: b/319235116 - Consider using the duration derived from the Xing/Info frame when - // it considers encoding delay and padding. - seeker = - getConstantBitrateSeeker(input, streamLength, /* allowSeeksIfLengthUnknown= */ false); + seeker = getConstantBitrateSeeker(startPosition, xingFrame, input.getLength()); } break; case SEEK_HEADER_VBRI: @@ -563,26 +573,67 @@ public final class Mp3Extractor implements Extractor { /** Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate. */ private Seeker getConstantBitrateSeeker(ExtractorInput input, boolean allowSeeksIfLengthUnknown) throws IOException { - return getConstantBitrateSeeker(input, C.LENGTH_UNSET, allowSeeksIfLengthUnknown); - } - - /** - * Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate. {@code - * streamLengthFallback} is used if {@link ExtractorInput#getLength() input.getLength()} is {@link - * C#LENGTH_UNSET}. {@code streamLengthFallback} may also be {@link C#LENGTH_UNSET} to indicate - * the length is unknown. - */ - private Seeker getConstantBitrateSeeker( - ExtractorInput input, long streamLengthFallback, boolean allowSeeksIfLengthUnknown) - throws IOException { input.peekFully(scratch.getData(), 0, 4); scratch.setPosition(0); synchronizedHeader.setForHeaderData(scratch.readInt()); return new ConstantBitrateSeeker( - input.getLength() != C.LENGTH_UNSET ? input.getLength() : streamLengthFallback, - input.getPosition(), - synchronizedHeader, - allowSeeksIfLengthUnknown); + input.getLength(), input.getPosition(), synchronizedHeader, allowSeeksIfLengthUnknown); + } + + /** + * Returns a {@link ConstantBitrateSeeker} based on the provided {@link XingFrame Info frame}. + * + * @param infoFramePosition The position of the Info frame (from the beginning of the stream). + * @param infoFrame The parsed Info frame. + * @param fallbackStreamLength The complete length of the input stream (only used if {@link + * XingFrame#dataSize} is unset). Can be {@link C#LENGTH_UNSET} if the length is not known. + * @return A {@link Seeker} if the {@link XingFrame} contains enough info to seek, or {@code null} + * otherwise. + */ + @Nullable + private Seeker getConstantBitrateSeeker( + long infoFramePosition, XingFrame infoFrame, long fallbackStreamLength) { + long durationUs = infoFrame.computeDurationUs(); + if (durationUs == C.TIME_UNSET) { + return null; + } + long streamLength; + long audioLength; + // Prefer the stream length from the Info frame, because it may deliberately + // exclude some unplayable/non-MP3 trailer data (e.g. album artwork), see + // https://github.com/androidx/media/issues/1376#issuecomment-2117211184. + if (infoFrame.dataSize != C.LENGTH_UNSET) { + streamLength = infoFramePosition + infoFrame.dataSize; + audioLength = infoFrame.dataSize - infoFrame.header.frameSize; + } else if (fallbackStreamLength != C.LENGTH_UNSET) { + streamLength = fallbackStreamLength; + audioLength = fallbackStreamLength - infoFramePosition - infoFrame.header.frameSize; + } else { + return null; + } + + // Derive the bitrate and frame size by averaging over the length of playable audio, to allow + // for 'mostly' CBR streams that might have a small number of frames with a different bitrate. + // We can assume infoFrame.frameCount is set, because otherwise computeDurationUs() would + // have returned C.TIME_UNSET above. See also https://github.com/androidx/media/issues/1376. + int averageBitrate = + Ints.checkedCast( + Util.scaleLargeValue( + audioLength, + C.BITS_PER_BYTE * C.MICROS_PER_SECOND, + durationUs, + RoundingMode.HALF_UP)); + int frameSize = + Ints.checkedCast(LongMath.divide(audioLength, infoFrame.frameCount, RoundingMode.HALF_UP)); + // Set the seeker frame size to the average frame size (even though some constant bitrate + // streams have variable frame sizes due to padding), to avoid the need to re-synchronize for + // constant frame size streams. + return new ConstantBitrateSeeker( + streamLength, + /* firstFramePosition= */ infoFramePosition + infoFrame.header.frameSize, + averageBitrate, + frameSize, + /* allowSeeksIfLengthUnknown= */ false); } @EnsuresNonNull({"extractorOutput", "realTrackOutput"}) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingFrame.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingFrame.java index fff20da055..45ddaf8f58 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingFrame.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingFrame.java @@ -18,6 +18,7 @@ package androidx.media3.extractor.mp3; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.common.util.Util; import androidx.media3.extractor.MpegAudioUtil; /** Representation of a LAME Xing or Info frame. */ @@ -115,4 +116,20 @@ import androidx.media3.extractor.MpegAudioUtil; return new XingFrame( mpegAudioHeader, frameCount, dataSize, tableOfContents, encoderDelay, encoderPadding); } + + /** + * Compute the stream duration, in microseconds, represented by this frame. Returns {@link + * C#LENGTH_UNSET} if the frame doesn't contain enough information to compute a duration. + */ + // TODO: b/319235116 - Handle encoder delay and padding when calculating duration. + public long computeDurationUs() { + if (frameCount == C.LENGTH_UNSET || frameCount == 0) { + // If the frame count is missing/invalid, the header can't be used to determine the duration. + return C.TIME_UNSET; + } + // Audio requires both a start and end PCM sample, so subtract one from the sample count before + // calculating the duration. + return Util.sampleCountToDurationUs( + (frameCount * header.samplesPerFrame) - 1, header.sampleRate); + } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingSeeker.java index 1a257b1ab7..f979931b87 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/XingSeeker.java @@ -18,7 +18,6 @@ package androidx.media3.extractor.mp3; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.util.Assertions; -import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.extractor.SeekPoint; @@ -32,35 +31,22 @@ import androidx.media3.extractor.SeekPoint; * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the * caller should reset it. * - * @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown. + * @param xingFrame The parsed Xing data from this audio frame. * @param position The position of the start of this frame in the stream. - * @param xingFrame The parsed XING data from this audio frame. * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required * information is not present. */ @Nullable - public static XingSeeker create(long inputLength, XingFrame xingFrame, long position) { - if (xingFrame.frameCount == C.LENGTH_UNSET || xingFrame.frameCount == 0) { - // If the frame count is missing/invalid, the header can't be used to determine the duration. + public static XingSeeker create(XingFrame xingFrame, long position) { + long durationUs = xingFrame.computeDurationUs(); + if (durationUs == C.TIME_UNSET) { return null; } - // TODO: b/319235116 - Handle encoder delay and padding when calculating duration. - // Audio requires both a start and end PCM sample, so subtract one from the sample count before - // calculating the duration. - long durationUs = - Util.sampleCountToDurationUs( - (xingFrame.frameCount * xingFrame.header.samplesPerFrame) - 1, - xingFrame.header.sampleRate); if (xingFrame.dataSize == C.LENGTH_UNSET || xingFrame.tableOfContents == null) { // If the size in bytes or table of contents is missing, the stream is not seekable. return new XingSeeker( position, xingFrame.header.frameSize, durationUs, xingFrame.header.bitrate); } - - if (inputLength != C.LENGTH_UNSET && inputLength != position + xingFrame.dataSize) { - Log.w( - TAG, "XING data size mismatch: " + inputLength + ", " + (position + xingFrame.dataSize)); - } return new XingSeeker( position, xingFrame.header.frameSize, diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mp3/XingSeekerTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mp3/XingSeekerTest.java index 9593084f70..3195428b7e 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/mp3/XingSeekerTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mp3/XingSeekerTest.java @@ -17,7 +17,6 @@ package androidx.media3.extractor.mp3; import static com.google.common.truth.Truth.assertThat; -import androidx.media3.common.C; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.Util; import androidx.media3.extractor.MpegAudioUtil; @@ -63,12 +62,10 @@ public final class XingSeekerTest { xingFrameHeader.setForHeaderData(XING_FRAME_HEADER_DATA); seeker = XingSeeker.create( - C.LENGTH_UNSET, XingFrame.parse(xingFrameHeader, new ParsableByteArray(XING_FRAME_PAYLOAD)), XING_FRAME_POSITION); seekerWithInputLength = XingSeeker.create( - C.LENGTH_UNSET, XingFrame.parse(xingFrameHeader, new ParsableByteArray(XING_FRAME_PAYLOAD)), XING_FRAME_POSITION); xingFrameSize = xingFrameHeader.frameSize; diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.0.dump index 67c6563098..f46ed23025 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.0.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.0.dump @@ -1,16 +1,16 @@ seekMap: isSeekable = true - duration = 2115750 + duration = 1070994 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] - getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] - getPosition(2115750) = [[timeUs=2089750, position=8586]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]] + getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]] + getPosition(1070994) = [[timeUs=1044925, position=8484]] numberOfTracks = 1 track 0: total output bytes = 8463 sample count = 41 format 0: - averageBitrate = 32000 + averageBitrate = 63216 sampleMimeType = audio/mpeg maxInputSize = 4096 channelCount = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.1.dump index 980313e82a..26d542cbdf 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.1.dump @@ -1,16 +1,16 @@ seekMap: isSeekable = true - duration = 2115750 + duration = 1070994 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] - getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] - getPosition(2115750) = [[timeUs=2089750, position=8586]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]] + getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]] + getPosition(1070994) = [[timeUs=1044925, position=8484]] numberOfTracks = 1 track 0: total output bytes = 5643 sample count = 27 format 0: - averageBitrate = 32000 + averageBitrate = 63216 sampleMimeType = audio/mpeg maxInputSize = 4096 channelCount = 1 @@ -19,111 +19,111 @@ track 0: encoderPadding = 1404 metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] sample 0: - time = 705000 + time = 356871 flags = 1 data = length 209, hash 6CCBBB3B sample 1: - time = 731122 + time = 382993 flags = 1 data = length 209, hash 34191E1 sample 2: - time = 757244 + time = 409115 flags = 1 data = length 209, hash 57323ED7 sample 3: - time = 783367 + time = 435238 flags = 1 data = length 209, hash 75618CF3 sample 4: - time = 809489 + time = 461360 flags = 1 data = length 209, hash 784C973B sample 5: - time = 835612 + time = 487483 flags = 1 data = length 209, hash 49106390 sample 6: - time = 861734 + time = 513605 flags = 1 data = length 209, hash 70F6A563 sample 7: - time = 887857 + time = 539728 flags = 1 data = length 209, hash 721882B0 sample 8: - time = 913979 + time = 565850 flags = 1 data = length 209, hash 81C62AEE sample 9: - time = 940102 + time = 591973 flags = 1 data = length 209, hash 16D22463 sample 10: - time = 966224 + time = 618095 flags = 1 data = length 209, hash 47033534 sample 11: - time = 992346 + time = 644217 flags = 1 data = length 209, hash CECB37A6 sample 12: - time = 1018469 + time = 670340 flags = 1 data = length 209, hash 6C9C307B sample 13: - time = 1044591 + time = 696462 flags = 1 data = length 209, hash 3EB1A364 sample 14: - time = 1070714 + time = 722585 flags = 1 data = length 209, hash 30962500 sample 15: - time = 1096836 + time = 748707 flags = 1 data = length 209, hash 2C5CCBB7 sample 16: - time = 1122959 + time = 774830 flags = 1 data = length 209, hash F9CB9E37 sample 17: - time = 1149081 + time = 800952 flags = 1 data = length 209, hash F75BC8C0 sample 18: - time = 1175204 + time = 827075 flags = 1 data = length 209, hash D00ED607 sample 19: - time = 1201326 + time = 853197 flags = 1 data = length 209, hash B4338395 sample 20: - time = 1227448 + time = 879319 flags = 1 data = length 209, hash E3E838A0 sample 21: - time = 1253571 + time = 905442 flags = 1 data = length 209, hash 2B0CF78 sample 22: - time = 1279693 + time = 931564 flags = 1 data = length 209, hash 31906FA9 sample 23: - time = 1305816 + time = 957687 flags = 1 data = length 209, hash C92FC08F sample 24: - time = 1331938 + time = 983809 flags = 1 data = length 209, hash 7C89994 sample 25: - time = 1358061 + time = 1009932 flags = 1 data = length 209, hash EC37743B sample 26: - time = 1384183 + time = 1036054 flags = 1 data = length 209, hash C974F6FB tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.2.dump index 27ea66dd9d..24662f77df 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.2.dump @@ -1,16 +1,16 @@ seekMap: isSeekable = true - duration = 2115750 + duration = 1070994 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] - getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] - getPosition(2115750) = [[timeUs=2089750, position=8586]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]] + getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]] + getPosition(1070994) = [[timeUs=1044925, position=8484]] numberOfTracks = 1 track 0: total output bytes = 2717 sample count = 13 format 0: - averageBitrate = 32000 + averageBitrate = 63216 sampleMimeType = audio/mpeg maxInputSize = 4096 channelCount = 1 @@ -19,55 +19,55 @@ track 0: encoderPadding = 1404 metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] sample 0: - time = 1436500 + time = 727157 flags = 1 data = length 209, hash 30962500 sample 1: - time = 1462622 + time = 753279 flags = 1 data = length 209, hash 2C5CCBB7 sample 2: - time = 1488744 + time = 779401 flags = 1 data = length 209, hash F9CB9E37 sample 3: - time = 1514867 + time = 805524 flags = 1 data = length 209, hash F75BC8C0 sample 4: - time = 1540989 + time = 831646 flags = 1 data = length 209, hash D00ED607 sample 5: - time = 1567112 + time = 857769 flags = 1 data = length 209, hash B4338395 sample 6: - time = 1593234 + time = 883891 flags = 1 data = length 209, hash E3E838A0 sample 7: - time = 1619357 + time = 910014 flags = 1 data = length 209, hash 2B0CF78 sample 8: - time = 1645479 + time = 936136 flags = 1 data = length 209, hash 31906FA9 sample 9: - time = 1671602 + time = 962259 flags = 1 data = length 209, hash C92FC08F sample 10: - time = 1697724 + time = 988381 flags = 1 data = length 209, hash 7C89994 sample 11: - time = 1723846 + time = 1014503 flags = 1 data = length 209, hash EC37743B sample 12: - time = 1749969 + time = 1040626 flags = 1 data = length 209, hash C974F6FB tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.3.dump index c8ea78c118..e961e045a8 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.3.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.3.dump @@ -1,16 +1,16 @@ seekMap: isSeekable = true - duration = 2115750 + duration = 1070994 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] - getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] - getPosition(2115750) = [[timeUs=2089750, position=8586]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]] + getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]] + getPosition(1070994) = [[timeUs=1044925, position=8484]] numberOfTracks = 1 track 0: total output bytes = 0 sample count = 0 format 0: - averageBitrate = 32000 + averageBitrate = 63216 sampleMimeType = audio/mpeg maxInputSize = 4096 channelCount = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.unknown_length.dump index 67c6563098..f46ed23025 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.unknown_length.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header-pcut-frame.mp3.unknown_length.dump @@ -1,16 +1,16 @@ seekMap: isSeekable = true - duration = 2115750 + duration = 1070994 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=331]] - getPosition(1057875) = [[timeUs=1040000, position=4387], [timeUs=1066000, position=4491]] - getPosition(2115750) = [[timeUs=2089750, position=8586]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26069, position=433]] + getPosition(535497) = [[timeUs=521386, position=4347], [timeUs=547456, position=4553]] + getPosition(1070994) = [[timeUs=1044925, position=8484]] numberOfTracks = 1 track 0: total output bytes = 8463 sample count = 41 format 0: - averageBitrate = 32000 + averageBitrate = 63216 sampleMimeType = audio/mpeg maxInputSize = 4096 channelCount = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.0.dump index 2703819722..09bea5d141 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.0.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.0.dump @@ -2,9 +2,9 @@ seekMap: isSeekable = true duration = 1044875 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] - getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] - getPosition(1044875) = [[timeUs=1018875, position=8378]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]] + getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]] + getPosition(1044875) = [[timeUs=1018750, position=8377]] numberOfTracks = 1 track 0: total output bytes = 8359 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.1.dump index 80ddbc5768..a74c470bb3 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.1.dump @@ -2,13 +2,13 @@ seekMap: isSeekable = true duration = 1044875 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] - getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] - getPosition(1044875) = [[timeUs=1018875, position=8378]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]] + getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]] + getPosition(1044875) = [[timeUs=1018750, position=8377]] numberOfTracks = 1 track 0: - total output bytes = 5643 - sample count = 27 + total output bytes = 5434 + sample count = 26 format 0: averageBitrate = 64000 sampleMimeType = audio/mpeg @@ -19,111 +19,107 @@ track 0: encoderPadding = 1404 metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] sample 0: - time = 339500 - flags = 1 - data = length 209, hash 6CCBBB3B - sample 1: - time = 365622 + time = 365625 flags = 1 data = length 209, hash 34191E1 - sample 2: - time = 391744 + sample 1: + time = 391747 flags = 1 data = length 209, hash 57323ED7 - sample 3: - time = 417867 + sample 2: + time = 417869 flags = 1 data = length 209, hash 75618CF3 - sample 4: - time = 443989 + sample 3: + time = 443992 flags = 1 data = length 209, hash 784C973B - sample 5: - time = 470112 + sample 4: + time = 470114 flags = 1 data = length 209, hash 49106390 - sample 6: - time = 496234 + sample 5: + time = 496237 flags = 1 data = length 209, hash 70F6A563 - sample 7: - time = 522357 + sample 6: + time = 522359 flags = 1 data = length 209, hash 721882B0 - sample 8: - time = 548479 + sample 7: + time = 548482 flags = 1 data = length 209, hash 81C62AEE - sample 9: - time = 574602 + sample 8: + time = 574604 flags = 1 data = length 209, hash 16D22463 - sample 10: - time = 600724 + sample 9: + time = 600727 flags = 1 data = length 209, hash 47033534 - sample 11: - time = 626846 + sample 10: + time = 626849 flags = 1 data = length 209, hash CECB37A6 - sample 12: - time = 652969 + sample 11: + time = 652971 flags = 1 data = length 209, hash 6C9C307B - sample 13: - time = 679091 + sample 12: + time = 679094 flags = 1 data = length 209, hash 3EB1A364 - sample 14: - time = 705214 + sample 13: + time = 705216 flags = 1 data = length 209, hash 30962500 - sample 15: - time = 731336 + sample 14: + time = 731339 flags = 1 data = length 209, hash 2C5CCBB7 - sample 16: - time = 757459 + sample 15: + time = 757461 flags = 1 data = length 209, hash F9CB9E37 - sample 17: - time = 783581 + sample 16: + time = 783584 flags = 1 data = length 209, hash F75BC8C0 - sample 18: - time = 809704 + sample 17: + time = 809706 flags = 1 data = length 209, hash D00ED607 - sample 19: - time = 835826 + sample 18: + time = 835829 flags = 1 data = length 209, hash B4338395 - sample 20: - time = 861948 + sample 19: + time = 861951 flags = 1 data = length 209, hash E3E838A0 - sample 21: - time = 888071 + sample 20: + time = 888073 flags = 1 data = length 209, hash 2B0CF78 - sample 22: - time = 914193 + sample 21: + time = 914196 flags = 1 data = length 209, hash 31906FA9 - sample 23: - time = 940316 + sample 22: + time = 940318 flags = 1 data = length 209, hash C92FC08F - sample 24: - time = 966438 + sample 23: + time = 966441 flags = 1 data = length 209, hash 7C89994 - sample 25: - time = 992561 + sample 24: + time = 992563 flags = 1 data = length 209, hash EC37743B - sample 26: - time = 1018683 + sample 25: + time = 1018686 flags = 1 data = length 209, hash C974F6FB tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.2.dump index 19af6f791a..0f738921cd 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.2.dump @@ -2,13 +2,13 @@ seekMap: isSeekable = true duration = 1044875 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] - getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] - getPosition(1044875) = [[timeUs=1018875, position=8378]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]] + getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]] + getPosition(1044875) = [[timeUs=1018750, position=8377]] numberOfTracks = 1 track 0: - total output bytes = 2926 - sample count = 14 + total output bytes = 2717 + sample count = 13 format 0: averageBitrate = 64000 sampleMimeType = audio/mpeg @@ -19,59 +19,55 @@ track 0: encoderPadding = 1404 metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] sample 0: - time = 679125 - flags = 1 - data = length 209, hash 3EB1A364 - sample 1: - time = 705247 + time = 705250 flags = 1 data = length 209, hash 30962500 - sample 2: - time = 731369 + sample 1: + time = 731372 flags = 1 data = length 209, hash 2C5CCBB7 - sample 3: - time = 757492 + sample 2: + time = 757494 flags = 1 data = length 209, hash F9CB9E37 - sample 4: - time = 783614 + sample 3: + time = 783617 flags = 1 data = length 209, hash F75BC8C0 - sample 5: - time = 809737 + sample 4: + time = 809739 flags = 1 data = length 209, hash D00ED607 - sample 6: - time = 835859 + sample 5: + time = 835862 flags = 1 data = length 209, hash B4338395 - sample 7: - time = 861982 + sample 6: + time = 861984 flags = 1 data = length 209, hash E3E838A0 - sample 8: - time = 888104 + sample 7: + time = 888107 flags = 1 data = length 209, hash 2B0CF78 - sample 9: - time = 914227 + sample 8: + time = 914229 flags = 1 data = length 209, hash 31906FA9 - sample 10: - time = 940349 + sample 9: + time = 940352 flags = 1 data = length 209, hash C92FC08F - sample 11: - time = 966471 + sample 10: + time = 966474 flags = 1 data = length 209, hash 7C89994 - sample 12: - time = 992594 + sample 11: + time = 992596 flags = 1 data = length 209, hash EC37743B - sample 13: - time = 1018716 + sample 12: + time = 1018719 flags = 1 data = length 209, hash C974F6FB tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.3.dump index 27a2286cec..d9fe0f8ac1 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.3.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.3.dump @@ -2,13 +2,13 @@ seekMap: isSeekable = true duration = 1044875 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] - getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] - getPosition(1044875) = [[timeUs=1018875, position=8378]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]] + getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]] + getPosition(1044875) = [[timeUs=1018750, position=8377]] numberOfTracks = 1 track 0: - total output bytes = 0 - sample count = 0 + total output bytes = 209 + sample count = 1 format 0: averageBitrate = 64000 sampleMimeType = audio/mpeg @@ -18,4 +18,8 @@ track 0: encoderDelay = 576 encoderPadding = 1404 metadata = entries=[TSSE: description=null: values=[Lavf58.45.100]] + sample 0: + time = 1018750 + flags = 1 + data = length 209, hash C974F6FB tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.unknown_length.dump index 2703819722..09bea5d141 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.unknown_length.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp3/test-cbr-info-header.mp3.unknown_length.dump @@ -2,9 +2,9 @@ seekMap: isSeekable = true duration = 1044875 getPosition(0) = [[timeUs=0, position=227]] - getPosition(1) = [[timeUs=0, position=227], [timeUs=26000, position=435]] - getPosition(522437) = [[timeUs=520000, position=4387], [timeUs=546000, position=4595]] - getPosition(1044875) = [[timeUs=1018875, position=8378]] + getPosition(1) = [[timeUs=0, position=227], [timeUs=26125, position=436]] + getPosition(522437) = [[timeUs=496375, position=4198], [timeUs=522500, position=4407]] + getPosition(1044875) = [[timeUs=1018750, position=8377]] numberOfTracks = 1 track 0: total output bytes = 8359