Add unit tests to FLAC extractor related classes

PiperOrigin-RevId: 287973192
This commit is contained in:
kimvde 2020-01-03 12:11:10 +00:00 committed by Ian Baker
parent b5fa338367
commit f76b4fe63e
4 changed files with 756 additions and 1 deletions

View File

@ -167,7 +167,7 @@ public final class FlacFrameReader {
* @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.
* @return The block size in samples, or -1 if the {@code blockSizeKey} is invalid.
*/
public static int readFrameBlockSizeSamplesFromKey(ParsableByteArray data, int blockSizeKey) {
switch (blockSizeKey) {

View File

@ -0,0 +1,323 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.extractor.FlacFrameReader.SampleNumberHolder;
import com.google.android.exoplayer2.extractor.FlacMetadataReader.FlacStreamMetadataHolder;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.FlacConstants;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Unit tests for {@link FlacFrameReader}.
*
* <p>Some expected results in these tests have been retrieved using the <a
* href="https://xiph.org/flac/documentation_tools_flac.html">flac</a> command.
*/
@RunWith(AndroidJUnit4.class)
public class FlacFrameReaderTest {
@Test
public void checkAndReadFrameHeader_validData_updatesPosition() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
int frameStartMarker = FlacMetadataReader.getFrameStartMarker(input);
ParsableByteArray scratch = new ParsableByteArray(FlacConstants.MAX_FRAME_HEADER_SIZE);
input.read(scratch.data, 0, FlacConstants.MAX_FRAME_HEADER_SIZE);
FlacFrameReader.checkAndReadFrameHeader(
scratch,
streamMetadataHolder.flacStreamMetadata,
frameStartMarker,
new SampleNumberHolder());
assertThat(scratch.getPosition()).isEqualTo(FlacConstants.MIN_FRAME_HEADER_SIZE);
}
@Test
public void checkAndReadFrameHeader_validData_isTrue() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
int frameStartMarker = FlacMetadataReader.getFrameStartMarker(input);
ParsableByteArray scratch = new ParsableByteArray(FlacConstants.MAX_FRAME_HEADER_SIZE);
input.read(scratch.data, 0, FlacConstants.MAX_FRAME_HEADER_SIZE);
boolean result =
FlacFrameReader.checkAndReadFrameHeader(
scratch,
streamMetadataHolder.flacStreamMetadata,
frameStartMarker,
new SampleNumberHolder());
assertThat(result).isTrue();
}
@Test
public void checkAndReadFrameHeader_validData_writesSampleNumber() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
int frameStartMarker = FlacMetadataReader.getFrameStartMarker(input);
// Skip first frame.
input.skip(5030);
ParsableByteArray scratch = new ParsableByteArray(FlacConstants.MAX_FRAME_HEADER_SIZE);
input.read(scratch.data, 0, FlacConstants.MAX_FRAME_HEADER_SIZE);
SampleNumberHolder sampleNumberHolder = new SampleNumberHolder();
FlacFrameReader.checkAndReadFrameHeader(
scratch, streamMetadataHolder.flacStreamMetadata, frameStartMarker, sampleNumberHolder);
assertThat(sampleNumberHolder.sampleNumber).isEqualTo(4096);
}
@Test
public void checkAndReadFrameHeader_invalidData_isFalse() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
ParsableByteArray scratch = new ParsableByteArray(FlacConstants.MAX_FRAME_HEADER_SIZE);
input.read(scratch.data, 0, FlacConstants.MAX_FRAME_HEADER_SIZE);
// The first bytes of the frame are not equal to the frame start marker.
boolean result =
FlacFrameReader.checkAndReadFrameHeader(
scratch,
streamMetadataHolder.flacStreamMetadata,
/* frameStartMarker= */ -1,
new SampleNumberHolder());
assertThat(result).isFalse();
}
@Test
public void checkFrameHeaderFromPeek_validData_doesNotUpdatePositions() throws Exception {
String file = "flac/bear_one_metadata_block.flac";
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input = buildExtractorInputReadingFromFirstFrame(file, streamMetadataHolder);
int frameStartMarker = FlacMetadataReader.getFrameStartMarker(input);
long peekPosition = input.getPosition();
// Set read position to 0.
input = buildExtractorInput(file);
input.advancePeekPosition((int) peekPosition);
FlacFrameReader.checkFrameHeaderFromPeek(
input, streamMetadataHolder.flacStreamMetadata, frameStartMarker, new SampleNumberHolder());
assertThat(input.getPosition()).isEqualTo(0);
assertThat(input.getPeekPosition()).isEqualTo(peekPosition);
}
@Test
public void checkFrameHeaderFromPeek_validData_isTrue() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
int frameStartMarker = FlacMetadataReader.getFrameStartMarker(input);
boolean result =
FlacFrameReader.checkFrameHeaderFromPeek(
input,
streamMetadataHolder.flacStreamMetadata,
frameStartMarker,
new SampleNumberHolder());
assertThat(result).isTrue();
}
@Test
public void checkFrameHeaderFromPeek_validData_writesSampleNumber() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
int frameStartMarker = FlacMetadataReader.getFrameStartMarker(input);
// Skip first frame.
input.skip(5030);
SampleNumberHolder sampleNumberHolder = new SampleNumberHolder();
FlacFrameReader.checkFrameHeaderFromPeek(
input, streamMetadataHolder.flacStreamMetadata, frameStartMarker, sampleNumberHolder);
assertThat(sampleNumberHolder.sampleNumber).isEqualTo(4096);
}
@Test
public void checkFrameHeaderFromPeek_invalidData_isFalse() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
// The first bytes of the frame are not equal to the frame start marker.
boolean result =
FlacFrameReader.checkFrameHeaderFromPeek(
input,
streamMetadataHolder.flacStreamMetadata,
/* frameStartMarker= */ -1,
new SampleNumberHolder());
assertThat(result).isFalse();
}
@Test
public void checkFrameHeaderFromPeek_invalidData_doesNotUpdatePositions() throws Exception {
String file = "flac/bear_one_metadata_block.flac";
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input = buildExtractorInputReadingFromFirstFrame(file, streamMetadataHolder);
long peekPosition = input.getPosition();
// Set read position to 0.
input = buildExtractorInput(file);
input.advancePeekPosition((int) peekPosition);
// The first bytes of the frame are not equal to the frame start marker.
FlacFrameReader.checkFrameHeaderFromPeek(
input,
streamMetadataHolder.flacStreamMetadata,
/* frameStartMarker= */ -1,
new SampleNumberHolder());
assertThat(input.getPosition()).isEqualTo(0);
assertThat(input.getPeekPosition()).isEqualTo(peekPosition);
}
@Test
public void getFirstSampleNumber_doesNotUpdateReadPositionAndAlignsPeekPosition()
throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
long initialReadPosition = input.getPosition();
// Advance peek position after block size bits.
input.advancePeekPosition(FlacConstants.MAX_FRAME_HEADER_SIZE);
FlacFrameReader.getFirstSampleNumber(input, streamMetadataHolder.flacStreamMetadata);
assertThat(input.getPosition()).isEqualTo(initialReadPosition);
assertThat(input.getPeekPosition()).isEqualTo(input.getPosition());
}
@Test
public void getFirstSampleNumber_returnsSampleNumber() throws Exception {
FlacStreamMetadataHolder streamMetadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
ExtractorInput input =
buildExtractorInputReadingFromFirstFrame(
"flac/bear_one_metadata_block.flac", streamMetadataHolder);
// Skip first frame.
input.skip(5030);
long result =
FlacFrameReader.getFirstSampleNumber(input, streamMetadataHolder.flacStreamMetadata);
assertThat(result).isEqualTo(4096);
}
@Test
public void readFrameBlockSizeSamplesFromKey_keyIs1_returnsCorrectBlockSize() {
int result =
FlacFrameReader.readFrameBlockSizeSamplesFromKey(
new ParsableByteArray(/* limit= */ 0), /* blockSizeKey= */ 1);
assertThat(result).isEqualTo(192);
}
@Test
public void readFrameBlockSizeSamplesFromKey_keyBetween2and5_returnsCorrectBlockSize() {
int result =
FlacFrameReader.readFrameBlockSizeSamplesFromKey(
new ParsableByteArray(/* limit= */ 0), /* blockSizeKey= */ 3);
assertThat(result).isEqualTo(1152);
}
@Test
public void readFrameBlockSizeSamplesFromKey_keyBetween6And7_returnsCorrectBlockSize()
throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_one_metadata_block.flac");
// Skip to block size bits of last frame.
input.skipFully(164033);
ParsableByteArray scratch = new ParsableByteArray(2);
input.readFully(scratch.data, 0, 2);
int result = FlacFrameReader.readFrameBlockSizeSamplesFromKey(scratch, /* blockSizeKey= */ 7);
assertThat(result).isEqualTo(496);
}
@Test
public void readFrameBlockSizeSamplesFromKey_keyBetween8and15_returnsCorrectBlockSize() {
int result =
FlacFrameReader.readFrameBlockSizeSamplesFromKey(
new ParsableByteArray(/* limit= */ 0), /* blockSizeKey= */ 11);
assertThat(result).isEqualTo(2048);
}
@Test
public void readFrameBlockSizeSamplesFromKey_invalidKey_returnsCorrectBlockSize() {
int result =
FlacFrameReader.readFrameBlockSizeSamplesFromKey(
new ParsableByteArray(/* limit= */ 0), /* blockSizeKey= */ 25);
assertThat(result).isEqualTo(-1);
}
private static ExtractorInput buildExtractorInput(String file) throws IOException {
byte[] fileData = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), file);
return new FakeExtractorInput.Builder().setData(fileData).build();
}
private ExtractorInput buildExtractorInputReadingFromFirstFrame(
String file, FlacStreamMetadataHolder streamMetadataHolder)
throws IOException, InterruptedException {
ExtractorInput input = buildExtractorInput(file);
input.skipFully(FlacConstants.STREAM_MARKER_SIZE);
boolean lastMetadataBlock = false;
while (!lastMetadataBlock) {
lastMetadataBlock = FlacMetadataReader.readMetadataBlock(input, streamMetadataHolder);
}
return input;
}
}

