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 @@
* Add Java FLAC extractor
([#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) ###
......@@ -35,6 +32,32 @@
([#6792](https://github.com/google/ExoPlayer/issues/6792)).
### 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:
* Replace `ExoPlayerFactory` by `SimpleExoPlayer.Builder` and
......
......@@ -26,15 +26,13 @@ import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
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.SeekMap;
import com.google.android.exoplayer2.extractor.SeekPoint;
import com.google.android.exoplayer2.extractor.TrackOutput;
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.FlacMetadataReader;
import com.google.android.exoplayer2.util.FlacStreamMetadata;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
......@@ -73,7 +71,6 @@ public final class FlacExtractor implements Extractor {
public static final int FLAG_DISABLE_ID3_METADATA = 1;
private final ParsableByteArray outputBuffer;
private final Id3Peeker id3Peeker;
private final boolean id3MetadataDisabled;
@Nullable private FlacDecoderJni decoderJni;
......@@ -87,7 +84,7 @@ public final class FlacExtractor implements Extractor {
@Nullable private Metadata id3Metadata;
@Nullable private FlacBinarySearchSeeker binarySearchSeeker;
/** Constructs an instance with flags = 0. */
/** Constructs an instance with {@code flags = 0}. */
public FlacExtractor() {
this(/* flags= */ 0);
}
......@@ -95,11 +92,11 @@ public final class FlacExtractor implements Extractor {
/**
* 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) {
outputBuffer = new ParsableByteArray();
id3Peeker = new Id3Peeker();
id3MetadataDisabled = (flags & FLAG_DISABLE_ID3_METADATA) != 0;
}
......@@ -117,7 +114,7 @@ public final class FlacExtractor implements Extractor {
@Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
id3Metadata = peekId3Data(input);
id3Metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ !id3MetadataDisabled);
return FlacMetadataReader.checkAndPeekStreamMarker(input);
}
......@@ -125,7 +122,7 @@ public final class FlacExtractor implements Extractor {
public int read(final ExtractorInput input, PositionHolder seekPosition)
throws IOException, InterruptedException {
if (input.getPosition() == 0 && !id3MetadataDisabled && id3Metadata == null) {
id3Metadata = peekId3Data(input);
id3Metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ true);
}
FlacDecoderJni decoderJni = initDecoderJni(input);
......@@ -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.
@SuppressWarnings({"contracts.postcondition.not.satisfied"})
private FlacDecoderJni initDecoderJni(ExtractorInput input) {
......@@ -220,10 +204,7 @@ public final class FlacExtractor implements Extractor {
this.streamMetadata = streamMetadata;
binarySearchSeeker =
outputSeekMap(decoderJni, streamMetadata, input.getLength(), extractorOutput);
Metadata metadata = id3MetadataDisabled ? null : id3Metadata;
if (streamMetadata.metadata != null) {
metadata = streamMetadata.metadata.copyWithAppendedEntriesFrom(metadata);
}
Metadata metadata = streamMetadata.getMetadataCopyWithAppendedEntriesFrom(id3Metadata);
outputFormat(streamMetadata, metadata, trackOutput);
outputBuffer.reset(streamMetadata.getMaxDecodedFrameSize());
outputFrameHolder = new OutputFrameHolder(ByteBuffer.wrap(outputBuffer.data));
......
......@@ -151,7 +151,7 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
"FlacStreamMetadata");
jmethodID flacStreamMetadataConstructor =
env->GetMethodID(flacStreamMetadataClass, "<init>",
"(IIIIIIIJLjava/util/List;Ljava/util/List;)V");
"(IIIIIIIJLjava/util/ArrayList;Ljava/util/ArrayList;)V");
return env->NewObject(flacStreamMetadataClass, flacStreamMetadataConstructor,
streamInfo.min_blocksize, streamInfo.max_blocksize,
......
......@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* 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. */
public final class FlacFrameReader {
......
......@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.ogg;
package com.google.android.exoplayer2.extractor;
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
* specification</a>
*/
/* package */ final class VorbisBitArray {
public final class VorbisBitArray {
private final byte[] data;
private final int byteLimit;
......
......@@ -18,20 +18,22 @@ package com.google.android.exoplayer2.extractor.flac;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
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.SeekMap;
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.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.ParsableByteArray;
import java.io.IOException;
......@@ -41,7 +43,6 @@ import java.lang.annotation.RetentionPolicy;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// TODO: implement seeking.
// TODO: expose vorbis and ID3 data.
// TODO: support live streams.
/**
* Extracts data from FLAC container format.
......@@ -53,23 +54,40 @@ public final class FlacExtractor implements Extractor {
/** Factory for {@link FlacExtractor} instances. */
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. */
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
STATE_READ_ID3_TAG,
STATE_READ_ID3_METADATA,
STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES,
STATE_READ_STREAM_MARKER,
STATE_READ_STREAM_INFO_BLOCK,
STATE_SKIP_OPTIONAL_METADATA_BLOCKS,
STATE_READ_METADATA_BLOCKS,
STATE_GET_FIRST_FRAME_METADATA,
STATE_READ_FRAMES
})
private @interface State {}
private static final int STATE_READ_ID3_TAG = 0;
private static final int STATE_READ_STREAM_MARKER = 1;
private static final int STATE_READ_STREAM_INFO_BLOCK = 2;
private static final int STATE_SKIP_OPTIONAL_METADATA_BLOCKS = 3;
private static final int STATE_READ_ID3_METADATA = 0;
private static final int STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES = 1;
private static final int STATE_READ_STREAM_MARKER = 2;
private static final int STATE_READ_METADATA_BLOCKS = 3;
private static final int STATE_GET_FIRST_FRAME_METADATA = 4;
private static final int STATE_READ_FRAMES = 5;
......@@ -81,6 +99,7 @@ public final class FlacExtractor implements Extractor {
private final byte[] streamMarkerAndInfoBlock;
private final ParsableByteArray scratch;
private final boolean id3MetadataDisabled;
private final BlockSizeHolder blockSizeHolder;
......@@ -88,6 +107,7 @@ public final class FlacExtractor implements Extractor {
@MonotonicNonNull private TrackOutput trackOutput;
private @State int state;
@Nullable private Metadata id3Metadata;
@MonotonicNonNull private FlacStreamMetadata flacStreamMetadata;
private int minFrameSize;
private int frameStartMarker;
......@@ -95,16 +115,28 @@ public final class FlacExtractor implements Extractor {
private int currentFrameBytesWritten;
private long totalSamplesWritten;
/** Constructs an instance with {@code flags = 0}. */
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 =
new byte[FlacConstants.STREAM_MARKER_SIZE + FlacConstants.STREAM_INFO_BLOCK_SIZE];
scratch = new ParsableByteArray(SCRATCH_LENGTH);
blockSizeHolder = new BlockSizeHolder();
id3MetadataDisabled = (flags & FLAG_DISABLE_ID3_METADATA) != 0;
}
@Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
FlacMetadataReader.peekId3Data(input);
FlacMetadataReader.peekId3Metadata(input, /* parseData= */ false);
return FlacMetadataReader.checkAndPeekStreamMarker(input);
}
......@@ -119,17 +151,17 @@ public final class FlacExtractor implements Extractor {
public int read(ExtractorInput input, PositionHolder seekPosition)
throws IOException, InterruptedException {
switch (state) {
case STATE_READ_ID3_TAG:
readId3Tag(input);
case STATE_READ_ID3_METADATA:
readId3Metadata(input);
return Extractor.RESULT_CONTINUE;
case STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES:
getStreamMarkerAndInfoBlockBytes(input);
return Extractor.RESULT_CONTINUE;
case STATE_READ_STREAM_MARKER:
readStreamMarker(input);
return Extractor.RESULT_CONTINUE;
case STATE_READ_STREAM_INFO_BLOCK:
readStreamInfoBlock(input);
return Extractor.RESULT_CONTINUE;
case STATE_SKIP_OPTIONAL_METADATA_BLOCKS:
skipOptionalMetadataBlocks(input);
case STATE_READ_METADATA_BLOCKS:
readMetadataBlocks(input);
return Extractor.RESULT_CONTINUE;
case STATE_GET_FIRST_FRAME_METADATA:
getFirstFrameMetadata(input);
......@@ -143,7 +175,7 @@ public final class FlacExtractor implements Extractor {
@Override
public void seek(long position, long timeUs) {
state = STATE_READ_ID3_TAG;
state = STATE_READ_ID3_METADATA;
currentFrameBytesWritten = 0;
totalSamplesWritten = 0;
scratch.reset();
......@@ -156,40 +188,40 @@ public final class FlacExtractor implements Extractor {
// Private methods.
private void readId3Tag(ExtractorInput input) throws IOException, InterruptedException {
FlacMetadataReader.readId3Data(input);
private void readId3Metadata(ExtractorInput input) throws IOException, InterruptedException {
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;
}
private void readStreamMarker(ExtractorInput input) throws IOException, InterruptedException {
FlacMetadataReader.readStreamMarker(
input, streamMarkerAndInfoBlock, /* scratchWriteIndex= */ 0);
state = STATE_READ_STREAM_INFO_BLOCK;
FlacMetadataReader.readStreamMarker(input);
state = STATE_READ_METADATA_BLOCKS;
}
private void readStreamInfoBlock(ExtractorInput input) throws IOException, InterruptedException {
flacStreamMetadata =
FlacMetadataReader.readStreamInfoBlock(
input,
/* scratchData= */ streamMarkerAndInfoBlock,
/* scratchWriteIndex= */ FlacConstants.STREAM_MARKER_SIZE);
private void readMetadataBlocks(ExtractorInput input) throws IOException, InterruptedException {
boolean isLastMetadataBlock = false;
FlacMetadataReader.FlacStreamMetadataHolder metadataHolder =
new FlacMetadataReader.FlacStreamMetadataHolder(flacStreamMetadata);
while (!isLastMetadataBlock) {
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);
boolean isLastMetadataBlock =
(streamMarkerAndInfoBlock[FlacConstants.STREAM_MARKER_SIZE] >> 7 & 1) == 1;
castNonNull(trackOutput).format(flacStreamMetadata.getFormat(streamMarkerAndInfoBlock));
castNonNull(trackOutput)
.format(flacStreamMetadata.getFormat(streamMarkerAndInfoBlock, id3Metadata));
castNonNull(extractorOutput)
.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;
}
......
......@@ -69,7 +69,7 @@ import java.util.Arrays;
if (streamMetadata == null) {
streamMetadata = new FlacStreamMetadata(data, 17);
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) {
flacOggSeeker = new FlacOggSeeker();
flacOggSeeker.parseSeekTable(packet);
......
......@@ -18,7 +18,8 @@ package com.google.android.exoplayer2.extractor.ogg;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.Format;
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.ParsableByteArray;
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 @@
* See the License for the specific language governing permissions and
* 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;
......
......@@ -13,16 +13,19 @@
* See the License for the specific language governing permissions and
* 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.ogg.VorbisUtil.verifyVorbisHeaderCapturePattern;
import static com.google.android.exoplayer2.extractor.VorbisUtil.iLog;
import static com.google.android.exoplayer2.extractor.VorbisUtil.verifyVorbisHeaderCapturePattern;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -45,7 +48,9 @@ public final class VorbisUtilTest {
@Test
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);
VorbisUtil.VorbisIdHeader vorbisIdHeader =
VorbisUtil.readVorbisIdentificationHeader(headerData);
......@@ -63,8 +68,10 @@ public final class VorbisUtilTest {
}
@Test
public void testReadCommentHeader() throws ParserException {
byte[] data = OggTestData.getCommentHeaderDataUTF8();
public void testReadCommentHeader() throws IOException {
byte[] data =
TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), "binary/vorbis/comment_header");
ParsableByteArray headerData = new ParsableByteArray(data, data.length);
VorbisUtil.CommentHeader commentHeader = VorbisUtil.readVorbisCommentHeader(headerData);
......@@ -76,8 +83,10 @@ public final class VorbisUtilTest {
}
@Test
public void testReadVorbisModes() throws ParserException {
byte[] data = OggTestData.getSetupHeaderData();
public void testReadVorbisModes() throws IOException {
byte[] data =
TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), "binary/vorbis/setup_header");
ParsableByteArray headerData = new ParsableByteArray(data, data.length);
VorbisUtil.Mode[] modes = VorbisUtil.readVorbisModes(headerData, 2);
......
......@@ -30,11 +30,31 @@ public class FlacExtractorTest {
}
@Test
public void testSampleWithId3() throws Exception {
public void testSampleWithId3HeaderAndId3Enabled() throws Exception {
ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_with_id3.flac");
}
@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 {
ExtractorAsserts.assertBehavior(FlacExtractor::new, "flac/bear_one_metadata_block.flac");
}
......
......@@ -19,11 +19,13 @@ import static com.google.android.exoplayer2.extractor.ogg.VorbisReader.readBits;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.ogg.VorbisReader.VorbisSetup;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException;
import org.junit.Test;
......@@ -55,7 +57,11 @@ public final class VorbisReaderTest {
@Test
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)
.setSimulateUnknownLength(true).setSimulatePartialReads(true).build();
......
......@@ -35,7 +35,18 @@ public final class FlacStreamMetadataTest {
commentsList.add("Artist=Singer");
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);
VorbisComment commentFrame = (VorbisComment) metadata.get(0);
......@@ -51,9 +62,20 @@ public final class FlacStreamMetadataTest {
ArrayList<String> commentsList = new ArrayList<>();
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
......@@ -62,7 +84,18 @@ public final class FlacStreamMetadataTest {
commentsList.add("Title=So=ng");
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);
VorbisComment commentFrame = (VorbisComment) metadata.get(0);
......@@ -77,7 +110,18 @@ public final class FlacStreamMetadataTest {
commentsList.add("Artist=Singer");
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);
VorbisComment commentFrame = (VorbisComment) metadata.get(0);
......
......@@ -152,6 +152,7 @@ public final class ExtractorAsserts {
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
* 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