Cleanup - Make RollingSampleBuffer append methods more consistent.

This commit is contained in:
Oliver Woodman 2015-07-30 17:13:24 +01:00
parent 0d42032ada
commit 5f51a4ff83
12 changed files with 101 additions and 64 deletions

View File

@ -57,8 +57,8 @@ public final class FakeTrackOutput implements TrackOutput {
} }
@Override @Override
public int sampleData(ExtractorInput input, int length) throws IOException, public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
InterruptedException { throws IOException, InterruptedException {
byte[] newData = new byte[length]; byte[] newData = new byte[length];
input.readFully(newData, 0, length); input.readFully(newData, 0, length);
sampleData = TestUtil.joinByteArrays(sampleData, newData); sampleData = TestUtil.joinByteArrays(sampleData, newData);

View File

@ -127,8 +127,9 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
} }
@Override @Override
public int sampleData(ExtractorInput input, int length) throws IOException, InterruptedException { public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
return output.sampleData(input, length); throws IOException, InterruptedException {
return output.sampleData(input, length, allowEndOfInput);
} }
@Override @Override

View File

@ -106,8 +106,9 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
} }
@Override @Override
public int sampleData(ExtractorInput input, int length) throws IOException, InterruptedException { public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
return getOutput().sampleData(input, length); throws IOException, InterruptedException {
return getOutput().sampleData(input, length, allowEndOfInput);
} }
@Override @Override

View File

@ -138,7 +138,8 @@ public final class InitializationChunk extends Chunk implements SingleTrackOutpu
} }
@Override @Override
public int sampleData(ExtractorInput input, int length) throws IOException, InterruptedException { public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException {
throw new IllegalStateException("Unexpected sample data in initialization chunk"); throw new IllegalStateException("Unexpected sample data in initialization chunk");
} }

View File

@ -109,10 +109,8 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
// Load the sample data. // Load the sample data.
int result = 0; int result = 0;
while (result != C.RESULT_END_OF_INPUT) { while (result != C.RESULT_END_OF_INPUT) {
result = getOutput().sampleData(dataSource, Integer.MAX_VALUE); bytesLoaded += result;
if (result != C.RESULT_END_OF_INPUT) { result = getOutput().sampleData(dataSource, Integer.MAX_VALUE, true);
bytesLoaded += result;
}
} }
int sampleSize = bytesLoaded; int sampleSize = bytesLoaded;
if (headerData != null) { if (headerData != null) {

View File

@ -15,12 +15,14 @@
*/ */
package com.google.android.exoplayer.extractor; package com.google.android.exoplayer.extractor;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.upstream.Allocator; import com.google.android.exoplayer.upstream.Allocator;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.ParsableByteArray;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
/** /**
@ -225,8 +227,20 @@ public class DefaultTrackOutput implements TrackOutput {
// Called by the loading thread. // Called by the loading thread.
public int sampleData(DataSource dataSource, int length) throws IOException { /**
return rollingBuffer.appendData(dataSource, length); * Invoked to write sample data to the output.
*
* @param dataSource A {@link DataSource} from which to read the sample data.
* @param length The maximum length to read from the input.
* @param allowEndOfInput True if encountering the end of the input having read no data is
* allowed, and should result in {@link C#RESULT_END_OF_INPUT} being returned. False if it
* should be considered an error, causing an {@link EOFException} to be thrown.
* @return The number of bytes appended.
* @throws IOException If an error occurred reading from the input.
*/
public int sampleData(DataSource dataSource, int length, boolean allowEndOfInput)
throws IOException {
return rollingBuffer.appendData(dataSource, length, allowEndOfInput);
} }
// TrackOutput implementation. Called by the loading thread. // TrackOutput implementation. Called by the loading thread.
@ -237,8 +251,9 @@ public class DefaultTrackOutput implements TrackOutput {
} }
@Override @Override
public int sampleData(ExtractorInput input, int length) throws IOException, InterruptedException { public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
return rollingBuffer.appendData(input, length); throws IOException, InterruptedException {
return rollingBuffer.appendData(input, length, allowEndOfInput);
} }
@Override @Override

View File

