ExtractorInput new skip methods to make skip methods analogous to the three read methods.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=110155932
This commit is contained in:
eguven 2015-12-14 08:15:23 -08:00 committed by Oliver Woodman
parent 009b454b69
commit f16b8baf75
4 changed files with 267 additions and 155 deletions

101
README.md
View File

@ -1,10 +1,14 @@
# ExoPlayer #
# ExoPlayer Readme #
ExoPlayer is an application level media player for Android. It provides an alternative to Androids
MediaPlayer API for playing audio and video both locally and over the Internet. ExoPlayer supports
features not currently supported by Androids MediaPlayer API, including DASH and SmoothStreaming
adaptive playbacks. Unlike the MediaPlayer API, ExoPlayer is easy to customize and extend, and can
be updated through Play Store application updates.
## Description ##
ExoPlayer is an application level media player for Android. It provides an
alternative to Androids MediaPlayer API for playing audio and video both
locally and over the Internet. ExoPlayer supports features not currently
supported by Androids MediaPlayer API, including DASH and SmoothStreaming
adaptive playbacks. Unlike the MediaPlayer API, ExoPlayer is easy to
customize and extend, and can be updated through Play Store application
updates.
## News ##
@ -14,7 +18,8 @@ Read news, hints and tips on the [news][] page.
## Documentation ##
* The [developer guide][] provides a wealth of information to help you get started.
* The [developer guide][] provides a wealth of information to help you get
started.
* The [class reference][] documents the ExoPlayer library classes.
* The [release notes][] document the major changes in each release.
@ -22,31 +27,37 @@ Read news, hints and tips on the [news][] page.
[class reference]: https://google.github.io/ExoPlayer/doc/reference
[release notes]: https://github.com/google/ExoPlayer/blob/dev/RELEASENOTES.md
## Using ExoPlayer ##
## Project branches ##
#### Via jCenter ####
* The [master][] branch holds the most recent minor release.
* Most development work happens on the [dev][] branch.
* Additional development branches may be established for major features.
The easiest way to get started using ExoPlayer is by including the following in your project's
`build.gradle` file:
[master]: https://github.com/google/ExoPlayer/tree/master
[dev]: https://github.com/google/ExoPlayer/tree/dev
## Using Eclipse ##
The repository includes Eclipse projects for both the ExoPlayer library and its
accompanying demo application. To get started:
1. Install Eclipse and setup the [Android SDK][].
1. Open Eclipse and navigate to File->Import->General->Existing Projects into
Workspace.
1. Select the root directory of the repository.
1. Import the ExoPlayerDemo and ExoPlayerLib projects.
[Android SDK]: http://developer.android.com/sdk/index.html
## Using Gradle ##
ExoPlayer can also be built using Gradle. You can include it as a dependent project and build from source:
```
gradle
compile 'com.google.android.exoplayer:exoplayer:rX.X.X'
```
where `rX.X.X` is the your preferred version. For the latest version, see the project's
[Releases][]. For more details, see the project on [Bintray][].
[Releases]: https://github.com/google/ExoPlayer/releases
[Bintray]: https://bintray.com/google/exoplayer/exoplayer/view
#### As source ####
ExoPlayer can also be built from source using Gradle. You can include it as a dependent project like
so:
```
gradle
// settings.gradle
include ':app', ':..:ExoPlayer:library'
@ -56,40 +67,18 @@ dependencies {
}
```
#### As a jar ####
If you want to use ExoPlayer as a jar, run:
```
sh
./gradlew jarRelease
```
and copy `library.jar` to the libs folder of your new project.
and copy library.jar to the libs-folder of your new project.
## Developing ExoPlayer ##
The project is also available on [jCenter](https://bintray.com/google/exoplayer/exoplayer/view):
#### Project branches ####
```
compile 'com.google.android.exoplayer:exoplayer:rX.X.X'
```
* The [`master`][master] branch holds the most recent minor release.
* Most development work happens on the [`dev`][dev] branch.
* Additional development branches may be established for major features.
[master]: https://github.com/google/ExoPlayer/tree/master
[dev]: https://github.com/google/ExoPlayer/tree/dev
#### Using Android Studio ####
To develop ExoPlayer using Android Studio, simply open the ExoPlayer project in the root directory
of the repository.
#### Using Eclipse ####
To develop ExoPlayer using Eclipse:
1. Install Eclipse and setup the [Android SDK][].
1. Open Eclipse and navigate to File->Import->General->Existing Projects into Workspace.
1. Select the root directory of the repository.
1. Import the projects.
[Android SDK]: http://developer.android.com/sdk/index.html
Where `rX.X.X` should be replaced with the desired version.

View File

@ -157,11 +157,39 @@ public class DefaultExtractorInputTest extends TestCase {
assertEquals(0, input.getPosition());
}
public void testReadFullyHalfPeeked() throws IOException, InterruptedException {
DefaultExtractorInput input = createDefaultExtractorInput();
byte[] target = new byte[TEST_DATA.length];
input.advancePeekPosition(4);
input.readFully(target, 0, TEST_DATA.length);
// Check the read data is correct.
assertTrue(Arrays.equals(TEST_DATA, target));
assertEquals(TEST_DATA.length, input.getPosition());
}
public void testSkip() throws IOException, InterruptedException {
FakeDataSource testDataSource = buildDataSource();
DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNBOUNDED);
// We expect to perform three skips of three bytes, as setup in buildTestDataSource.
for (int i = 0; i < 3; i++) {
assertEquals(3, input.skip(TEST_DATA.length));
}
// Check we're now indicated that the end of input is reached.
int expectedEndOfInput = input.skip(TEST_DATA.length);
assertEquals(-1, expectedEndOfInput);
}
public void testSkipFullyOnce() throws IOException, InterruptedException {
// Skip TEST_DATA.
DefaultExtractorInput input = createDefaultExtractorInput();
input.skipFully(TEST_DATA.length);
assertEquals(TEST_DATA.length, input.getPosition());
// Check that we see end of input if we skip again with allowEndOfInput set.
boolean result = input.skipFully(1, true);
assertFalse(result);
// Check that we fail with EOFException we skip again.
try {
input.skipFully(1);
@ -180,6 +208,20 @@ public class DefaultExtractorInputTest extends TestCase {
assertEquals(5 + 4, input.getPosition());
}
public void testSkipFullyTwicePeeked() throws IOException, InterruptedException {
// Skip TEST_DATA.
DefaultExtractorInput input = createDefaultExtractorInput();
input.advancePeekPosition(TEST_DATA.length);
int halfLength = TEST_DATA.length / 2;
input.skipFully(halfLength);
assertEquals(halfLength, input.getPosition());
input.skipFully(TEST_DATA.length - halfLength);
assertEquals(TEST_DATA.length, input.getPosition());
}
public void testSkipFullyTooMuch() throws IOException, InterruptedException {
// Skip more than TEST_DATA. Should fail with an EOFException. Position should not update.
DefaultExtractorInput input = createDefaultExtractorInput();
@ -190,6 +232,17 @@ public class DefaultExtractorInputTest extends TestCase {
// Expected.
}
assertEquals(0, input.getPosition());
// Skip more than TEST_DATA with allowEndOfInput set. Should fail with an EOFException because
// the end of input isn't encountered immediately. Position should not update.
input = createDefaultExtractorInput();
try {
input.skipFully(TEST_DATA.length + 1, true);
fail();
} catch (EOFException e) {
// Expected.
}
assertEquals(0, input.getPosition());
}
public void testSkipFullyWithFailingDataSource() throws IOException, InterruptedException {
@ -225,6 +278,29 @@ public class DefaultExtractorInputTest extends TestCase {
}
}
public void testPeekFully() throws IOException, InterruptedException {
DefaultExtractorInput input = createDefaultExtractorInput();
byte[] target = new byte[TEST_DATA.length];
input.peekFully(target, 0, TEST_DATA.length);
// Check that we read the whole of TEST_DATA.
assertTrue(Arrays.equals(TEST_DATA, target));
assertEquals(0, input.getPosition());
// Check that we can read again from the buffer
byte[] target2 = new byte[TEST_DATA.length];
input.readFully(target2, 0, TEST_DATA.length);
assertTrue(Arrays.equals(TEST_DATA, target2));
// Check that we fail with EOFException if we peek again
try {
input.peekFully(target, 0, 1);
fail();
} catch (EOFException e) {
// Expected.
}
}
private static FakeDataSource buildDataSource() throws IOException {
FakeDataSource.Builder builder = new FakeDataSource.Builder();
builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 3));

View File

@ -51,58 +51,13 @@ public final class DefaultExtractorInput implements ExtractorInput {
@Override
public int read(byte[] target, int offset, int length) throws IOException, InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
int totalBytesRead = 0;
if (peekBufferLength > 0) {
int peekBytes = Math.min(peekBufferLength, length);
System.arraycopy(peekBuffer, 0, target, offset, peekBytes);
updatePeekBuffer(peekBytes);
totalBytesRead += peekBytes;
}
if (totalBytesRead != length) {
int readBytes = dataSource.read(target, offset + totalBytesRead, length - totalBytesRead);
if (readBytes == C.RESULT_END_OF_INPUT) {
if (totalBytesRead == 0) {
return C.RESULT_END_OF_INPUT;
}
} else {
totalBytesRead += readBytes;
}
}
position += totalBytesRead;
return totalBytesRead;
return internalRead(target, offset, length, true, false, false);
}
@Override
public boolean readFully(byte[] target, int offset, int length, boolean allowEndOfInput)
throws IOException, InterruptedException {
int peekBytes = Math.min(peekBufferLength, length);
System.arraycopy(peekBuffer, 0, target, offset, peekBytes);
offset += peekBytes;
int remaining = length - peekBytes;
while (remaining > 0) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
int bytesRead = dataSource.read(target, offset, remaining);
if (bytesRead == C.RESULT_END_OF_INPUT) {
if (allowEndOfInput && remaining == length) {
return false;
}
throw new EOFException();
}
offset += bytesRead;
remaining -= bytesRead;
}
updatePeekBuffer(peekBytes);
position += length;
return true;
return internalReadFully(target, offset, length, allowEndOfInput, false);
}
@Override
@ -111,71 +66,39 @@ public final class DefaultExtractorInput implements ExtractorInput {
readFully(target, offset, length, false);
}
@Override
public int skip(int length) throws IOException, InterruptedException {
return internalRead(null, 0, Math.min(SCRATCH_SPACE.length + peekBufferLength, length), true,
true, false);
}
@Override
public boolean skipFully(final int length, boolean allowEndOfInput)
throws IOException, InterruptedException {
return internalReadFully(null, 0, length, allowEndOfInput, true);
}
@Override
public void skipFully(int length) throws IOException, InterruptedException {
int peekBytes = Math.min(peekBufferLength, length);
int remaining = length - peekBytes;
while (remaining > 0) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
int bytesRead = dataSource.read(SCRATCH_SPACE, 0, Math.min(SCRATCH_SPACE.length, remaining));
if (bytesRead == C.RESULT_END_OF_INPUT) {
throw new EOFException();
}
remaining -= bytesRead;
}
updatePeekBuffer(peekBytes);
position += length;
skipFully(length, false);
}
@Override
public void peekFully(byte[] target, int offset, int length)
throws IOException, InterruptedException {
ensureSpaceForPeek(length);
int peekBytes = Math.min(peekBufferLength - peekBufferPosition, length);
System.arraycopy(peekBuffer, peekBufferPosition, target, offset, peekBytes);
offset += peekBytes;
int fillBytes = length - peekBytes;
int remaining = fillBytes;
int writePosition = peekBufferLength;
while (remaining > 0) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
int bytesRead = dataSource.read(peekBuffer, writePosition, remaining);
if (bytesRead == C.RESULT_END_OF_INPUT) {
throw new EOFException();
}
System.arraycopy(peekBuffer, writePosition, target, offset, bytesRead);
remaining -= bytesRead;
writePosition += bytesRead;
offset += bytesRead;
}
peekBufferPosition += length;
peekBufferLength += fillBytes;
advancePeekPosition(length);
System.arraycopy(peekBuffer, peekBufferPosition - length, target, offset, length);
}
@Override
public void advancePeekPosition(int length) throws IOException, InterruptedException {
ensureSpaceForPeek(length);
int peekBytes = Math.min(peekBufferLength - peekBufferPosition, length);
int fillBytes = length - peekBytes;
int remaining = fillBytes;
int writePosition = peekBufferLength;
while (remaining > 0) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
int bytesRead = dataSource.read(peekBuffer, writePosition, remaining);
if (bytesRead == C.RESULT_END_OF_INPUT) {
throw new EOFException();
}
remaining -= bytesRead;
writePosition += bytesRead;
}
peekBufferPosition += length;
peekBufferLength += fillBytes;
if (peekBufferPosition > peekBufferLength) {
readFromDataSource(peekBuffer, peekBufferLength, peekBufferPosition - peekBufferLength,
false, false, 0, true);
peekBufferLength = peekBufferPosition;
}
}
@Override
@ -215,4 +138,101 @@ public final class DefaultExtractorInput implements ExtractorInput {
System.arraycopy(peekBuffer, bytesConsumed, peekBuffer, 0, peekBufferLength);
}
/**
* Internal read method.
*
* @see #internalRead(byte[], int, int, boolean, boolean, boolean)
*/
private boolean internalReadFully(byte[] target, int offset, int length, boolean allowEndOfInput,
boolean skip) throws InterruptedException, IOException {
return internalRead(target, offset, length, allowEndOfInput, skip, true)
!= C.RESULT_END_OF_INPUT;
}
/**
* Internal read method.
*
* @see #readFromPeekBuffer(byte[], int, int, boolean)
* @see #readFromDataSource(byte[], int, int, boolean, boolean, int, boolean)
*/
private int internalRead(byte[] target, int offset, int length, boolean allowEndOfInput,
boolean skip, boolean readFully) throws InterruptedException, IOException {
int totalBytesRead = readFromPeekBuffer(target, offset, length, skip);
totalBytesRead = readFromDataSource(target, offset, length, allowEndOfInput, skip,
totalBytesRead, readFully);
if (totalBytesRead != C.RESULT_END_OF_INPUT) {
position += totalBytesRead;
}
return totalBytesRead;
}
/**
* Reads from the peek buffer
*
* @param target A target array into which data should be written.
* @param offset The offset into the target array at which to write.
* @param length The maximum number of bytes to read from the input.
* @param skip If true, instead of reading skip the data
* @return The number of bytes read
*/
private int readFromPeekBuffer(byte[] target, int offset, int length, boolean skip) {
if (peekBufferLength == 0) {
return 0;
}
int peekBytes = Math.min(peekBufferLength, length);
if (!skip) {
System.arraycopy(peekBuffer, 0, target, offset, peekBytes);
}
updatePeekBuffer(peekBytes);
return peekBytes;
}
/**
* Reads from the data source
*
* @param target A target array into which data should be written.
* @param offset The offset into the target array at which to write.
* @param length The maximum number of bytes to read from the input.
* @param returnEOIifNoReadBytes If true on encountering the end of the input having read no data
* should result in {@link C#RESULT_END_OF_INPUT} being returned.
* @param skip If true, instead of reading skip the data
* @param totalBytesRead Number of bytes read until now for the external request
* @param readFully If true reads the requested {@code length} in full.
* @return The number of bytes read, or {@link C#RESULT_END_OF_INPUT} if the input has ended.
* @throws EOFException If the end of input was encountered.
* @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread is interrupted.
*/
private int readFromDataSource(byte[] target, int offset, int length,
boolean returnEOIifNoReadBytes, boolean skip, int totalBytesRead, boolean readFully)
throws InterruptedException, IOException {
while (totalBytesRead < length) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
int bytesRead = !skip
? dataSource.read(target, offset + totalBytesRead, length - totalBytesRead)
: dataSource.read(SCRATCH_SPACE, 0,
Math.min(SCRATCH_SPACE.length, length - totalBytesRead));
if (bytesRead == C.RESULT_END_OF_INPUT) {
if (returnEOIifNoReadBytes && totalBytesRead == 0) {
return C.RESULT_END_OF_INPUT;
}
if (readFully) {
throw new EOFException();
}
} else {
totalBytesRead += bytesRead;
}
if (!readFully) {
break;
}
}
return totalBytesRead;
}
}