View File

@ -0,0 +1,408 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.FlacMetadataReader.FlacStreamMetadataHolder;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.flac.PictureFrame;
import com.google.android.exoplayer2.metadata.flac.VorbisComment;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.FlacConstants;
import com.google.android.exoplayer2.util.FlacStreamMetadata;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Unit tests for {@link FlacMetadataReader}.
*
* <p>Most expected results in these tests have been retrieved using the <a
* href="https://xiph.org/flac/documentation_tools_metaflac.html">metaflac</a> command.
*/
@RunWith(AndroidJUnit4.class)
public class FlacMetadataReaderTest {
@Test
public void peekId3Metadata_updatesPeekPosition() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_id3_enabled.flac");
FlacMetadataReader.peekId3Metadata(input, /* parseData= */ false);
assertThat(input.getPosition()).isEqualTo(0);
assertThat(input.getPeekPosition()).isNotEqualTo(0);
}
@Test
public void peekId3Metadata_parseData_returnsNonEmptyMetadata() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_id3_enabled.flac");
Metadata metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ true);
assertThat(metadata).isNotNull();
assertThat(metadata.length()).isNotEqualTo(0);
}
@Test
public void peekId3Metadata_doNotParseData_returnsNull() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_id3_enabled.flac");
Metadata metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ false);
assertThat(metadata).isNull();
}
@Test
public void peekId3Metadata_noId3Metadata_returnsNull() throws Exception {
String fileWithoutId3Metadata = "flac/bear.flac";
ExtractorInput input = buildExtractorInput(fileWithoutId3Metadata);
Metadata metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ true);
assertThat(metadata).isNull();
}
@Test
public void checkAndPeekStreamMarker_updatesPeekPosition() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
FlacMetadataReader.checkAndPeekStreamMarker(input);
assertThat(input.getPosition()).isEqualTo(0);
assertThat(input.getPeekPosition()).isEqualTo(FlacConstants.STREAM_MARKER_SIZE);
}
@Test
public void checkAndPeekStreamMarker_validData_isTrue() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
boolean result = FlacMetadataReader.checkAndPeekStreamMarker(input);
assertThat(result).isTrue();
}
@Test
public void checkAndPeekStreamMarker_invalidData_isFalse() throws Exception {
ExtractorInput input = buildExtractorInput("mp3/bear.mp3");
boolean result = FlacMetadataReader.checkAndPeekStreamMarker(input);
assertThat(result).isFalse();
}
@Test
public void readId3Metadata_updatesReadPositionAndAlignsPeekPosition() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_id3_enabled.flac");
// Advance peek position after ID3 metadata.
FlacMetadataReader.peekId3Metadata(input, /* parseData= */ false);
input.advancePeekPosition(1);
FlacMetadataReader.readId3Metadata(input, /* parseData= */ false);
assertThat(input.getPosition()).isNotEqualTo(0);
assertThat(input.getPeekPosition()).isEqualTo(input.getPosition());
}
@Test
public void readId3Metadata_parseData_returnsNonEmptyMetadata() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_id3_enabled.flac");
Metadata metadata = FlacMetadataReader.readId3Metadata(input, /* parseData= */ true);
assertThat(metadata).isNotNull();
assertThat(metadata.length()).isNotEqualTo(0);
}
@Test
public void readId3Metadata_doNotParseData_returnsNull() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_id3_enabled.flac");
Metadata metadata = FlacMetadataReader.readId3Metadata(input, /* parseData= */ false);
assertThat(metadata).isNull();
}
@Test
public void readId3Metadata_noId3Metadata_returnsNull() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
Metadata metadata = FlacMetadataReader.readId3Metadata(input, /* parseData= */ true);
assertThat(metadata).isNull();
}
@Test
public void readStreamMarker_updatesReadPosition() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
FlacMetadataReader.readStreamMarker(input);
assertThat(input.getPosition()).isEqualTo(FlacConstants.STREAM_MARKER_SIZE);
assertThat(input.getPeekPosition()).isEqualTo(input.getPosition());
}
@Test(expected = ParserException.class)
public void readStreamMarker_invalidData_throwsException() throws Exception {
ExtractorInput input = buildExtractorInput("mp3/bear.mp3");
FlacMetadataReader.readStreamMarker(input);
}
@Test
public void readMetadataBlock_updatesReadPositionAndAlignsPeekPosition() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
input.skipFully(FlacConstants.STREAM_MARKER_SIZE);
// Advance peek position after metadata block.
input.advancePeekPosition(FlacConstants.STREAM_INFO_BLOCK_SIZE + 1);
FlacMetadataReader.readMetadataBlock(
input, new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null));
assertThat(input.getPosition()).isNotEqualTo(0);
assertThat(input.getPeekPosition()).isEqualTo(input.getPosition());
}
@Test
public void readMetadataBlock_lastMetadataBlock_isTrue() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_one_metadata_block.flac");
input.skipFully(FlacConstants.STREAM_MARKER_SIZE);
boolean result =
FlacMetadataReader.readMetadataBlock(
input, new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null));
assertThat(result).isTrue();
}
@Test
public void readMetadataBlock_notLastMetadataBlock_isFalse() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
input.skipFully(FlacConstants.STREAM_MARKER_SIZE);
boolean result =
FlacMetadataReader.readMetadataBlock(
input, new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null));
assertThat(result).isFalse();
}
@Test
public void readMetadataBlock_streamInfoBlock_setsStreamMetadata() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
input.skipFully(FlacConstants.STREAM_MARKER_SIZE);
FlacStreamMetadataHolder metadataHolder =
new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null);
FlacMetadataReader.readMetadataBlock(input, metadataHolder);
assertThat(metadataHolder.flacStreamMetadata).isNotNull();
assertThat(metadataHolder.flacStreamMetadata.sampleRate).isEqualTo(48000);
}
@Test
public void readMetadataBlock_seekTableBlock_updatesStreamMetadata() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
// Skip to seek table block.
input.skipFully(FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE);
FlacStreamMetadataHolder metadataHolder = new FlacStreamMetadataHolder(buildStreamMetadata());
long originalSampleRate = metadataHolder.flacStreamMetadata.sampleRate;
FlacMetadataReader.readMetadataBlock(input, metadataHolder);
assertThat(metadataHolder.flacStreamMetadata).isNotNull();
// Check that metadata passed has not been erased.
assertThat(metadataHolder.flacStreamMetadata.sampleRate).isEqualTo(originalSampleRate);
assertThat(metadataHolder.flacStreamMetadata.seekTable).isNotNull();
assertThat(metadataHolder.flacStreamMetadata.seekTable.pointSampleNumbers.length).isEqualTo(32);
}
@Test
public void readMetadataBlock_vorbisCommentBlock_updatesStreamMetadata() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_vorbis_comments.flac");
// Skip to Vorbis comment block.
input.skipFully(640);
FlacStreamMetadataHolder metadataHolder = new FlacStreamMetadataHolder(buildStreamMetadata());
long originalSampleRate = metadataHolder.flacStreamMetadata.sampleRate;
FlacMetadataReader.readMetadataBlock(input, metadataHolder);
assertThat(metadataHolder.flacStreamMetadata).isNotNull();
// Check that metadata passed has not been erased.
assertThat(metadataHolder.flacStreamMetadata.sampleRate).isEqualTo(originalSampleRate);
Metadata metadata =
metadataHolder.flacStreamMetadata.getMetadataCopyWithAppendedEntriesFrom(null);
assertThat(metadata).isNotNull();
VorbisComment vorbisComment = (VorbisComment) metadata.get(0);
assertThat(vorbisComment.key).isEqualTo("TITLE");
assertThat(vorbisComment.value).isEqualTo("test title");
}
@Test
public void readMetadataBlock_pictureBlock_updatesStreamMetadata() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear_with_picture.flac");
// Skip to picture block.
input.skipFully(640);
FlacStreamMetadataHolder metadataHolder = new FlacStreamMetadataHolder(buildStreamMetadata());
long originalSampleRate = metadataHolder.flacStreamMetadata.sampleRate;
FlacMetadataReader.readMetadataBlock(input, metadataHolder);
assertThat(metadataHolder.flacStreamMetadata).isNotNull();
// Check that metadata passed has not been erased.
assertThat(metadataHolder.flacStreamMetadata.sampleRate).isEqualTo(originalSampleRate);
Metadata metadata =
metadataHolder.flacStreamMetadata.getMetadataCopyWithAppendedEntriesFrom(null);
assertThat(metadata).isNotNull();
PictureFrame pictureFrame = (PictureFrame) metadata.get(0);
assertThat(pictureFrame.pictureType).isEqualTo(3);
assertThat(pictureFrame.mimeType).isEqualTo("image/png");
assertThat(pictureFrame.description).isEqualTo("");
assertThat(pictureFrame.width).isEqualTo(371);
assertThat(pictureFrame.height).isEqualTo(320);
assertThat(pictureFrame.depth).isEqualTo(24);
assertThat(pictureFrame.colors).isEqualTo(0);
assertThat(pictureFrame.pictureData).hasLength(30943);
}
@Test
public void readMetadataBlock_blockToSkip_updatesReadPosition() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
// Skip to padding block.
input.skipFully(640);
FlacStreamMetadataHolder metadataHolder = new FlacStreamMetadataHolder(buildStreamMetadata());
FlacMetadataReader.readMetadataBlock(input, metadataHolder);
assertThat(input.getPosition()).isGreaterThan(640);
assertThat(input.getPeekPosition()).isEqualTo(input.getPosition());
}
@Test(expected = IllegalArgumentException.class)
public void readMetadataBlock_nonStreamInfoBlockWithNullStreamMetadata_throwsException()
throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
// Skip to seek table block.
input.skipFully(FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE);
FlacMetadataReader.readMetadataBlock(
input, new FlacStreamMetadataHolder(/* flacStreamMetadata= */ null));
}
@Test
public void readSeekTableMetadataBlock_updatesPosition() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
// Skip to seek table block.
input.skipFully(FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE);
int seekTableBlockSize = 598;
ParsableByteArray scratch = new ParsableByteArray(seekTableBlockSize);
input.read(scratch.data, 0, seekTableBlockSize);
FlacMetadataReader.readSeekTableMetadataBlock(scratch);
assertThat(scratch.getPosition()).isEqualTo(seekTableBlockSize);
}
@Test
public void readSeekTableMetadataBlock_returnsCorrectSeekPoints() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
// Skip to seek table block.
input.skipFully(FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE);
int seekTableBlockSize = 598;
ParsableByteArray scratch = new ParsableByteArray(seekTableBlockSize);
input.read(scratch.data, 0, seekTableBlockSize);
FlacStreamMetadata.SeekTable seekTable = FlacMetadataReader.readSeekTableMetadataBlock(scratch);
assertThat(seekTable.pointOffsets[0]).isEqualTo(0);
assertThat(seekTable.pointSampleNumbers[0]).isEqualTo(0);
assertThat(seekTable.pointOffsets[31]).isEqualTo(160602);
assertThat(seekTable.pointSampleNumbers[31]).isEqualTo(126976);
}
@Test
public void readSeekTableMetadataBlock_ignoresPlaceholders() throws IOException {
byte[] fileData =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), "flac/bear.flac");
ParsableByteArray scratch = new ParsableByteArray(fileData);
// Skip to seek table block.
scratch.skipBytes(FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE);
FlacStreamMetadata.SeekTable seekTable = FlacMetadataReader.readSeekTableMetadataBlock(scratch);
// Seek point at index 32 is a placeholder.
assertThat(seekTable.pointSampleNumbers).hasLength(32);
}
@Test
public void getFrameStartMarker_doesNotUpdateReadPositionAndAlignsPeekPosition()
throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
int firstFramePosition = 8880;
input.skipFully(firstFramePosition);
// Advance the peek position after the frame start marker.
input.advancePeekPosition(3);
FlacMetadataReader.getFrameStartMarker(input);
assertThat(input.getPosition()).isEqualTo(firstFramePosition);
assertThat(input.getPeekPosition()).isEqualTo(input.getPosition());
}
@Test
public void getFrameStartMarker_returnsCorrectFrameStartMarker() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
// Skip to first frame.
input.skipFully(8880);
int result = FlacMetadataReader.getFrameStartMarker(input);
assertThat(result).isEqualTo(0xFFF8);
}
@Test(expected = ParserException.class)
public void getFrameStartMarker_invalidData_throwsException() throws Exception {
ExtractorInput input = buildExtractorInput("flac/bear.flac");
// Input position is incorrect.
FlacMetadataReader.getFrameStartMarker(input);
}
private static ExtractorInput buildExtractorInput(String file) throws IOException {
byte[] fileData = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), file);
return new FakeExtractorInput.Builder().setData(fileData).build();
}
private static FlacStreamMetadata buildStreamMetadata() {
return new FlacStreamMetadata(
/* minBlockSizeSamples= */ 10,
/* maxBlockSizeSamples= */ 20,
/* minFrameSize= */ 5,
/* maxFrameSize= */ 10,
/* sampleRate= */ 44100,
/* channels= */ 2,
/* bitsPerSample= */ 8,
/* totalSamples= */ 1000,
/* vorbisComments= */ new ArrayList<>(),
/* pictureFrames= */ new ArrayList<>());
}
}