@ -23,6 +23,7 @@ import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.ParsableByteArray;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
@ -350,26 +351,27 @@ import java.util.concurrent.LinkedBlockingDeque;
* Appends data to the rolling buffer. * Appends data to the rolling buffer.
* *
* @param dataSource The source from which to read. * @param dataSource The source from which to read.
* @param length The maximum length of the read, or {@link C#LENGTH_UNBOUNDED} if the caller does * @param length The maximum length of the read.
* not wish to impose a limit. * @param allowEndOfInput True if encountering the end of the input having appended no data is
* @return The number of bytes appended. * allowed, and should result in {@link C#RESULT_END_OF_INPUT} being returned. False if it
* should be considered an error, causing an {@link EOFException} to be thrown.
* @return The number of bytes appended, or {@link C#RESULT_END_OF_INPUT} if the input has ended.
* @throws IOException If an error occurs reading from the source. * @throws IOException If an error occurs reading from the source.
*/ */
public int appendData(DataSource dataSource, int length) throws IOException { public int appendData(DataSource dataSource, int length, boolean allowEndOfInput)
ensureSpaceForWrite(); throws IOException {
int remainingAllocationCapacity = allocationLength - lastAllocationOffset; length = prepareForAppend(length);
length = length != C.LENGTH_UNBOUNDED ? Math.min(length, remainingAllocationCapacity) int bytesAppended = dataSource.read(lastAllocation.data,
: remainingAllocationCapacity;
int bytesRead = dataSource.read(lastAllocation.data,
lastAllocation.translateOffset(lastAllocationOffset), length); lastAllocation.translateOffset(lastAllocationOffset), length);
if (bytesRead == C.RESULT_END_OF_INPUT) { if (bytesAppended == C.RESULT_END_OF_INPUT) {
return C.RESULT_END_OF_INPUT; if (allowEndOfInput) {
return C.RESULT_END_OF_INPUT;
}
throw new EOFException();
} }
lastAllocationOffset += bytesAppended;
lastAllocationOffset += bytesRead; totalBytesWritten += bytesAppended;
totalBytesWritten += bytesRead; return bytesAppended;
return bytesRead;
} }
/** /**
@ -377,17 +379,27 @@ import java.util.concurrent.LinkedBlockingDeque;
* *
* @param input The source from which to read. * @param input The source from which to read.
* @param length The maximum length of the read. * @param length The maximum length of the read.
* @return The number of bytes appended. * @param allowEndOfInput True if encountering the end of the input having appended no data is
* allowed, and should result in {@link C#RESULT_END_OF_INPUT} being returned. False if it
* should be considered an error, causing an {@link EOFException} to be thrown.
* @return The number of bytes appended, or {@link C#RESULT_END_OF_INPUT} if the input has ended.
* @throws IOException If an error occurs reading from the source. * @throws IOException If an error occurs reading from the source.
* @throws InterruptedException If the thread has been interrupted.
*/ */
public int appendData(ExtractorInput input, int length) throws IOException, InterruptedException { public int appendData(ExtractorInput input, int length, boolean allowEndOfInput)
ensureSpaceForWrite(); throws IOException, InterruptedException {
int thisWriteLength = Math.min(length, allocationLength - lastAllocationOffset); length = prepareForAppend(length);
input.readFully(lastAllocation.data, lastAllocation.translateOffset(lastAllocationOffset), int bytesAppended = input.read(lastAllocation.data,
thisWriteLength); lastAllocation.translateOffset(lastAllocationOffset), length);
lastAllocationOffset += thisWriteLength; if (bytesAppended == C.RESULT_END_OF_INPUT) {
totalBytesWritten += thisWriteLength; if (allowEndOfInput) {
return thisWriteLength; return C.RESULT_END_OF_INPUT;
}
throw new EOFException();
}
lastAllocationOffset += bytesAppended;
totalBytesWritten += bytesAppended;
return bytesAppended;
} }
/** /**
@ -397,16 +409,14 @@ import java.util.concurrent.LinkedBlockingDeque;
* @param length The length of the data to append. * @param length The length of the data to append.
*/ */
public void appendData(ParsableByteArray buffer, int length) { public void appendData(ParsableByteArray buffer, int length) {
int remainingWriteLength = length; while (length > 0) {
while (remainingWriteLength > 0) { int thisAppendLength = prepareForAppend(length);
ensureSpaceForWrite();
int thisWriteLength = Math.min(remainingWriteLength, allocationLength - lastAllocationOffset);
buffer.readBytes(lastAllocation.data, lastAllocation.translateOffset(lastAllocationOffset), buffer.readBytes(lastAllocation.data, lastAllocation.translateOffset(lastAllocationOffset),
thisWriteLength); thisAppendLength);
lastAllocationOffset += thisWriteLength; lastAllocationOffset += thisAppendLength;
remainingWriteLength -= thisWriteLength; totalBytesWritten += thisAppendLength;
length -= thisAppendLength;
} }
totalBytesWritten += length;
} }
/** /**
@ -424,14 +434,16 @@ import java.util.concurrent.LinkedBlockingDeque;
} }
/** /**
* Ensures at least one byte can be written, obtaining an additional allocation if necessary. * Prepares the rolling sample buffer for an append of up to {@code length} bytes, returning the
* number of bytes that can actually be appended.
*/ */
private void ensureSpaceForWrite() { private int prepareForAppend(int length) {
if (lastAllocationOffset == allocationLength) { if (lastAllocationOffset == allocationLength) {
lastAllocationOffset = 0; lastAllocationOffset = 0;
lastAllocation = allocator.allocate(); lastAllocation = allocator.allocate();
dataQueue.add(lastAllocation); dataQueue.add(lastAllocation);
} }
return Math.min(length, allocationLength - lastAllocationOffset);
} }
/** /**

View File

@ -15,10 +15,12 @@
*/ */
package com.google.android.exoplayer.extractor; package com.google.android.exoplayer.extractor;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.ParsableByteArray;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
/** /**
@ -38,11 +40,15 @@ public interface TrackOutput {
* *
* @param input An {@link ExtractorInput} from which to read the sample data. * @param input An {@link ExtractorInput} from which to read the sample data.
* @param length The maximum length to read from the input. * @param length The maximum length to read from the input.
* @param allowEndOfInput True if encountering the end of the input having read no data is
* allowed, and should result in {@link C#RESULT_END_OF_INPUT} being returned. False if it
* should be considered an error, causing an {@link EOFException} to be thrown.
* @return The number of bytes appended. * @return The number of bytes appended.
* @throws IOException If an error occurred reading from the input. * @throws IOException If an error occurred reading from the input.
* @throws InterruptedException If the thread was interrupted. * @throws InterruptedException If the thread was interrupted.
*/ */
int sampleData(ExtractorInput input, int length) throws IOException, InterruptedException; int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException;
/** /**
* Invoked to write sample data to the output. * Invoked to write sample data to the output.
@ -56,14 +62,16 @@ public interface TrackOutput {
* Invoked when metadata associated with a sample has been extracted from the stream. * Invoked when metadata associated with a sample has been extracted from the stream.
* <p> * <p>
* The corresponding sample data will have already been passed to the output via calls to * The corresponding sample data will have already been passed to the output via calls to
* {@link #sampleData(ExtractorInput, int)} or {@link #sampleData(ParsableByteArray, int)}. * {@link #sampleData(ExtractorInput, int, boolean)} or
* {@link #sampleData(ParsableByteArray, int)}.
* *
* @param timeUs The media timestamp associated with the sample, in microseconds. * @param timeUs The media timestamp associated with the sample, in microseconds.
* @param flags Flags associated with the sample. See {@link SampleHolder#flags}. * @param flags Flags associated with the sample. See {@link SampleHolder#flags}.
* @param size The size of the sample data, in bytes. * @param size The size of the sample data, in bytes.
* @param offset The number of bytes that have been passed to * @param offset The number of bytes that have been passed to
* {@link #sampleData(ExtractorInput, int)} or {@link #sampleData(ParsableByteArray, int)} * {@link #sampleData(ExtractorInput, int, boolean)} or
* since the last byte belonging to the sample whose metadata is being passed. * {@link #sampleData(ParsableByteArray, int)} since the last byte belonging to the sample
* whose metadata is being passed.
* @param encryptionKey The encryption key associated with the sample. May be null. * @param encryptionKey The encryption key associated with the sample. May be null.
*/ */
void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey); void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey);

View File

@ -170,11 +170,11 @@ public final class Mp3Extractor implements Extractor {
sampleBytesRemaining -= inputBuffer.drainToOutput(trackOutput, sampleBytesRemaining); sampleBytesRemaining -= inputBuffer.drainToOutput(trackOutput, sampleBytesRemaining);
if (sampleBytesRemaining > 0) { if (sampleBytesRemaining > 0) {
inputBuffer.mark(); inputBuffer.mark();
try { int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
sampleBytesRemaining -= trackOutput.sampleData(extractorInput, sampleBytesRemaining); if (bytesAppended == C.RESULT_END_OF_INPUT) {
} catch (EOFException e) {
return RESULT_END_OF_INPUT; return RESULT_END_OF_INPUT;
} }
sampleBytesRemaining -= bytesAppended;
// Return if we still need more data. // Return if we still need more data.
if (sampleBytesRemaining > 0) { if (sampleBytesRemaining > 0) {
return RESULT_CONTINUE; return RESULT_CONTINUE;

View File

@ -663,14 +663,14 @@ public final class FragmentedMp4Extractor implements Extractor {
sampleSize += nalUnitLengthFieldLengthDiff; sampleSize += nalUnitLengthFieldLengthDiff;
} else { } else {
// Write the payload of the NAL unit. // Write the payload of the NAL unit.
int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining); int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining, false);
sampleBytesWritten += writtenBytes; sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes; sampleCurrentNalBytesRemaining -= writtenBytes;
} }
} }
} else { } else {
while (sampleBytesWritten < sampleSize) { while (sampleBytesWritten < sampleSize) {
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten); int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes; sampleBytesWritten += writtenBytes;
} }
} }