View File

@ -79,6 +79,33 @@ public interface ExtractorInput {
*/
void readFully(byte[] target, int offset, int length) throws IOException, InterruptedException;
/**
* Like {@link #read(byte[], int, int)}, except the data is skipped instead of read.
*
* @param length The maximum number of bytes to skip from the input.
* @return The number of bytes skipped, or {@link C#RESULT_END_OF_INPUT} if the input has ended.
* @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread has been interrupted.
*/
int skip(int length) throws IOException, InterruptedException;
/**
* Like {@link #readFully(byte[], int, int, boolean)}, except the data is skipped instead of read.
*
* @param length The number of bytes to skip from the input.
* @param allowEndOfInput True if encountering the end of the input having skipped no data is
* allowed, and should result in {@code false} being returned. False if it should be
* considered an error, causing an {@link EOFException} to be thrown.
* @return True if the skip was successful. False if the end of the input was encountered having
* skipped no data.
* @throws EOFException If the end of input was encountered having partially satisfied the skip
* (i.e. having skipped at least one byte, but fewer than {@code length}), or if no bytes were
* skipped and {@code allowEndOfInput} is false.
* @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread has been interrupted.
*/
boolean skipFully(int length, boolean allowEndOfInput) throws IOException, InterruptedException;
/**
* Like {@link #readFully(byte[], int, int)}, except the data is skipped instead of read.
* <p>