diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacFrameReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacFrameReader.java index 58397b3e21..1e498cb677 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacFrameReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacFrameReader.java @@ -22,7 +22,10 @@ import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import java.io.IOException; -/** Reads and peeks FLAC frame elements. */ +/** + * Reads and peeks FLAC frame elements according to the FLAC format specification. + */ public final class FlacFrameReader { /** Holds a sample number. */ @@ -35,10 +38,10 @@ public final class FlacFrameReader { * Checks whether the given FLAC frame header is valid and, if so, reads it and writes the frame * first sample number in {@code sampleNumberHolder}. * - *

If the header is valid, the position of {@code scratch} is moved to the byte following it. + *

If the header is valid, the position of {@code data} is moved to the byte following it. * Otherwise, there is no guarantee on the position. * - * @param scratch The array to read the data from, whose position must correspond to the frame + * @param data The array to read the data from, whose position must correspond to the frame * header. * @param flacStreamMetadata The stream metadata. * @param frameStartMarker The frame start marker of the stream. @@ -46,13 +49,13 @@ public final class FlacFrameReader { * @return Whether the frame header is valid. */ public static boolean checkAndReadFrameHeader( - ParsableByteArray scratch, + ParsableByteArray data, FlacStreamMetadata flacStreamMetadata, int frameStartMarker, SampleNumberHolder sampleNumberHolder) { - int frameStartPosition = scratch.getPosition(); + int frameStartPosition = data.getPosition(); - long frameHeaderBytes = scratch.readUnsignedInt(); + long frameHeaderBytes = data.readUnsignedInt(); if (frameHeaderBytes >>> 16 != frameStartMarker) { return false; } @@ -67,10 +70,10 @@ public final class FlacFrameReader { && checkBitsPerSample(bitsPerSampleKey, flacStreamMetadata) && !reservedBit && checkAndReadFirstSampleNumber( - scratch, flacStreamMetadata, isBlockSizeVariable, sampleNumberHolder) - && checkAndReadBlockSizeSamples(scratch, flacStreamMetadata, blockSizeKey) - && checkAndReadSampleRate(scratch, flacStreamMetadata, sampleRateKey) - && checkAndReadCrc(scratch, frameStartPosition); + data, flacStreamMetadata, isBlockSizeVariable, sampleNumberHolder) + && checkAndReadBlockSizeSamples(data, flacStreamMetadata, blockSizeKey) + && checkAndReadSampleRate(data, flacStreamMetadata, sampleRateKey) + && checkAndReadCrc(data, frameStartPosition); } /** @@ -161,12 +164,12 @@ public final class FlacFrameReader { /** * Reads the given block size. * - * @param scratch The array to read the data from, whose position must correspond to the block - * size bits. + * @param data The array to read the data from, whose position must correspond to the block size + * bits. * @param blockSizeKey The key in the block size lookup table. * @return The block size in samples. */ - public static int readFrameBlockSizeSamplesFromKey(ParsableByteArray scratch, int blockSizeKey) { + public static int readFrameBlockSizeSamplesFromKey(ParsableByteArray data, int blockSizeKey) { switch (blockSizeKey) { case 1: return 192; @@ -176,9 +179,9 @@ public final class FlacFrameReader { case 5: return 576 << (blockSizeKey - 2); case 6: - return scratch.readUnsignedByte() + 1; + return data.readUnsignedByte() + 1; case 7: - return scratch.readUnsignedShort() + 1; + return data.readUnsignedShort() + 1; case 8: case 9: case 10: @@ -230,10 +233,10 @@ public final class FlacFrameReader { * Checks whether the given sample number is valid and, if so, reads it and writes it in {@code * sampleNumberHolder}. * - *

If the sample number is valid, the position of {@code scratch} is moved to the byte - * following it. Otherwise, there is no guarantee on the position. + *

If the sample number is valid, the position of {@code data} is moved to the byte following + * it. Otherwise, there is no guarantee on the position. * - * @param scratch The array to read the data from, whose position must correspond to the sample + * @param data The array to read the data from, whose position must correspond to the sample * number data. * @param flacStreamMetadata The stream metadata. * @param isBlockSizeVariable Whether the stream blocking strategy is variable block size or fixed @@ -242,13 +245,13 @@ public final class FlacFrameReader { * @return Whether the sample number is valid. */ private static boolean checkAndReadFirstSampleNumber( - ParsableByteArray scratch, + ParsableByteArray data, FlacStreamMetadata flacStreamMetadata, boolean isBlockSizeVariable, SampleNumberHolder sampleNumberHolder) { long utf8Value; try { - utf8Value = scratch.readUtf8EncodedLong(); + utf8Value = data.readUtf8EncodedLong(); } catch (NumberFormatException e) { return false; } @@ -262,18 +265,18 @@ public final class FlacFrameReader { * Checks whether the given frame block size key and block size bits are valid and, if so, reads * the block size bits. * - *

If the block size is valid, the position of {@code scratch} is moved to the byte following - * the block size bits. Otherwise, there is no guarantee on the position. + *

If the block size is valid, the position of {@code data} is moved to the byte following the + * block size bits. Otherwise, there is no guarantee on the position. * - * @param scratch The array to read the data from, whose position must correspond to the block - * size bits. + * @param data The array to read the data from, whose position must correspond to the block size + * bits. * @param flacStreamMetadata The stream metadata. * @param blockSizeKey The key in the block size lookup table. * @return Whether the block size is valid. */ private static boolean checkAndReadBlockSizeSamples( - ParsableByteArray scratch, FlacStreamMetadata flacStreamMetadata, int blockSizeKey) { - int blockSizeSamples = readFrameBlockSizeSamplesFromKey(scratch, blockSizeKey); + ParsableByteArray data, FlacStreamMetadata flacStreamMetadata, int blockSizeKey) { + int blockSizeSamples = readFrameBlockSizeSamplesFromKey(data, blockSizeKey); return blockSizeSamples != -1 && blockSizeSamples <= flacStreamMetadata.maxBlockSizeSamples; } @@ -281,26 +284,25 @@ public final class FlacFrameReader { * Checks whether the given sample rate key and sample rate bits are valid and, if so, reads the * sample rate bits. * - *

If the sample rate is valid, the position of {@code scratch} is moved to the byte following - * the sample rate bits. Otherwise, there is no guarantee on the position. + *

If the sample rate is valid, the position of {@code data} is moved to the byte following the + * sample rate bits. Otherwise, there is no guarantee on the position. * - * @param scratch The array to read the data from, whose position must indicate the sample rate - * bits. + * @param data The array to read the data from, whose position must indicate the sample rate bits. * @param flacStreamMetadata The stream metadata. * @param sampleRateKey The key in the sample rate lookup table. * @return Whether the sample rate is valid. */ private static boolean checkAndReadSampleRate( - ParsableByteArray scratch, FlacStreamMetadata flacStreamMetadata, int sampleRateKey) { + ParsableByteArray data, FlacStreamMetadata flacStreamMetadata, int sampleRateKey) { int expectedSampleRate = flacStreamMetadata.sampleRate; if (sampleRateKey == 0) { return true; } else if (sampleRateKey <= 11) { return sampleRateKey == flacStreamMetadata.sampleRateLookupKey; } else if (sampleRateKey == 12) { - return scratch.readUnsignedByte() * 1000 == expectedSampleRate; + return data.readUnsignedByte() * 1000 == expectedSampleRate; } else if (sampleRateKey <= 14) { - int sampleRate = scratch.readUnsignedShort(); + int sampleRate = data.readUnsignedShort(); if (sampleRateKey == 14) { sampleRate *= 10; } @@ -313,20 +315,20 @@ public final class FlacFrameReader { /** * Checks whether the given CRC is valid and, if so, reads it. * - *

If the CRC is valid, the position of {@code scratch} is moved to the byte following it. + *

If the CRC is valid, the position of {@code data} is moved to the byte following it. * Otherwise, there is no guarantee on the position. * - *

The {@code scratch} array must contain the whole frame header. + *

The {@code data} array must contain the whole frame header. * - * @param scratch The array to read the data from, whose position must indicate the CRC. - * @param frameStartPosition The frame start offset in {@code scratch}. + * @param data The array to read the data from, whose position must indicate the CRC. + * @param frameStartPosition The frame start offset in {@code data}. * @return Whether the CRC is valid. */ - private static boolean checkAndReadCrc(ParsableByteArray scratch, int frameStartPosition) { - int crc = scratch.readUnsignedByte(); - int frameEndPosition = scratch.getPosition(); + private static boolean checkAndReadCrc(ParsableByteArray data, int frameStartPosition) { + int crc = data.readUnsignedByte(); + int frameEndPosition = data.getPosition(); int expectedCrc = - Util.crc8(scratch.data, frameStartPosition, frameEndPosition - 1, /* initialValue= */ 0); + Util.crc8(data.data, frameStartPosition, frameEndPosition - 1, /* initialValue= */ 0); return crc == expectedCrc; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacMetadataReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacMetadataReader.java index c3b0d75300..49d4558ddc 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacMetadataReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacMetadataReader.java @@ -32,7 +32,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -/** Reads and peeks FLAC stream metadata elements from an {@link ExtractorInput}. */ +/** + * Reads and peeks FLAC stream metadata elements according to the FLAC format specification. + */ public final class FlacMetadataReader { /** Holds a {@link FlacStreamMetadata}. */ @@ -47,9 +50,7 @@ public final class FlacMetadataReader { private static final int STREAM_MARKER = 0x664C6143; // ASCII for "fLaC" private static final int SYNC_CODE = 0x3FFE; - private static final int STREAM_INFO_TYPE = 0; - private static final int VORBIS_COMMENT_TYPE = 4; - private static final int PICTURE_TYPE = 6; + private static final int SEEK_POINT_SIZE = 18; /** * Peeks ID3 Data. @@ -167,18 +168,21 @@ public final class FlacMetadataReader { boolean isLastMetadataBlock = scratch.readBit(); int type = scratch.readBits(7); int length = FlacConstants.METADATA_BLOCK_HEADER_SIZE + scratch.readBits(24); - if (type == STREAM_INFO_TYPE) { + if (type == FlacConstants.METADATA_TYPE_STREAM_INFO) { metadataHolder.flacStreamMetadata = readStreamInfoBlock(input); } else { FlacStreamMetadata flacStreamMetadata = metadataHolder.flacStreamMetadata; if (flacStreamMetadata == null) { throw new IllegalArgumentException(); } - if (type == VORBIS_COMMENT_TYPE) { + if (type == FlacConstants.METADATA_TYPE_SEEK_TABLE) { + FlacStreamMetadata.SeekTable seekTable = readSeekTableMetadataBlock(input, length); + metadataHolder.flacStreamMetadata = flacStreamMetadata.copyWithSeekTable(seekTable); + } else if (type == FlacConstants.METADATA_TYPE_VORBIS_COMMENT) { List vorbisComments = readVorbisCommentMetadataBlock(input, length); metadataHolder.flacStreamMetadata = flacStreamMetadata.copyWithVorbisComments(vorbisComments); - } else if (type == PICTURE_TYPE) { + } else if (type == FlacConstants.METADATA_TYPE_PICTURE) { PictureFrame pictureFrame = readPictureMetadataBlock(input, length); metadataHolder.flacStreamMetadata = flacStreamMetadata.copyWithPictureFrames(Collections.singletonList(pictureFrame)); @@ -190,6 +194,42 @@ public final class FlacMetadataReader { return isLastMetadataBlock; } + /** + * Reads a FLAC seek table metadata block. + * + *

The position of {@code data} is moved to the byte following the seek table metadata block + * (placeholder points included). + * + * @param data The array to read the data from, whose position must correspond to the seek table + * metadata block (header included). + * @return The seek table, without the placeholder points. + */ + public static FlacStreamMetadata.SeekTable readSeekTableMetadataBlock(ParsableByteArray data) { + data.skipBytes(1); + int length = data.readUnsignedInt24(); + + long seekTableEndPosition = data.getPosition() + length; + int seekPointCount = length / SEEK_POINT_SIZE; + long[] pointSampleNumbers = new long[seekPointCount]; + long[] pointOffsets = new long[seekPointCount]; + for (int i = 0; i < seekPointCount; i++) { + // The sample number is expected to fit in a signed long, except if it is a placeholder, in + // which case its value is -1. + long sampleNumber = data.readLong(); + if (sampleNumber == -1) { + pointSampleNumbers = Arrays.copyOf(pointSampleNumbers, i); + pointOffsets = Arrays.copyOf(pointOffsets, i); + break; + } + pointSampleNumbers[i] = sampleNumber; + pointOffsets[i] = data.readLong(); + data.skipBytes(2); + } + + data.skipBytes((int) (seekTableEndPosition - data.getPosition())); + return new FlacStreamMetadata.SeekTable(pointSampleNumbers, pointOffsets); + } + /** * Returns the frame start marker, consisting of the 2 first bytes of the first frame. * @@ -227,6 +267,13 @@ public final class FlacMetadataReader { scratchData, /* offset= */ FlacConstants.METADATA_BLOCK_HEADER_SIZE); } + private static FlacStreamMetadata.SeekTable readSeekTableMetadataBlock( + ExtractorInput input, int length) throws IOException, InterruptedException { + ParsableByteArray scratch = new ParsableByteArray(length); + input.readFully(scratch.data, 0, length); + return readSeekTableMetadataBlock(scratch); + } + private static List readVorbisCommentMetadataBlock(ExtractorInput input, int length) throws IOException, InterruptedException { ParsableByteArray scratch = new ParsableByteArray(length); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacSeekTableSeekMap.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacSeekTableSeekMap.java new file mode 100644 index 0000000000..a711f09e2f --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/FlacSeekTableSeekMap.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.extractor; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.FlacStreamMetadata; +import com.google.android.exoplayer2.util.Util; + +/** + * A {@link SeekMap} implementation for FLAC streams that contain a seek table. + */ +public final class FlacSeekTableSeekMap implements SeekMap { + + private final FlacStreamMetadata flacStreamMetadata; + private final long firstFrameOffset; + + /** + * Creates a seek map from the FLAC stream seek table. + * + * @param flacStreamMetadata The stream metadata. + * @param firstFrameOffset The byte offset of the first frame in the stream. + */ + public FlacSeekTableSeekMap(FlacStreamMetadata flacStreamMetadata, long firstFrameOffset) { + this.flacStreamMetadata = flacStreamMetadata; + this.firstFrameOffset = firstFrameOffset; + } + + @Override + public boolean isSeekable() { + return true; + } + + @Override + public long getDurationUs() { + return flacStreamMetadata.getDurationUs(); + } + + @Override + public SeekPoints getSeekPoints(long timeUs) { + Assertions.checkNotNull(flacStreamMetadata.seekTable); + long[] pointSampleNumbers = flacStreamMetadata.seekTable.pointSampleNumbers; + long[] pointOffsets = flacStreamMetadata.seekTable.pointOffsets; + + long targetSampleNumber = flacStreamMetadata.getSampleNumber(timeUs); + int index = + Util.binarySearchFloor( + pointSampleNumbers, + targetSampleNumber, + /* inclusive= */ true, + /* stayInBounds= */ false); + + long seekPointSampleNumber = index == -1 ? 0 : pointSampleNumbers[index]; + long seekPointOffsetFromFirstFrame = index == -1 ? 0 : pointOffsets[index]; + SeekPoint seekPoint = getSeekPoint(seekPointSampleNumber, seekPointOffsetFromFirstFrame); + if (seekPoint.timeUs == timeUs || index == pointSampleNumbers.length - 1) { + return new SeekPoints(seekPoint); + } else { + SeekPoint secondSeekPoint = + getSeekPoint(pointSampleNumbers[index + 1], pointOffsets[index + 1]); + return new SeekPoints(seekPoint, secondSeekPoint); + } + } + + private SeekPoint getSeekPoint(long sampleNumber, long offsetFromFirstFrame) { + long seekTimeUs = sampleNumber * C.MICROS_PER_SECOND / flacStreamMetadata.sampleRate; + long seekPosition = firstFrameOffset + offsetFromFirstFrame; + return new SeekPoint(seekTimeUs, seekPosition); + } +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flac/FlacExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flac/FlacExtractor.java index 078fdddbd1..831a900b94 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flac/FlacExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flac/FlacExtractor.java @@ -27,6 +27,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.FlacFrameReader; import com.google.android.exoplayer2.extractor.FlacFrameReader.SampleNumberHolder; import com.google.android.exoplayer2.extractor.FlacMetadataReader; +import com.google.android.exoplayer2.extractor.FlacSeekTableSeekMap; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.TrackOutput; @@ -41,7 +42,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -// TODO: implement seeking using the optional seek table. /** * Extracts data from FLAC container format. * @@ -175,11 +175,10 @@ public final class FlacExtractor implements Extractor { public void seek(long position, long timeUs) { if (position == 0) { state = STATE_READ_ID3_METADATA; - currentFrameFirstSampleNumber = 0; } else if (binarySearchSeeker != null) { - currentFrameFirstSampleNumber = SAMPLE_NUMBER_UNKNOWN; binarySearchSeeker.setSeekTargetUs(timeUs); } + currentFrameFirstSampleNumber = timeUs == 0 ? 0 : SAMPLE_NUMBER_UNKNOWN; currentFrameBytesWritten = 0; scratch.reset(); } @@ -231,7 +230,7 @@ public final class FlacExtractor implements Extractor { castNonNull(extractorOutput) .seekMap( getSeekMap( - /* firstFramePosition= */ (int) input.getPosition(), + /* firstFramePosition= */ input.getPosition(), /* streamLength= */ input.getLength())); state = STATE_READ_FRAMES; @@ -242,7 +241,7 @@ public final class FlacExtractor implements Extractor { Assertions.checkNotNull(trackOutput); Assertions.checkNotNull(flacStreamMetadata); - // Handle pending seek if necessary. + // Handle pending binary search seek if necessary. if (binarySearchSeeker != null && binarySearchSeeker.isSeeking()) { return binarySearchSeeker.handlePendingSeek(input, seekPosition); } @@ -299,39 +298,42 @@ public final class FlacExtractor implements Extractor { return Extractor.RESULT_CONTINUE; } - private SeekMap getSeekMap(int firstFramePosition, long streamLength) { + private SeekMap getSeekMap(long firstFramePosition, long streamLength) { Assertions.checkNotNull(flacStreamMetadata); - if (streamLength == C.LENGTH_UNSET || flacStreamMetadata.totalSamples == 0) { + if (flacStreamMetadata.seekTable != null) { + return new FlacSeekTableSeekMap(flacStreamMetadata, firstFramePosition); + } else if (streamLength != C.LENGTH_UNSET && flacStreamMetadata.totalSamples > 0) { + binarySearchSeeker = + new FlacBinarySearchSeeker( + flacStreamMetadata, frameStartMarker, firstFramePosition, streamLength); + return binarySearchSeeker.getSeekMap(); + } else { return new SeekMap.Unseekable(flacStreamMetadata.getDurationUs()); } - binarySearchSeeker = - new FlacBinarySearchSeeker( - flacStreamMetadata, frameStartMarker, firstFramePosition, streamLength); - return binarySearchSeeker.getSeekMap(); } /** - * Searches for the start of a frame in {@code scratch}. + * Searches for the start of a frame in {@code data}. * *

* - * @param scratch The array to be searched. - * @param foundEndOfInput If the end of input was met when filling in the {@code scratch}. + * @param data The array to be searched. + * @param foundEndOfInput If the end of input was met when filling in the {@code data}. * @return The number of the first sample in the frame found, or {@code SAMPLE_NUMBER_UNKNOWN} if * the search was not successful. */ - private long findFrame(ParsableByteArray scratch, boolean foundEndOfInput) { + private long findFrame(ParsableByteArray data, boolean foundEndOfInput) { Assertions.checkNotNull(flacStreamMetadata); - int frameOffset = scratch.getPosition(); - while (frameOffset <= scratch.limit() - FlacConstants.MAX_FRAME_HEADER_SIZE) { - scratch.setPosition(frameOffset); + int frameOffset = data.getPosition(); + while (frameOffset <= data.limit() - FlacConstants.MAX_FRAME_HEADER_SIZE) { + data.setPosition(frameOffset); if (FlacFrameReader.checkAndReadFrameHeader( - scratch, flacStreamMetadata, frameStartMarker, sampleNumberHolder)) { - scratch.setPosition(frameOffset); + data, flacStreamMetadata, frameStartMarker, sampleNumberHolder)) { + data.setPosition(frameOffset); return sampleNumberHolder.sampleNumber; } frameOffset++; @@ -339,9 +341,9 @@ public final class FlacExtractor implements Extractor { if (foundEndOfInput) { // Reached the end of the file. Assume it's the end of the frame. - scratch.setPosition(scratch.limit()); + data.setPosition(data.limit()); } else { - scratch.setPosition(frameOffset); + data.setPosition(frameOffset); } return SAMPLE_NUMBER_UNKNOWN; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java index 9889c7f220..f99b2420cc 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java @@ -17,8 +17,11 @@ package com.google.android.exoplayer2.extractor.ogg; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.FlacFrameReader; +import com.google.android.exoplayer2.extractor.FlacMetadataReader; +import com.google.android.exoplayer2.extractor.FlacSeekTableSeekMap; import com.google.android.exoplayer2.extractor.SeekMap; -import com.google.android.exoplayer2.extractor.SeekPoint; +import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.FlacConstants; import com.google.android.exoplayer2.util.FlacStreamMetadata; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; @@ -31,7 +34,6 @@ import java.util.Arrays; /* package */ final class FlacReader extends StreamReader { private static final byte AUDIO_PACKET_TYPE = (byte) 0xFF; - private static final byte SEEKTABLE_PACKET_TYPE = 0x03; private static final int FRAME_HEADER_SAMPLE_NUMBER_OFFSET = 4; @@ -71,9 +73,11 @@ import java.util.Arrays; streamMetadata = new FlacStreamMetadata(data, 17); byte[] metadata = Arrays.copyOfRange(data, 9, packet.limit()); setupData.format = streamMetadata.getFormat(metadata, /* id3Metadata= */ null); - } else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) { + } else if ((data[0] & 0x7F) == FlacConstants.METADATA_TYPE_SEEK_TABLE) { flacOggSeeker = new FlacOggSeeker(); - flacOggSeeker.parseSeekTable(packet); + FlacStreamMetadata.SeekTable seekTable = + FlacMetadataReader.readSeekTableMetadataBlock(packet); + streamMetadata = streamMetadata.copyWithSeekTable(seekTable); } else if (isAudioPacket(data)) { if (flacOggSeeker != null) { flacOggSeeker.setFirstFrameOffset(position); @@ -96,13 +100,8 @@ import java.util.Arrays; return result; } - private class FlacOggSeeker implements OggSeeker, SeekMap { + private class FlacOggSeeker implements OggSeeker { - private static final int METADATA_LENGTH_OFFSET = 1; - private static final int SEEK_POINT_SIZE = 18; - - private long[] seekPointGranules; - private long[] seekPointOffsets; private long firstFrameOffset; private long pendingSeekGranule; @@ -115,27 +114,6 @@ import java.util.Arrays; this.firstFrameOffset = firstFrameOffset; } - /** - * Parses a FLAC file seek table metadata structure and initializes internal fields. - * - * @param data A {@link ParsableByteArray} including whole seek table metadata block. Its - * position should be set to the beginning of the block. - * @see FLAC format - * METADATA_BLOCK_SEEKTABLE - */ - public void parseSeekTable(ParsableByteArray data) { - data.skipBytes(METADATA_LENGTH_OFFSET); - int length = data.readUnsignedInt24(); - int numberOfSeekPoints = length / SEEK_POINT_SIZE; - seekPointGranules = new long[numberOfSeekPoints]; - seekPointOffsets = new long[numberOfSeekPoints]; - for (int i = 0; i < numberOfSeekPoints; i++) { - seekPointGranules[i] = data.readLong(); - seekPointOffsets[i] = data.readLong(); - data.skipBytes(2); // Skip "Number of samples in the target frame." - } - } - @Override public long read(ExtractorInput input) throws IOException, InterruptedException { if (pendingSeekGranule >= 0) { @@ -148,40 +126,16 @@ import java.util.Arrays; @Override public void startSeek(long targetGranule) { + Assertions.checkNotNull(streamMetadata.seekTable); + long[] seekPointGranules = streamMetadata.seekTable.pointSampleNumbers; int index = Util.binarySearchFloor(seekPointGranules, targetGranule, true, true); pendingSeekGranule = seekPointGranules[index]; } @Override public SeekMap createSeekMap() { - return this; - } - - @Override - public boolean isSeekable() { - return true; - } - - @Override - public SeekPoints getSeekPoints(long timeUs) { - long granule = convertTimeToGranule(timeUs); - int index = Util.binarySearchFloor(seekPointGranules, granule, true, true); - long seekTimeUs = convertGranuleToTime(seekPointGranules[index]); - long seekPosition = firstFrameOffset + seekPointOffsets[index]; - SeekPoint seekPoint = new SeekPoint(seekTimeUs, seekPosition); - if (seekTimeUs >= timeUs || index == seekPointGranules.length - 1) { - return new SeekPoints(seekPoint); - } else { - long secondSeekTimeUs = convertGranuleToTime(seekPointGranules[index + 1]); - long secondSeekPosition = firstFrameOffset + seekPointOffsets[index + 1]; - SeekPoint secondSeekPoint = new SeekPoint(secondSeekTimeUs, secondSeekPosition); - return new SeekPoints(seekPoint, secondSeekPoint); - } - } - - @Override - public long getDurationUs() { - return streamMetadata.getDurationUs(); + Assertions.checkState(firstFrameOffset != -1); + return new FlacSeekTableSeekMap(streamMetadata, firstFrameOffset); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/FlacConstants.java b/library/core/src/main/java/com/google/android/exoplayer2/util/FlacConstants.java index 75b153d6f9..0d36d78ff9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/FlacConstants.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/FlacConstants.java @@ -29,5 +29,14 @@ public final class FlacConstants { /** Maximum size of a FLAC frame header in bytes. */ public static final int MAX_FRAME_HEADER_SIZE = 16; + /** Stream info metadata block type. */ + public static final int METADATA_TYPE_STREAM_INFO = 0; + /** Seek table metadata block type. */ + public static final int METADATA_TYPE_SEEK_TABLE = 3; + /** Vorbis comment metadata block type. */ + public static final int METADATA_TYPE_VORBIS_COMMENT = 4; + /** Picture metadata block type. */ + public static final int METADATA_TYPE_PICTURE = 6; + private FlacConstants() {} } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamMetadata.java b/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamMetadata.java index a0b26f8e56..deced8ebe6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamMetadata.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamMetadata.java @@ -30,6 +30,8 @@ import java.util.List; * * @see FLAC format * METADATA_BLOCK_STREAMINFO + * @see FLAC format + * METADATA_BLOCK_SEEKTABLE * @see FLAC format * METADATA_BLOCK_VORBIS_COMMENT * @see FLAC format @@ -37,6 +39,19 @@ import java.util.List; */ public final class FlacStreamMetadata { + /** A FLAC seek table. */ + public static class SeekTable { + /** Seek points sample numbers. */ + public final long[] pointSampleNumbers; + /** Seek points byte offsets from the first frame. */ + public final long[] pointOffsets; + + public SeekTable(long[] pointSampleNumbers, long[] pointOffsets) { + this.pointSampleNumbers = pointSampleNumbers; + this.pointOffsets = pointOffsets; + } + } + private static final String TAG = "FlacStreamMetadata"; /** Indicates that a value is not in the corresponding lookup table. */ @@ -79,7 +94,8 @@ public final class FlacStreamMetadata { public final int bitsPerSampleLookupKey; /** Total number of samples, or 0 if the value is unknown. */ public final long totalSamples; - + /** Seek table, or {@code null} if it is not provided. */ + @Nullable public final SeekTable seekTable; /** Content metadata, or {@code null} if it is not provided. */ @Nullable private final Metadata metadata; @@ -102,6 +118,7 @@ public final class FlacStreamMetadata { bitsPerSample = scratch.readBits(5) + 1; bitsPerSampleLookupKey = getBitsPerSampleLookupKey(bitsPerSample); totalSamples = scratch.readBitsToLong(36); + seekTable = null; metadata = null; } @@ -126,6 +143,7 @@ public final class FlacStreamMetadata { channels, bitsPerSample, totalSamples, + /* seekTable= */ null, buildMetadata(vorbisComments, pictureFrames)); } @@ -138,6 +156,7 @@ public final class FlacStreamMetadata { int channels, int bitsPerSample, long totalSamples, + @Nullable SeekTable seekTable, @Nullable Metadata metadata) { this.minBlockSizeSamples = minBlockSizeSamples; this.maxBlockSizeSamples = maxBlockSizeSamples; @@ -149,6 +168,7 @@ public final class FlacStreamMetadata { this.bitsPerSample = bitsPerSample; this.bitsPerSampleLookupKey = getBitsPerSampleLookupKey(bitsPerSample); this.totalSamples = totalSamples; + this.seekTable = seekTable; this.metadata = metadata; } @@ -239,6 +259,21 @@ public final class FlacStreamMetadata { return metadata == null ? other : metadata.copyWithAppendedEntriesFrom(other); } + /** Returns a copy of {@code this} with the seek table replaced by the one given. */ + public FlacStreamMetadata copyWithSeekTable(@Nullable SeekTable seekTable) { + return new FlacStreamMetadata( + minBlockSizeSamples, + maxBlockSizeSamples, + minFrameSize, + maxFrameSize, + sampleRate, + channels, + bitsPerSample, + totalSamples, + seekTable, + metadata); + } + /** Returns a copy of {@code this} with the given Vorbis comments added to the metadata. */ public FlacStreamMetadata copyWithVorbisComments(List vorbisComments) { @Nullable @@ -254,6 +289,7 @@ public final class FlacStreamMetadata { channels, bitsPerSample, totalSamples, + seekTable, appendedMetadata); } @@ -272,6 +308,7 @@ public final class FlacStreamMetadata { channels, bitsPerSample, totalSamples, + seekTable, appendedMetadata); } diff --git a/library/core/src/test/assets/flac/bear.flac.3.dump b/library/core/src/test/assets/flac/bear.flac.3.dump index cf1f7e9946..7553b6bdcb 100644 --- a/library/core/src/test/assets/flac/bear.flac.3.dump +++ b/library/core/src/test/assets/flac/bear.flac.3.dump @@ -27,9 +27,13 @@ track 0: metadata = null initializationData: data = length 42, hash 83F6895 - total output bytes = 445 - sample count = 1 + total output bytes = 3829 + sample count = 2 sample 0: + time = 2645333 + flags = 1 + data = length 3384, hash 938BCDD9 + sample 1: time = 2730666 flags = 1 data = length 445, hash A388E3D6 diff --git a/library/core/src/test/assets/flac/bear_no_min_max_frame_size.flac.3.dump b/library/core/src/test/assets/flac/bear_no_min_max_frame_size.flac.3.dump index 1704e4df0c..b27b4b7295 100644 --- a/library/core/src/test/assets/flac/bear_no_min_max_frame_size.flac.3.dump +++ b/library/core/src/test/assets/flac/bear_no_min_max_frame_size.flac.3.dump @@ -27,9 +27,13 @@ track 0: metadata = null initializationData: data = length 42, hash 9218FDB7 - total output bytes = 445 - sample count = 1 + total output bytes = 3829 + sample count = 2 sample 0: + time = 2645333 + flags = 1 + data = length 3384, hash 938BCDD9 + sample 1: time = 2730666 flags = 1 data = length 445, hash A388E3D6 diff --git a/library/core/src/test/assets/flac/bear_no_min_max_frame_size.flac.unklen.dump b/library/core/src/test/assets/flac/bear_no_min_max_frame_size.flac.unklen.dump deleted file mode 100644 index 4ef7138487..0000000000 --- a/library/core/src/test/assets/flac/bear_no_min_max_frame_size.flac.unklen.dump +++ /dev/null @@ -1,164 +0,0 @@ -seekMap: - isSeekable = false - duration = 2741000 - getPosition(0) = [[timeUs=0, position=0]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = -1 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - metadata = null - initializationData: - data = length 42, hash 9218FDB7 - total output bytes = 164431 - sample count = 33 - sample 0: - time = 0 - flags = 1 - data = length 5030, hash D2B60530 - sample 1: - time = 85333 - flags = 1 - data = length 5066, hash 4C932A54 - sample 2: - time = 170666 - flags = 1 - data = length 5112, hash 7E5A7B61 - sample 3: - time = 256000 - flags = 1 - data = length 5044, hash 7EF93F13 - sample 4: - time = 341333 - flags = 1 - data = length 4943, hash DE7E27F8 - sample 5: - time = 426666 - flags = 1 - data = length 5121, hash 6D0D0B40 - sample 6: - time = 512000 - flags = 1 - data = length 5068, hash 9924644F - sample 7: - time = 597333 - flags = 1 - data = length 5143, hash 6C34F0CE - sample 8: - time = 682666 - flags = 1 - data = length 5109, hash E3B7BEFB - sample 9: - time = 768000 - flags = 1 - data = length 5129, hash 44111D9B - sample 10: - time = 853333 - flags = 1 - data = length 5031, hash 9D55EA53 - sample 11: - time = 938666 - flags = 1 - data = length 5119, hash E1CB9BA6 - sample 12: - time = 1024000 - flags = 1 - data = length 5360, hash 17265C5D - sample 13: - time = 1109333 - flags = 1 - data = length 5340, hash A90FDDF1 - sample 14: - time = 1194666 - flags = 1 - data = length 5162, hash 31F65AD5 - sample 15: - time = 1280000 - flags = 1 - data = length 5168, hash F2394F2D - sample 16: - time = 1365333 - flags = 1 - data = length 5776, hash 58437AB3 - sample 17: - time = 1450666 - flags = 1 - data = length 5394, hash EBAB20A8 - sample 18: - time = 1536000 - flags = 1 - data = length 5168, hash BF37C7A5 - sample 19: - time = 1621333 - flags = 1 - data = length 5324, hash 59546B7B - sample 20: - time = 1706666 - flags = 1 - data = length 5172, hash 6036EF0B - sample 21: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 22: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 23: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 24: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 25: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 26: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 27: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 28: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 29: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 30: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 31: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 32: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_no_num_samples.flac.0.dump b/library/core/src/test/assets/flac/bear_no_num_samples.flac.0.dump index 45b75392b3..0a1736f3e2 100644 --- a/library/core/src/test/assets/flac/bear_no_num_samples.flac.0.dump +++ b/library/core/src/test/assets/flac/bear_no_num_samples.flac.0.dump @@ -1,7 +1,7 @@ seekMap: - isSeekable = false + isSeekable = true duration = UNSET TIME - getPosition(0) = [[timeUs=0, position=0]] + getPosition(0) = [[timeUs=0, position=8880]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/test/assets/flac/bear_no_seek_table_no_num_samples.flac b/library/core/src/test/assets/flac/bear_no_seek_table_no_num_samples.flac new file mode 100644 index 0000000000..0f1dff1e84 Binary files /dev/null and b/library/core/src/test/assets/flac/bear_no_seek_table_no_num_samples.flac differ diff --git a/library/core/src/test/assets/flac/bear.flac.unklen.dump b/library/core/src/test/assets/flac/bear_no_seek_table_no_num_samples.flac.0.dump similarity index 98% rename from library/core/src/test/assets/flac/bear.flac.unklen.dump rename to library/core/src/test/assets/flac/bear_no_seek_table_no_num_samples.flac.0.dump index bd8f1827e4..45b75392b3 100644 --- a/library/core/src/test/assets/flac/bear.flac.unklen.dump +++ b/library/core/src/test/assets/flac/bear_no_seek_table_no_num_samples.flac.0.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = false - duration = 2741000 + duration = UNSET TIME getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: @@ -26,7 +26,7 @@ track 0: drmInitData = - metadata = null initializationData: - data = length 42, hash 83F6895 + data = length 42, hash 49FA2C21 total output bytes = 164431 sample count = 33 sample 0: diff --git a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac index 2fc58661eb..e2e0b6a83d 100644 Binary files a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac and b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac differ diff --git a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.0.dump b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.0.dump index 4aab67d304..932cb8dc69 100644 --- a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.0.dump +++ b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = [[timeUs=0, position=8288]] + getPosition(0) = [[timeUs=0, position=8230]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.1.dump b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.1.dump index 95321cb88b..7b5cc6c31e 100644 --- a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.1.dump +++ b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = [[timeUs=0, position=8288]] + getPosition(0) = [[timeUs=0, position=8230]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.2.dump b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.2.dump index eae20735e3..4e2d478a57 100644 --- a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.2.dump +++ b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = [[timeUs=0, position=8288]] + getPosition(0) = [[timeUs=0, position=8230]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.3.dump b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.3.dump index 2f3a34e246..3870f8d88e 100644 --- a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.3.dump +++ b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = [[timeUs=0, position=8288]] + getPosition(0) = [[timeUs=0, position=8230]] numberOfTracks = 1 track 0: format: @@ -27,9 +27,13 @@ track 0: metadata = null initializationData: data = length 42, hash 7249A1B8 - total output bytes = 548 - sample count = 1 + total output bytes = 3385 + sample count = 2 sample 0: + time = 2618181 + flags = 1 + data = length 2837, hash 10F1716E + sample 1: time = 2722909 flags = 1 data = length 548, hash B46F603C diff --git a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.unklen.dump b/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.unklen.dump deleted file mode 100644 index e6caad8e84..0000000000 --- a/library/core/src/test/assets/flac/bear_uncommon_sample_rate.flac.unklen.dump +++ /dev/null @@ -1,140 +0,0 @@ -seekMap: - isSeekable = false - duration = 2741000 - getPosition(0) = [[timeUs=0, position=0]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1408000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 6456 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 44000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - metadata = null - initializationData: - data = length 42, hash 7249A1B8 - total output bytes = 144086 - sample count = 27 - sample 0: - time = 0 - flags = 1 - data = length 5415, hash 915DBC66 - sample 1: - time = 104727 - flags = 1 - data = length 5529, hash EFD564F7 - sample 2: - time = 209454 - flags = 1 - data = length 5480, hash ADA922FB - sample 3: - time = 314181 - flags = 1 - data = length 5290, hash 7BCEA5FC - sample 4: - time = 418909 - flags = 1 - data = length 5579, hash DBB36F37 - sample 5: - time = 523636 - flags = 1 - data = length 5423, hash AB53F799 - sample 6: - time = 628363 - flags = 1 - data = length 5583, hash 7243C284 - sample 7: - time = 733090 - flags = 1 - data = length 5547, hash 9DA9C99E - sample 8: - time = 837818 - flags = 1 - data = length 5414, hash 90768345 - sample 9: - time = 942545 - flags = 1 - data = length 5531, hash 1CD2FF67 - sample 10: - time = 1047272 - flags = 1 - data = length 5870, hash A9A5CAEE - sample 11: - time = 1152000 - flags = 1 - data = length 5667, hash 875566A1 - sample 12: - time = 1256727 - flags = 1 - data = length 5614, hash 5573694C - sample 13: - time = 1361454 - flags = 1 - data = length 6456, hash 921F3DE7 - sample 14: - time = 1466181 - flags = 1 - data = length 5817, hash EBECBD16 - sample 15: - time = 1570909 - flags = 1 - data = length 5751, hash 4A7D4C6B - sample 16: - time = 1675636 - flags = 1 - data = length 5620, hash B78F8E8D - sample 17: - time = 1780363 - flags = 1 - data = length 5535, hash 8187C107 - sample 18: - time = 1885090 - flags = 1 - data = length 5517, hash 79FF36CB - sample 19: - time = 1989818 - flags = 1 - data = length 5716, hash 349FC281 - sample 20: - time = 2094545 - flags = 1 - data = length 5556, hash BE97B2CA - sample 21: - time = 2199272 - flags = 1 - data = length 5703, hash 531F9FE3 - sample 22: - time = 2304000 - flags = 1 - data = length 5652, hash 1277485D - sample 23: - time = 2408727 - flags = 1 - data = length 5607, hash 14862CB6 - sample 24: - time = 2513454 - flags = 1 - data = length 5829, hash FCAF2F1C - sample 25: - time = 2618181 - flags = 1 - data = length 2837, hash 10F1716E - sample 26: - time = 2722909 - flags = 1 - data = length 548, hash B46F603C -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_id3.flac.1.dump b/library/core/src/test/assets/flac/bear_with_id3.flac.1.dump deleted file mode 100644 index e30ade2d9c..0000000000 --- a/library/core/src/test/assets/flac/bear_with_id3.flac.1.dump +++ /dev/null @@ -1,123 +0,0 @@ -seekMap: - isSeekable = true - duration = 2741000 - getPosition(0) = [[timeUs=0, position=55284]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - initializationData: - data = length 42, hash 83F6895 - total output bytes = 113666 - sample count = 23 - sample 0: - time = 853333 - flags = 1 - data = length 5031, hash 9D55EA53 - sample 1: - time = 938666 - flags = 1 - data = length 5119, hash E1CB9BA6 - sample 2: - time = 1024000 - flags = 1 - data = length 5360, hash 17265C5D - sample 3: - time = 1109333 - flags = 1 - data = length 5340, hash A90FDDF1 - sample 4: - time = 1194666 - flags = 1 - data = length 5162, hash 31F65AD5 - sample 5: - time = 1280000 - flags = 1 - data = length 5168, hash F2394F2D - sample 6: - time = 1365333 - flags = 1 - data = length 5776, hash 58437AB3 - sample 7: - time = 1450666 - flags = 1 - data = length 5394, hash EBAB20A8 - sample 8: - time = 1536000 - flags = 1 - data = length 5168, hash BF37C7A5 - sample 9: - time = 1621333 - flags = 1 - data = length 5324, hash 59546B7B - sample 10: - time = 1706666 - flags = 1 - data = length 5172, hash 6036EF0B - sample 11: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 12: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 13: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 14: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 15: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 16: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 17: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 18: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 19: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 20: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 21: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 22: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_id3.flac.2.dump b/library/core/src/test/assets/flac/bear_with_id3.flac.2.dump deleted file mode 100644 index bd3280baa5..0000000000 --- a/library/core/src/test/assets/flac/bear_with_id3.flac.2.dump +++ /dev/null @@ -1,79 +0,0 @@ -seekMap: - isSeekable = true - duration = 2741000 - getPosition(0) = [[timeUs=0, position=55284]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - initializationData: - data = length 42, hash 83F6895 - total output bytes = 55652 - sample count = 12 - sample 0: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 1: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 2: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 3: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 4: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 5: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 6: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 7: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 8: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 9: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 10: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 11: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_id3.flac.3.dump b/library/core/src/test/assets/flac/bear_with_id3.flac.3.dump deleted file mode 100644 index ceabd2f50e..0000000000 --- a/library/core/src/test/assets/flac/bear_with_id3.flac.3.dump +++ /dev/null @@ -1,35 +0,0 @@ -seekMap: - isSeekable = true - duration = 2741000 - getPosition(0) = [[timeUs=0, position=55284]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - initializationData: - data = length 42, hash 83F6895 - total output bytes = 445 - sample count = 1 - sample 0: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_id3.flac.unklen.dump b/library/core/src/test/assets/flac/bear_with_id3.flac.unklen.dump deleted file mode 100644 index e35dcc2081..0000000000 --- a/library/core/src/test/assets/flac/bear_with_id3.flac.unklen.dump +++ /dev/null @@ -1,163 +0,0 @@ -seekMap: - isSeekable = false - duration = 2741000 - getPosition(0) = [[timeUs=0, position=0]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - initializationData: - data = length 42, hash 83F6895 - total output bytes = 164431 - sample count = 33 - sample 0: - time = 0 - flags = 1 - data = length 5030, hash D2B60530 - sample 1: - time = 85333 - flags = 1 - data = length 5066, hash 4C932A54 - sample 2: - time = 170666 - flags = 1 - data = length 5112, hash 7E5A7B61 - sample 3: - time = 256000 - flags = 1 - data = length 5044, hash 7EF93F13 - sample 4: - time = 341333 - flags = 1 - data = length 4943, hash DE7E27F8 - sample 5: - time = 426666 - flags = 1 - data = length 5121, hash 6D0D0B40 - sample 6: - time = 512000 - flags = 1 - data = length 5068, hash 9924644F - sample 7: - time = 597333 - flags = 1 - data = length 5143, hash 6C34F0CE - sample 8: - time = 682666 - flags = 1 - data = length 5109, hash E3B7BEFB - sample 9: - time = 768000 - flags = 1 - data = length 5129, hash 44111D9B - sample 10: - time = 853333 - flags = 1 - data = length 5031, hash 9D55EA53 - sample 11: - time = 938666 - flags = 1 - data = length 5119, hash E1CB9BA6 - sample 12: - time = 1024000 - flags = 1 - data = length 5360, hash 17265C5D - sample 13: - time = 1109333 - flags = 1 - data = length 5340, hash A90FDDF1 - sample 14: - time = 1194666 - flags = 1 - data = length 5162, hash 31F65AD5 - sample 15: - time = 1280000 - flags = 1 - data = length 5168, hash F2394F2D - sample 16: - time = 1365333 - flags = 1 - data = length 5776, hash 58437AB3 - sample 17: - time = 1450666 - flags = 1 - data = length 5394, hash EBAB20A8 - sample 18: - time = 1536000 - flags = 1 - data = length 5168, hash BF37C7A5 - sample 19: - time = 1621333 - flags = 1 - data = length 5324, hash 59546B7B - sample 20: - time = 1706666 - flags = 1 - data = length 5172, hash 6036EF0B - sample 21: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 22: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 23: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 24: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 25: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 26: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 27: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 28: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 29: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 30: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 31: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 32: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_id3_disabled.flac.3.dump b/library/core/src/test/assets/flac/bear_with_id3_disabled.flac.3.dump index 2260aebd8e..4fd2cb37f6 100644 --- a/library/core/src/test/assets/flac/bear_with_id3_disabled.flac.3.dump +++ b/library/core/src/test/assets/flac/bear_with_id3_disabled.flac.3.dump @@ -27,9 +27,13 @@ track 0: metadata = null initializationData: data = length 42, hash 83F6895 - total output bytes = 445 - sample count = 1 + total output bytes = 3829 + sample count = 2 sample 0: + time = 2645333 + flags = 1 + data = length 3384, hash 938BCDD9 + sample 1: time = 2730666 flags = 1 data = length 445, hash A388E3D6 diff --git a/library/core/src/test/assets/flac/bear_with_id3_disabled.flac.unklen.dump b/library/core/src/test/assets/flac/bear_with_id3_disabled.flac.unklen.dump deleted file mode 100644 index bd8f1827e4..0000000000 --- a/library/core/src/test/assets/flac/bear_with_id3_disabled.flac.unklen.dump +++ /dev/null @@ -1,164 +0,0 @@ -seekMap: - isSeekable = false - duration = 2741000 - getPosition(0) = [[timeUs=0, position=0]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - metadata = null - initializationData: - data = length 42, hash 83F6895 - total output bytes = 164431 - sample count = 33 - sample 0: - time = 0 - flags = 1 - data = length 5030, hash D2B60530 - sample 1: - time = 85333 - flags = 1 - data = length 5066, hash 4C932A54 - sample 2: - time = 170666 - flags = 1 - data = length 5112, hash 7E5A7B61 - sample 3: - time = 256000 - flags = 1 - data = length 5044, hash 7EF93F13 - sample 4: - time = 341333 - flags = 1 - data = length 4943, hash DE7E27F8 - sample 5: - time = 426666 - flags = 1 - data = length 5121, hash 6D0D0B40 - sample 6: - time = 512000 - flags = 1 - data = length 5068, hash 9924644F - sample 7: - time = 597333 - flags = 1 - data = length 5143, hash 6C34F0CE - sample 8: - time = 682666 - flags = 1 - data = length 5109, hash E3B7BEFB - sample 9: - time = 768000 - flags = 1 - data = length 5129, hash 44111D9B - sample 10: - time = 853333 - flags = 1 - data = length 5031, hash 9D55EA53 - sample 11: - time = 938666 - flags = 1 - data = length 5119, hash E1CB9BA6 - sample 12: - time = 1024000 - flags = 1 - data = length 5360, hash 17265C5D - sample 13: - time = 1109333 - flags = 1 - data = length 5340, hash A90FDDF1 - sample 14: - time = 1194666 - flags = 1 - data = length 5162, hash 31F65AD5 - sample 15: - time = 1280000 - flags = 1 - data = length 5168, hash F2394F2D - sample 16: - time = 1365333 - flags = 1 - data = length 5776, hash 58437AB3 - sample 17: - time = 1450666 - flags = 1 - data = length 5394, hash EBAB20A8 - sample 18: - time = 1536000 - flags = 1 - data = length 5168, hash BF37C7A5 - sample 19: - time = 1621333 - flags = 1 - data = length 5324, hash 59546B7B - sample 20: - time = 1706666 - flags = 1 - data = length 5172, hash 6036EF0B - sample 21: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 22: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 23: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 24: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 25: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 26: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 27: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 28: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 29: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 30: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 31: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 32: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_id3_enabled.flac.3.dump b/library/core/src/test/assets/flac/bear_with_id3_enabled.flac.3.dump index b5069a4581..51c667d2f9 100644 --- a/library/core/src/test/assets/flac/bear_with_id3_enabled.flac.3.dump +++ b/library/core/src/test/assets/flac/bear_with_id3_enabled.flac.3.dump @@ -27,9 +27,13 @@ track 0: metadata = entries=[TXXX: description=ID: value=105519843, TIT2: description=null: value=那么爱你为什么, TPE1: description=null: value=阿强, TALB: description=null: value=华丽的外衣, TXXX: description=ID: value=105519843, APIC: mimeType=image/jpeg, description=] initializationData: data = length 42, hash 83F6895 - total output bytes = 445 - sample count = 1 + total output bytes = 3829 + sample count = 2 sample 0: + time = 2645333 + flags = 1 + data = length 3384, hash 938BCDD9 + sample 1: time = 2730666 flags = 1 data = length 445, hash A388E3D6 diff --git a/library/core/src/test/assets/flac/bear_with_id3_enabled.flac.unklen.dump b/library/core/src/test/assets/flac/bear_with_id3_enabled.flac.unklen.dump deleted file mode 100644 index 47402623c1..0000000000 --- a/library/core/src/test/assets/flac/bear_with_id3_enabled.flac.unklen.dump +++ /dev/null @@ -1,164 +0,0 @@ -seekMap: - isSeekable = false - duration = 2741000 - getPosition(0) = [[timeUs=0, position=0]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - metadata = entries=[TXXX: description=ID: value=105519843, TIT2: description=null: value=那么爱你为什么, TPE1: description=null: value=阿强, TALB: description=null: value=华丽的外衣, TXXX: description=ID: value=105519843, APIC: mimeType=image/jpeg, description=] - initializationData: - data = length 42, hash 83F6895 - total output bytes = 164431 - sample count = 33 - sample 0: - time = 0 - flags = 1 - data = length 5030, hash D2B60530 - sample 1: - time = 85333 - flags = 1 - data = length 5066, hash 4C932A54 - sample 2: - time = 170666 - flags = 1 - data = length 5112, hash 7E5A7B61 - sample 3: - time = 256000 - flags = 1 - data = length 5044, hash 7EF93F13 - sample 4: - time = 341333 - flags = 1 - data = length 4943, hash DE7E27F8 - sample 5: - time = 426666 - flags = 1 - data = length 5121, hash 6D0D0B40 - sample 6: - time = 512000 - flags = 1 - data = length 5068, hash 9924644F - sample 7: - time = 597333 - flags = 1 - data = length 5143, hash 6C34F0CE - sample 8: - time = 682666 - flags = 1 - data = length 5109, hash E3B7BEFB - sample 9: - time = 768000 - flags = 1 - data = length 5129, hash 44111D9B - sample 10: - time = 853333 - flags = 1 - data = length 5031, hash 9D55EA53 - sample 11: - time = 938666 - flags = 1 - data = length 5119, hash E1CB9BA6 - sample 12: - time = 1024000 - flags = 1 - data = length 5360, hash 17265C5D - sample 13: - time = 1109333 - flags = 1 - data = length 5340, hash A90FDDF1 - sample 14: - time = 1194666 - flags = 1 - data = length 5162, hash 31F65AD5 - sample 15: - time = 1280000 - flags = 1 - data = length 5168, hash F2394F2D - sample 16: - time = 1365333 - flags = 1 - data = length 5776, hash 58437AB3 - sample 17: - time = 1450666 - flags = 1 - data = length 5394, hash EBAB20A8 - sample 18: - time = 1536000 - flags = 1 - data = length 5168, hash BF37C7A5 - sample 19: - time = 1621333 - flags = 1 - data = length 5324, hash 59546B7B - sample 20: - time = 1706666 - flags = 1 - data = length 5172, hash 6036EF0B - sample 21: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 22: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 23: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 24: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 25: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 26: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 27: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 28: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 29: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 30: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 31: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 32: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_picture.flac.3.dump b/library/core/src/test/assets/flac/bear_with_picture.flac.3.dump index 8a3c80c935..d5cb96739e 100644 --- a/library/core/src/test/assets/flac/bear_with_picture.flac.3.dump +++ b/library/core/src/test/assets/flac/bear_with_picture.flac.3.dump @@ -27,9 +27,13 @@ track 0: metadata = entries=[Picture: mimeType=image/png, description=] initializationData: data = length 42, hash 83F6895 - total output bytes = 445 - sample count = 1 + total output bytes = 3829 + sample count = 2 sample 0: + time = 2645333 + flags = 1 + data = length 3384, hash 938BCDD9 + sample 1: time = 2730666 flags = 1 data = length 445, hash A388E3D6 diff --git a/library/core/src/test/assets/flac/bear_with_picture.flac.unklen.dump b/library/core/src/test/assets/flac/bear_with_picture.flac.unklen.dump deleted file mode 100644 index 93e33e7c23..0000000000 --- a/library/core/src/test/assets/flac/bear_with_picture.flac.unklen.dump +++ /dev/null @@ -1,164 +0,0 @@ -seekMap: - isSeekable = false - duration = 2741000 - getPosition(0) = [[timeUs=0, position=0]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - metadata = entries=[Picture: mimeType=image/png, description=] - initializationData: - data = length 42, hash 83F6895 - total output bytes = 164431 - sample count = 33 - sample 0: - time = 0 - flags = 1 - data = length 5030, hash D2B60530 - sample 1: - time = 85333 - flags = 1 - data = length 5066, hash 4C932A54 - sample 2: - time = 170666 - flags = 1 - data = length 5112, hash 7E5A7B61 - sample 3: - time = 256000 - flags = 1 - data = length 5044, hash 7EF93F13 - sample 4: - time = 341333 - flags = 1 - data = length 4943, hash DE7E27F8 - sample 5: - time = 426666 - flags = 1 - data = length 5121, hash 6D0D0B40 - sample 6: - time = 512000 - flags = 1 - data = length 5068, hash 9924644F - sample 7: - time = 597333 - flags = 1 - data = length 5143, hash 6C34F0CE - sample 8: - time = 682666 - flags = 1 - data = length 5109, hash E3B7BEFB - sample 9: - time = 768000 - flags = 1 - data = length 5129, hash 44111D9B - sample 10: - time = 853333 - flags = 1 - data = length 5031, hash 9D55EA53 - sample 11: - time = 938666 - flags = 1 - data = length 5119, hash E1CB9BA6 - sample 12: - time = 1024000 - flags = 1 - data = length 5360, hash 17265C5D - sample 13: - time = 1109333 - flags = 1 - data = length 5340, hash A90FDDF1 - sample 14: - time = 1194666 - flags = 1 - data = length 5162, hash 31F65AD5 - sample 15: - time = 1280000 - flags = 1 - data = length 5168, hash F2394F2D - sample 16: - time = 1365333 - flags = 1 - data = length 5776, hash 58437AB3 - sample 17: - time = 1450666 - flags = 1 - data = length 5394, hash EBAB20A8 - sample 18: - time = 1536000 - flags = 1 - data = length 5168, hash BF37C7A5 - sample 19: - time = 1621333 - flags = 1 - data = length 5324, hash 59546B7B - sample 20: - time = 1706666 - flags = 1 - data = length 5172, hash 6036EF0B - sample 21: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 22: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 23: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 24: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 25: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 26: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 27: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 28: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 29: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 30: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 31: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 32: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/assets/flac/bear_with_vorbis_comments.flac.3.dump b/library/core/src/test/assets/flac/bear_with_vorbis_comments.flac.3.dump index 86f6da89fe..8d8fd28e3f 100644 --- a/library/core/src/test/assets/flac/bear_with_vorbis_comments.flac.3.dump +++ b/library/core/src/test/assets/flac/bear_with_vorbis_comments.flac.3.dump @@ -27,9 +27,13 @@ track 0: metadata = entries=[VC: TITLE=test title, VC: ARTIST=test artist] initializationData: data = length 42, hash 83F6895 - total output bytes = 445 - sample count = 1 + total output bytes = 3829 + sample count = 2 sample 0: + time = 2645333 + flags = 1 + data = length 3384, hash 938BCDD9 + sample 1: time = 2730666 flags = 1 data = length 445, hash A388E3D6 diff --git a/library/core/src/test/assets/flac/bear_with_vorbis_comments.flac.unklen.dump b/library/core/src/test/assets/flac/bear_with_vorbis_comments.flac.unklen.dump deleted file mode 100644 index f7f67b12ab..0000000000 --- a/library/core/src/test/assets/flac/bear_with_vorbis_comments.flac.unklen.dump +++ /dev/null @@ -1,164 +0,0 @@ -seekMap: - isSeekable = false - duration = 2741000 - getPosition(0) = [[timeUs=0, position=0]] -numberOfTracks = 1 -track 0: - format: - bitrate = 1536000 - id = null - containerMimeType = null - sampleMimeType = audio/flac - maxInputSize = 5776 - width = -1 - height = -1 - frameRate = -1.0 - rotationDegrees = 0 - pixelWidthHeightRatio = 1.0 - channelCount = 2 - sampleRate = 48000 - pcmEncoding = -1 - encoderDelay = 0 - encoderPadding = 0 - subsampleOffsetUs = 9223372036854775807 - selectionFlags = 0 - language = null - drmInitData = - - metadata = entries=[VC: TITLE=test title, VC: ARTIST=test artist] - initializationData: - data = length 42, hash 83F6895 - total output bytes = 164431 - sample count = 33 - sample 0: - time = 0 - flags = 1 - data = length 5030, hash D2B60530 - sample 1: - time = 85333 - flags = 1 - data = length 5066, hash 4C932A54 - sample 2: - time = 170666 - flags = 1 - data = length 5112, hash 7E5A7B61 - sample 3: - time = 256000 - flags = 1 - data = length 5044, hash 7EF93F13 - sample 4: - time = 341333 - flags = 1 - data = length 4943, hash DE7E27F8 - sample 5: - time = 426666 - flags = 1 - data = length 5121, hash 6D0D0B40 - sample 6: - time = 512000 - flags = 1 - data = length 5068, hash 9924644F - sample 7: - time = 597333 - flags = 1 - data = length 5143, hash 6C34F0CE - sample 8: - time = 682666 - flags = 1 - data = length 5109, hash E3B7BEFB - sample 9: - time = 768000 - flags = 1 - data = length 5129, hash 44111D9B - sample 10: - time = 853333 - flags = 1 - data = length 5031, hash 9D55EA53 - sample 11: - time = 938666 - flags = 1 - data = length 5119, hash E1CB9BA6 - sample 12: - time = 1024000 - flags = 1 - data = length 5360, hash 17265C5D - sample 13: - time = 1109333 - flags = 1 - data = length 5340, hash A90FDDF1 - sample 14: - time = 1194666 - flags = 1 - data = length 5162, hash 31F65AD5 - sample 15: - time = 1280000 - flags = 1 - data = length 5168, hash F2394F2D - sample 16: - time = 1365333 - flags = 1 - data = length 5776, hash 58437AB3 - sample 17: - time = 1450666 - flags = 1 - data = length 5394, hash EBAB20A8 - sample 18: - time = 1536000 - flags = 1 - data = length 5168, hash BF37C7A5 - sample 19: - time = 1621333 - flags = 1 - data = length 5324, hash 59546B7B - sample 20: - time = 1706666 - flags = 1 - data = length 5172, hash 6036EF0B - sample 21: - time = 1792000 - flags = 1 - data = length 5102, hash 5A131071 - sample 22: - time = 1877333 - flags = 1 - data = length 5111, hash 3D9EBB3B - sample 23: - time = 1962666 - flags = 1 - data = length 5113, hash 61101D4F - sample 24: - time = 2048000 - flags = 1 - data = length 5229, hash D2E55742 - sample 25: - time = 2133333 - flags = 1 - data = length 5162, hash 7F2E97FA - sample 26: - time = 2218666 - flags = 1 - data = length 5255, hash D92A782 - sample 27: - time = 2304000 - flags = 1 - data = length 5196, hash 98FE5138 - sample 28: - time = 2389333 - flags = 1 - data = length 5214, hash 3D35C38C - sample 29: - time = 2474666 - flags = 1 - data = length 5211, hash 7E25420F - sample 30: - time = 2560000 - flags = 1 - data = length 5230, hash 2AD96FBC - sample 31: - time = 2645333 - flags = 1 - data = length 3384, hash 938BCDD9 - sample 32: - time = 2730666 - flags = 1 - data = length 445, hash A388E3D6 -tracksEnded = true diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorTest.java index 9389f776f9..97bfc949de 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorTest.java @@ -20,7 +20,6 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.testutil.ExtractorAsserts; -import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.IOException; import org.junit.Test; @@ -32,41 +31,49 @@ public class FlacExtractorTest { @Test public void testSample() throws Exception { - assertBehavior(FlacExtractor::new, "flac/bear.flac"); + ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear.flac"); } @Test public void testSampleWithId3HeaderAndId3Enabled() throws Exception { - assertBehavior(FlacExtractor::new, "flac/bear_with_id3_enabled.flac"); + ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_id3_enabled.flac"); } @Test public void testSampleWithId3HeaderAndId3Disabled() throws Exception { // bear_with_id3_disabled.flac is identical to bear_with_id3_enabled.flac, but the dump file is // different due to setting FLAG_DISABLE_ID3_METADATA. - assertBehavior( + ExtractorAsserts.assertBehavior( () -> new FlacExtractor(FlacExtractor.FLAG_DISABLE_ID3_METADATA), "flac/bear_with_id3_disabled.flac"); } + @Test + public void testSampleUnseekable() throws Exception { + ExtractorAsserts.assertBehavior( + FlacExtractor::new, "flac/bear_no_seek_table_no_num_samples.flac"); + } + @Test public void testSampleWithVorbisComments() throws Exception { - assertBehavior(FlacExtractor::new, "flac/bear_with_vorbis_comments.flac"); + ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_vorbis_comments.flac"); } @Test public void testSampleWithPicture() throws Exception { - assertBehavior(FlacExtractor::new, "flac/bear_with_picture.flac"); + ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_picture.flac"); } @Test public void testOneMetadataBlock() throws Exception { - assertBehavior(FlacExtractor::new, "flac/bear_one_metadata_block.flac"); + // Don't simulate IO errors as it is too slow when using the binary search seek map (see + // [Internal: b/145994869]). + assertBehaviorWithoutSimulatingIOErrors("flac/bear_one_metadata_block.flac"); } @Test public void testNoMinMaxFrameSize() throws Exception { - assertBehavior(FlacExtractor::new, "flac/bear_no_min_max_frame_size.flac"); + ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_no_min_max_frame_size.flac"); } @Test @@ -76,24 +83,63 @@ public class FlacExtractorTest { @Test public void testUncommonSampleRate() throws Exception { - assertBehavior(FlacExtractor::new, "flac/bear_uncommon_sample_rate.flac"); + ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_uncommon_sample_rate.flac"); } - private void assertBehavior(ExtractorFactory factory, String file) + private static void assertBehaviorWithoutSimulatingIOErrors(String file) throws IOException, InterruptedException { // Check behavior prior to initialization. - Extractor extractor = factory.create(); + Extractor extractor = new FlacExtractor(); extractor.seek(0, 0); extractor.release(); + // Assert output. Context context = ApplicationProvider.getApplicationContext(); byte[] data = TestUtil.getByteArray(context, file); - // Don't simulate IO errors as it is too slow (see b/145994869). - ExtractorAsserts.assertOutput(factory.create(), file, data, context, true, false, false, false); - ExtractorAsserts.assertOutput(factory.create(), file, data, context, true, false, false, true); - ExtractorAsserts.assertOutput(factory.create(), file, data, context, true, false, true, false); - ExtractorAsserts.assertOutput(factory.create(), file, data, context, true, false, true, true); ExtractorAsserts.assertOutput( - factory.create(), file, data, context, false, false, false, false); + new FlacExtractor(), + file, + data, + context, + /* sniffFirst= */ true, + /* simulateIOErrors= */ false, + /* simulateUnknownLength= */ false, + /* simulatePartialReads= */ false); + ExtractorAsserts.assertOutput( + new FlacExtractor(), + file, + data, + context, + /* sniffFirst= */ true, + /* simulateIOErrors= */ false, + /* simulateUnknownLength= */ false, + /* simulatePartialReads= */ true); + ExtractorAsserts.assertOutput( + new FlacExtractor(), + file, + data, + context, + /* sniffFirst= */ true, + /* simulateIOErrors= */ false, + /* simulateUnknownLength= */ true, + /* simulatePartialReads= */ false); + ExtractorAsserts.assertOutput( + new FlacExtractor(), + file, + data, + context, + /* sniffFirst= */ true, + /* simulateIOErrors= */ false, + /* simulateUnknownLength= */ true, + /* simulatePartialReads= */ true); + ExtractorAsserts.assertOutput( + new FlacExtractor(), + file, + data, + context, + /* sniffFirst= */ false, + /* simulateIOErrors= */ false, + /* simulateUnknownLength= */ false, + /* simulatePartialReads= */ false); } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java index a721e81db9..050898a20e 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java @@ -208,18 +208,21 @@ public final class ExtractorAsserts { extractorOutput.assertOutput(context, file + ".0" + DUMP_EXTENSION); } - // If the SeekMap is seekable, test seeking to 4 positions in the stream. + // If the SeekMap is seekable, test seeking in the stream. SeekMap seekMap = extractorOutput.seekMap; if (seekMap.isSeekable()) { long durationUs = seekMap.getDurationUs(); for (int j = 0; j < 4; j++) { extractorOutput.clearTrackOutputs(); - long timeUs = (durationUs * j) / 3; + long timeUs = durationUs == C.TIME_UNSET ? 0 : (durationUs * j) / 3; long position = seekMap.getSeekPoints(timeUs).first.position; input.reset(); input.setPosition((int) position); consumeTestData(extractor, input, timeUs, extractorOutput, false); extractorOutput.assertOutput(context, file + '.' + j + DUMP_EXTENSION); + if (durationUs == C.TIME_UNSET) { + break; + } } }