View File

@ -298,6 +298,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
return RESULT_END_OF_INPUT; return RESULT_END_OF_INPUT;
} }
Mp4Track track = tracks[trackIndex]; Mp4Track track = tracks[trackIndex];
TrackOutput trackOutput = track.trackOutput;
int sampleIndex = track.sampleIndex; int sampleIndex = track.sampleIndex;
long position = track.sampleTable.offsets[sampleIndex]; long position = track.sampleTable.offsets[sampleIndex];
long skipAmount = position - input.getPosition() + sampleBytesWritten; long skipAmount = position - input.getPosition() + sampleBytesWritten;
@ -327,24 +328,24 @@ public final class Mp4Extractor implements Extractor, SeekMap {
sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt(); sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
// Write a start code for the current NAL unit. // Write a start code for the current NAL unit.
nalStartCode.setPosition(0); nalStartCode.setPosition(0);
track.trackOutput.sampleData(nalStartCode, 4); trackOutput.sampleData(nalStartCode, 4);
sampleBytesWritten += 4; sampleBytesWritten += 4;
sampleSize += nalUnitLengthFieldLengthDiff; sampleSize += nalUnitLengthFieldLengthDiff;
} else { } else {
// Write the payload of the NAL unit. // Write the payload of the NAL unit.
int writtenBytes = track.trackOutput.sampleData(input, sampleCurrentNalBytesRemaining); int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining, false);
sampleBytesWritten += writtenBytes; sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes; sampleCurrentNalBytesRemaining -= writtenBytes;
} }
} }
} else { } else {
while (sampleBytesWritten < sampleSize) { while (sampleBytesWritten < sampleSize) {
int writtenBytes = track.trackOutput.sampleData(input, sampleSize - sampleBytesWritten); int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes; sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes; sampleCurrentNalBytesRemaining -= writtenBytes;
} }
} }
track.trackOutput.sampleMetadata(track.sampleTable.timestampsUs[sampleIndex], trackOutput.sampleMetadata(track.sampleTable.timestampsUs[sampleIndex],
track.sampleTable.flags[sampleIndex], sampleSize, 0, null); track.sampleTable.flags[sampleIndex], sampleSize, 0, null);
track.sampleIndex++; track.sampleIndex++;
sampleBytesWritten = 0; sampleBytesWritten = 0;

View File

@ -885,7 +885,7 @@ public final class WebmExtractor implements Extractor {
bytesRead = Math.min(length, strippedBytesLeft); bytesRead = Math.min(length, strippedBytesLeft);
output.sampleData(sampleStrippedBytes, bytesRead); output.sampleData(sampleStrippedBytes, bytesRead);
} else { } else {
bytesRead = output.sampleData(input, length); bytesRead = output.sampleData(input, length, false);
} }
sampleBytesRead += bytesRead; sampleBytesRead += bytesRead;
sampleBytesWritten += bytesRead; sampleBytesWritten += bytesRead;