View File

@ -17,9 +17,12 @@ package com.google.android.exoplayer2.util;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.flac.VorbisComment;
import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.IOException;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -28,6 +31,27 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public final class FlacStreamMetadataTest {
@Test
public void constructFromByteArray_setsFieldsCorrectly() throws IOException {
byte[] fileData =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), "flac/bear.flac");
FlacStreamMetadata streamMetadata =
new FlacStreamMetadata(
fileData, FlacConstants.STREAM_MARKER_SIZE + FlacConstants.METADATA_BLOCK_HEADER_SIZE);
assertThat(streamMetadata.minBlockSizeSamples).isEqualTo(4096);
assertThat(streamMetadata.maxBlockSizeSamples).isEqualTo(4096);
assertThat(streamMetadata.minFrameSize).isEqualTo(445);
assertThat(streamMetadata.maxFrameSize).isEqualTo(5776);
assertThat(streamMetadata.sampleRate).isEqualTo(48000);
assertThat(streamMetadata.sampleRateLookupKey).isEqualTo(10);
assertThat(streamMetadata.channels).isEqualTo(2);
assertThat(streamMetadata.bitsPerSample).isEqualTo(16);
assertThat(streamMetadata.bitsPerSampleLookupKey).isEqualTo(4);
assertThat(streamMetadata.totalSamples).isEqualTo(131568);
}
@Test
public void parseVorbisComments() {
ArrayList<String> commentsList = new ArrayList<>();