Commit f09f62da by kimvde Committed by Oliver Woodman

Expose metadata in FLAC extractor

PiperOrigin-RevId: 281538423
parent a558501e
Showing with 770 additions and 239 deletions
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
* Add Java FLAC extractor * Add Java FLAC extractor
([#6406](https://github.com/google/ExoPlayer/issues/6406)). ([#6406](https://github.com/google/ExoPlayer/issues/6406)).
This extractor does not support seeking and live streams, and does not expose
vorbis, ID3 and picture data. If `DefaultExtractorsFactory` is used, this
extractor is only used if the FLAC extension is not loaded.
### 2.11.1 (2019-12-20) ### ### 2.11.1 (2019-12-20) ###
...@@ -35,6 +32,32 @@ ...@@ -35,6 +32,32 @@
([#6792](https://github.com/google/ExoPlayer/issues/6792)). ([#6792](https://github.com/google/ExoPlayer/issues/6792)).
### 2.11.0 (2019-12-11) ### ### 2.11.0 (2019-12-11) ###
This extractor does not support seeking and live streams. If
`DefaultExtractorsFactory` is used, this extractor is only used if the FLAC
extension is not loaded.
* Video tunneling: Fix renderer end-of-stream with `OnFrameRenderedListener`
from API 23, tunneled renderer must send a special timestamp on EOS.
Previously the EOS was reported when the input stream reached EOS.
* Require an end time or duration for SubRip (SRT) and SubStation Alpha
(SSA/ASS) subtitles. This applies to both sidecar files & subtitles
[embedded in Matroska streams](https://matroska.org/technical/specs/subtitles/index.html).
* Use `ExoMediaDrm.Provider` in `OfflineLicenseHelper` to avoid `ExoMediaDrm`
leaks ([#4721](https://github.com/google/ExoPlayer/issues/4721)).
* Improve `Format` propagation within the `MediaCodecRenderer` and subclasses.
For example, fix handling of pixel aspect ratio changes in playlists where
video resolution does not change.
([#6646](https://github.com/google/ExoPlayer/issues/6646)).
* Rename `MediaCodecRenderer.onOutputFormatChanged` to
`MediaCodecRenderer.onOutputMediaFormatChanged`, further
clarifying the distinction between `Format` and `MediaFormat`.
* Fix byte order of HDR10+ static metadata to match CTA-861.3.
* Reconfigure audio sink when PCM encoding changes
([#6601](https://github.com/google/ExoPlayer/issues/6601)).
* Make `MediaSourceEventListener.LoadEventInfo` and
`MediaSourceEventListener.MediaLoadData` top-level classes.
### 2.11.0 (not yet released) ###
>>>>>>> b18650fdc... Expose metadata in FLAC extractor
* Core library: * Core library:
* Replace `ExoPlayerFactory` by `SimpleExoPlayer.Builder` and * Replace `ExoPlayerFactory` by `SimpleExoPlayer.Builder` and
......
...@@ -26,15 +26,13 @@ import com.google.android.exoplayer2.extractor.Extractor; ...@@ -26,15 +26,13 @@ import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.Id3Peeker; import com.google.android.exoplayer2.extractor.FlacMetadataReader;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.SeekPoint; import com.google.android.exoplayer2.extractor.SeekPoint;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.FlacMetadataReader;
import com.google.android.exoplayer2.util.FlacStreamMetadata; import com.google.android.exoplayer2.util.FlacStreamMetadata;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
...@@ -73,7 +71,6 @@ public final class FlacExtractor implements Extractor { ...@@ -73,7 +71,6 @@ public final class FlacExtractor implements Extractor {
public static final int FLAG_DISABLE_ID3_METADATA = 1; public static final int FLAG_DISABLE_ID3_METADATA = 1;
private final ParsableByteArray outputBuffer; private final ParsableByteArray outputBuffer;
private final Id3Peeker id3Peeker;
private final boolean id3MetadataDisabled; private final boolean id3MetadataDisabled;
@Nullable private FlacDecoderJni decoderJni; @Nullable private FlacDecoderJni decoderJni;
...@@ -87,7 +84,7 @@ public final class FlacExtractor implements Extractor { ...@@ -87,7 +84,7 @@ public final class FlacExtractor implements Extractor {
@Nullable private Metadata id3Metadata; @Nullable private Metadata id3Metadata;
@Nullable private FlacBinarySearchSeeker binarySearchSeeker; @Nullable private FlacBinarySearchSeeker binarySearchSeeker;
/** Constructs an instance with flags = 0. */ /** Constructs an instance with {@code flags = 0}. */
public FlacExtractor() { public FlacExtractor() {
this(/* flags= */ 0); this(/* flags= */ 0);
} }
...@@ -95,11 +92,11 @@ public final class FlacExtractor implements Extractor { ...@@ -95,11 +92,11 @@ public final class FlacExtractor implements Extractor {
/** /**
* Constructs an instance. * Constructs an instance.
* *
* @param flags Flags that control the extractor's behavior. * @param flags Flags that control the extractor's behavior. Possible flags are described by
* {@link Flags}.
*/ */
public FlacExtractor(int flags) { public FlacExtractor(int flags) {
outputBuffer = new ParsableByteArray(); outputBuffer = new ParsableByteArray();
id3Peeker = new Id3Peeker();
id3MetadataDisabled = (flags & FLAG_DISABLE_ID3_METADATA) != 0; id3MetadataDisabled = (flags & FLAG_DISABLE_ID3_METADATA) != 0;
} }
...@@ -117,7 +114,7 @@ public final class FlacExtractor implements Extractor { ...@@ -117,7 +114,7 @@ public final class FlacExtractor implements Extractor {
@Override @Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
id3Metadata = peekId3Data(input); id3Metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ !id3MetadataDisabled);
return FlacMetadataReader.checkAndPeekStreamMarker(input); return FlacMetadataReader.checkAndPeekStreamMarker(input);
} }
...@@ -125,7 +122,7 @@ public final class FlacExtractor implements Extractor { ...@@ -125,7 +122,7 @@ public final class FlacExtractor implements Extractor {
public int read(final ExtractorInput input, PositionHolder seekPosition) public int read(final ExtractorInput input, PositionHolder seekPosition)
throws IOException, InterruptedException { throws IOException, InterruptedException {
if (input.getPosition() == 0 && !id3MetadataDisabled && id3Metadata == null) { if (input.getPosition() == 0 && !id3MetadataDisabled && id3Metadata == null) {
id3Metadata = peekId3Data(input); id3Metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ true);
} }
FlacDecoderJni decoderJni = initDecoderJni(input); FlacDecoderJni decoderJni = initDecoderJni(input);
...@@ -177,19 +174,6 @@ public final class FlacExtractor implements Extractor { ...@@ -177,19 +174,6 @@ public final class FlacExtractor implements Extractor {
} }
} }
/**
* Peeks ID3 tag data at the beginning of the input.
*
* @return The first ID3 tag {@link Metadata}, or null if an ID3 tag is not present in the input.
*/
@Nullable
private Metadata peekId3Data(ExtractorInput input) throws IOException, InterruptedException {
input.resetPeekPosition();
Id3Decoder.FramePredicate id3FramePredicate =
id3MetadataDisabled ? Id3Decoder.NO_FRAMES_PREDICATE : null;
return id3Peeker.peekId3Data(input, id3FramePredicate);
}
@EnsuresNonNull({"decoderJni", "extractorOutput", "trackOutput"}) // Ensures initialized. @EnsuresNonNull({"decoderJni", "extractorOutput", "trackOutput"}) // Ensures initialized.
@SuppressWarnings({"contracts.postcondition.not.satisfied"}) @SuppressWarnings({"contracts.postcondition.not.satisfied"})
private FlacDecoderJni initDecoderJni(ExtractorInput input) { private FlacDecoderJni initDecoderJni(ExtractorInput input) {
...@@ -220,10 +204,7 @@ public final class FlacExtractor implements Extractor { ...@@ -220,10 +204,7 @@ public final class FlacExtractor implements Extractor {
this.streamMetadata = streamMetadata; this.streamMetadata = streamMetadata;
binarySearchSeeker = binarySearchSeeker =
outputSeekMap(decoderJni, streamMetadata, input.getLength(), extractorOutput); outputSeekMap(decoderJni, streamMetadata, input.getLength(), extractorOutput);
Metadata metadata = id3MetadataDisabled ? null : id3Metadata; Metadata metadata = streamMetadata.getMetadataCopyWithAppendedEntriesFrom(id3Metadata);
if (streamMetadata.metadata != null) {
metadata = streamMetadata.metadata.copyWithAppendedEntriesFrom(metadata);
}
outputFormat(streamMetadata, metadata, trackOutput); outputFormat(streamMetadata, metadata, trackOutput);
outputBuffer.reset(streamMetadata.getMaxDecodedFrameSize()); outputBuffer.reset(streamMetadata.getMaxDecodedFrameSize());
outputFrameHolder = new OutputFrameHolder(ByteBuffer.wrap(outputBuffer.data)); outputFrameHolder = new OutputFrameHolder(ByteBuffer.wrap(outputBuffer.data));
......
...@@ -151,7 +151,7 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) { ...@@ -151,7 +151,7 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
"FlacStreamMetadata"); "FlacStreamMetadata");
jmethodID flacStreamMetadataConstructor = jmethodID flacStreamMetadataConstructor =
env->GetMethodID(flacStreamMetadataClass, "<init>", env->GetMethodID(flacStreamMetadataClass, "<init>",
"(IIIIIIIJLjava/util/List;Ljava/util/List;)V"); "(IIIIIIIJLjava/util/ArrayList;Ljava/util/ArrayList;)V");
return env->NewObject(flacStreamMetadataClass, flacStreamMetadataConstructor, return env->NewObject(flacStreamMetadataClass, flacStreamMetadataConstructor,
streamInfo.min_blocksize, streamInfo.max_blocksize, streamInfo.min_blocksize, streamInfo.max_blocksize,
......
...@@ -13,7 +13,11 @@ ...@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.extractor;
import com.google.android.exoplayer2.util.FlacStreamMetadata;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
/** Reads and peeks FLAC frame elements. */ /** Reads and peeks FLAC frame elements. */
public final class FlacFrameReader { public final class FlacFrameReader {
......
...@@ -13,17 +13,17 @@ ...@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.extractor.ogg; package com.google.android.exoplayer2.extractor;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
/** /**
* Wraps a byte array, providing methods that allow it to be read as a vorbis bitstream. * Wraps a byte array, providing methods that allow it to be read as a Vorbis bitstream.
* *
* @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-360002">Vorbis bitpacking * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-360002">Vorbis bitpacking
* specification</a> * specification</a>
*/ */
/* package */ final class VorbisBitArray { public final class VorbisBitArray {
private final byte[] data; private final byte[] data;
private final int byteLimit; private final int byteLimit;
......
...@@ -18,20 +18,22 @@ package com.google.android.exoplayer2.extractor.flac; ...@@ -18,20 +18,22 @@ package com.google.android.exoplayer2.extractor.flac;
import static com.google.android.exoplayer2.util.Util.castNonNull; import static com.google.android.exoplayer2.util.Util.castNonNull;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.FlacFrameReader;
import com.google.android.exoplayer2.extractor.FlacFrameReader.BlockSizeHolder;
import com.google.android.exoplayer2.extractor.FlacMetadataReader;
import com.google.android.exoplayer2.extractor.FlacMetadataReader.FirstFrameMetadata;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.FlacConstants; import com.google.android.exoplayer2.util.FlacConstants;
import com.google.android.exoplayer2.util.FlacFrameReader;
import com.google.android.exoplayer2.util.FlacFrameReader.BlockSizeHolder;
import com.google.android.exoplayer2.util.FlacMetadataReader;
import com.google.android.exoplayer2.util.FlacMetadataReader.FirstFrameMetadata;
import com.google.android.exoplayer2.util.FlacStreamMetadata; import com.google.android.exoplayer2.util.FlacStreamMetadata;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException; import java.io.IOException;
...@@ -41,7 +43,6 @@ import java.lang.annotation.RetentionPolicy; ...@@ -41,7 +43,6 @@ import java.lang.annotation.RetentionPolicy;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// TODO: implement seeking. // TODO: implement seeking.
// TODO: expose vorbis and ID3 data.
// TODO: support live streams. // TODO: support live streams.
/** /**
* Extracts data from FLAC container format. * Extracts data from FLAC container format.
...@@ -53,23 +54,40 @@ public final class FlacExtractor implements Extractor { ...@@ -53,23 +54,40 @@ public final class FlacExtractor implements Extractor {
/** Factory for {@link FlacExtractor} instances. */ /** Factory for {@link FlacExtractor} instances. */
public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new FlacExtractor()}; public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new FlacExtractor()};
/**
* Flags controlling the behavior of the extractor. Possible flag value is {@link
* #FLAG_DISABLE_ID3_METADATA}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef(
flag = true,
value = {FLAG_DISABLE_ID3_METADATA})
public @interface Flags {}
/**
* Flag to disable parsing of ID3 metadata. Can be set to save memory if ID3 metadata is not
* required.
*/
public static final int FLAG_DISABLE_ID3_METADATA = 1;
/** Parser state. */ /** Parser state. */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({ @IntDef({
STATE_READ_ID3_TAG, STATE_READ_ID3_METADATA,
STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES,
STATE_READ_STREAM_MARKER, STATE_READ_STREAM_MARKER,
STATE_READ_STREAM_INFO_BLOCK, STATE_READ_METADATA_BLOCKS,
STATE_SKIP_OPTIONAL_METADATA_BLOCKS,
STATE_GET_FIRST_FRAME_METADATA, STATE_GET_FIRST_FRAME_METADATA,
STATE_READ_FRAMES STATE_READ_FRAMES
}) })
private @interface State {} private @interface State {}
private static final int STATE_READ_ID3_TAG = 0; private static final int STATE_READ_ID3_METADATA = 0;
private static final int STATE_READ_STREAM_MARKER = 1; private static final int STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES = 1;
private static final int STATE_READ_STREAM_INFO_BLOCK = 2; private static final int STATE_READ_STREAM_MARKER = 2;
private static final int STATE_SKIP_OPTIONAL_METADATA_BLOCKS = 3; private static final int STATE_READ_METADATA_BLOCKS = 3;
private static final int STATE_GET_FIRST_FRAME_METADATA = 4; private static final int STATE_GET_FIRST_FRAME_METADATA = 4;
private static final int STATE_READ_FRAMES = 5; private static final int STATE_READ_FRAMES = 5;
...@@ -81,6 +99,7 @@ public final class FlacExtractor implements Extractor { ...@@ -81,6 +99,7 @@ public final class FlacExtractor implements Extractor {
private final byte[] streamMarkerAndInfoBlock; private final byte[] streamMarkerAndInfoBlock;
private final ParsableByteArray scratch; private final ParsableByteArray scratch;
private final boolean id3MetadataDisabled;
private final BlockSizeHolder blockSizeHolder; private final BlockSizeHolder blockSizeHolder;
...@@ -88,6 +107,7 @@ public final class FlacExtractor implements Extractor { ...@@ -88,6 +107,7 @@ public final class FlacExtractor implements Extractor {
@MonotonicNonNull private TrackOutput trackOutput; @MonotonicNonNull private TrackOutput trackOutput;
private @State int state; private @State int state;
@Nullable private Metadata id3Metadata;
@MonotonicNonNull private FlacStreamMetadata flacStreamMetadata; @MonotonicNonNull private FlacStreamMetadata flacStreamMetadata;
private int minFrameSize; private int minFrameSize;
private int frameStartMarker; private int frameStartMarker;
...@@ -95,16 +115,28 @@ public final class FlacExtractor implements Extractor { ...@@ -95,16 +115,28 @@ public final class FlacExtractor implements Extractor {
private int currentFrameBytesWritten; private int currentFrameBytesWritten;
private long totalSamplesWritten; private long totalSamplesWritten;
/** Constructs an instance with {@code flags = 0}. */
public FlacExtractor() { public FlacExtractor() {
this(/* flags= */ 0);
}
/**
* Constructs an instance.
*
* @param flags Flags that control the extractor's behavior. Possible flags are described by
* {@link Flags}.
*/
public FlacExtractor(int flags) {
streamMarkerAndInfoBlock = streamMarkerAndInfoBlock =
new byte[FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE]; new byte[FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE];
scratch = new ParsableByteArray(SCRATCH_LENGTH); scratch = new ParsableByteArray(SCRATCH_LENGTH);
blockSizeHolder = new BlockSizeHolder(); blockSizeHolder = new BlockSizeHolder();
id3MetadataDisabled = (flags & FLAG_DISABLE_ID3_METADATA) != 0;
} }
@Override @Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
FlacMetadataReader.peekId3Data(input); FlacMetadataReader.peekId3Metadata(input, /* parseData= */ false);
return FlacMetadataReader.checkAndPeekStreamMarker(input); return FlacMetadataReader.checkAndPeekStreamMarker(input);
} }
...@@ -119,17 +151,17 @@ public final class FlacExtractor implements Extractor { ...@@ -119,17 +151,17 @@ public final class FlacExtractor implements Extractor {
public int read(ExtractorInput input, PositionHolder seekPosition) public int read(ExtractorInput input, PositionHolder seekPosition)
throws IOException, InterruptedException { throws IOException, InterruptedException {
switch (state) { switch (state) {
case STATE_READ_ID3_TAG: case STATE_READ_ID3_METADATA:
readId3Tag(input); readId3Metadata(input);
return Extractor.RESULT_CONTINUE;
case STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES:
getStreamMarkerAndInfoBlockBytes(input);
return Extractor.RESULT_CONTINUE; return Extractor.RESULT_CONTINUE;
case STATE_READ_STREAM_MARKER: case STATE_READ_STREAM_MARKER:
readStreamMarker(input); readStreamMarker(input);
return Extractor.RESULT_CONTINUE; return Extractor.RESULT_CONTINUE;
case STATE_READ_STREAM_INFO_BLOCK: case STATE_READ_METADATA_BLOCKS:
readStreamInfoBlock(input); readMetadataBlocks(input);
return Extractor.RESULT_CONTINUE;
case STATE_SKIP_OPTIONAL_METADATA_BLOCKS:
skipOptionalMetadataBlocks(input);
return Extractor.RESULT_CONTINUE; return Extractor.RESULT_CONTINUE;
case STATE_GET_FIRST_FRAME_METADATA: case STATE_GET_FIRST_FRAME_METADATA:
getFirstFrameMetadata(input); getFirstFrameMetadata(input);
...@@ -143,7 +175,7 @@ public final class FlacExtractor implements Extractor { ...@@ -143,7 +175,7 @@ public final class FlacExtractor implements Extractor {
@Override @Override
public void seek(long position, long timeUs) { public void seek(long position, long timeUs) {
state = STATE_READ_ID3_TAG; state = STATE_READ_ID3_METADATA;
currentFrameBytesWritten = 0; currentFrameBytesWritten = 0;
totalSamplesWritten = 0; totalSamplesWritten = 0;
scratch.reset(); scratch.reset();
...@@ -156,40 +188,40 @@ public final class FlacExtractor implements Extractor { ...@@ -156,40 +188,40 @@ public final class FlacExtractor implements Extractor {
// Private methods. // Private methods.
private void readId3Tag(ExtractorInput input) throws IOException, InterruptedException { private void readId3Metadata(ExtractorInput input) throws IOException, InterruptedException {
FlacMetadataReader.readId3Data(input); id3Metadata = FlacMetadataReader.readId3Metadata(input, /* parseData= */ !id3MetadataDisabled);
state = STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES;
}
private void getStreamMarkerAndInfoBlockBytes(ExtractorInput input)
throws IOException, InterruptedException {
input.peekFully(streamMarkerAndInfoBlock, 0, streamMarkerAndInfoBlock.length);
input.resetPeekPosition();
state = STATE_READ_STREAM_MARKER; state = STATE_READ_STREAM_MARKER;
} }
private void readStreamMarker(ExtractorInput input) throws IOException, InterruptedException { private void readStreamMarker(ExtractorInput input) throws IOException, InterruptedException {
FlacMetadataReader.readStreamMarker( FlacMetadataReader.readStreamMarker(input);
input, streamMarkerAndInfoBlock, /* scratchWriteIndex= */ 0); state = STATE_READ_METADATA_BLOCKS;
state = STATE_READ_STREAM_INFO_BLOCK;
} }
private void readStreamInfoBlock(ExtractorInput input) throws IOException, InterruptedException { private void readMetadataBlocks(ExtractorInput input) throws IOException, InterruptedException {
flacStreamMetadata = boolean isLastMetadataBlock = false;
FlacMetadataReader.readStreamInfoBlock( FlacMetadataReader.FlacStreamMetadataHolder metadataHolder =
input, new FlacMetadataReader.FlacStreamMetadataHolder(flacStreamMetadata);
/* scratchData= */ streamMarkerAndInfoBlock, while (!isLastMetadataBlock) {
/* scratchWriteIndex= */ FlacConstants.STREAM_MARKER_SIZE); isLastMetadataBlock = FlacMetadataReader.readMetadataBlock(input, metadataHolder);
// Save the current metadata in case an exception occurs.
flacStreamMetadata = castNonNull(metadataHolder.flacStreamMetadata);
}
Assertions.checkNotNull(flacStreamMetadata);
minFrameSize = Math.max(flacStreamMetadata.minFrameSize, FlacConstants.MIN_FRAME_HEADER_SIZE); minFrameSize = Math.max(flacStreamMetadata.minFrameSize, FlacConstants.MIN_FRAME_HEADER_SIZE);
boolean isLastMetadataBlock = castNonNull(trackOutput)
(streamMarkerAndInfoBlock[FlacConstants.STREAM_MARKER_SIZE] >> 7 & 1) == 1; .format(flacStreamMetadata.getFormat(streamMarkerAndInfoBlock, id3Metadata));
castNonNull(trackOutput).format(flacStreamMetadata.getFormat(streamMarkerAndInfoBlock));
castNonNull(extractorOutput) castNonNull(extractorOutput)
.seekMap(new SeekMap.Unseekable(flacStreamMetadata.getDurationUs())); .seekMap(new SeekMap.Unseekable(flacStreamMetadata.getDurationUs()));
if (isLastMetadataBlock) {
state = STATE_GET_FIRST_FRAME_METADATA;
} else {
state = STATE_SKIP_OPTIONAL_METADATA_BLOCKS;
}
}
private void skipOptionalMetadataBlocks(ExtractorInput input)
throws IOException, InterruptedException {
FlacMetadataReader.skipMetadataBlocks(input);
state = STATE_GET_FIRST_FRAME_METADATA; state = STATE_GET_FIRST_FRAME_METADATA;
} }
......
...@@ -69,7 +69,7 @@ import java.util.Arrays; ...@@ -69,7 +69,7 @@ import java.util.Arrays;
if (streamMetadata == null) { if (streamMetadata == null) {
streamMetadata = new FlacStreamMetadata(data, 17); streamMetadata = new FlacStreamMetadata(data, 17);
byte[] metadata = Arrays.copyOfRange(data, 9, packet.limit()); byte[] metadata = Arrays.copyOfRange(data, 9, packet.limit());
setupData.format = streamMetadata.getFormat(metadata); setupData.format = streamMetadata.getFormat(metadata, /* id3Metadata= */ null);
} else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) { } else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) {
flacOggSeeker = new FlacOggSeeker(); flacOggSeeker = new FlacOggSeeker();
flacOggSeeker.parseSeekTable(packet); flacOggSeeker.parseSeekTable(packet);
......
...@@ -18,7 +18,8 @@ package com.google.android.exoplayer2.extractor.ogg; ...@@ -18,7 +18,8 @@ package com.google.android.exoplayer2.extractor.ogg;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.ogg.VorbisUtil.Mode; import com.google.android.exoplayer2.extractor.VorbisUtil;
import com.google.android.exoplayer2.extractor.VorbisUtil.Mode;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException; import java.io.IOException;
......
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
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
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.extractor.ogg; package com.google.android.exoplayer2.extractor;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
......
...@@ -13,16 +13,19 @@ ...@@ -13,16 +13,19 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.extractor.ogg; package com.google.android.exoplayer2.extractor;
import static com.google.android.exoplayer2.extractor.ogg.VorbisUtil.iLog; import static com.google.android.exoplayer2.extractor.VorbisUtil.iLog;
import static com.google.android.exoplayer2.extractor.ogg.VorbisUtil.verifyVorbisHeaderCapturePattern; import static com.google.android.exoplayer2.extractor.VorbisUtil.verifyVorbisHeaderCapturePattern;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -45,7 +48,9 @@ public final class VorbisUtilTest { ...@@ -45,7 +48,9 @@ public final class VorbisUtilTest {
@Test @Test
public void testReadIdHeader() throws Exception { public void testReadIdHeader() throws Exception {
byte[] data = OggTestData.getIdentificationHeaderData(); byte[] data =
TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), "binary/vorbis/id_header");
ParsableByteArray headerData = new ParsableByteArray(data, data.length); ParsableByteArray headerData = new ParsableByteArray(data, data.length);
VorbisUtil.VorbisIdHeader vorbisIdHeader = VorbisUtil.VorbisIdHeader vorbisIdHeader =
VorbisUtil.readVorbisIdentificationHeader(headerData); VorbisUtil.readVorbisIdentificationHeader(headerData);
...@@ -63,8 +68,10 @@ public final class VorbisUtilTest { ...@@ -63,8 +68,10 @@ public final class VorbisUtilTest {
} }
@Test @Test
public void testReadCommentHeader() throws ParserException { public void testReadCommentHeader() throws IOException {
byte[] data = OggTestData.getCommentHeaderDataUTF8(); byte[] data =
TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), "binary/vorbis/comment_header");
ParsableByteArray headerData = new ParsableByteArray(data, data.length); ParsableByteArray headerData = new ParsableByteArray(data, data.length);
VorbisUtil.CommentHeader commentHeader = VorbisUtil.readVorbisCommentHeader(headerData); VorbisUtil.CommentHeader commentHeader = VorbisUtil.readVorbisCommentHeader(headerData);
...@@ -76,8 +83,10 @@ public final class VorbisUtilTest { ...@@ -76,8 +83,10 @@ public final class VorbisUtilTest {
} }
@Test @Test
public void testReadVorbisModes() throws ParserException { public void testReadVorbisModes() throws IOException {
byte[] data = OggTestData.getSetupHeaderData(); byte[] data =
TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), "binary/vorbis/setup_header");
ParsableByteArray headerData = new ParsableByteArray(data, data.length); ParsableByteArray headerData = new ParsableByteArray(data, data.length);
VorbisUtil.Mode[] modes = VorbisUtil.readVorbisModes(headerData, 2); VorbisUtil.Mode[] modes = VorbisUtil.readVorbisModes(headerData, 2);
......
...@@ -30,11 +30,31 @@ public class FlacExtractorTest { ...@@ -30,11 +30,31 @@ public class FlacExtractorTest {
} }
@Test @Test
public void testSampleWithId3() throws Exception { public void testSampleWithId3HeaderAndId3Enabled() throws Exception {
ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_id3.flac"); ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_id3.flac");
} }
@Test @Test
public void testSampleWithId3HeaderAndId3Disabled() throws Exception {
// The same file is used for testing the extractor with and without ID3 enabled as the test does
// not check the metadata outputted. It only checks that the file is parsed correctly in both
// cases.
ExtractorAsserts.assertBehavior(
() -> new FlacExtractor(FlacExtractor.FLAG_DISABLE_ID3_METADATA),
"flac/bear_with_id3.flac");
}
@Test
public void testSampleWithVorbisComments() throws Exception {
ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_vorbis_comments.flac");
}
@Test
public void testSampleWithPicture() throws Exception {
ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_picture.flac");
}
@Test
public void testOneMetadataBlock() throws Exception { public void testOneMetadataBlock() throws Exception {
ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_one_metadata_block.flac"); ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_one_metadata_block.flac");
} }
......
...@@ -19,11 +19,13 @@ import static com.google.android.exoplayer2.extractor.ogg.VorbisReader.readBits; ...@@ -19,11 +19,13 @@ import static com.google.android.exoplayer2.extractor.ogg.VorbisReader.readBits;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.ogg.VorbisReader.VorbisSetup; import com.google.android.exoplayer2.extractor.ogg.VorbisReader.VorbisSetup;
import com.google.android.exoplayer2.testutil.FakeExtractorInput; import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException; import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException; import java.io.IOException;
import org.junit.Test; import org.junit.Test;
...@@ -55,7 +57,11 @@ public final class VorbisReaderTest { ...@@ -55,7 +57,11 @@ public final class VorbisReaderTest {
@Test @Test
public void testReadSetupHeadersWithIOExceptions() throws IOException, InterruptedException { public void testReadSetupHeadersWithIOExceptions() throws IOException, InterruptedException {
byte[] data = OggTestData.getVorbisHeaderPages(); // initial two pages of bytes which by spec contain the three Vorbis header packets:
// identification, comment and setup header.
byte[] data =
TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), "binary/ogg/vorbis_header_pages");
ExtractorInput input = new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(true) ExtractorInput input = new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(true)
.setSimulateUnknownLength(true).setSimulatePartialReads(true).build(); .setSimulateUnknownLength(true).setSimulatePartialReads(true).build();
......
...@@ -35,7 +35,18 @@ public final class FlacStreamMetadataTest { ...@@ -35,7 +35,18 @@ public final class FlacStreamMetadataTest {
commentsList.add("Artist=Singer"); commentsList.add("Artist=Singer");
Metadata metadata = Metadata metadata =
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>()).metadata; new FlacStreamMetadata(
/* minBlockSizeSamples= */ 0,
/* maxBlockSizeSamples= */ 0,
/* minFrameSize= */ 0,
/* maxFrameSize= */ 0,
/* sampleRate= */ 0,
/* channels= */ 0,
/* bitsPerSample= */ 0,
/* totalSamples= */ 0,
commentsList,
/* pictureFrames= */ new ArrayList<>())
.getMetadataCopyWithAppendedEntriesFrom(/* other= */ null);
assertThat(metadata.length()).isEqualTo(2); assertThat(metadata.length()).isEqualTo(2);
VorbisComment commentFrame = (VorbisComment) metadata.get(0); VorbisComment commentFrame = (VorbisComment) metadata.get(0);
...@@ -51,9 +62,20 @@ public final class FlacStreamMetadataTest { ...@@ -51,9 +62,20 @@ public final class FlacStreamMetadataTest {
ArrayList<String> commentsList = new ArrayList<>(); ArrayList<String> commentsList = new ArrayList<>();
Metadata metadata = Metadata metadata =
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>()).metadata; new FlacStreamMetadata(
/* minBlockSizeSamples= */ 0,
/* maxBlockSizeSamples= */ 0,
/* minFrameSize= */ 0,
/* maxFrameSize= */ 0,
/* sampleRate= */ 0,
/* channels= */ 0,
/* bitsPerSample= */ 0,
/* totalSamples= */ 0,
commentsList,
/* pictureFrames= */ new ArrayList<>())
.getMetadataCopyWithAppendedEntriesFrom(/* other= */ null);
assertThat(metadata).isNull(); assertThat(metadata.length()).isEqualTo(0);
} }
@Test @Test
...@@ -62,7 +84,18 @@ public final class FlacStreamMetadataTest { ...@@ -62,7 +84,18 @@ public final class FlacStreamMetadataTest {
commentsList.add("Title=So=ng"); commentsList.add("Title=So=ng");
Metadata metadata = Metadata metadata =
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>()).metadata; new FlacStreamMetadata(
/* minBlockSizeSamples= */ 0,
/* maxBlockSizeSamples= */ 0,
/* minFrameSize= */ 0,
/* maxFrameSize= */ 0,
/* sampleRate= */ 0,
/* channels= */ 0,
/* bitsPerSample= */ 0,
/* totalSamples= */ 0,
commentsList,
/* pictureFrames= */ new ArrayList<>())
.getMetadataCopyWithAppendedEntriesFrom(/* other= */ null);
assertThat(metadata.length()).isEqualTo(1); assertThat(metadata.length()).isEqualTo(1);
VorbisComment commentFrame = (VorbisComment) metadata.get(0); VorbisComment commentFrame = (VorbisComment) metadata.get(0);
...@@ -77,7 +110,18 @@ public final class FlacStreamMetadataTest { ...@@ -77,7 +110,18 @@ public final class FlacStreamMetadataTest {
commentsList.add("Artist=Singer"); commentsList.add("Artist=Singer");
Metadata metadata = Metadata metadata =
new FlacStreamMetadata(0, 0, 0, 0, 0, 0, 0, 0, commentsList, new ArrayList<>()).metadata; new FlacStreamMetadata(
/* minBlockSizeSamples= */ 0,
/* maxBlockSizeSamples= */ 0,
/* minFrameSize= */ 0,
/* maxFrameSize= */ 0,
/* sampleRate= */ 0,
/* channels= */ 0,
/* bitsPerSample= */ 0,
/* totalSamples= */ 0,
commentsList,
/* pictureFrames= */ new ArrayList<>())
.getMetadataCopyWithAppendedEntriesFrom(/* other= */ null);
assertThat(metadata.length()).isEqualTo(1); assertThat(metadata.length()).isEqualTo(1);
VorbisComment commentFrame = (VorbisComment) metadata.get(0); VorbisComment commentFrame = (VorbisComment) metadata.get(0);
......
...@@ -152,6 +152,7 @@ public final class ExtractorAsserts { ...@@ -152,6 +152,7 @@ public final class ExtractorAsserts {
assertOutput(factory.create(), file, data, context, false, false, false, false); assertOutput(factory.create(), file, data, context, false, false, false, false);
} }
// TODO: Assert format metadata [Internal ref: b/144771011].
/** /**
* Asserts that {@code extractor} consumes {@code sampleFile} successfully and its output equals * Asserts that {@code extractor} consumes {@code sampleFile} successfully and its output equals
* to a prerecorded output dump file with the name {@code sampleFile} + "{@value * to a prerecorded output dump file with the name {@code sampleFile} + "{@value
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment