Improve support for Ogg truncated content
#minor-release Issue:#7608 PiperOrigin-RevId: 382081687
This commit is contained in:
parent
155e27ec7c
commit
6e4508daec
@ -83,6 +83,8 @@
|
|||||||
is set incorrectly
|
is set incorrectly
|
||||||
([#4083](https://github.com/google/ExoPlayer/issues/4083)). Such content
|
([#4083](https://github.com/google/ExoPlayer/issues/4083)). Such content
|
||||||
is malformed and should be re-encoded.
|
is malformed and should be re-encoded.
|
||||||
|
* Improve support for truncated Ogg streams
|
||||||
|
([#7608](https://github.com/google/ExoPlayer/issues/7608)).
|
||||||
* HLS:
|
* HLS:
|
||||||
* Fix issue where playback of a live event could become stuck rather than
|
* Fix issue where playback of a live event could become stuck rather than
|
||||||
transitioning to `STATE_ENDED` when the event ends
|
transitioning to `STATE_ENDED` when the event ends
|
||||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ParserException;
|
import com.google.android.exoplayer2.ParserException;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.checkerframework.dataflow.qual.Pure;
|
import org.checkerframework.dataflow.qual.Pure;
|
||||||
|
|
||||||
@ -62,5 +63,62 @@ public final class ExtractorUtil {
|
|||||||
return totalBytesPeeked;
|
return totalBytesPeeked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@link ExtractorInput#readFully(byte[], int, int)} except that it returns {@code
|
||||||
|
* false} instead of throwing an {@link EOFException} if the end of input is encountered without
|
||||||
|
* having fully satisfied the read.
|
||||||
|
*/
|
||||||
|
public static boolean readFullyQuietly(
|
||||||
|
ExtractorInput input, byte[] output, int offset, int length) throws IOException {
|
||||||
|
try {
|
||||||
|
input.readFully(output, offset, length);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@link ExtractorInput#skipFully(int)} except that it returns {@code false}
|
||||||
|
* instead of throwing an {@link EOFException} if the end of input is encountered without having
|
||||||
|
* fully satisfied the skip.
|
||||||
|
*/
|
||||||
|
public static boolean skipFullyQuietly(ExtractorInput input, int length) throws IOException {
|
||||||
|
try {
|
||||||
|
input.skipFully(length);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peeks data from {@code input}, respecting {@code allowEndOfInput}. Returns true if the peek is
|
||||||
|
* successful.
|
||||||
|
*
|
||||||
|
* <p>If {@code allowEndOfInput=false} then encountering the end of the input (whether before or
|
||||||
|
* after reading some data) will throw {@link EOFException}.
|
||||||
|
*
|
||||||
|
* <p>If {@code allowEndOfInput=true} then encountering the end of the input (even after reading
|
||||||
|
* some data) will return {@code false}.
|
||||||
|
*
|
||||||
|
* <p>This is slightly different to the behaviour of {@link ExtractorInput#peekFully(byte[], int,
|
||||||
|
* int, boolean)}, where {@code allowEndOfInput=true} only returns false (and suppresses the
|
||||||
|
* exception) if the end of the input is reached before reading any data.
|
||||||
|
*/
|
||||||
|
public static boolean peekFullyQuietly(
|
||||||
|
ExtractorInput input, byte[] output, int offset, int length, boolean allowEndOfInput)
|
||||||
|
throws IOException {
|
||||||
|
try {
|
||||||
|
return input.peekFully(output, offset, length, /* allowEndOfInput= */ allowEndOfInput);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
if (allowEndOfInput) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ExtractorUtil() {}
|
private ExtractorUtil() {}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.extractor.ogg;
|
package com.google.android.exoplayer2.extractor.ogg;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.extractor.ExtractorUtil.skipFullyQuietly;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
@ -229,13 +231,21 @@ import java.io.IOException;
|
|||||||
if (!pageHeader.skipToNextPage(input)) {
|
if (!pageHeader.skipToNextPage(input)) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
do {
|
pageHeader.populate(input, /* quiet= */ false);
|
||||||
pageHeader.populate(input, /* quiet= */ false);
|
input.skipFully(pageHeader.headerSize + pageHeader.bodySize);
|
||||||
input.skipFully(pageHeader.headerSize + pageHeader.bodySize);
|
long granulePosition = pageHeader.granulePosition;
|
||||||
} while ((pageHeader.type & 0x04) != 0x04
|
while ((pageHeader.type & 0x04) != 0x04
|
||||||
&& pageHeader.skipToNextPage(input)
|
&& pageHeader.skipToNextPage(input)
|
||||||
&& input.getPosition() < payloadEndPosition);
|
&& input.getPosition() < payloadEndPosition) {
|
||||||
return pageHeader.granulePosition;
|
boolean hasPopulated = pageHeader.populate(input, /* quiet= */ true);
|
||||||
|
if (!hasPopulated || !skipFullyQuietly(input, pageHeader.headerSize + pageHeader.bodySize)) {
|
||||||
|
// The input file contains a partial page at the end. Ignore it and return the granule
|
||||||
|
// position of the last complete page.
|
||||||
|
return granulePosition;
|
||||||
|
}
|
||||||
|
granulePosition = pageHeader.granulePosition;
|
||||||
|
}
|
||||||
|
return granulePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class OggSeekMap implements SeekMap {
|
private final class OggSeekMap implements SeekMap {
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.extractor.ogg;
|
package com.google.android.exoplayer2.extractor.ogg;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.extractor.ExtractorUtil.readFullyQuietly;
|
||||||
|
import static com.google.android.exoplayer2.extractor.ExtractorUtil.skipFullyQuietly;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
@ -51,7 +53,7 @@ import java.util.Arrays;
|
|||||||
*
|
*
|
||||||
* @param input The {@link ExtractorInput} to read data from.
|
* @param input The {@link ExtractorInput} to read data from.
|
||||||
* @return {@code true} if the read was successful. The read fails if the end of the input is
|
* @return {@code true} if the read was successful. The read fails if the end of the input is
|
||||||
* encountered without reading data.
|
* encountered without reading the whole packet.
|
||||||
* @throws IOException If reading from the input fails.
|
* @throws IOException If reading from the input fails.
|
||||||
*/
|
*/
|
||||||
public boolean populate(ExtractorInput input) throws IOException {
|
public boolean populate(ExtractorInput input) throws IOException {
|
||||||
@ -76,7 +78,9 @@ import java.util.Arrays;
|
|||||||
bytesToSkip += calculatePacketSize(segmentIndex);
|
bytesToSkip += calculatePacketSize(segmentIndex);
|
||||||
segmentIndex += segmentCount;
|
segmentIndex += segmentCount;
|
||||||
}
|
}
|
||||||
input.skipFully(bytesToSkip);
|
if (!skipFullyQuietly(input, bytesToSkip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
currentSegmentIndex = segmentIndex;
|
currentSegmentIndex = segmentIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +88,9 @@ import java.util.Arrays;
|
|||||||
int segmentIndex = currentSegmentIndex + segmentCount;
|
int segmentIndex = currentSegmentIndex + segmentCount;
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
packetArray.ensureCapacity(packetArray.limit() + size);
|
packetArray.ensureCapacity(packetArray.limit() + size);
|
||||||
input.readFully(packetArray.getData(), packetArray.limit(), size);
|
if (!readFullyQuietly(input, packetArray.getData(), packetArray.limit(), size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
packetArray.setLimit(packetArray.limit() + size);
|
packetArray.setLimit(packetArray.limit() + size);
|
||||||
populated = pageHeader.laces[segmentIndex - 1] != 255;
|
populated = pageHeader.laces[segmentIndex - 1] != 255;
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.extractor.ogg;
|
package com.google.android.exoplayer2.extractor.ogg;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.extractor.ExtractorUtil.peekFullyQuietly;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ParserException;
|
import com.google.android.exoplayer2.ParserException;
|
||||||
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/** Data object to store header information. */
|
/** Data object to store header information. */
|
||||||
@ -102,7 +103,8 @@ import java.io.IOException;
|
|||||||
Assertions.checkArgument(input.getPosition() == input.getPeekPosition());
|
Assertions.checkArgument(input.getPosition() == input.getPeekPosition());
|
||||||
scratch.reset(/* limit= */ CAPTURE_PATTERN_SIZE);
|
scratch.reset(/* limit= */ CAPTURE_PATTERN_SIZE);
|
||||||
while ((limit == C.POSITION_UNSET || input.getPosition() + CAPTURE_PATTERN_SIZE < limit)
|
while ((limit == C.POSITION_UNSET || input.getPosition() + CAPTURE_PATTERN_SIZE < limit)
|
||||||
&& peekSafely(input, scratch.getData(), 0, CAPTURE_PATTERN_SIZE, /* quiet= */ true)) {
|
&& peekFullyQuietly(
|
||||||
|
input, scratch.getData(), 0, CAPTURE_PATTERN_SIZE, /* allowEndOfInput= */ true)) {
|
||||||
scratch.setPosition(0);
|
scratch.setPosition(0);
|
||||||
if (scratch.readUnsignedInt() == CAPTURE_PATTERN) {
|
if (scratch.readUnsignedInt() == CAPTURE_PATTERN) {
|
||||||
input.resetPeekPosition();
|
input.resetPeekPosition();
|
||||||
@ -123,14 +125,13 @@ import java.io.IOException;
|
|||||||
* @param input The {@link ExtractorInput} to read from.
|
* @param input The {@link ExtractorInput} to read from.
|
||||||
* @param quiet Whether to return {@code false} rather than throwing an exception if the header
|
* @param quiet Whether to return {@code false} rather than throwing an exception if the header
|
||||||
* cannot be populated.
|
* cannot be populated.
|
||||||
* @return Whether the read was successful. The read fails if the end of the input is encountered
|
* @return Whether the header was entirely populated.
|
||||||
* without reading data.
|
|
||||||
* @throws IOException If reading data fails or the stream is invalid.
|
* @throws IOException If reading data fails or the stream is invalid.
|
||||||
*/
|
*/
|
||||||
public boolean populate(ExtractorInput input, boolean quiet) throws IOException {
|
public boolean populate(ExtractorInput input, boolean quiet) throws IOException {
|
||||||
reset();
|
reset();
|
||||||
scratch.reset(/* limit= */ EMPTY_PAGE_HEADER_SIZE);
|
scratch.reset(/* limit= */ EMPTY_PAGE_HEADER_SIZE);
|
||||||
if (!peekSafely(input, scratch.getData(), 0, EMPTY_PAGE_HEADER_SIZE, quiet)
|
if (!peekFullyQuietly(input, scratch.getData(), 0, EMPTY_PAGE_HEADER_SIZE, quiet)
|
||||||
|| scratch.readUnsignedInt() != CAPTURE_PATTERN) {
|
|| scratch.readUnsignedInt() != CAPTURE_PATTERN) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -155,7 +156,9 @@ import java.io.IOException;
|
|||||||
|
|
||||||
// calculate total size of header including laces
|
// calculate total size of header including laces
|
||||||
scratch.reset(/* limit= */ pageSegmentCount);
|
scratch.reset(/* limit= */ pageSegmentCount);
|
||||||
input.peekFully(scratch.getData(), 0, pageSegmentCount);
|
if (!peekFullyQuietly(input, scratch.getData(), 0, pageSegmentCount, quiet)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for (int i = 0; i < pageSegmentCount; i++) {
|
for (int i = 0; i < pageSegmentCount; i++) {
|
||||||
laces[i] = scratch.readUnsignedByte();
|
laces[i] = scratch.readUnsignedByte();
|
||||||
bodySize += laces[i];
|
bodySize += laces[i];
|
||||||
@ -163,31 +166,4 @@ import java.io.IOException;
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Peek data from {@code input}, respecting {@code quiet}. Return true if the peek is successful.
|
|
||||||
*
|
|
||||||
* <p>If {@code quiet=false} then encountering the end of the input (whether before or after
|
|
||||||
* reading some data) will throw {@link EOFException}.
|
|
||||||
*
|
|
||||||
* <p>If {@code quiet=true} then encountering the end of the input (even after reading some data)
|
|
||||||
* will return {@code false}.
|
|
||||||
*
|
|
||||||
* <p>This is slightly different to the behaviour of {@link ExtractorInput#peekFully(byte[], int,
|
|
||||||
* int, boolean)}, where {@code allowEndOfInput=true} only returns false (and suppresses the
|
|
||||||
* exception) if the end of the input is reached before reading any data.
|
|
||||||
*/
|
|
||||||
private static boolean peekSafely(
|
|
||||||
ExtractorInput input, byte[] output, int offset, int length, boolean quiet)
|
|
||||||
throws IOException {
|
|
||||||
try {
|
|
||||||
return input.peekFully(output, offset, length, /* allowEndOfInput= */ quiet);
|
|
||||||
} catch (EOFException e) {
|
|
||||||
if (quiet) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,14 @@
|
|||||||
package com.google.android.exoplayer2.extractor;
|
package com.google.android.exoplayer2.extractor;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -36,12 +38,12 @@ public class ExtractorUtilTest {
|
|||||||
private static final byte[] TEST_DATA = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8};
|
private static final byte[] TEST_DATA = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void peekToLengthEndNotReached() throws Exception {
|
public void peekToLength_endNotReached() throws Exception {
|
||||||
FakeDataSource testDataSource = new FakeDataSource();
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
testDataSource
|
testDataSource
|
||||||
.getDataSet()
|
.getDataSet()
|
||||||
.newDefaultData()
|
.newDefaultData()
|
||||||
.appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 3))
|
.appendReadData(Arrays.copyOf(TEST_DATA, 3))
|
||||||
.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6))
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6))
|
||||||
.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9));
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9));
|
||||||
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
@ -59,12 +61,12 @@ public class ExtractorUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void peekToLengthEndReached() throws Exception {
|
public void peekToLength_endReached() throws Exception {
|
||||||
FakeDataSource testDataSource = new FakeDataSource();
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
testDataSource
|
testDataSource
|
||||||
.getDataSet()
|
.getDataSet()
|
||||||
.newDefaultData()
|
.newDefaultData()
|
||||||
.appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 3))
|
.appendReadData(Arrays.copyOf(TEST_DATA, 3))
|
||||||
.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6))
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6))
|
||||||
.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9));
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9));
|
||||||
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
@ -79,4 +81,135 @@ public class ExtractorUtilTest {
|
|||||||
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
||||||
assertThat(target).isEqualTo(TEST_DATA);
|
assertThat(target).isEqualTo(TEST_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readFullyQuietly_endNotReached_isTrueAndReadsData() throws Exception {
|
||||||
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
|
testDataSource
|
||||||
|
.getDataSet()
|
||||||
|
.newDefaultData()
|
||||||
|
.appendReadData(Arrays.copyOf(TEST_DATA, 3))
|
||||||
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6))
|
||||||
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9));
|
||||||
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
|
ExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
int offset = 2;
|
||||||
|
int length = 4;
|
||||||
|
|
||||||
|
boolean hasRead = ExtractorUtil.readFullyQuietly(input, target, offset, length);
|
||||||
|
|
||||||
|
assertThat(hasRead).isTrue();
|
||||||
|
assertThat(input.getPosition()).isEqualTo(length);
|
||||||
|
assertThat(Arrays.copyOfRange(target, offset, offset + length))
|
||||||
|
.isEqualTo(Arrays.copyOf(TEST_DATA, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readFullyQuietly_endReached_isFalse() throws Exception {
|
||||||
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
|
testDataSource.getDataSet().newDefaultData().appendReadData(Arrays.copyOf(TEST_DATA, 3));
|
||||||
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
|
ExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
int offset = 0;
|
||||||
|
int length = TEST_DATA.length + 1;
|
||||||
|
|
||||||
|
boolean hasRead = ExtractorUtil.readFullyQuietly(input, target, offset, length);
|
||||||
|
|
||||||
|
assertThat(hasRead).isFalse();
|
||||||
|
assertThat(input.getPosition()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipFullyQuietly_endNotReached_isTrueAndSkipsData() throws Exception {
|
||||||
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
|
testDataSource
|
||||||
|
.getDataSet()
|
||||||
|
.newDefaultData()
|
||||||
|
.appendReadData(Arrays.copyOf(TEST_DATA, 3))
|
||||||
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6))
|
||||||
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9));
|
||||||
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
|
ExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
||||||
|
int length = 4;
|
||||||
|
|
||||||
|
boolean hasRead = ExtractorUtil.skipFullyQuietly(input, length);
|
||||||
|
|
||||||
|
assertThat(hasRead).isTrue();
|
||||||
|
assertThat(input.getPosition()).isEqualTo(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipFullyQuietly_endReached_isFalse() throws Exception {
|
||||||
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
|
testDataSource.getDataSet().newDefaultData().appendReadData(Arrays.copyOf(TEST_DATA, 3));
|
||||||
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
|
ExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
||||||
|
int length = TEST_DATA.length + 1;
|
||||||
|
|
||||||
|
boolean hasRead = ExtractorUtil.skipFullyQuietly(input, length);
|
||||||
|
|
||||||
|
assertThat(hasRead).isFalse();
|
||||||
|
assertThat(input.getPosition()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void peekFullyQuietly_endNotReached_isTrueAndPeeksData() throws Exception {
|
||||||
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
|
testDataSource
|
||||||
|
.getDataSet()
|
||||||
|
.newDefaultData()
|
||||||
|
.appendReadData(Arrays.copyOf(TEST_DATA, 3))
|
||||||
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6))
|
||||||
|
.appendReadData(Arrays.copyOfRange(TEST_DATA, 6, 9));
|
||||||
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
|
ExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
int offset = 2;
|
||||||
|
int length = 4;
|
||||||
|
|
||||||
|
boolean hasRead =
|
||||||
|
ExtractorUtil.peekFullyQuietly(input, target, offset, length, /* allowEndOfInput= */ false);
|
||||||
|
|
||||||
|
assertThat(hasRead).isTrue();
|
||||||
|
assertThat(input.getPeekPosition()).isEqualTo(length);
|
||||||
|
assertThat(Arrays.copyOfRange(target, offset, offset + length))
|
||||||
|
.isEqualTo(Arrays.copyOf(TEST_DATA, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void peekFullyQuietly_endReachedWithEndOfInputAllowed_isFalse() throws Exception {
|
||||||
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
|
testDataSource.getDataSet().newDefaultData().appendReadData(Arrays.copyOf(TEST_DATA, 3));
|
||||||
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
|
ExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
int offset = 0;
|
||||||
|
int length = TEST_DATA.length + 1;
|
||||||
|
|
||||||
|
boolean hasRead =
|
||||||
|
ExtractorUtil.peekFullyQuietly(input, target, offset, length, /* allowEndOfInput= */ true);
|
||||||
|
|
||||||
|
assertThat(hasRead).isFalse();
|
||||||
|
assertThat(input.getPeekPosition()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void peekFullyQuietly_endReachedWithoutEndOfInputAllowed_throws() throws Exception {
|
||||||
|
FakeDataSource testDataSource = new FakeDataSource();
|
||||||
|
testDataSource.getDataSet().newDefaultData().appendReadData(Arrays.copyOf(TEST_DATA, 3));
|
||||||
|
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
|
||||||
|
ExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
int offset = 0;
|
||||||
|
int length = TEST_DATA.length + 1;
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
EOFException.class,
|
||||||
|
() ->
|
||||||
|
ExtractorUtil.peekFullyQuietly(
|
||||||
|
input, target, offset, length, /* allowEndOfInput= */ false));
|
||||||
|
assertThat(input.getPeekPosition()).isEqualTo(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user