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:
parent
009b454b69
commit
f16b8baf75
101
README.md
101
README.md
@ -1,10 +1,14 @@
|
||||
# ExoPlayer #
|
||||
# ExoPlayer Readme #
|
||||
|
||||
ExoPlayer is an application level media player for Android. It provides an alternative to Android’s
|
||||
MediaPlayer API for playing audio and video both locally and over the Internet. ExoPlayer supports
|
||||
features not currently supported by Android’s 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 Android’s MediaPlayer API for playing audio and video both
|
||||
locally and over the Internet. ExoPlayer supports features not currently
|
||||
supported by Android’s 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.
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user