diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 12897521cc..ddf2d0fc4e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -95,6 +95,8 @@ * MP4: Parse bitrates from `esds` boxes. * Ogg: Allow duplicate Opus ID and comment headers ([#10038](https://github.com/google/ExoPlayer/issues/10038)). + * Add support for AVI + ([#2092](https://github.com/google/ExoPlayer/issues/2092)). * UI: * Fix delivery of events to `OnClickListener`s set on `PlayerView` and `LegacyPlayerView`, in the case that `useController=false` diff --git a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java index a7ba44cdcc..c5ec04927b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java @@ -44,7 +44,7 @@ public final class FileTypes { @Target(TYPE_USE) @IntDef({ UNKNOWN, AC3, AC4, ADTS, AMR, FLAC, FLV, MATROSKA, MP3, MP4, OGG, PS, TS, WAV, WEBVTT, JPEG, - MIDI + MIDI, AVI }) public @interface Type {} /** Unknown file type. */ @@ -81,6 +81,8 @@ public final class FileTypes { public static final int JPEG = 14; /** File type for the MIDI format. */ public static final int MIDI = 15; + /** File type for the AVI format. */ + public static final int AVI = 16; @VisibleForTesting /* package */ static final String HEADER_CONTENT_TYPE = "Content-Type"; @@ -116,6 +118,7 @@ public final class FileTypes { private static final String EXTENSION_WEBVTT = ".webvtt"; private static final String EXTENSION_JPG = ".jpg"; private static final String EXTENSION_JPEG = ".jpeg"; + private static final String EXTENSION_AVI = ".avi"; private FileTypes() {} @@ -179,6 +182,8 @@ public final class FileTypes { return FileTypes.WEBVTT; case MimeTypes.IMAGE_JPEG: return FileTypes.JPEG; + case MimeTypes.VIDEO_AVI: + return FileTypes.AVI; default: return FileTypes.UNKNOWN; } @@ -244,6 +249,8 @@ public final class FileTypes { return FileTypes.WEBVTT; } else if (filename.endsWith(EXTENSION_JPG) || filename.endsWith(EXTENSION_JPEG)) { return FileTypes.JPEG; + } else if (filename.endsWith(EXTENSION_AVI)) { + return FileTypes.AVI; } else { return FileTypes.UNKNOWN; } diff --git a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java index 0ad8752a5a..28928ba15a 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java @@ -56,6 +56,10 @@ public final class MimeTypes { @UnstableApi public static final String VIDEO_FLV = BASE_TYPE_VIDEO + "/x-flv"; public static final String VIDEO_DOLBY_VISION = BASE_TYPE_VIDEO + "/dolby-vision"; public static final String VIDEO_OGG = BASE_TYPE_VIDEO + "/ogg"; + public static final String VIDEO_AVI = BASE_TYPE_VIDEO + "/x-msvideo"; + public static final String VIDEO_MJPEG = BASE_TYPE_VIDEO + "/mjpeg"; + public static final String VIDEO_MP42 = BASE_TYPE_VIDEO + "/mp42"; + public static final String VIDEO_MP43 = BASE_TYPE_VIDEO + "/mp43"; @UnstableApi public static final String VIDEO_UNKNOWN = BASE_TYPE_VIDEO + "/x-unknown"; // audio/ MIME types diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java index 65aff22e3a..eff4e7c0fe 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java @@ -27,6 +27,7 @@ import androidx.media3.common.Player; import androidx.media3.common.util.TimestampAdjuster; import androidx.media3.common.util.UnstableApi; import androidx.media3.extractor.amr.AmrExtractor; +import androidx.media3.extractor.avi.AviExtractor; import androidx.media3.extractor.flac.FlacExtractor; import androidx.media3.extractor.flv.FlvExtractor; import androidx.media3.extractor.jpeg.JpegExtractor; @@ -103,8 +104,11 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { FileTypes.AC3, FileTypes.AC4, FileTypes.MP3, - FileTypes.JPEG, + // The following extractors are not part of the optimized ordering, and were appended + // without further analysis. + FileTypes.AVI, FileTypes.MIDI, + FileTypes.JPEG, }; private static final ExtensionLoader FLAC_EXTENSION_LOADER = @@ -309,7 +313,8 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { @Override public synchronized Extractor[] createExtractors( Uri uri, Map> responseHeaders) { - List extractors = new ArrayList<>(/* initialCapacity= */ 14); + List extractors = + new ArrayList<>(/* initialCapacity= */ DEFAULT_EXTRACTOR_ORDER.length); @FileTypes.Type int responseHeadersInferredFileType = inferFileTypeFromResponseHeaders(responseHeaders); @@ -412,6 +417,9 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { extractors.add(midiExtractor); } break; + case FileTypes.AVI: + extractors.add(new AviExtractor()); + break; case FileTypes.WEBVTT: case FileTypes.UNKNOWN: default: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviChunk.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviChunk.java new file mode 100644 index 0000000000..bd3b05eae9 --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviChunk.java @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +/** + * A chunk, as defined in the AVI spec. + * + *

See https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference. + */ +/* package */ interface AviChunk { + + /** Returns the chunk type fourcc. */ + int getType(); +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviExtractor.java new file mode 100644 index 0000000000..21f1175c05 --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviExtractor.java @@ -0,0 +1,557 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import static java.lang.annotation.ElementType.TYPE_USE; + +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.ParserException; +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.extractor.DummyExtractorOutput; +import androidx.media3.extractor.Extractor; +import androidx.media3.extractor.ExtractorInput; +import androidx.media3.extractor.ExtractorOutput; +import androidx.media3.extractor.PositionHolder; +import androidx.media3.extractor.SeekMap; +import androidx.media3.extractor.TrackOutput; +import java.io.IOException; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ArrayList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** + * Extracts data from the AVI container format. + * + *

Spec: https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference. + */ +@UnstableApi +public final class AviExtractor implements Extractor { + + private static final String TAG = "AviExtractor"; + + public static final int FOURCC_RIFF = 0x46464952; + public static final int FOURCC_AVI_ = 0x20495641; // AVI + public static final int FOURCC_LIST = 0x5453494c; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_avih = 0x68697661; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_hdrl = 0x6c726468; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_strl = 0x6c727473; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_movi = 0x69766f6d; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_idx1 = 0x31786469; + + public static final int FOURCC_JUNK = 0x4b4e554a; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_strf = 0x66727473; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_strn = 0x6e727473; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_strh = 0x68727473; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_auds = 0x73647561; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_txts = 0x73747874; + + @SuppressWarnings("ConstantCaseForConstants") + public static final int FOURCC_vids = 0x73646976; + + /** Parser states. */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) + @IntDef({ + STATE_SKIPPING_TO_HDRL, + STATE_READING_HDRL_HEADER, + STATE_READING_HDRL_BODY, + STATE_FINDING_MOVI_HEADER, + STATE_FINDING_IDX1_HEADER, + STATE_READING_IDX1_BODY, + STATE_READING_SAMPLES, + }) + private @interface State {} + + private static final int STATE_SKIPPING_TO_HDRL = 0; + private static final int STATE_READING_HDRL_HEADER = 1; + private static final int STATE_READING_HDRL_BODY = 2; + private static final int STATE_FINDING_MOVI_HEADER = 3; + private static final int STATE_FINDING_IDX1_HEADER = 4; + private static final int STATE_READING_IDX1_BODY = 5; + private static final int STATE_READING_SAMPLES = 6; + + private static final int AVIIF_KEYFRAME = 16; + + /** + * Maximum size to skip using {@link ExtractorInput#skip}. Boxes larger than this size are skipped + * using {@link #RESULT_SEEK}. + */ + private static final long RELOAD_MINIMUM_SEEK_DISTANCE = 256 * 1024; + + private final ParsableByteArray scratch; + private final ChunkHeaderHolder chunkHeaderHolder; + + private @State int state; + private ExtractorOutput extractorOutput; + private @MonotonicNonNull AviMainHeaderChunk aviHeader; + private long durationUs; + private ChunkReader[] chunkReaders; + + private long pendingReposition; + @Nullable private ChunkReader currentChunkReader; + private int hdrlSize; + private long moviStart; + private long moviEnd; + private int idx1BodySize; + private boolean seekMapHasBeenOutput; + + public AviExtractor() { + scratch = new ParsableByteArray(/* limit= */ 12); + chunkHeaderHolder = new ChunkHeaderHolder(); + extractorOutput = new DummyExtractorOutput(); + chunkReaders = new ChunkReader[0]; + moviStart = C.POSITION_UNSET; + moviEnd = C.POSITION_UNSET; + hdrlSize = C.LENGTH_UNSET; + durationUs = C.TIME_UNSET; + } + + // Extractor implementation. + + @Override + public void init(ExtractorOutput output) { + this.state = STATE_SKIPPING_TO_HDRL; + this.extractorOutput = output; + pendingReposition = C.POSITION_UNSET; + } + + @Override + public boolean sniff(ExtractorInput input) throws IOException { + input.peekFully(scratch.getData(), /* offset= */ 0, /* length= */ 12); + scratch.setPosition(0); + if (scratch.readLittleEndianInt() != FOURCC_RIFF) { + return false; + } + scratch.skipBytes(4); // Skip the RIFF chunk length. + return scratch.readLittleEndianInt() == FOURCC_AVI_; + } + + @Override + public int read(ExtractorInput input, PositionHolder positionHolder) throws IOException { + if (resolvePendingReposition(input, positionHolder)) { + return RESULT_SEEK; + } + switch (state) { + case STATE_SKIPPING_TO_HDRL: + // Check for RIFF and AVI fourcc's just in case the caller did not sniff, in order to + // provide a meaningful error if the input is not an AVI file. + if (sniff(input)) { + input.skipFully(/* length= */ 12); + } else { + throw ParserException.createForMalformedContainer( + /* message= */ "AVI Header List not found", /* cause= */ null); + } + state = STATE_READING_HDRL_HEADER; + return RESULT_CONTINUE; + case STATE_READING_HDRL_HEADER: + input.readFully(scratch.getData(), /* offset= */ 0, /* length= */ 12); + scratch.setPosition(0); + chunkHeaderHolder.populateWithListHeaderFrom(scratch); + if (chunkHeaderHolder.listType != FOURCC_hdrl) { + throw ParserException.createForMalformedContainer( + /* message= */ "hdrl expected, found: " + chunkHeaderHolder.listType, + /* cause= */ null); + } + hdrlSize = chunkHeaderHolder.size; + state = STATE_READING_HDRL_BODY; + return RESULT_CONTINUE; + case STATE_READING_HDRL_BODY: + // hdrlSize includes the LIST type (hdrl), so we subtract 4 to the size. + int bytesToRead = hdrlSize - 4; + ParsableByteArray hdrlBody = new ParsableByteArray(bytesToRead); + input.readFully(hdrlBody.getData(), /* offset= */ 0, bytesToRead); + parseHdrlBody(hdrlBody); + state = STATE_FINDING_MOVI_HEADER; + return RESULT_CONTINUE; + case STATE_FINDING_MOVI_HEADER: + if (moviStart != C.POSITION_UNSET && input.getPosition() != moviStart) { + pendingReposition = moviStart; + return RESULT_CONTINUE; + } + input.peekFully(scratch.getData(), /* offset= */ 0, /* length= */ 12); + input.resetPeekPosition(); + scratch.setPosition(0); + chunkHeaderHolder.populateFrom(scratch); + int listType = scratch.readLittleEndianInt(); + if (chunkHeaderHolder.chunkType == FOURCC_RIFF) { + // We are at the start of the file. The movi chunk is in the RIFF chunk, so we skip the + // header, so as to read the RIFF chunk's body. + input.skipFully(12); + return RESULT_CONTINUE; + } + if (chunkHeaderHolder.chunkType != FOURCC_LIST || listType != FOURCC_movi) { + // The chunk header (8 bytes) plus the whole body. + pendingReposition = input.getPosition() + chunkHeaderHolder.size + 8; + return RESULT_CONTINUE; + } + moviStart = input.getPosition(); + // Size includes the list type, but not the LIST or size fields, so we add 8. + moviEnd = moviStart + chunkHeaderHolder.size + 8; + if (!seekMapHasBeenOutput) { + if (Assertions.checkNotNull(aviHeader).hasIndex()) { + state = STATE_FINDING_IDX1_HEADER; + pendingReposition = moviEnd; + return RESULT_CONTINUE; + } else { + extractorOutput.seekMap(new SeekMap.Unseekable(durationUs)); + seekMapHasBeenOutput = true; + } + } + // No need to parse the idx1, so we start reading the samples from the movi chunk straight + // away. We skip 12 bytes to move to the start of the movi's body. + pendingReposition = input.getPosition() + 12; + state = STATE_READING_SAMPLES; + return RESULT_CONTINUE; + case STATE_FINDING_IDX1_HEADER: + input.readFully(scratch.getData(), /* offset= */ 0, /* length= */ 8); + scratch.setPosition(0); + int idx1Fourcc = scratch.readLittleEndianInt(); + int boxSize = scratch.readLittleEndianInt(); + if (idx1Fourcc == FOURCC_idx1) { + state = STATE_READING_IDX1_BODY; + idx1BodySize = boxSize; + } else { + // This one is not idx1, skip to the next box. + pendingReposition = input.getPosition() + boxSize; + } + return RESULT_CONTINUE; + case STATE_READING_IDX1_BODY: + ParsableByteArray idx1Body = new ParsableByteArray(idx1BodySize); + input.readFully(idx1Body.getData(), /* offset= */ 0, /* length= */ idx1BodySize); + parseIdx1Body(idx1Body); + state = STATE_READING_SAMPLES; + pendingReposition = moviStart; + return RESULT_CONTINUE; + case STATE_READING_SAMPLES: + return readMoviChunks(input); + default: + throw new AssertionError(); // Should never happen. + } + } + + @Override + public void seek(long position, long timeUs) { + pendingReposition = C.POSITION_UNSET; + currentChunkReader = null; + for (ChunkReader chunkReader : chunkReaders) { + chunkReader.seekToPosition(position); + } + if (position == 0) { + if (chunkReaders.length == 0) { + // Still unprepared. + state = STATE_SKIPPING_TO_HDRL; + } else { + state = STATE_FINDING_MOVI_HEADER; + } + return; + } + state = STATE_READING_SAMPLES; + } + + @Override + public void release() { + // Nothing to release. + } + + // Internal methods. + + /** + * Returns whether a {@link #RESULT_SEEK} is required for the pending reposition. A seek may not + * be necessary when the desired position (as held by {@link #pendingReposition}) is after the + * {@link ExtractorInput#getPosition() current position}, but not further than {@link + * #RELOAD_MINIMUM_SEEK_DISTANCE}. + */ + private boolean resolvePendingReposition(ExtractorInput input, PositionHolder positionHolder) + throws IOException { + boolean needSeek = false; + if (pendingReposition != C.POSITION_UNSET) { + long currentPosition = input.getPosition(); + if (pendingReposition < currentPosition + || pendingReposition > currentPosition + RELOAD_MINIMUM_SEEK_DISTANCE) { + positionHolder.position = pendingReposition; + needSeek = true; + } else { + // The distance to the target position is short enough that it makes sense to just skip the + // bytes, instead of doing a seek which might re-create an HTTP connection. + input.skipFully((int) (pendingReposition - currentPosition)); + } + } + pendingReposition = C.POSITION_UNSET; + return needSeek; + } + + private void parseHdrlBody(ParsableByteArray hrdlBody) throws IOException { + ListChunk headerList = ListChunk.parseFrom(FOURCC_hdrl, hrdlBody); + if (headerList.getType() != FOURCC_hdrl) { + throw ParserException.createForMalformedContainer( + /* message= */ "Unexpected header list type " + headerList.getType(), /* cause= */ null); + } + @Nullable AviMainHeaderChunk aviHeader = headerList.getChild(AviMainHeaderChunk.class); + if (aviHeader == null) { + throw ParserException.createForMalformedContainer( + /* message= */ "AviHeader not found", /* cause= */ null); + } + this.aviHeader = aviHeader; + // This is usually wrong, so it will be overwritten by video if present + durationUs = aviHeader.totalFrames * (long) aviHeader.frameDurationUs; + ArrayList chunkReaderList = new ArrayList<>(); + int streamId = 0; + for (AviChunk aviChunk : headerList.children) { + if (aviChunk.getType() == FOURCC_strl) { + ListChunk streamList = (ListChunk) aviChunk; + // Note the streamId needs to increment even if the corresponding `strl` is discarded. + // See + // https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference#avi-stream-headers. + @Nullable ChunkReader chunkReader = processStreamList(streamList, streamId++); + if (chunkReader != null) { + chunkReaderList.add(chunkReader); + } + } + } + chunkReaders = chunkReaderList.toArray(new ChunkReader[0]); + extractorOutput.endTracks(); + } + + /** Builds and outputs the {@link SeekMap} from the idx1 chunk. */ + private void parseIdx1Body(ParsableByteArray body) { + long seekOffset = peekSeekOffset(body); + while (body.bytesLeft() >= 16) { + int chunkId = body.readLittleEndianInt(); + int flags = body.readLittleEndianInt(); + long offset = body.readLittleEndianInt() + seekOffset; + body.readLittleEndianInt(); // We ignore the size. + ChunkReader chunkReader = getChunkReader(chunkId); + if (chunkReader == null) { + // We ignore unknown chunk IDs. + continue; + } + if ((flags & AVIIF_KEYFRAME) == AVIIF_KEYFRAME) { + chunkReader.appendKeyFrameToIndex(offset); + } + chunkReader.incrementIndexChunkCount(); + } + for (ChunkReader chunkReader : chunkReaders) { + chunkReader.compactIndex(); + } + seekMapHasBeenOutput = true; + extractorOutput.seekMap(new AviSeekMap(durationUs)); + } + + private long peekSeekOffset(ParsableByteArray idx1Body) { + // The spec states the offset is based on the start of the movi list type fourcc, but it also + // says some files base the offset on the start of the file. We use a best effort approach to + // figure out which is the case. See: + // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/api/Aviriff/ns-aviriff-avioldindex#dwoffset. + if (idx1Body.bytesLeft() < 16) { + // There are no full entries in the index, meaning we don't need to apply an offset. + return 0; + } + int startingPosition = idx1Body.getPosition(); + idx1Body.skipBytes(8); // Skip chunkId (4 bytes) and flags (4 bytes). + int offset = idx1Body.readLittleEndianInt(); + + // moviStart poitns at the start of the LIST, while the seek offset is based at the start of the + // movi fourCC, so we add 8 to reconcile the difference. + long seekOffset = offset > moviStart ? 0L : moviStart + 8; + idx1Body.setPosition(startingPosition); + return seekOffset; + } + + @Nullable + private ChunkReader getChunkReader(int chunkId) { + for (ChunkReader chunkReader : chunkReaders) { + if (chunkReader.handlesChunkId(chunkId)) { + return chunkReader; + } + } + return null; + } + + private int readMoviChunks(ExtractorInput input) throws IOException { + if (input.getPosition() >= moviEnd) { + return C.RESULT_END_OF_INPUT; + } else if (currentChunkReader != null) { + if (currentChunkReader.onChunkData(input)) { + currentChunkReader = null; + } + } else { + alignInputToEvenPosition(input); + input.peekFully(scratch.getData(), /* offset= */ 0, 12); + scratch.setPosition(0); + int chunkType = scratch.readLittleEndianInt(); + if (chunkType == FOURCC_LIST) { + scratch.setPosition(8); + int listType = scratch.readLittleEndianInt(); + input.skipFully(listType == FOURCC_movi ? 12 : 8); + input.resetPeekPosition(); + return RESULT_CONTINUE; + } + int size = scratch.readLittleEndianInt(); + if (chunkType == FOURCC_JUNK) { + pendingReposition = input.getPosition() + size + 8; + return RESULT_CONTINUE; + } + input.skipFully(8); + input.resetPeekPosition(); + ChunkReader chunkReader = getChunkReader(chunkType); + if (chunkReader == null) { + // No handler for this chunk. We skip it. + pendingReposition = input.getPosition() + size; + return RESULT_CONTINUE; + } else { + chunkReader.onChunkStart(size); + this.currentChunkReader = chunkReader; + } + } + return RESULT_CONTINUE; + } + + @Nullable + private ChunkReader processStreamList(ListChunk streamList, int streamId) { + AviStreamHeaderChunk aviStreamHeaderChunk = streamList.getChild(AviStreamHeaderChunk.class); + StreamFormatChunk streamFormatChunk = streamList.getChild(StreamFormatChunk.class); + if (aviStreamHeaderChunk == null) { + Log.w(TAG, "Missing Stream Header"); + return null; + } + if (streamFormatChunk == null) { + Log.w(TAG, "Missing Stream Format"); + return null; + } + long durationUs = aviStreamHeaderChunk.getDurationUs(); + Format streamFormat = streamFormatChunk.format; + Format.Builder builder = streamFormat.buildUpon(); + builder.setId(streamId); + int suggestedBufferSize = aviStreamHeaderChunk.suggestedBufferSize; + if (suggestedBufferSize != 0) { + builder.setMaxInputSize(suggestedBufferSize); + } + StreamNameChunk streamName = streamList.getChild(StreamNameChunk.class); + if (streamName != null) { + builder.setLabel(streamName.name); + } + int trackType = MimeTypes.getTrackType(streamFormat.sampleMimeType); + if (trackType == C.TRACK_TYPE_AUDIO || trackType == C.TRACK_TYPE_VIDEO) { + TrackOutput trackOutput = extractorOutput.track(streamId, trackType); + trackOutput.format(builder.build()); + ChunkReader chunkReader = + new ChunkReader( + streamId, trackType, durationUs, aviStreamHeaderChunk.length, trackOutput); + this.durationUs = durationUs; + return chunkReader; + } else { + // We don't currently support tracks other than video and audio. + return null; + } + } + + /** + * Skips one byte from the given {@code input} if the current position is odd. + * + *

This isn't documented anywhere, but AVI files are aligned to even bytes and fill gaps with + * zeros. + */ + private static void alignInputToEvenPosition(ExtractorInput input) throws IOException { + if ((input.getPosition() & 1) == 1) { + input.skipFully(1); + } + } + + // Internal classes. + + private class AviSeekMap implements SeekMap { + + private final long durationUs; + + public AviSeekMap(long durationUs) { + this.durationUs = durationUs; + } + + @Override + public boolean isSeekable() { + return true; + } + + @Override + public long getDurationUs() { + return durationUs; + } + + @Override + public SeekPoints getSeekPoints(long timeUs) { + SeekPoints result = chunkReaders[0].getSeekPoints(timeUs); + for (int i = 1; i < chunkReaders.length; i++) { + SeekPoints seekPoints = chunkReaders[i].getSeekPoints(timeUs); + if (seekPoints.first.position < result.first.position) { + result = seekPoints; + } + } + return result; + } + } + + private static class ChunkHeaderHolder { + public int chunkType; + public int size; + public int listType; + + public void populateWithListHeaderFrom(ParsableByteArray headerBytes) throws ParserException { + populateFrom(headerBytes); + if (chunkType != AviExtractor.FOURCC_LIST) { + throw ParserException.createForMalformedContainer( + /* message= */ "LIST expected, found: " + chunkType, /* cause= */ null); + } + listType = headerBytes.readLittleEndianInt(); + } + + public void populateFrom(ParsableByteArray headerBytes) { + chunkType = headerBytes.readLittleEndianInt(); + size = headerBytes.readLittleEndianInt(); + listType = 0; + } + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviMainHeaderChunk.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviMainHeaderChunk.java new file mode 100644 index 0000000000..4a6c11bc4c --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviMainHeaderChunk.java @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import androidx.media3.common.util.ParsableByteArray; + +/** Wrapper around the AVIMAINHEADER structure */ +/* package */ final class AviMainHeaderChunk implements AviChunk { + + private static final int AVIF_HAS_INDEX = 0x10; + + public static AviMainHeaderChunk parseFrom(ParsableByteArray body) { + int microSecPerFrame = body.readLittleEndianInt(); + body.skipBytes(8); // Skip dwMaxBytesPerSec (4 bytes), dwPaddingGranularity (4 bytes). + int flags = body.readLittleEndianInt(); + int totalFrames = body.readLittleEndianInt(); + body.skipBytes(4); // dwInitialFrames (4 bytes). + int streams = body.readLittleEndianInt(); + body.skipBytes(12); // dwSuggestedBufferSize (4 bytes), dwWidth (4 bytes), dwHeight (4 bytes). + return new AviMainHeaderChunk(microSecPerFrame, flags, totalFrames, streams); + } + + public final int frameDurationUs; + public final int flags; + public final int totalFrames; + public final int streams; + + private AviMainHeaderChunk(int frameDurationUs, int flags, int totalFrames, int streams) { + this.frameDurationUs = frameDurationUs; + this.flags = flags; + this.totalFrames = totalFrames; + this.streams = streams; + } + + @Override + public int getType() { + return AviExtractor.FOURCC_avih; + } + + public boolean hasIndex() { + return (flags & AVIF_HAS_INDEX) == AVIF_HAS_INDEX; + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviStreamHeaderChunk.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviStreamHeaderChunk.java new file mode 100644 index 0000000000..99f83e8824 --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/AviStreamHeaderChunk.java @@ -0,0 +1,88 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import androidx.media3.common.C; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.common.util.Util; + +/** Parses and holds information from the AVISTREAMHEADER structure. */ +/* package */ final class AviStreamHeaderChunk implements AviChunk { + private static final String TAG = "AviStreamHeaderChunk"; + + public static AviStreamHeaderChunk parseFrom(ParsableByteArray body) { + int streamType = body.readLittleEndianInt(); + body.skipBytes(12); // fccHandler (4 bytes), dwFlags (4 bytes), wPriority (2 bytes), + // wLanguage (2 bytes). + int initialFrames = body.readLittleEndianInt(); + int scale = body.readLittleEndianInt(); + int rate = body.readLittleEndianInt(); + body.skipBytes(4); // dwStart (4 bytes). + int length = body.readLittleEndianInt(); + int suggestedBufferSize = body.readLittleEndianInt(); + body.skipBytes(8); // dwQuality (4 bytes), dwSampleSize (4 bytes). + return new AviStreamHeaderChunk( + streamType, initialFrames, scale, rate, length, suggestedBufferSize); + } + + public final int streamType; + public final int initialFrames; + public final int scale; + public final int rate; + public final int length; + public final int suggestedBufferSize; + + private AviStreamHeaderChunk( + int streamType, int initialFrames, int scale, int rate, int length, int suggestedBufferSize) { + this.streamType = streamType; + this.initialFrames = initialFrames; + this.scale = scale; + this.rate = rate; + this.length = length; + this.suggestedBufferSize = suggestedBufferSize; + } + + @Override + public int getType() { + return AviExtractor.FOURCC_strh; + } + + public @C.TrackType int getTrackType() { + switch (streamType) { + case AviExtractor.FOURCC_auds: + return C.TRACK_TYPE_AUDIO; + case AviExtractor.FOURCC_vids: + return C.TRACK_TYPE_VIDEO; + case AviExtractor.FOURCC_txts: + return C.TRACK_TYPE_TEXT; + default: + Log.w(TAG, "Found unsupported streamType fourCC: " + Integer.toHexString(streamType)); + return C.TRACK_TYPE_UNKNOWN; + } + } + + public float getFrameRate() { + return rate / (float) scale; + } + + public long getDurationUs() { + return Util.scaleLargeTimestamp( + /* timestamp= */ length, + /* multiplier= */ C.MICROS_PER_SECOND * scale, + /* divisor= */ rate); + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/ChunkReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/ChunkReader.java new file mode 100644 index 0000000000..4f788429ce --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/ChunkReader.java @@ -0,0 +1,212 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import static java.lang.annotation.ElementType.TYPE_USE; + +import androidx.annotation.IntDef; +import androidx.media3.common.C; +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Util; +import androidx.media3.extractor.ExtractorInput; +import androidx.media3.extractor.SeekMap; +import androidx.media3.extractor.SeekPoint; +import androidx.media3.extractor.TrackOutput; +import java.io.IOException; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Arrays; + +/** Reads chunks holding sample data. */ +/* package */ final class ChunkReader { + + /** Parser states. */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) + @IntDef({ + CHUNK_TYPE_VIDEO_COMPRESSED, + CHUNK_TYPE_VIDEO_UNCOMPRESSED, + CHUNK_TYPE_AUDIO, + }) + private @interface ChunkType {} + + private static final int INITIAL_INDEX_SIZE = 512; + private static final int CHUNK_TYPE_VIDEO_COMPRESSED = ('d' << 16) | ('c' << 24); + private static final int CHUNK_TYPE_VIDEO_UNCOMPRESSED = ('d' << 16) | ('b' << 24); + private static final int CHUNK_TYPE_AUDIO = ('w' << 16) | ('b' << 24); + + protected final TrackOutput trackOutput; + + /** The chunk id fourCC (example: `01wb`), as defined in the index and the movi. */ + private final int chunkId; + /** Secondary chunk id. Bad muxers sometimes use an uncompressed video id (db) for key frames */ + private final int alternativeChunkId; + + private final long durationUs; + private final int streamHeaderChunkCount; + + private int currentChunkSize; + private int bytesRemainingInCurrentChunk; + + /** Number of chunks as calculated by the index */ + private int currentChunkIndex; + + private int indexChunkCount; + private int indexSize; + private long[] keyFrameOffsets; + private int[] keyFrameIndices; + + public ChunkReader( + int id, + @C.TrackType int trackType, + long durationnUs, + int streamHeaderChunkCount, + TrackOutput trackOutput) { + Assertions.checkArgument(trackType == C.TRACK_TYPE_AUDIO || trackType == C.TRACK_TYPE_VIDEO); + this.durationUs = durationnUs; + this.streamHeaderChunkCount = streamHeaderChunkCount; + this.trackOutput = trackOutput; + @ChunkType + int chunkType = + trackType == C.TRACK_TYPE_VIDEO ? CHUNK_TYPE_VIDEO_COMPRESSED : CHUNK_TYPE_AUDIO; + chunkId = getChunkIdFourCc(id, chunkType); + alternativeChunkId = + trackType == C.TRACK_TYPE_VIDEO ? getChunkIdFourCc(id, CHUNK_TYPE_VIDEO_UNCOMPRESSED) : -1; + keyFrameOffsets = new long[INITIAL_INDEX_SIZE]; + keyFrameIndices = new int[INITIAL_INDEX_SIZE]; + } + + public void appendKeyFrameToIndex(long offset) { + if (indexSize == keyFrameIndices.length) { + keyFrameOffsets = Arrays.copyOf(keyFrameOffsets, keyFrameOffsets.length * 3 / 2); + keyFrameIndices = Arrays.copyOf(keyFrameIndices, keyFrameIndices.length * 3 / 2); + } + keyFrameOffsets[indexSize] = offset; + keyFrameIndices[indexSize] = indexChunkCount; + indexSize++; + } + + public void advanceCurrentChunk() { + currentChunkIndex++; + } + + public long getCurrentChunkTimestampUs() { + return getChunkTimestampUs(currentChunkIndex); + } + + public long getFrameDurationUs() { + return getChunkTimestampUs(/* chunkIndex= */ 1); + } + + public void incrementIndexChunkCount() { + indexChunkCount++; + } + + public void compactIndex() { + keyFrameOffsets = Arrays.copyOf(keyFrameOffsets, indexSize); + keyFrameIndices = Arrays.copyOf(keyFrameIndices, indexSize); + } + + public boolean handlesChunkId(int chunkId) { + return this.chunkId == chunkId || alternativeChunkId == chunkId; + } + + public boolean isCurrentFrameAKeyFrame() { + return Arrays.binarySearch(keyFrameIndices, currentChunkIndex) >= 0; + } + + public boolean isVideo() { + return (chunkId & CHUNK_TYPE_VIDEO_COMPRESSED) == CHUNK_TYPE_VIDEO_COMPRESSED; + } + + public boolean isAudio() { + return (chunkId & CHUNK_TYPE_AUDIO) == CHUNK_TYPE_AUDIO; + } + + /** Prepares for parsing a chunk with the given {@code size}. */ + public void onChunkStart(int size) { + currentChunkSize = size; + bytesRemainingInCurrentChunk = size; + } + + /** + * Provides data associated to the current chunk and returns whether the full chunk has been + * parsed. + */ + public boolean onChunkData(ExtractorInput input) throws IOException { + bytesRemainingInCurrentChunk -= + trackOutput.sampleData(input, bytesRemainingInCurrentChunk, false); + boolean done = bytesRemainingInCurrentChunk == 0; + if (done) { + if (currentChunkSize > 0) { + trackOutput.sampleMetadata( + getCurrentChunkTimestampUs(), + (isCurrentFrameAKeyFrame() ? C.BUFFER_FLAG_KEY_FRAME : 0), + currentChunkSize, + 0, + null); + } + advanceCurrentChunk(); + } + return done; + } + + public void seekToPosition(long position) { + if (indexSize == 0) { + currentChunkIndex = 0; + } else { + int index = + Util.binarySearchFloor( + keyFrameOffsets, position, /* inclusive= */ true, /* stayInBounds= */ true); + currentChunkIndex = keyFrameIndices[index]; + } + } + + public SeekMap.SeekPoints getSeekPoints(long timeUs) { + int targetFrameIndex = (int) (timeUs / getFrameDurationUs()); + int keyFrameIndex = + Util.binarySearchFloor( + keyFrameIndices, targetFrameIndex, /* inclusive= */ true, /* stayInBounds= */ true); + if (keyFrameIndices[keyFrameIndex] == targetFrameIndex) { + return new SeekMap.SeekPoints(getSeekPoint(keyFrameIndex)); + } + // The target frame is not a key frame, we look for the two closest ones. + SeekPoint precedingKeyFrameSeekPoint = getSeekPoint(keyFrameIndex); + if (keyFrameIndex + 1 < keyFrameOffsets.length) { + return new SeekMap.SeekPoints(precedingKeyFrameSeekPoint, getSeekPoint(keyFrameIndex + 1)); + } else { + return new SeekMap.SeekPoints(precedingKeyFrameSeekPoint); + } + } + + private long getChunkTimestampUs(int chunkIndex) { + return durationUs * chunkIndex / streamHeaderChunkCount; + } + + private SeekPoint getSeekPoint(int keyFrameIndex) { + return new SeekPoint( + keyFrameIndices[keyFrameIndex] * getFrameDurationUs(), keyFrameOffsets[keyFrameIndex]); + } + + private static int getChunkIdFourCc(int streamId, @ChunkType int chunkType) { + int tens = streamId / 10; + int ones = streamId % 10; + return (('0' + ones) << 8) | ('0' + tens) | chunkType; + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/ListChunk.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/ListChunk.java new file mode 100644 index 0000000000..68e6cbf6f9 --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/ListChunk.java @@ -0,0 +1,94 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.util.ParsableByteArray; +import com.google.common.collect.ImmutableList; + +/** Represents an AVI LIST. */ +/* package */ final class ListChunk implements AviChunk { + + public static ListChunk parseFrom(int listType, ParsableByteArray body) { + ImmutableList.Builder builder = new ImmutableList.Builder<>(); + int listBodyEndPosition = body.limit(); + @C.TrackType int currentTrackType = C.TRACK_TYPE_NONE; + while (body.bytesLeft() > 8) { + int type = body.readLittleEndianInt(); + int size = body.readLittleEndianInt(); + int innerBoxBodyEndPosition = body.getPosition() + size; + body.setLimit(innerBoxBodyEndPosition); + @Nullable AviChunk aviChunk; + if (type == AviExtractor.FOURCC_LIST) { + int innerListType = body.readLittleEndianInt(); + aviChunk = parseFrom(innerListType, body); + } else { + aviChunk = createBox(type, currentTrackType, body); + } + if (aviChunk != null) { + if (aviChunk.getType() == AviExtractor.FOURCC_strh) { + currentTrackType = ((AviStreamHeaderChunk) aviChunk).getTrackType(); + } + builder.add(aviChunk); + } + body.setPosition(innerBoxBodyEndPosition); + body.setLimit(listBodyEndPosition); + } + return new ListChunk(listType, builder.build()); + } + + public final ImmutableList children; + private final int type; + + private ListChunk(int type, ImmutableList children) { + this.type = type; + this.children = children; + } + + @Override + public int getType() { + return type; + } + + @Nullable + @SuppressWarnings("unchecked") + public T getChild(Class c) { + for (AviChunk aviChunk : children) { + if (aviChunk.getClass() == c) { + return (T) aviChunk; + } + } + return null; + } + + @Nullable + private static AviChunk createBox( + int chunkType, @C.TrackType int trackType, ParsableByteArray body) { + switch (chunkType) { + case AviExtractor.FOURCC_avih: + return AviMainHeaderChunk.parseFrom(body); + case AviExtractor.FOURCC_strh: + return AviStreamHeaderChunk.parseFrom(body); + case AviExtractor.FOURCC_strf: + return StreamFormatChunk.parseFrom(trackType, body); + case AviExtractor.FOURCC_strn: + return StreamNameChunk.parseFrom(body); + default: + return null; + } + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/StreamFormatChunk.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/StreamFormatChunk.java new file mode 100644 index 0000000000..7ecdcd2816 --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/StreamFormatChunk.java @@ -0,0 +1,150 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.common.util.Util; +import com.google.common.collect.ImmutableList; + +/** Holds the {@link Format} information contained in an STRF chunk. */ +/* package */ final class StreamFormatChunk implements AviChunk { + private static final String TAG = "StreamFormatChunk"; + + @Nullable + public static AviChunk parseFrom(int trackType, ParsableByteArray body) { + if (trackType == C.TRACK_TYPE_VIDEO) { + return parseBitmapInfoHeader(body); + } else if (trackType == C.TRACK_TYPE_AUDIO) { + return parseWaveFormatEx(body); + } else { + Log.w( + TAG, + "Ignoring strf box for unsupported track type: " + Util.getTrackTypeString(trackType)); + return null; + } + } + + public final Format format; + + public StreamFormatChunk(Format format) { + this.format = format; + } + + @Override + public int getType() { + return AviExtractor.FOURCC_strf; + } + + @Nullable + private static AviChunk parseBitmapInfoHeader(ParsableByteArray body) { + body.skipBytes(4); // biSize. + int width = body.readLittleEndianInt(); + int height = body.readLittleEndianInt(); + body.skipBytes(4); // biPlanes (2 bytes), biBitCount (2 bytes). + int compression = body.readLittleEndianInt(); + String mimeType = getMimeTypeFromCompression(compression); + if (mimeType == null) { + Log.w(TAG, "Ignoring track with unsupported compression " + compression); + return null; + } + Format.Builder formatBuilder = new Format.Builder(); + formatBuilder.setWidth(width).setHeight(height).setSampleMimeType(mimeType); + return new StreamFormatChunk(formatBuilder.build()); + } + + // Syntax defined by the WAVEFORMATEX structure. See + // https://docs.microsoft.com/en-us/previous-versions/dd757713(v=vs.85). + @Nullable + private static AviChunk parseWaveFormatEx(ParsableByteArray body) { + int formatTag = body.readLittleEndianUnsignedShort(); + @Nullable String mimeType = getMimeTypeFromTag(formatTag); + if (mimeType == null) { + Log.w(TAG, "Ignoring track with unsupported format tag " + formatTag); + return null; + } + int channelCount = body.readLittleEndianUnsignedShort(); + int samplesPerSecond = body.readLittleEndianInt(); + body.skipBytes(6); // averageBytesPerSecond (4 bytes), nBlockAlign (2 bytes). + int bitsPerSample = body.readUnsignedShort(); + int pcmEncoding = Util.getPcmEncoding(bitsPerSample); + int cbSize = body.readLittleEndianUnsignedShort(); + byte[] codecData = new byte[cbSize]; + body.readBytes(codecData, /* offset= */ 0, codecData.length); + + Format.Builder formatBuilder = new Format.Builder(); + formatBuilder + .setSampleMimeType(mimeType) + .setChannelCount(channelCount) + .setSampleRate(samplesPerSecond); + if (MimeTypes.AUDIO_RAW.equals(mimeType) && pcmEncoding != C.ENCODING_INVALID) { + formatBuilder.setPcmEncoding(pcmEncoding); + } + if (MimeTypes.AUDIO_AAC.equals(mimeType) && codecData.length > 0) { + formatBuilder.setInitializationData(ImmutableList.of(codecData)); + } + return new StreamFormatChunk(formatBuilder.build()); + } + + @Nullable + private static String getMimeTypeFromTag(int tag) { + switch (tag) { + case 0x1: // WAVE_FORMAT_PCM + return MimeTypes.AUDIO_RAW; + case 0x55: // WAVE_FORMAT_MPEGLAYER3 + return MimeTypes.AUDIO_MPEG; + case 0xff: // WAVE_FORMAT_AAC + return MimeTypes.AUDIO_AAC; + case 0x2000: // WAVE_FORMAT_DVM - AC3 + return MimeTypes.AUDIO_AC3; + case 0x2001: // WAVE_FORMAT_DTS2 + return MimeTypes.AUDIO_DTS; + default: + return null; + } + } + + @Nullable + private static String getMimeTypeFromCompression(int compression) { + switch (compression) { + case 0x3234504d: // MP42 + return MimeTypes.VIDEO_MP42; + case 0x3334504d: // MP43 + return MimeTypes.VIDEO_MP43; + case 0x34363248: // H264 + case 0x31637661: // avc1 + case 0x31435641: // AVC1 + return MimeTypes.VIDEO_H264; + case 0x44495633: // 3VID + case 0x78766964: // divx + case 0x58564944: // DIVX + case 0x30355844: // DX50 + case 0x34504d46: // FMP4 + case 0x64697678: // xvid + case 0x44495658: // XVID + return MimeTypes.VIDEO_MP4V; + case 0x47504a4d: // MJPG + case 0x67706a6d: // mjpg + return MimeTypes.VIDEO_MJPEG; + default: + return null; + } + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/StreamNameChunk.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/StreamNameChunk.java new file mode 100644 index 0000000000..8d7a9eb5be --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/StreamNameChunk.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import androidx.media3.common.util.ParsableByteArray; + +/** Parses and contains the name from the STRN chunk. */ +/* package */ final class StreamNameChunk implements AviChunk { + + public static StreamNameChunk parseFrom(ParsableByteArray body) { + return new StreamNameChunk(body.readString(body.bytesLeft())); + } + + public final String name; + + private StreamNameChunk(String name) { + this.name = name; + } + + @Override + public int getType() { + return AviExtractor.FOURCC_strn; + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/avi/package-info.java b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/package-info.java new file mode 100644 index 0000000000..41b6f5a4ed --- /dev/null +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/avi/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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. + */ +@NonNullApi +package androidx.media3.extractor.avi; + +import androidx.media3.common.util.NonNullApi; diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/DefaultExtractorsFactoryTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/DefaultExtractorsFactoryTest.java index 5c6394482d..0654d5aa6b 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/DefaultExtractorsFactoryTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/DefaultExtractorsFactoryTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; import androidx.media3.common.MimeTypes; import androidx.media3.extractor.amr.AmrExtractor; +import androidx.media3.extractor.avi.AviExtractor; import androidx.media3.extractor.flac.FlacExtractor; import androidx.media3.extractor.flv.FlvExtractor; import androidx.media3.extractor.jpeg.JpegExtractor; @@ -70,6 +71,7 @@ public final class DefaultExtractorsFactoryTest { Ac3Extractor.class, Ac4Extractor.class, Mp3Extractor.class, + AviExtractor.class, JpegExtractor.class) .inOrder(); } @@ -112,6 +114,7 @@ public final class DefaultExtractorsFactoryTest { AdtsExtractor.class, Ac3Extractor.class, Ac4Extractor.class, + AviExtractor.class, JpegExtractor.class) .inOrder(); } diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/avi/AviExtractorTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/avi/AviExtractorTest.java new file mode 100644 index 0000000000..ab479240e0 --- /dev/null +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/avi/AviExtractorTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2022 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 androidx.media3.extractor.avi; + +import androidx.media3.test.utils.ExtractorAsserts; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.ParameterizedRobolectricTestRunner; +import org.robolectric.ParameterizedRobolectricTestRunner.Parameter; +import org.robolectric.ParameterizedRobolectricTestRunner.Parameters; + +/** Tests for {@link AviExtractor}. */ +@RunWith(ParameterizedRobolectricTestRunner.class) +public final class AviExtractorTest { + + @Parameters(name = "{0}") + public static ImmutableList params() { + return ExtractorAsserts.configs(); + } + + @Parameter public ExtractorAsserts.SimulationConfig simulationConfig; + + @Test + public void aviSample() throws Exception { + ExtractorAsserts.assertBehavior(AviExtractor::new, "media/avi/sample.avi", simulationConfig); + } +} diff --git a/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.0.dump b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.0.dump new file mode 100644 index 0000000000..666526a13d --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.0.dump @@ -0,0 +1,1091 @@ +seekMap: + isSeekable = true + duration = 4080000 + getPosition(0) = [[timeUs=0, position=9996]] + getPosition(1) = [[timeUs=0, position=9996]] + getPosition(2040000) = [[timeUs=1835152, position=160708], [timeUs=2335648, position=198850]] + getPosition(4080000) = [[timeUs=3837136, position=308434]] +numberOfTracks = 2 +track 0: + total output bytes = 252777 + sample count = 96 + format 0: + id = 0 + sampleMimeType = video/mp4v-es + maxInputSize = 6761 + width = 960 + height = 400 + sample 0: + time = 0 + flags = 1 + data = length 4279, hash C8074EBB + sample 1: + time = 125124 + flags = 0 + data = length 268, hash 14B72252 + sample 2: + time = 166833 + flags = 0 + data = length 268, hash ED2AEF52 + sample 3: + time = 208541 + flags = 0 + data = length 268, hash DD9B546B + sample 4: + time = 250249 + flags = 0 + data = length 268, hash B60F216B + sample 5: + time = 291958 + flags = 0 + data = length 268, hash 89782584 + sample 6: + time = 333666 + flags = 0 + data = length 268, hash 61EBF284 + sample 7: + time = 375374 + flags = 0 + data = length 268, hash B4D111DE + sample 8: + time = 417083 + flags = 0 + data = length 268, hash 8D44DEDE + sample 9: + time = 458791 + flags = 0 + data = length 268, hash 60ADE2F7 + sample 10: + time = 500499 + flags = 1 + data = length 4656, hash F1F35C82 + sample 11: + time = 542208 + flags = 1 + data = length 5119, hash 3CFA0CA2 + sample 12: + time = 583916 + flags = 1 + data = length 5466, hash 7E1F6F63 + sample 13: + time = 625624 + flags = 1 + data = length 5990, hash 70A2D835 + sample 14: + time = 667333 + flags = 1 + data = length 6476, hash E633D374 + sample 15: + time = 709041 + flags = 1 + data = length 6761, hash 922BC7A6 + sample 16: + time = 750749 + flags = 1 + data = length 6501, hash B03632B9 + sample 17: + time = 792458 + flags = 1 + data = length 5824, hash 89BCFDCC + sample 18: + time = 834166 + flags = 1 + data = length 5816, hash 4B321EB2 + sample 19: + time = 875874 + flags = 0 + data = length 5307, hash EF15AF2D + sample 20: + time = 917583 + flags = 0 + data = length 2791, hash B48241CD + sample 21: + time = 959291 + flags = 0 + data = length 2505, hash FB9EE72B + sample 22: + time = 1000999 + flags = 0 + data = length 1747, hash 89DC0982 + sample 23: + time = 1042708 + flags = 0 + data = length 1948, hash B8642019 + sample 24: + time = 1084416 + flags = 0 + data = length 2134, hash E6115E1C + sample 25: + time = 1126124 + flags = 0 + data = length 2035, hash 86FD9E1E + sample 26: + time = 1167833 + flags = 0 + data = length 2109, hash D66E00D + sample 27: + time = 1209541 + flags = 0 + data = length 2427, hash 63E16CB5 + sample 28: + time = 1251249 + flags = 0 + data = length 2485, hash 38F83F6D + sample 29: + time = 1292958 + flags = 0 + data = length 2458, hash 48900F9D + sample 30: + time = 1334666 + flags = 1 + data = length 5891, hash 4627CBC3 + sample 31: + time = 1376374 + flags = 0 + data = length 3154, hash B7484F2C + sample 32: + time = 1418083 + flags = 0 + data = length 2409, hash 93E50DB6 + sample 33: + time = 1459791 + flags = 0 + data = length 2296, hash 73A46768 + sample 34: + time = 1501499 + flags = 0 + data = length 2514, hash F71DCA93 + sample 35: + time = 1543208 + flags = 0 + data = length 2614, hash BDD6744E + sample 36: + time = 1584916 + flags = 0 + data = length 2797, hash 81BED431 + sample 37: + time = 1626624 + flags = 0 + data = length 1549, hash D892E824 + sample 38: + time = 1668333 + flags = 0 + data = length 2714, hash B3EE7E2A + sample 39: + time = 1710041 + flags = 0 + data = length 2002, hash BC9E16ED + sample 40: + time = 1751749 + flags = 0 + data = length 2726, hash C31D5A82 + sample 41: + time = 1793458 + flags = 0 + data = length 2639, hash AE67DC59 + sample 42: + time = 1835166 + flags = 1 + data = length 5011, hash 630ADA59 + sample 43: + time = 1876874 + flags = 0 + data = length 4356, hash 76CE0D21 + sample 44: + time = 1918583 + flags = 0 + data = length 1986, hash AC41A7FC + sample 45: + time = 1960291 + flags = 0 + data = length 2792, hash 497D3A2D + sample 46: + time = 2001999 + flags = 0 + data = length 2176, hash FADAC8ED + sample 47: + time = 2043708 + flags = 0 + data = length 2463, hash 379DE4C8 + sample 48: + time = 2085416 + flags = 0 + data = length 2472, hash 9E68BAC5 + sample 49: + time = 2127124 + flags = 0 + data = length 1960, hash 38BC3EFC + sample 50: + time = 2168832 + flags = 0 + data = length 1833, hash 139C885B + sample 51: + time = 2210541 + flags = 0 + data = length 1865, hash A14BE838 + sample 52: + time = 2252249 + flags = 0 + data = length 1491, hash 8EC33935 + sample 53: + time = 2293957 + flags = 0 + data = length 1403, hash 78D87F2C + sample 54: + time = 2335666 + flags = 1 + data = length 4936, hash C34CC2D0 + sample 55: + time = 2377374 + flags = 0 + data = length 2539, hash D0EDEC2B + sample 56: + time = 2419082 + flags = 0 + data = length 3052, hash 3F68900F + sample 57: + time = 2460791 + flags = 0 + data = length 2998, hash B531AC4 + sample 58: + time = 2502499 + flags = 0 + data = length 1670, hash 734A2739 + sample 59: + time = 2544207 + flags = 0 + data = length 1634, hash 60A39EA5 + sample 60: + time = 2585916 + flags = 0 + data = length 1623, hash B18B39FE + sample 61: + time = 2627624 + flags = 0 + data = length 806, hash DA70C12B + sample 62: + time = 2669332 + flags = 0 + data = length 990, hash A1642D2C + sample 63: + time = 2711041 + flags = 0 + data = length 903, hash 411ECEA3 + sample 64: + time = 2752749 + flags = 0 + data = length 713, hash A4DAFA22 + sample 65: + time = 2794457 + flags = 0 + data = length 749, hash F39941EF + sample 66: + time = 2836166 + flags = 1 + data = length 5258, hash 19670F6D + sample 67: + time = 2877874 + flags = 0 + data = length 1932, hash 3F7F6D21 + sample 68: + time = 2919582 + flags = 0 + data = length 731, hash 45EF5D68 + sample 69: + time = 2961291 + flags = 0 + data = length 1076, hash 8C23B3FF + sample 70: + time = 3002999 + flags = 0 + data = length 1560, hash D6133304 + sample 71: + time = 3044707 + flags = 0 + data = length 2564, hash B7B256B + sample 72: + time = 3086416 + flags = 0 + data = length 2789, hash 97736B63 + sample 73: + time = 3128124 + flags = 0 + data = length 2469, hash C65A89B6 + sample 74: + time = 3169832 + flags = 0 + data = length 2203, hash D89686B4 + sample 75: + time = 3211541 + flags = 0 + data = length 2097, hash 91358D88 + sample 76: + time = 3253249 + flags = 0 + data = length 2043, hash 50547CF1 + sample 77: + time = 3294957 + flags = 0 + data = length 2198, hash F93F1606 + sample 78: + time = 3336666 + flags = 1 + data = length 5084, hash BEC89380 + sample 79: + time = 3378374 + flags = 0 + data = length 3043, hash F3C50E5A + sample 80: + time = 3420082 + flags = 0 + data = length 2786, hash 49C8C67C + sample 81: + time = 3461791 + flags = 0 + data = length 2652, hash D0A93BE7 + sample 82: + time = 3503499 + flags = 0 + data = length 2675, hash 81F7F5BD + sample 83: + time = 3545207 + flags = 0 + data = length 2916, hash E2A38AE1 + sample 84: + time = 3586916 + flags = 0 + data = length 2574, hash 50EC13BD + sample 85: + time = 3628624 + flags = 0 + data = length 2644, hash 3DF461F4 + sample 86: + time = 3670332 + flags = 0 + data = length 2932, hash E2F2DAB0 + sample 87: + time = 3712041 + flags = 0 + data = length 2625, hash 100D69E1 + sample 88: + time = 3753749 + flags = 0 + data = length 2773, hash 347DCC1F + sample 89: + time = 3795457 + flags = 0 + data = length 2348, hash 51FC01A3 + sample 90: + time = 3837166 + flags = 1 + data = length 5356, hash 190A3CAE + sample 91: + time = 3878874 + flags = 0 + data = length 3172, hash 538FA2AE + sample 92: + time = 3920582 + flags = 0 + data = length 2393, hash 525B26D6 + sample 93: + time = 3962291 + flags = 0 + data = length 2307, hash C894745F + sample 94: + time = 4003999 + flags = 0 + data = length 2490, hash 800FED70 + sample 95: + time = 4045707 + flags = 0 + data = length 2115, hash A2512D3 +track 1: + total output bytes = 65280 + sample count = 170 + format 0: + id = 1 + sampleMimeType = audio/mpeg + maxInputSize = 384 + channelCount = 2 + sampleRate = 48000 + sample 0: + time = 0 + flags = 1 + data = length 384, hash 4FA643C4 + sample 1: + time = 24000 + flags = 1 + data = length 384, hash 5ED84707 + sample 2: + time = 48000 + flags = 1 + data = length 384, hash 5ED84707 + sample 3: + time = 72000 + flags = 1 + data = length 384, hash 5ED84707 + sample 4: + time = 96000 + flags = 1 + data = length 384, hash 5ED84707 + sample 5: + time = 120000 + flags = 1 + data = length 384, hash 5ED84707 + sample 6: + time = 144000 + flags = 1 + data = length 384, hash 5ED84707 + sample 7: + time = 168000 + flags = 1 + data = length 384, hash 5ED84707 + sample 8: + time = 192000 + flags = 1 + data = length 384, hash 5ED84707 + sample 9: + time = 216000 + flags = 1 + data = length 384, hash 5ED84707 + sample 10: + time = 240000 + flags = 1 + data = length 384, hash 5ED84707 + sample 11: + time = 264000 + flags = 1 + data = length 384, hash 5ED84707 + sample 12: + time = 288000 + flags = 1 + data = length 384, hash 5ED84707 + sample 13: + time = 312000 + flags = 1 + data = length 384, hash 5ED84707 + sample 14: + time = 336000 + flags = 1 + data = length 384, hash 5ED84707 + sample 15: + time = 360000 + flags = 1 + data = length 384, hash 5ED84707 + sample 16: + time = 384000 + flags = 1 + data = length 384, hash D77E2886 + sample 17: + time = 408000 + flags = 1 + data = length 384, hash 5C372185 + sample 18: + time = 432000 + flags = 1 + data = length 384, hash F9589AE7 + sample 19: + time = 456000 + flags = 1 + data = length 384, hash F14EBCAC + sample 20: + time = 480000 + flags = 1 + data = length 384, hash 2B688404 + sample 21: + time = 504000 + flags = 1 + data = length 384, hash E889FC6B + sample 22: + time = 528000 + flags = 1 + data = length 384, hash 53CBDEC0 + sample 23: + time = 552000 + flags = 1 + data = length 384, hash 91769951 + sample 24: + time = 576000 + flags = 1 + data = length 384, hash 749935FF + sample 25: + time = 600000 + flags = 1 + data = length 384, hash 2B794BC6 + sample 26: + time = 624000 + flags = 1 + data = length 384, hash B6A1870B + sample 27: + time = 648000 + flags = 1 + data = length 384, hash 7D729EEC + sample 28: + time = 672000 + flags = 1 + data = length 384, hash AFBD0EF5 + sample 29: + time = 696000 + flags = 1 + data = length 384, hash C1DDC412 + sample 30: + time = 720000 + flags = 1 + data = length 384, hash CF1807A4 + sample 31: + time = 744000 + flags = 1 + data = length 384, hash CD1E8F85 + sample 32: + time = 768000 + flags = 1 + data = length 384, hash FF56854C + sample 33: + time = 792000 + flags = 1 + data = length 384, hash F6F8D897 + sample 34: + time = 816000 + flags = 1 + data = length 384, hash 9C3F1566 + sample 35: + time = 840000 + flags = 1 + data = length 384, hash C5D788D1 + sample 36: + time = 864000 + flags = 1 + data = length 384, hash 81FC222A + sample 37: + time = 888000 + flags = 1 + data = length 384, hash 749C0516 + sample 38: + time = 912000 + flags = 1 + data = length 384, hash 63C232FF + sample 39: + time = 936000 + flags = 1 + data = length 384, hash FB4FABBB + sample 40: + time = 960000 + flags = 1 + data = length 384, hash B787C813 + sample 41: + time = 984000 + flags = 1 + data = length 384, hash E18B955C + sample 42: + time = 1008000 + flags = 1 + data = length 384, hash 2085B856 + sample 43: + time = 1032000 + flags = 1 + data = length 384, hash BDF70D7C + sample 44: + time = 1056000 + flags = 1 + data = length 384, hash 47838243 + sample 45: + time = 1080000 + flags = 1 + data = length 384, hash 5CF6CC33 + sample 46: + time = 1104000 + flags = 1 + data = length 384, hash 2A979CF6 + sample 47: + time = 1128000 + flags = 1 + data = length 384, hash 26D5CF5A + sample 48: + time = 1152000 + flags = 1 + data = length 384, hash E1BFEE5D + sample 49: + time = 1176000 + flags = 1 + data = length 384, hash A4DF110B + sample 50: + time = 1200000 + flags = 1 + data = length 384, hash 8595335A + sample 51: + time = 1224000 + flags = 1 + data = length 384, hash 5CA30C8 + sample 52: + time = 1248000 + flags = 1 + data = length 384, hash 1219C18C + sample 53: + time = 1272000 + flags = 1 + data = length 384, hash 41DC2F24 + sample 54: + time = 1296000 + flags = 1 + data = length 384, hash 664A60E1 + sample 55: + time = 1320000 + flags = 1 + data = length 384, hash 4338D4A1 + sample 56: + time = 1344000 + flags = 1 + data = length 384, hash C65E6D68 + sample 57: + time = 1368000 + flags = 1 + data = length 384, hash AE2762E8 + sample 58: + time = 1392000 + flags = 1 + data = length 384, hash 8CFEAA7F + sample 59: + time = 1416000 + flags = 1 + data = length 384, hash A96A80B4 + sample 60: + time = 1440000 + flags = 1 + data = length 384, hash 69A84538 + sample 61: + time = 1464000 + flags = 1 + data = length 384, hash 9131F77E + sample 62: + time = 1488000 + flags = 1 + data = length 384, hash 818091B1 + sample 63: + time = 1512000 + flags = 1 + data = length 384, hash 6DBECC10 + sample 64: + time = 1536000 + flags = 1 + data = length 384, hash A9912967 + sample 65: + time = 1560000 + flags = 1 + data = length 384, hash 4DA97472 + sample 66: + time = 1584000 + flags = 1 + data = length 384, hash 31B363DC + sample 67: + time = 1608000 + flags = 1 + data = length 384, hash E15BB36C + sample 68: + time = 1632000 + flags = 1 + data = length 384, hash 159C963C + sample 69: + time = 1656000 + flags = 1 + data = length 384, hash 50B874D + sample 70: + time = 1680000 + flags = 1 + data = length 384, hash 8727F339 + sample 71: + time = 1704000 + flags = 1 + data = length 384, hash C0853B6 + sample 72: + time = 1728000 + flags = 1 + data = length 384, hash 9E026376 + sample 73: + time = 1752000 + flags = 1 + data = length 384, hash C190380F + sample 74: + time = 1776000 + flags = 1 + data = length 384, hash 20925F33 + sample 75: + time = 1800000 + flags = 1 + data = length 384, hash 9F740DAF + sample 76: + time = 1824000 + flags = 1 + data = length 384, hash F75757D6 + sample 77: + time = 1848000 + flags = 1 + data = length 384, hash 46D76ED0 + sample 78: + time = 1872000 + flags = 1 + data = length 384, hash 11DC480F + sample 79: + time = 1896000 + flags = 1 + data = length 384, hash 3428D6D8 + sample 80: + time = 1920000 + flags = 1 + data = length 384, hash 16A11668 + sample 81: + time = 1944000 + flags = 1 + data = length 384, hash 4CFBA63C + sample 82: + time = 1968000 + flags = 1 + data = length 384, hash 2B6702A9 + sample 83: + time = 1992000 + flags = 1 + data = length 384, hash D047CEF9 + sample 84: + time = 2016000 + flags = 1 + data = length 384, hash 25F05663 + sample 85: + time = 2040000 + flags = 1 + data = length 384, hash 947441C7 + sample 86: + time = 2064000 + flags = 1 + data = length 384, hash E82145F7 + sample 87: + time = 2088000 + flags = 1 + data = length 384, hash 6C40F859 + sample 88: + time = 2112000 + flags = 1 + data = length 384, hash 273FBEF8 + sample 89: + time = 2136000 + flags = 1 + data = length 384, hash 2FF062B6 + sample 90: + time = 2160000 + flags = 1 + data = length 384, hash 73FF8D58 + sample 91: + time = 2184000 + flags = 1 + data = length 384, hash F2BAB943 + sample 92: + time = 2208000 + flags = 1 + data = length 384, hash 507DEF9F + sample 93: + time = 2232000 + flags = 1 + data = length 384, hash 913E927A + sample 94: + time = 2256000 + flags = 1 + data = length 384, hash AFFD0AED + sample 95: + time = 2280000 + flags = 1 + data = length 384, hash EE0C6F4C + sample 96: + time = 2304000 + flags = 1 + data = length 384, hash 70726632 + sample 97: + time = 2328000 + flags = 1 + data = length 384, hash B5D49F8 + sample 98: + time = 2352000 + flags = 1 + data = length 384, hash B341AF3F + sample 99: + time = 2376000 + flags = 1 + data = length 384, hash 6AC1D8C4 + sample 100: + time = 2400000 + flags = 1 + data = length 384, hash BC666685 + sample 101: + time = 2424000 + flags = 1 + data = length 384, hash E58054E8 + sample 102: + time = 2448000 + flags = 1 + data = length 384, hash 404AB403 + sample 103: + time = 2472000 + flags = 1 + data = length 384, hash 265A86B8 + sample 104: + time = 2496000 + flags = 1 + data = length 384, hash 306316F6 + sample 105: + time = 2520000 + flags = 1 + data = length 384, hash 7BFDEA60 + sample 106: + time = 2544000 + flags = 1 + data = length 384, hash 2EFF8E5B + sample 107: + time = 2568000 + flags = 1 + data = length 384, hash C06CE84C + sample 108: + time = 2592000 + flags = 1 + data = length 384, hash 9069A01E + sample 109: + time = 2616000 + flags = 1 + data = length 384, hash 4A78F181 + sample 110: + time = 2640000 + flags = 1 + data = length 384, hash 57FD4BE7 + sample 111: + time = 2664000 + flags = 1 + data = length 384, hash B09DB688 + sample 112: + time = 2688000 + flags = 1 + data = length 384, hash 5602C52F + sample 113: + time = 2712000 + flags = 1 + data = length 384, hash 77762F5D + sample 114: + time = 2736000 + flags = 1 + data = length 384, hash 6A0BDB6 + sample 115: + time = 2760000 + flags = 1 + data = length 384, hash 2428C91 + sample 116: + time = 2784000 + flags = 1 + data = length 384, hash DEB54354 + sample 117: + time = 2808000 + flags = 1 + data = length 384, hash FB0B7BEE + sample 118: + time = 2832000 + flags = 1 + data = length 384, hash BDD82F68 + sample 119: + time = 2856000 + flags = 1 + data = length 384, hash BAB3B808 + sample 120: + time = 2880000 + flags = 1 + data = length 384, hash E9183572 + sample 121: + time = 2904000 + flags = 1 + data = length 384, hash 9E36BC40 + sample 122: + time = 2928000 + flags = 1 + data = length 384, hash 937ED026 + sample 123: + time = 2952000 + flags = 1 + data = length 384, hash BF337AD1 + sample 124: + time = 2976000 + flags = 1 + data = length 384, hash E381C534 + sample 125: + time = 3000000 + flags = 1 + data = length 384, hash 6C9E1D71 + sample 126: + time = 3024000 + flags = 1 + data = length 384, hash 1C359B93 + sample 127: + time = 3048000 + flags = 1 + data = length 384, hash 3D137C16 + sample 128: + time = 3072000 + flags = 1 + data = length 384, hash 90D23677 + sample 129: + time = 3096000 + flags = 1 + data = length 384, hash 438F4839 + sample 130: + time = 3120000 + flags = 1 + data = length 384, hash EBAF44EF + sample 131: + time = 3144000 + flags = 1 + data = length 384, hash D8F64C54 + sample 132: + time = 3168000 + flags = 1 + data = length 384, hash 994F2EA6 + sample 133: + time = 3192000 + flags = 1 + data = length 384, hash 7E9DF6E4 + sample 134: + time = 3216000 + flags = 1 + data = length 384, hash 577F18B8 + sample 135: + time = 3240000 + flags = 1 + data = length 384, hash A47FCEE + sample 136: + time = 3264000 + flags = 1 + data = length 384, hash 5A1C435E + sample 137: + time = 3288000 + flags = 1 + data = length 384, hash 1D9EDC36 + sample 138: + time = 3312000 + flags = 1 + data = length 384, hash 3355333 + sample 139: + time = 3336000 + flags = 1 + data = length 384, hash 27CA9735 + sample 140: + time = 3360000 + flags = 1 + data = length 384, hash EB74B3F7 + sample 141: + time = 3384000 + flags = 1 + data = length 384, hash 22AC46CB + sample 142: + time = 3408000 + flags = 1 + data = length 384, hash 6002643D + sample 143: + time = 3432000 + flags = 1 + data = length 384, hash 487449E + sample 144: + time = 3456000 + flags = 1 + data = length 384, hash C5B10A14 + sample 145: + time = 3480000 + flags = 1 + data = length 384, hash 6050635D + sample 146: + time = 3504000 + flags = 1 + data = length 384, hash 437EAD63 + sample 147: + time = 3528000 + flags = 1 + data = length 384, hash 55A02C25 + sample 148: + time = 3552000 + flags = 1 + data = length 384, hash 171CCC00 + sample 149: + time = 3576000 + flags = 1 + data = length 384, hash 911127C8 + sample 150: + time = 3600000 + flags = 1 + data = length 384, hash AA157B50 + sample 151: + time = 3624000 + flags = 1 + data = length 384, hash 26F2D866 + sample 152: + time = 3648000 + flags = 1 + data = length 384, hash 67ADB3A9 + sample 153: + time = 3672000 + flags = 1 + data = length 384, hash F118D82D + sample 154: + time = 3696000 + flags = 1 + data = length 384, hash F51C252B + sample 155: + time = 3720000 + flags = 1 + data = length 384, hash BD13B97C + sample 156: + time = 3744000 + flags = 1 + data = length 384, hash 24BCF0AB + sample 157: + time = 3768000 + flags = 1 + data = length 384, hash 18DE9193 + sample 158: + time = 3792000 + flags = 1 + data = length 384, hash 234D8C99 + sample 159: + time = 3816000 + flags = 1 + data = length 384, hash EDFD2511 + sample 160: + time = 3840000 + flags = 1 + data = length 384, hash 69E3E157 + sample 161: + time = 3864000 + flags = 1 + data = length 384, hash AC90ADEC + sample 162: + time = 3888000 + flags = 1 + data = length 384, hash 6A333A56 + sample 163: + time = 3912000 + flags = 1 + data = length 384, hash 493D75A3 + sample 164: + time = 3936000 + flags = 1 + data = length 384, hash 53FE2A9E + sample 165: + time = 3960000 + flags = 1 + data = length 384, hash 65D6147C + sample 166: + time = 3984000 + flags = 1 + data = length 384, hash 5E744FB2 + sample 167: + time = 4008000 + flags = 1 + data = length 384, hash 68AEB7CA + sample 168: + time = 4032000 + flags = 1 + data = length 384, hash AC2972C + sample 169: + time = 4056000 + flags = 1 + data = length 384, hash E2A06CB9 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.1.dump b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.1.dump new file mode 100644 index 0000000000..94a7031440 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.1.dump @@ -0,0 +1,751 @@ +seekMap: + isSeekable = true + duration = 4080000 + getPosition(0) = [[timeUs=0, position=9996]] + getPosition(1) = [[timeUs=0, position=9996]] + getPosition(2040000) = [[timeUs=1835152, position=160708], [timeUs=2335648, position=198850]] + getPosition(4080000) = [[timeUs=3837136, position=308434]] +numberOfTracks = 2 +track 0: + total output bytes = 165531 + sample count = 66 + format 0: + id = 0 + sampleMimeType = video/mp4v-es + maxInputSize = 6761 + width = 960 + height = 400 + sample 0: + time = 1334666 + flags = 1 + data = length 5891, hash 4627CBC3 + sample 1: + time = 1376374 + flags = 0 + data = length 3154, hash B7484F2C + sample 2: + time = 1418083 + flags = 0 + data = length 2409, hash 93E50DB6 + sample 3: + time = 1459791 + flags = 0 + data = length 2296, hash 73A46768 + sample 4: + time = 1501499 + flags = 0 + data = length 2514, hash F71DCA93 + sample 5: + time = 1543208 + flags = 0 + data = length 2614, hash BDD6744E + sample 6: + time = 1584916 + flags = 0 + data = length 2797, hash 81BED431 + sample 7: + time = 1626624 + flags = 0 + data = length 1549, hash D892E824 + sample 8: + time = 1668333 + flags = 0 + data = length 2714, hash B3EE7E2A + sample 9: + time = 1710041 + flags = 0 + data = length 2002, hash BC9E16ED + sample 10: + time = 1751749 + flags = 0 + data = length 2726, hash C31D5A82 + sample 11: + time = 1793458 + flags = 0 + data = length 2639, hash AE67DC59 + sample 12: + time = 1835166 + flags = 1 + data = length 5011, hash 630ADA59 + sample 13: + time = 1876874 + flags = 0 + data = length 4356, hash 76CE0D21 + sample 14: + time = 1918583 + flags = 0 + data = length 1986, hash AC41A7FC + sample 15: + time = 1960291 + flags = 0 + data = length 2792, hash 497D3A2D + sample 16: + time = 2001999 + flags = 0 + data = length 2176, hash FADAC8ED + sample 17: + time = 2043708 + flags = 0 + data = length 2463, hash 379DE4C8 + sample 18: + time = 2085416 + flags = 0 + data = length 2472, hash 9E68BAC5 + sample 19: + time = 2127124 + flags = 0 + data = length 1960, hash 38BC3EFC + sample 20: + time = 2168832 + flags = 0 + data = length 1833, hash 139C885B + sample 21: + time = 2210541 + flags = 0 + data = length 1865, hash A14BE838 + sample 22: + time = 2252249 + flags = 0 + data = length 1491, hash 8EC33935 + sample 23: + time = 2293957 + flags = 0 + data = length 1403, hash 78D87F2C + sample 24: + time = 2335666 + flags = 1 + data = length 4936, hash C34CC2D0 + sample 25: + time = 2377374 + flags = 0 + data = length 2539, hash D0EDEC2B + sample 26: + time = 2419082 + flags = 0 + data = length 3052, hash 3F68900F + sample 27: + time = 2460791 + flags = 0 + data = length 2998, hash B531AC4 + sample 28: + time = 2502499 + flags = 0 + data = length 1670, hash 734A2739 + sample 29: + time = 2544207 + flags = 0 + data = length 1634, hash 60A39EA5 + sample 30: + time = 2585916 + flags = 0 + data = length 1623, hash B18B39FE + sample 31: + time = 2627624 + flags = 0 + data = length 806, hash DA70C12B + sample 32: + time = 2669332 + flags = 0 + data = length 990, hash A1642D2C + sample 33: + time = 2711041 + flags = 0 + data = length 903, hash 411ECEA3 + sample 34: + time = 2752749 + flags = 0 + data = length 713, hash A4DAFA22 + sample 35: + time = 2794457 + flags = 0 + data = length 749, hash F39941EF + sample 36: + time = 2836166 + flags = 1 + data = length 5258, hash 19670F6D + sample 37: + time = 2877874 + flags = 0 + data = length 1932, hash 3F7F6D21 + sample 38: + time = 2919582 + flags = 0 + data = length 731, hash 45EF5D68 + sample 39: + time = 2961291 + flags = 0 + data = length 1076, hash 8C23B3FF + sample 40: + time = 3002999 + flags = 0 + data = length 1560, hash D6133304 + sample 41: + time = 3044707 + flags = 0 + data = length 2564, hash B7B256B + sample 42: + time = 3086416 + flags = 0 + data = length 2789, hash 97736B63 + sample 43: + time = 3128124 + flags = 0 + data = length 2469, hash C65A89B6 + sample 44: + time = 3169832 + flags = 0 + data = length 2203, hash D89686B4 + sample 45: + time = 3211541 + flags = 0 + data = length 2097, hash 91358D88 + sample 46: + time = 3253249 + flags = 0 + data = length 2043, hash 50547CF1 + sample 47: + time = 3294957 + flags = 0 + data = length 2198, hash F93F1606 + sample 48: + time = 3336666 + flags = 1 + data = length 5084, hash BEC89380 + sample 49: + time = 3378374 + flags = 0 + data = length 3043, hash F3C50E5A + sample 50: + time = 3420082 + flags = 0 + data = length 2786, hash 49C8C67C + sample 51: + time = 3461791 + flags = 0 + data = length 2652, hash D0A93BE7 + sample 52: + time = 3503499 + flags = 0 + data = length 2675, hash 81F7F5BD + sample 53: + time = 3545207 + flags = 0 + data = length 2916, hash E2A38AE1 + sample 54: + time = 3586916 + flags = 0 + data = length 2574, hash 50EC13BD + sample 55: + time = 3628624 + flags = 0 + data = length 2644, hash 3DF461F4 + sample 56: + time = 3670332 + flags = 0 + data = length 2932, hash E2F2DAB0 + sample 57: + time = 3712041 + flags = 0 + data = length 2625, hash 100D69E1 + sample 58: + time = 3753749 + flags = 0 + data = length 2773, hash 347DCC1F + sample 59: + time = 3795457 + flags = 0 + data = length 2348, hash 51FC01A3 + sample 60: + time = 3837166 + flags = 1 + data = length 5356, hash 190A3CAE + sample 61: + time = 3878874 + flags = 0 + data = length 3172, hash 538FA2AE + sample 62: + time = 3920582 + flags = 0 + data = length 2393, hash 525B26D6 + sample 63: + time = 3962291 + flags = 0 + data = length 2307, hash C894745F + sample 64: + time = 4003999 + flags = 0 + data = length 2490, hash 800FED70 + sample 65: + time = 4045707 + flags = 0 + data = length 2115, hash A2512D3 +track 1: + total output bytes = 44160 + sample count = 115 + format 0: + id = 1 + sampleMimeType = audio/mpeg + maxInputSize = 384 + channelCount = 2 + sampleRate = 48000 + sample 0: + time = 1296000 + flags = 1 + data = length 384, hash 4338D4A1 + sample 1: + time = 1320000 + flags = 1 + data = length 384, hash C65E6D68 + sample 2: + time = 1344000 + flags = 1 + data = length 384, hash AE2762E8 + sample 3: + time = 1368000 + flags = 1 + data = length 384, hash 8CFEAA7F + sample 4: + time = 1392000 + flags = 1 + data = length 384, hash A96A80B4 + sample 5: + time = 1416000 + flags = 1 + data = length 384, hash 69A84538 + sample 6: + time = 1440000 + flags = 1 + data = length 384, hash 9131F77E + sample 7: + time = 1464000 + flags = 1 + data = length 384, hash 818091B1 + sample 8: + time = 1488000 + flags = 1 + data = length 384, hash 6DBECC10 + sample 9: + time = 1512000 + flags = 1 + data = length 384, hash A9912967 + sample 10: + time = 1536000 + flags = 1 + data = length 384, hash 4DA97472 + sample 11: + time = 1560000 + flags = 1 + data = length 384, hash 31B363DC + sample 12: + time = 1584000 + flags = 1 + data = length 384, hash E15BB36C + sample 13: + time = 1608000 + flags = 1 + data = length 384, hash 159C963C + sample 14: + time = 1632000 + flags = 1 + data = length 384, hash 50B874D + sample 15: + time = 1656000 + flags = 1 + data = length 384, hash 8727F339 + sample 16: + time = 1680000 + flags = 1 + data = length 384, hash C0853B6 + sample 17: + time = 1704000 + flags = 1 + data = length 384, hash 9E026376 + sample 18: + time = 1728000 + flags = 1 + data = length 384, hash C190380F + sample 19: + time = 1752000 + flags = 1 + data = length 384, hash 20925F33 + sample 20: + time = 1776000 + flags = 1 + data = length 384, hash 9F740DAF + sample 21: + time = 1800000 + flags = 1 + data = length 384, hash F75757D6 + sample 22: + time = 1824000 + flags = 1 + data = length 384, hash 46D76ED0 + sample 23: + time = 1848000 + flags = 1 + data = length 384, hash 11DC480F + sample 24: + time = 1872000 + flags = 1 + data = length 384, hash 3428D6D8 + sample 25: + time = 1896000 + flags = 1 + data = length 384, hash 16A11668 + sample 26: + time = 1920000 + flags = 1 + data = length 384, hash 4CFBA63C + sample 27: + time = 1944000 + flags = 1 + data = length 384, hash 2B6702A9 + sample 28: + time = 1968000 + flags = 1 + data = length 384, hash D047CEF9 + sample 29: + time = 1992000 + flags = 1 + data = length 384, hash 25F05663 + sample 30: + time = 2016000 + flags = 1 + data = length 384, hash 947441C7 + sample 31: + time = 2040000 + flags = 1 + data = length 384, hash E82145F7 + sample 32: + time = 2064000 + flags = 1 + data = length 384, hash 6C40F859 + sample 33: + time = 2088000 + flags = 1 + data = length 384, hash 273FBEF8 + sample 34: + time = 2112000 + flags = 1 + data = length 384, hash 2FF062B6 + sample 35: + time = 2136000 + flags = 1 + data = length 384, hash 73FF8D58 + sample 36: + time = 2160000 + flags = 1 + data = length 384, hash F2BAB943 + sample 37: + time = 2184000 + flags = 1 + data = length 384, hash 507DEF9F + sample 38: + time = 2208000 + flags = 1 + data = length 384, hash 913E927A + sample 39: + time = 2232000 + flags = 1 + data = length 384, hash AFFD0AED + sample 40: + time = 2256000 + flags = 1 + data = length 384, hash EE0C6F4C + sample 41: + time = 2280000 + flags = 1 + data = length 384, hash 70726632 + sample 42: + time = 2304000 + flags = 1 + data = length 384, hash B5D49F8 + sample 43: + time = 2328000 + flags = 1 + data = length 384, hash B341AF3F + sample 44: + time = 2352000 + flags = 1 + data = length 384, hash 6AC1D8C4 + sample 45: + time = 2376000 + flags = 1 + data = length 384, hash BC666685 + sample 46: + time = 2400000 + flags = 1 + data = length 384, hash E58054E8 + sample 47: + time = 2424000 + flags = 1 + data = length 384, hash 404AB403 + sample 48: + time = 2448000 + flags = 1 + data = length 384, hash 265A86B8 + sample 49: + time = 2472000 + flags = 1 + data = length 384, hash 306316F6 + sample 50: + time = 2496000 + flags = 1 + data = length 384, hash 7BFDEA60 + sample 51: + time = 2520000 + flags = 1 + data = length 384, hash 2EFF8E5B + sample 52: + time = 2544000 + flags = 1 + data = length 384, hash C06CE84C + sample 53: + time = 2568000 + flags = 1 + data = length 384, hash 9069A01E + sample 54: + time = 2592000 + flags = 1 + data = length 384, hash 4A78F181 + sample 55: + time = 2616000 + flags = 1 + data = length 384, hash 57FD4BE7 + sample 56: + time = 2640000 + flags = 1 + data = length 384, hash B09DB688 + sample 57: + time = 2664000 + flags = 1 + data = length 384, hash 5602C52F + sample 58: + time = 2688000 + flags = 1 + data = length 384, hash 77762F5D + sample 59: + time = 2712000 + flags = 1 + data = length 384, hash 6A0BDB6 + sample 60: + time = 2736000 + flags = 1 + data = length 384, hash 2428C91 + sample 61: + time = 2760000 + flags = 1 + data = length 384, hash DEB54354 + sample 62: + time = 2784000 + flags = 1 + data = length 384, hash FB0B7BEE + sample 63: + time = 2808000 + flags = 1 + data = length 384, hash BDD82F68 + sample 64: + time = 2832000 + flags = 1 + data = length 384, hash BAB3B808 + sample 65: + time = 2856000 + flags = 1 + data = length 384, hash E9183572 + sample 66: + time = 2880000 + flags = 1 + data = length 384, hash 9E36BC40 + sample 67: + time = 2904000 + flags = 1 + data = length 384, hash 937ED026 + sample 68: + time = 2928000 + flags = 1 + data = length 384, hash BF337AD1 + sample 69: + time = 2952000 + flags = 1 + data = length 384, hash E381C534 + sample 70: + time = 2976000 + flags = 1 + data = length 384, hash 6C9E1D71 + sample 71: + time = 3000000 + flags = 1 + data = length 384, hash 1C359B93 + sample 72: + time = 3024000 + flags = 1 + data = length 384, hash 3D137C16 + sample 73: + time = 3048000 + flags = 1 + data = length 384, hash 90D23677 + sample 74: + time = 3072000 + flags = 1 + data = length 384, hash 438F4839 + sample 75: + time = 3096000 + flags = 1 + data = length 384, hash EBAF44EF + sample 76: + time = 3120000 + flags = 1 + data = length 384, hash D8F64C54 + sample 77: + time = 3144000 + flags = 1 + data = length 384, hash 994F2EA6 + sample 78: + time = 3168000 + flags = 1 + data = length 384, hash 7E9DF6E4 + sample 79: + time = 3192000 + flags = 1 + data = length 384, hash 577F18B8 + sample 80: + time = 3216000 + flags = 1 + data = length 384, hash A47FCEE + sample 81: + time = 3240000 + flags = 1 + data = length 384, hash 5A1C435E + sample 82: + time = 3264000 + flags = 1 + data = length 384, hash 1D9EDC36 + sample 83: + time = 3288000 + flags = 1 + data = length 384, hash 3355333 + sample 84: + time = 3312000 + flags = 1 + data = length 384, hash 27CA9735 + sample 85: + time = 3336000 + flags = 1 + data = length 384, hash EB74B3F7 + sample 86: + time = 3360000 + flags = 1 + data = length 384, hash 22AC46CB + sample 87: + time = 3384000 + flags = 1 + data = length 384, hash 6002643D + sample 88: + time = 3408000 + flags = 1 + data = length 384, hash 487449E + sample 89: + time = 3432000 + flags = 1 + data = length 384, hash C5B10A14 + sample 90: + time = 3456000 + flags = 1 + data = length 384, hash 6050635D + sample 91: + time = 3480000 + flags = 1 + data = length 384, hash 437EAD63 + sample 92: + time = 3504000 + flags = 1 + data = length 384, hash 55A02C25 + sample 93: + time = 3528000 + flags = 1 + data = length 384, hash 171CCC00 + sample 94: + time = 3552000 + flags = 1 + data = length 384, hash 911127C8 + sample 95: + time = 3576000 + flags = 1 + data = length 384, hash AA157B50 + sample 96: + time = 3600000 + flags = 1 + data = length 384, hash 26F2D866 + sample 97: + time = 3624000 + flags = 1 + data = length 384, hash 67ADB3A9 + sample 98: + time = 3648000 + flags = 1 + data = length 384, hash F118D82D + sample 99: + time = 3672000 + flags = 1 + data = length 384, hash F51C252B + sample 100: + time = 3696000 + flags = 1 + data = length 384, hash BD13B97C + sample 101: + time = 3720000 + flags = 1 + data = length 384, hash 24BCF0AB + sample 102: + time = 3744000 + flags = 1 + data = length 384, hash 18DE9193 + sample 103: + time = 3768000 + flags = 1 + data = length 384, hash 234D8C99 + sample 104: + time = 3792000 + flags = 1 + data = length 384, hash EDFD2511 + sample 105: + time = 3816000 + flags = 1 + data = length 384, hash 69E3E157 + sample 106: + time = 3840000 + flags = 1 + data = length 384, hash AC90ADEC + sample 107: + time = 3864000 + flags = 1 + data = length 384, hash 6A333A56 + sample 108: + time = 3888000 + flags = 1 + data = length 384, hash 493D75A3 + sample 109: + time = 3912000 + flags = 1 + data = length 384, hash 53FE2A9E + sample 110: + time = 3936000 + flags = 1 + data = length 384, hash 65D6147C + sample 111: + time = 3960000 + flags = 1 + data = length 384, hash 5E744FB2 + sample 112: + time = 3984000 + flags = 1 + data = length 384, hash 68AEB7CA + sample 113: + time = 4008000 + flags = 1 + data = length 384, hash AC2972C + sample 114: + time = 4032000 + flags = 1 + data = length 384, hash E2A06CB9 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.2.dump b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.2.dump new file mode 100644 index 0000000000..ba5d85a844 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.2.dump @@ -0,0 +1,487 @@ +seekMap: + isSeekable = true + duration = 4080000 + getPosition(0) = [[timeUs=0, position=9996]] + getPosition(1) = [[timeUs=0, position=9996]] + getPosition(2040000) = [[timeUs=1835152, position=160708], [timeUs=2335648, position=198850]] + getPosition(4080000) = [[timeUs=3837136, position=308434]] +numberOfTracks = 2 +track 0: + total output bytes = 102418 + sample count = 42 + format 0: + id = 0 + sampleMimeType = video/mp4v-es + maxInputSize = 6761 + width = 960 + height = 400 + sample 0: + time = 2335666 + flags = 1 + data = length 4936, hash C34CC2D0 + sample 1: + time = 2377374 + flags = 0 + data = length 2539, hash D0EDEC2B + sample 2: + time = 2419082 + flags = 0 + data = length 3052, hash 3F68900F + sample 3: + time = 2460791 + flags = 0 + data = length 2998, hash B531AC4 + sample 4: + time = 2502499 + flags = 0 + data = length 1670, hash 734A2739 + sample 5: + time = 2544207 + flags = 0 + data = length 1634, hash 60A39EA5 + sample 6: + time = 2585916 + flags = 0 + data = length 1623, hash B18B39FE + sample 7: + time = 2627624 + flags = 0 + data = length 806, hash DA70C12B + sample 8: + time = 2669332 + flags = 0 + data = length 990, hash A1642D2C + sample 9: + time = 2711041 + flags = 0 + data = length 903, hash 411ECEA3 + sample 10: + time = 2752749 + flags = 0 + data = length 713, hash A4DAFA22 + sample 11: + time = 2794457 + flags = 0 + data = length 749, hash F39941EF + sample 12: + time = 2836166 + flags = 1 + data = length 5258, hash 19670F6D + sample 13: + time = 2877874 + flags = 0 + data = length 1932, hash 3F7F6D21 + sample 14: + time = 2919582 + flags = 0 + data = length 731, hash 45EF5D68 + sample 15: + time = 2961291 + flags = 0 + data = length 1076, hash 8C23B3FF + sample 16: + time = 3002999 + flags = 0 + data = length 1560, hash D6133304 + sample 17: + time = 3044707 + flags = 0 + data = length 2564, hash B7B256B + sample 18: + time = 3086416 + flags = 0 + data = length 2789, hash 97736B63 + sample 19: + time = 3128124 + flags = 0 + data = length 2469, hash C65A89B6 + sample 20: + time = 3169832 + flags = 0 + data = length 2203, hash D89686B4 + sample 21: + time = 3211541 + flags = 0 + data = length 2097, hash 91358D88 + sample 22: + time = 3253249 + flags = 0 + data = length 2043, hash 50547CF1 + sample 23: + time = 3294957 + flags = 0 + data = length 2198, hash F93F1606 + sample 24: + time = 3336666 + flags = 1 + data = length 5084, hash BEC89380 + sample 25: + time = 3378374 + flags = 0 + data = length 3043, hash F3C50E5A + sample 26: + time = 3420082 + flags = 0 + data = length 2786, hash 49C8C67C + sample 27: + time = 3461791 + flags = 0 + data = length 2652, hash D0A93BE7 + sample 28: + time = 3503499 + flags = 0 + data = length 2675, hash 81F7F5BD + sample 29: + time = 3545207 + flags = 0 + data = length 2916, hash E2A38AE1 + sample 30: + time = 3586916 + flags = 0 + data = length 2574, hash 50EC13BD + sample 31: + time = 3628624 + flags = 0 + data = length 2644, hash 3DF461F4 + sample 32: + time = 3670332 + flags = 0 + data = length 2932, hash E2F2DAB0 + sample 33: + time = 3712041 + flags = 0 + data = length 2625, hash 100D69E1 + sample 34: + time = 3753749 + flags = 0 + data = length 2773, hash 347DCC1F + sample 35: + time = 3795457 + flags = 0 + data = length 2348, hash 51FC01A3 + sample 36: + time = 3837166 + flags = 1 + data = length 5356, hash 190A3CAE + sample 37: + time = 3878874 + flags = 0 + data = length 3172, hash 538FA2AE + sample 38: + time = 3920582 + flags = 0 + data = length 2393, hash 525B26D6 + sample 39: + time = 3962291 + flags = 0 + data = length 2307, hash C894745F + sample 40: + time = 4003999 + flags = 0 + data = length 2490, hash 800FED70 + sample 41: + time = 4045707 + flags = 0 + data = length 2115, hash A2512D3 +track 1: + total output bytes = 28032 + sample count = 73 + format 0: + id = 1 + sampleMimeType = audio/mpeg + maxInputSize = 384 + channelCount = 2 + sampleRate = 48000 + sample 0: + time = 2304000 + flags = 1 + data = length 384, hash B5D49F8 + sample 1: + time = 2328000 + flags = 1 + data = length 384, hash B341AF3F + sample 2: + time = 2352000 + flags = 1 + data = length 384, hash 6AC1D8C4 + sample 3: + time = 2376000 + flags = 1 + data = length 384, hash BC666685 + sample 4: + time = 2400000 + flags = 1 + data = length 384, hash E58054E8 + sample 5: + time = 2424000 + flags = 1 + data = length 384, hash 404AB403 + sample 6: + time = 2448000 + flags = 1 + data = length 384, hash 265A86B8 + sample 7: + time = 2472000 + flags = 1 + data = length 384, hash 306316F6 + sample 8: + time = 2496000 + flags = 1 + data = length 384, hash 7BFDEA60 + sample 9: + time = 2520000 + flags = 1 + data = length 384, hash 2EFF8E5B + sample 10: + time = 2544000 + flags = 1 + data = length 384, hash C06CE84C + sample 11: + time = 2568000 + flags = 1 + data = length 384, hash 9069A01E + sample 12: + time = 2592000 + flags = 1 + data = length 384, hash 4A78F181 + sample 13: + time = 2616000 + flags = 1 + data = length 384, hash 57FD4BE7 + sample 14: + time = 2640000 + flags = 1 + data = length 384, hash B09DB688 + sample 15: + time = 2664000 + flags = 1 + data = length 384, hash 5602C52F + sample 16: + time = 2688000 + flags = 1 + data = length 384, hash 77762F5D + sample 17: + time = 2712000 + flags = 1 + data = length 384, hash 6A0BDB6 + sample 18: + time = 2736000 + flags = 1 + data = length 384, hash 2428C91 + sample 19: + time = 2760000 + flags = 1 + data = length 384, hash DEB54354 + sample 20: + time = 2784000 + flags = 1 + data = length 384, hash FB0B7BEE + sample 21: + time = 2808000 + flags = 1 + data = length 384, hash BDD82F68 + sample 22: + time = 2832000 + flags = 1 + data = length 384, hash BAB3B808 + sample 23: + time = 2856000 + flags = 1 + data = length 384, hash E9183572 + sample 24: + time = 2880000 + flags = 1 + data = length 384, hash 9E36BC40 + sample 25: + time = 2904000 + flags = 1 + data = length 384, hash 937ED026 + sample 26: + time = 2928000 + flags = 1 + data = length 384, hash BF337AD1 + sample 27: + time = 2952000 + flags = 1 + data = length 384, hash E381C534 + sample 28: + time = 2976000 + flags = 1 + data = length 384, hash 6C9E1D71 + sample 29: + time = 3000000 + flags = 1 + data = length 384, hash 1C359B93 + sample 30: + time = 3024000 + flags = 1 + data = length 384, hash 3D137C16 + sample 31: + time = 3048000 + flags = 1 + data = length 384, hash 90D23677 + sample 32: + time = 3072000 + flags = 1 + data = length 384, hash 438F4839 + sample 33: + time = 3096000 + flags = 1 + data = length 384, hash EBAF44EF + sample 34: + time = 3120000 + flags = 1 + data = length 384, hash D8F64C54 + sample 35: + time = 3144000 + flags = 1 + data = length 384, hash 994F2EA6 + sample 36: + time = 3168000 + flags = 1 + data = length 384, hash 7E9DF6E4 + sample 37: + time = 3192000 + flags = 1 + data = length 384, hash 577F18B8 + sample 38: + time = 3216000 + flags = 1 + data = length 384, hash A47FCEE + sample 39: + time = 3240000 + flags = 1 + data = length 384, hash 5A1C435E + sample 40: + time = 3264000 + flags = 1 + data = length 384, hash 1D9EDC36 + sample 41: + time = 3288000 + flags = 1 + data = length 384, hash 3355333 + sample 42: + time = 3312000 + flags = 1 + data = length 384, hash 27CA9735 + sample 43: + time = 3336000 + flags = 1 + data = length 384, hash EB74B3F7 + sample 44: + time = 3360000 + flags = 1 + data = length 384, hash 22AC46CB + sample 45: + time = 3384000 + flags = 1 + data = length 384, hash 6002643D + sample 46: + time = 3408000 + flags = 1 + data = length 384, hash 487449E + sample 47: + time = 3432000 + flags = 1 + data = length 384, hash C5B10A14 + sample 48: + time = 3456000 + flags = 1 + data = length 384, hash 6050635D + sample 49: + time = 3480000 + flags = 1 + data = length 384, hash 437EAD63 + sample 50: + time = 3504000 + flags = 1 + data = length 384, hash 55A02C25 + sample 51: + time = 3528000 + flags = 1 + data = length 384, hash 171CCC00 + sample 52: + time = 3552000 + flags = 1 + data = length 384, hash 911127C8 + sample 53: + time = 3576000 + flags = 1 + data = length 384, hash AA157B50 + sample 54: + time = 3600000 + flags = 1 + data = length 384, hash 26F2D866 + sample 55: + time = 3624000 + flags = 1 + data = length 384, hash 67ADB3A9 + sample 56: + time = 3648000 + flags = 1 + data = length 384, hash F118D82D + sample 57: + time = 3672000 + flags = 1 + data = length 384, hash F51C252B + sample 58: + time = 3696000 + flags = 1 + data = length 384, hash BD13B97C + sample 59: + time = 3720000 + flags = 1 + data = length 384, hash 24BCF0AB + sample 60: + time = 3744000 + flags = 1 + data = length 384, hash 18DE9193 + sample 61: + time = 3768000 + flags = 1 + data = length 384, hash 234D8C99 + sample 62: + time = 3792000 + flags = 1 + data = length 384, hash EDFD2511 + sample 63: + time = 3816000 + flags = 1 + data = length 384, hash 69E3E157 + sample 64: + time = 3840000 + flags = 1 + data = length 384, hash AC90ADEC + sample 65: + time = 3864000 + flags = 1 + data = length 384, hash 6A333A56 + sample 66: + time = 3888000 + flags = 1 + data = length 384, hash 493D75A3 + sample 67: + time = 3912000 + flags = 1 + data = length 384, hash 53FE2A9E + sample 68: + time = 3936000 + flags = 1 + data = length 384, hash 65D6147C + sample 69: + time = 3960000 + flags = 1 + data = length 384, hash 5E744FB2 + sample 70: + time = 3984000 + flags = 1 + data = length 384, hash 68AEB7CA + sample 71: + time = 4008000 + flags = 1 + data = length 384, hash AC2972C + sample 72: + time = 4032000 + flags = 1 + data = length 384, hash E2A06CB9 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.3.dump b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.3.dump new file mode 100644 index 0000000000..2be55648da --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.3.dump @@ -0,0 +1,91 @@ +seekMap: + isSeekable = true + duration = 4080000 + getPosition(0) = [[timeUs=0, position=9996]] + getPosition(1) = [[timeUs=0, position=9996]] + getPosition(2040000) = [[timeUs=1835152, position=160708], [timeUs=2335648, position=198850]] + getPosition(4080000) = [[timeUs=3837136, position=308434]] +numberOfTracks = 2 +track 0: + total output bytes = 17833 + sample count = 6 + format 0: + id = 0 + sampleMimeType = video/mp4v-es + maxInputSize = 6761 + width = 960 + height = 400 + sample 0: + time = 3837166 + flags = 1 + data = length 5356, hash 190A3CAE + sample 1: + time = 3878874 + flags = 0 + data = length 3172, hash 538FA2AE + sample 2: + time = 3920582 + flags = 0 + data = length 2393, hash 525B26D6 + sample 3: + time = 3962291 + flags = 0 + data = length 2307, hash C894745F + sample 4: + time = 4003999 + flags = 0 + data = length 2490, hash 800FED70 + sample 5: + time = 4045707 + flags = 0 + data = length 2115, hash A2512D3 +track 1: + total output bytes = 3840 + sample count = 10 + format 0: + id = 1 + sampleMimeType = audio/mpeg + maxInputSize = 384 + channelCount = 2 + sampleRate = 48000 + sample 0: + time = 3816000 + flags = 1 + data = length 384, hash 69E3E157 + sample 1: + time = 3840000 + flags = 1 + data = length 384, hash AC90ADEC + sample 2: + time = 3864000 + flags = 1 + data = length 384, hash 6A333A56 + sample 3: + time = 3888000 + flags = 1 + data = length 384, hash 493D75A3 + sample 4: + time = 3912000 + flags = 1 + data = length 384, hash 53FE2A9E + sample 5: + time = 3936000 + flags = 1 + data = length 384, hash 65D6147C + sample 6: + time = 3960000 + flags = 1 + data = length 384, hash 5E744FB2 + sample 7: + time = 3984000 + flags = 1 + data = length 384, hash 68AEB7CA + sample 8: + time = 4008000 + flags = 1 + data = length 384, hash AC2972C + sample 9: + time = 4032000 + flags = 1 + data = length 384, hash E2A06CB9 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.unknown_length.dump new file mode 100644 index 0000000000..666526a13d --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/avi/sample.avi.unknown_length.dump @@ -0,0 +1,1091 @@ +seekMap: + isSeekable = true + duration = 4080000 + getPosition(0) = [[timeUs=0, position=9996]] + getPosition(1) = [[timeUs=0, position=9996]] + getPosition(2040000) = [[timeUs=1835152, position=160708], [timeUs=2335648, position=198850]] + getPosition(4080000) = [[timeUs=3837136, position=308434]] +numberOfTracks = 2 +track 0: + total output bytes = 252777 + sample count = 96 + format 0: + id = 0 + sampleMimeType = video/mp4v-es + maxInputSize = 6761 + width = 960 + height = 400 + sample 0: + time = 0 + flags = 1 + data = length 4279, hash C8074EBB + sample 1: + time = 125124 + flags = 0 + data = length 268, hash 14B72252 + sample 2: + time = 166833 + flags = 0 + data = length 268, hash ED2AEF52 + sample 3: + time = 208541 + flags = 0 + data = length 268, hash DD9B546B + sample 4: + time = 250249 + flags = 0 + data = length 268, hash B60F216B + sample 5: + time = 291958 + flags = 0 + data = length 268, hash 89782584 + sample 6: + time = 333666 + flags = 0 + data = length 268, hash 61EBF284 + sample 7: + time = 375374 + flags = 0 + data = length 268, hash B4D111DE + sample 8: + time = 417083 + flags = 0 + data = length 268, hash 8D44DEDE + sample 9: + time = 458791 + flags = 0 + data = length 268, hash 60ADE2F7 + sample 10: + time = 500499 + flags = 1 + data = length 4656, hash F1F35C82 + sample 11: + time = 542208 + flags = 1 + data = length 5119, hash 3CFA0CA2 + sample 12: + time = 583916 + flags = 1 + data = length 5466, hash 7E1F6F63 + sample 13: + time = 625624 + flags = 1 + data = length 5990, hash 70A2D835 + sample 14: + time = 667333 + flags = 1 + data = length 6476, hash E633D374 + sample 15: + time = 709041 + flags = 1 + data = length 6761, hash 922BC7A6 + sample 16: + time = 750749 + flags = 1 + data = length 6501, hash B03632B9 + sample 17: + time = 792458 + flags = 1 + data = length 5824, hash 89BCFDCC + sample 18: + time = 834166 + flags = 1 + data = length 5816, hash 4B321EB2 + sample 19: + time = 875874 + flags = 0 + data = length 5307, hash EF15AF2D + sample 20: + time = 917583 + flags = 0 + data = length 2791, hash B48241CD + sample 21: + time = 959291 + flags = 0 + data = length 2505, hash FB9EE72B + sample 22: + time = 1000999 + flags = 0 + data = length 1747, hash 89DC0982 + sample 23: + time = 1042708 + flags = 0 + data = length 1948, hash B8642019 + sample 24: + time = 1084416 + flags = 0 + data = length 2134, hash E6115E1C + sample 25: + time = 1126124 + flags = 0 + data = length 2035, hash 86FD9E1E + sample 26: + time = 1167833 + flags = 0 + data = length 2109, hash D66E00D + sample 27: + time = 1209541 + flags = 0 + data = length 2427, hash 63E16CB5 + sample 28: + time = 1251249 + flags = 0 + data = length 2485, hash 38F83F6D + sample 29: + time = 1292958 + flags = 0 + data = length 2458, hash 48900F9D + sample 30: + time = 1334666 + flags = 1 + data = length 5891, hash 4627CBC3 + sample 31: + time = 1376374 + flags = 0 + data = length 3154, hash B7484F2C + sample 32: + time = 1418083 + flags = 0 + data = length 2409, hash 93E50DB6 + sample 33: + time = 1459791 + flags = 0 + data = length 2296, hash 73A46768 + sample 34: + time = 1501499 + flags = 0 + data = length 2514, hash F71DCA93 + sample 35: + time = 1543208 + flags = 0 + data = length 2614, hash BDD6744E + sample 36: + time = 1584916 + flags = 0 + data = length 2797, hash 81BED431 + sample 37: + time = 1626624 + flags = 0 + data = length 1549, hash D892E824 + sample 38: + time = 1668333 + flags = 0 + data = length 2714, hash B3EE7E2A + sample 39: + time = 1710041 + flags = 0 + data = length 2002, hash BC9E16ED + sample 40: + time = 1751749 + flags = 0 + data = length 2726, hash C31D5A82 + sample 41: + time = 1793458 + flags = 0 + data = length 2639, hash AE67DC59 + sample 42: + time = 1835166 + flags = 1 + data = length 5011, hash 630ADA59 + sample 43: + time = 1876874 + flags = 0 + data = length 4356, hash 76CE0D21 + sample 44: + time = 1918583 + flags = 0 + data = length 1986, hash AC41A7FC + sample 45: + time = 1960291 + flags = 0 + data = length 2792, hash 497D3A2D + sample 46: + time = 2001999 + flags = 0 + data = length 2176, hash FADAC8ED + sample 47: + time = 2043708 + flags = 0 + data = length 2463, hash 379DE4C8 + sample 48: + time = 2085416 + flags = 0 + data = length 2472, hash 9E68BAC5 + sample 49: + time = 2127124 + flags = 0 + data = length 1960, hash 38BC3EFC + sample 50: + time = 2168832 + flags = 0 + data = length 1833, hash 139C885B + sample 51: + time = 2210541 + flags = 0 + data = length 1865, hash A14BE838 + sample 52: + time = 2252249 + flags = 0 + data = length 1491, hash 8EC33935 + sample 53: + time = 2293957 + flags = 0 + data = length 1403, hash 78D87F2C + sample 54: + time = 2335666 + flags = 1 + data = length 4936, hash C34CC2D0 + sample 55: + time = 2377374 + flags = 0 + data = length 2539, hash D0EDEC2B + sample 56: + time = 2419082 + flags = 0 + data = length 3052, hash 3F68900F + sample 57: + time = 2460791 + flags = 0 + data = length 2998, hash B531AC4 + sample 58: + time = 2502499 + flags = 0 + data = length 1670, hash 734A2739 + sample 59: + time = 2544207 + flags = 0 + data = length 1634, hash 60A39EA5 + sample 60: + time = 2585916 + flags = 0 + data = length 1623, hash B18B39FE + sample 61: + time = 2627624 + flags = 0 + data = length 806, hash DA70C12B + sample 62: + time = 2669332 + flags = 0 + data = length 990, hash A1642D2C + sample 63: + time = 2711041 + flags = 0 + data = length 903, hash 411ECEA3 + sample 64: + time = 2752749 + flags = 0 + data = length 713, hash A4DAFA22 + sample 65: + time = 2794457 + flags = 0 + data = length 749, hash F39941EF + sample 66: + time = 2836166 + flags = 1 + data = length 5258, hash 19670F6D + sample 67: + time = 2877874 + flags = 0 + data = length 1932, hash 3F7F6D21 + sample 68: + time = 2919582 + flags = 0 + data = length 731, hash 45EF5D68 + sample 69: + time = 2961291 + flags = 0 + data = length 1076, hash 8C23B3FF + sample 70: + time = 3002999 + flags = 0 + data = length 1560, hash D6133304 + sample 71: + time = 3044707 + flags = 0 + data = length 2564, hash B7B256B + sample 72: + time = 3086416 + flags = 0 + data = length 2789, hash 97736B63 + sample 73: + time = 3128124 + flags = 0 + data = length 2469, hash C65A89B6 + sample 74: + time = 3169832 + flags = 0 + data = length 2203, hash D89686B4 + sample 75: + time = 3211541 + flags = 0 + data = length 2097, hash 91358D88 + sample 76: + time = 3253249 + flags = 0 + data = length 2043, hash 50547CF1 + sample 77: + time = 3294957 + flags = 0 + data = length 2198, hash F93F1606 + sample 78: + time = 3336666 + flags = 1 + data = length 5084, hash BEC89380 + sample 79: + time = 3378374 + flags = 0 + data = length 3043, hash F3C50E5A + sample 80: + time = 3420082 + flags = 0 + data = length 2786, hash 49C8C67C + sample 81: + time = 3461791 + flags = 0 + data = length 2652, hash D0A93BE7 + sample 82: + time = 3503499 + flags = 0 + data = length 2675, hash 81F7F5BD + sample 83: + time = 3545207 + flags = 0 + data = length 2916, hash E2A38AE1 + sample 84: + time = 3586916 + flags = 0 + data = length 2574, hash 50EC13BD + sample 85: + time = 3628624 + flags = 0 + data = length 2644, hash 3DF461F4 + sample 86: + time = 3670332 + flags = 0 + data = length 2932, hash E2F2DAB0 + sample 87: + time = 3712041 + flags = 0 + data = length 2625, hash 100D69E1 + sample 88: + time = 3753749 + flags = 0 + data = length 2773, hash 347DCC1F + sample 89: + time = 3795457 + flags = 0 + data = length 2348, hash 51FC01A3 + sample 90: + time = 3837166 + flags = 1 + data = length 5356, hash 190A3CAE + sample 91: + time = 3878874 + flags = 0 + data = length 3172, hash 538FA2AE + sample 92: + time = 3920582 + flags = 0 + data = length 2393, hash 525B26D6 + sample 93: + time = 3962291 + flags = 0 + data = length 2307, hash C894745F + sample 94: + time = 4003999 + flags = 0 + data = length 2490, hash 800FED70 + sample 95: + time = 4045707 + flags = 0 + data = length 2115, hash A2512D3 +track 1: + total output bytes = 65280 + sample count = 170 + format 0: + id = 1 + sampleMimeType = audio/mpeg + maxInputSize = 384 + channelCount = 2 + sampleRate = 48000 + sample 0: + time = 0 + flags = 1 + data = length 384, hash 4FA643C4 + sample 1: + time = 24000 + flags = 1 + data = length 384, hash 5ED84707 + sample 2: + time = 48000 + flags = 1 + data = length 384, hash 5ED84707 + sample 3: + time = 72000 + flags = 1 + data = length 384, hash 5ED84707 + sample 4: + time = 96000 + flags = 1 + data = length 384, hash 5ED84707 + sample 5: + time = 120000 + flags = 1 + data = length 384, hash 5ED84707 + sample 6: + time = 144000 + flags = 1 + data = length 384, hash 5ED84707 + sample 7: + time = 168000 + flags = 1 + data = length 384, hash 5ED84707 + sample 8: + time = 192000 + flags = 1 + data = length 384, hash 5ED84707 + sample 9: + time = 216000 + flags = 1 + data = length 384, hash 5ED84707 + sample 10: + time = 240000 + flags = 1 + data = length 384, hash 5ED84707 + sample 11: + time = 264000 + flags = 1 + data = length 384, hash 5ED84707 + sample 12: + time = 288000 + flags = 1 + data = length 384, hash 5ED84707 + sample 13: + time = 312000 + flags = 1 + data = length 384, hash 5ED84707 + sample 14: + time = 336000 + flags = 1 + data = length 384, hash 5ED84707 + sample 15: + time = 360000 + flags = 1 + data = length 384, hash 5ED84707 + sample 16: + time = 384000 + flags = 1 + data = length 384, hash D77E2886 + sample 17: + time = 408000 + flags = 1 + data = length 384, hash 5C372185 + sample 18: + time = 432000 + flags = 1 + data = length 384, hash F9589AE7 + sample 19: + time = 456000 + flags = 1 + data = length 384, hash F14EBCAC + sample 20: + time = 480000 + flags = 1 + data = length 384, hash 2B688404 + sample 21: + time = 504000 + flags = 1 + data = length 384, hash E889FC6B + sample 22: + time = 528000 + flags = 1 + data = length 384, hash 53CBDEC0 + sample 23: + time = 552000 + flags = 1 + data = length 384, hash 91769951 + sample 24: + time = 576000 + flags = 1 + data = length 384, hash 749935FF + sample 25: + time = 600000 + flags = 1 + data = length 384, hash 2B794BC6 + sample 26: + time = 624000 + flags = 1 + data = length 384, hash B6A1870B + sample 27: + time = 648000 + flags = 1 + data = length 384, hash 7D729EEC + sample 28: + time = 672000 + flags = 1 + data = length 384, hash AFBD0EF5 + sample 29: + time = 696000 + flags = 1 + data = length 384, hash C1DDC412 + sample 30: + time = 720000 + flags = 1 + data = length 384, hash CF1807A4 + sample 31: + time = 744000 + flags = 1 + data = length 384, hash CD1E8F85 + sample 32: + time = 768000 + flags = 1 + data = length 384, hash FF56854C + sample 33: + time = 792000 + flags = 1 + data = length 384, hash F6F8D897 + sample 34: + time = 816000 + flags = 1 + data = length 384, hash 9C3F1566 + sample 35: + time = 840000 + flags = 1 + data = length 384, hash C5D788D1 + sample 36: + time = 864000 + flags = 1 + data = length 384, hash 81FC222A + sample 37: + time = 888000 + flags = 1 + data = length 384, hash 749C0516 + sample 38: + time = 912000 + flags = 1 + data = length 384, hash 63C232FF + sample 39: + time = 936000 + flags = 1 + data = length 384, hash FB4FABBB + sample 40: + time = 960000 + flags = 1 + data = length 384, hash B787C813 + sample 41: + time = 984000 + flags = 1 + data = length 384, hash E18B955C + sample 42: + time = 1008000 + flags = 1 + data = length 384, hash 2085B856 + sample 43: + time = 1032000 + flags = 1 + data = length 384, hash BDF70D7C + sample 44: + time = 1056000 + flags = 1 + data = length 384, hash 47838243 + sample 45: + time = 1080000 + flags = 1 + data = length 384, hash 5CF6CC33 + sample 46: + time = 1104000 + flags = 1 + data = length 384, hash 2A979CF6 + sample 47: + time = 1128000 + flags = 1 + data = length 384, hash 26D5CF5A + sample 48: + time = 1152000 + flags = 1 + data = length 384, hash E1BFEE5D + sample 49: + time = 1176000 + flags = 1 + data = length 384, hash A4DF110B + sample 50: + time = 1200000 + flags = 1 + data = length 384, hash 8595335A + sample 51: + time = 1224000 + flags = 1 + data = length 384, hash 5CA30C8 + sample 52: + time = 1248000 + flags = 1 + data = length 384, hash 1219C18C + sample 53: + time = 1272000 + flags = 1 + data = length 384, hash 41DC2F24 + sample 54: + time = 1296000 + flags = 1 + data = length 384, hash 664A60E1 + sample 55: + time = 1320000 + flags = 1 + data = length 384, hash 4338D4A1 + sample 56: + time = 1344000 + flags = 1 + data = length 384, hash C65E6D68 + sample 57: + time = 1368000 + flags = 1 + data = length 384, hash AE2762E8 + sample 58: + time = 1392000 + flags = 1 + data = length 384, hash 8CFEAA7F + sample 59: + time = 1416000 + flags = 1 + data = length 384, hash A96A80B4 + sample 60: + time = 1440000 + flags = 1 + data = length 384, hash 69A84538 + sample 61: + time = 1464000 + flags = 1 + data = length 384, hash 9131F77E + sample 62: + time = 1488000 + flags = 1 + data = length 384, hash 818091B1 + sample 63: + time = 1512000 + flags = 1 + data = length 384, hash 6DBECC10 + sample 64: + time = 1536000 + flags = 1 + data = length 384, hash A9912967 + sample 65: + time = 1560000 + flags = 1 + data = length 384, hash 4DA97472 + sample 66: + time = 1584000 + flags = 1 + data = length 384, hash 31B363DC + sample 67: + time = 1608000 + flags = 1 + data = length 384, hash E15BB36C + sample 68: + time = 1632000 + flags = 1 + data = length 384, hash 159C963C + sample 69: + time = 1656000 + flags = 1 + data = length 384, hash 50B874D + sample 70: + time = 1680000 + flags = 1 + data = length 384, hash 8727F339 + sample 71: + time = 1704000 + flags = 1 + data = length 384, hash C0853B6 + sample 72: + time = 1728000 + flags = 1 + data = length 384, hash 9E026376 + sample 73: + time = 1752000 + flags = 1 + data = length 384, hash C190380F + sample 74: + time = 1776000 + flags = 1 + data = length 384, hash 20925F33 + sample 75: + time = 1800000 + flags = 1 + data = length 384, hash 9F740DAF + sample 76: + time = 1824000 + flags = 1 + data = length 384, hash F75757D6 + sample 77: + time = 1848000 + flags = 1 + data = length 384, hash 46D76ED0 + sample 78: + time = 1872000 + flags = 1 + data = length 384, hash 11DC480F + sample 79: + time = 1896000 + flags = 1 + data = length 384, hash 3428D6D8 + sample 80: + time = 1920000 + flags = 1 + data = length 384, hash 16A11668 + sample 81: + time = 1944000 + flags = 1 + data = length 384, hash 4CFBA63C + sample 82: + time = 1968000 + flags = 1 + data = length 384, hash 2B6702A9 + sample 83: + time = 1992000 + flags = 1 + data = length 384, hash D047CEF9 + sample 84: + time = 2016000 + flags = 1 + data = length 384, hash 25F05663 + sample 85: + time = 2040000 + flags = 1 + data = length 384, hash 947441C7 + sample 86: + time = 2064000 + flags = 1 + data = length 384, hash E82145F7 + sample 87: + time = 2088000 + flags = 1 + data = length 384, hash 6C40F859 + sample 88: + time = 2112000 + flags = 1 + data = length 384, hash 273FBEF8 + sample 89: + time = 2136000 + flags = 1 + data = length 384, hash 2FF062B6 + sample 90: + time = 2160000 + flags = 1 + data = length 384, hash 73FF8D58 + sample 91: + time = 2184000 + flags = 1 + data = length 384, hash F2BAB943 + sample 92: + time = 2208000 + flags = 1 + data = length 384, hash 507DEF9F + sample 93: + time = 2232000 + flags = 1 + data = length 384, hash 913E927A + sample 94: + time = 2256000 + flags = 1 + data = length 384, hash AFFD0AED + sample 95: + time = 2280000 + flags = 1 + data = length 384, hash EE0C6F4C + sample 96: + time = 2304000 + flags = 1 + data = length 384, hash 70726632 + sample 97: + time = 2328000 + flags = 1 + data = length 384, hash B5D49F8 + sample 98: + time = 2352000 + flags = 1 + data = length 384, hash B341AF3F + sample 99: + time = 2376000 + flags = 1 + data = length 384, hash 6AC1D8C4 + sample 100: + time = 2400000 + flags = 1 + data = length 384, hash BC666685 + sample 101: + time = 2424000 + flags = 1 + data = length 384, hash E58054E8 + sample 102: + time = 2448000 + flags = 1 + data = length 384, hash 404AB403 + sample 103: + time = 2472000 + flags = 1 + data = length 384, hash 265A86B8 + sample 104: + time = 2496000 + flags = 1 + data = length 384, hash 306316F6 + sample 105: + time = 2520000 + flags = 1 + data = length 384, hash 7BFDEA60 + sample 106: + time = 2544000 + flags = 1 + data = length 384, hash 2EFF8E5B + sample 107: + time = 2568000 + flags = 1 + data = length 384, hash C06CE84C + sample 108: + time = 2592000 + flags = 1 + data = length 384, hash 9069A01E + sample 109: + time = 2616000 + flags = 1 + data = length 384, hash 4A78F181 + sample 110: + time = 2640000 + flags = 1 + data = length 384, hash 57FD4BE7 + sample 111: + time = 2664000 + flags = 1 + data = length 384, hash B09DB688 + sample 112: + time = 2688000 + flags = 1 + data = length 384, hash 5602C52F + sample 113: + time = 2712000 + flags = 1 + data = length 384, hash 77762F5D + sample 114: + time = 2736000 + flags = 1 + data = length 384, hash 6A0BDB6 + sample 115: + time = 2760000 + flags = 1 + data = length 384, hash 2428C91 + sample 116: + time = 2784000 + flags = 1 + data = length 384, hash DEB54354 + sample 117: + time = 2808000 + flags = 1 + data = length 384, hash FB0B7BEE + sample 118: + time = 2832000 + flags = 1 + data = length 384, hash BDD82F68 + sample 119: + time = 2856000 + flags = 1 + data = length 384, hash BAB3B808 + sample 120: + time = 2880000 + flags = 1 + data = length 384, hash E9183572 + sample 121: + time = 2904000 + flags = 1 + data = length 384, hash 9E36BC40 + sample 122: + time = 2928000 + flags = 1 + data = length 384, hash 937ED026 + sample 123: + time = 2952000 + flags = 1 + data = length 384, hash BF337AD1 + sample 124: + time = 2976000 + flags = 1 + data = length 384, hash E381C534 + sample 125: + time = 3000000 + flags = 1 + data = length 384, hash 6C9E1D71 + sample 126: + time = 3024000 + flags = 1 + data = length 384, hash 1C359B93 + sample 127: + time = 3048000 + flags = 1 + data = length 384, hash 3D137C16 + sample 128: + time = 3072000 + flags = 1 + data = length 384, hash 90D23677 + sample 129: + time = 3096000 + flags = 1 + data = length 384, hash 438F4839 + sample 130: + time = 3120000 + flags = 1 + data = length 384, hash EBAF44EF + sample 131: + time = 3144000 + flags = 1 + data = length 384, hash D8F64C54 + sample 132: + time = 3168000 + flags = 1 + data = length 384, hash 994F2EA6 + sample 133: + time = 3192000 + flags = 1 + data = length 384, hash 7E9DF6E4 + sample 134: + time = 3216000 + flags = 1 + data = length 384, hash 577F18B8 + sample 135: + time = 3240000 + flags = 1 + data = length 384, hash A47FCEE + sample 136: + time = 3264000 + flags = 1 + data = length 384, hash 5A1C435E + sample 137: + time = 3288000 + flags = 1 + data = length 384, hash 1D9EDC36 + sample 138: + time = 3312000 + flags = 1 + data = length 384, hash 3355333 + sample 139: + time = 3336000 + flags = 1 + data = length 384, hash 27CA9735 + sample 140: + time = 3360000 + flags = 1 + data = length 384, hash EB74B3F7 + sample 141: + time = 3384000 + flags = 1 + data = length 384, hash 22AC46CB + sample 142: + time = 3408000 + flags = 1 + data = length 384, hash 6002643D + sample 143: + time = 3432000 + flags = 1 + data = length 384, hash 487449E + sample 144: + time = 3456000 + flags = 1 + data = length 384, hash C5B10A14 + sample 145: + time = 3480000 + flags = 1 + data = length 384, hash 6050635D + sample 146: + time = 3504000 + flags = 1 + data = length 384, hash 437EAD63 + sample 147: + time = 3528000 + flags = 1 + data = length 384, hash 55A02C25 + sample 148: + time = 3552000 + flags = 1 + data = length 384, hash 171CCC00 + sample 149: + time = 3576000 + flags = 1 + data = length 384, hash 911127C8 + sample 150: + time = 3600000 + flags = 1 + data = length 384, hash AA157B50 + sample 151: + time = 3624000 + flags = 1 + data = length 384, hash 26F2D866 + sample 152: + time = 3648000 + flags = 1 + data = length 384, hash 67ADB3A9 + sample 153: + time = 3672000 + flags = 1 + data = length 384, hash F118D82D + sample 154: + time = 3696000 + flags = 1 + data = length 384, hash F51C252B + sample 155: + time = 3720000 + flags = 1 + data = length 384, hash BD13B97C + sample 156: + time = 3744000 + flags = 1 + data = length 384, hash 24BCF0AB + sample 157: + time = 3768000 + flags = 1 + data = length 384, hash 18DE9193 + sample 158: + time = 3792000 + flags = 1 + data = length 384, hash 234D8C99 + sample 159: + time = 3816000 + flags = 1 + data = length 384, hash EDFD2511 + sample 160: + time = 3840000 + flags = 1 + data = length 384, hash 69E3E157 + sample 161: + time = 3864000 + flags = 1 + data = length 384, hash AC90ADEC + sample 162: + time = 3888000 + flags = 1 + data = length 384, hash 6A333A56 + sample 163: + time = 3912000 + flags = 1 + data = length 384, hash 493D75A3 + sample 164: + time = 3936000 + flags = 1 + data = length 384, hash 53FE2A9E + sample 165: + time = 3960000 + flags = 1 + data = length 384, hash 65D6147C + sample 166: + time = 3984000 + flags = 1 + data = length 384, hash 5E744FB2 + sample 167: + time = 4008000 + flags = 1 + data = length 384, hash 68AEB7CA + sample 168: + time = 4032000 + flags = 1 + data = length 384, hash AC2972C + sample 169: + time = 4056000 + flags = 1 + data = length 384, hash E2A06CB9 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/media/avi/sample.avi b/libraries/test_data/src/test/assets/media/avi/sample.avi new file mode 100644 index 0000000000..9f95d3fa71 Binary files /dev/null and b/libraries/test_data/src/test/assets/media/avi/sample.avi differ