Clean up expansion of sample buffers.

- The old approach was technically incorrect, because the checks
were "capacity < sampleSize" and hence neglected the fact that
the buffer position may be greater than 0 (e.g. if the caller
wants to prefix the sample with some additional data).

- Also proactively throw an exception if the buffer is too small,
rather than wait for the failure when we actually do the write.
This commit is contained in:
Oliver Woodman 2015-09-28 20:23:45 +01:00
parent 15c2f9c328
commit e96e618046
7 changed files with 54 additions and 30 deletions

View File

@ -324,7 +324,7 @@ import java.util.LinkedList;
}
public void reset() {
sampleHolder.data.clear();
sampleHolder.clearData();
flags = 0;
}

View File

@ -72,7 +72,7 @@ import java.util.LinkedList;
}
InputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount];
inputBuffer.flags = 0;
inputBuffer.sampleHolder.data.clear();
inputBuffer.sampleHolder.clearData();
return inputBuffer;
}
}

View File

@ -531,7 +531,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
return false;
}
sampleHolder.data = inputBuffers[inputIndex];
sampleHolder.data.clear();
sampleHolder.clearData();
}
if (codecReinitializationState == REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM) {
@ -579,7 +579,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
// We received two formats in a row. Clear the current buffer of any reconfiguration data
// associated with the first format.
sampleHolder.data.clear();
sampleHolder.clearData();
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
}
onInputFormatChanged(formatHolder);
@ -590,7 +590,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
// We received a new format immediately before the end of the stream. We need to clear
// the corresponding reconfiguration data from the current buffer, but re-write it into
// a subsequent buffer if there are any (e.g. if the user seeks backwards).
sampleHolder.data.clear();
sampleHolder.clearData();
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
}
inputStreamEnded = true;
@ -616,7 +616,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
// TODO: Find out if it's possible to supply samples prior to the first sync
// frame for HE-AAC.
if (!sampleHolder.isSyncFrame()) {
sampleHolder.data.clear();
sampleHolder.clearData();
if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
// The buffer we just cleared contained reconfiguration data. We need to re-write this
// data into a subsequent buffer (if there is one).

View File

@ -63,8 +63,8 @@ public final class SampleHolder {
private final int bufferReplacementMode;
/**
* @param bufferReplacementMode Determines the behavior of {@link #replaceBuffer(int)}. One of
* {@link #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} and
* @param bufferReplacementMode Determines the behavior of {@link #ensureSpaceForWrite(int)}. One
* of {@link #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} and
* {@link #BUFFER_REPLACEMENT_MODE_DIRECT}.
*/
public SampleHolder(int bufferReplacementMode) {
@ -73,21 +73,39 @@ public final class SampleHolder {
}
/**
* Attempts to replace {@link #data} with a {@link ByteBuffer} of the specified capacity.
* Ensures that {@link #data} is large enough to accommodate a write of a given length at its
* current position.
* <p>
* If the capacity of {@link #data} is sufficient this method does nothing. If the capacity is
* insufficient then an attempt is made to replace {@link #data} with a new {@link ByteBuffer}
* whose capacity is sufficient. Data up to the current position is copied to the new buffer.
*
* @param capacity The capacity of the replacement buffer, in bytes.
* @return True if the buffer was replaced. False otherwise.
* @param length The length of the write that must be accommodated, in bytes.
* @throws IllegalStateException If there is insufficient capacity to accommodate the write and
* the buffer replacement mode of the holder is {@link #BUFFER_REPLACEMENT_MODE_DISABLED}.
*/
public boolean replaceBuffer(int capacity) {
switch (bufferReplacementMode) {
case BUFFER_REPLACEMENT_MODE_NORMAL:
data = ByteBuffer.allocate(capacity);
return true;
case BUFFER_REPLACEMENT_MODE_DIRECT:
data = ByteBuffer.allocateDirect(capacity);
return true;
public void ensureSpaceForWrite(int length) throws IllegalStateException {
if (data == null) {
data = createReplacementBuffer(length);
return;
}
return false;
// Check whether the current buffer is sufficient.
int capacity = data.capacity();
int position = data.position();
int requiredCapacity = position + length;
if (capacity >= requiredCapacity) {
return;
}
// Instantiate a new buffer if possible.
ByteBuffer newData = createReplacementBuffer(requiredCapacity);
// Copy data up to the current position from the old buffer to the new one.
if (position > 0) {
data.position(0);
data.limit(position);
newData.put(data);
}
// Set the new buffer.
data = newData;
}
/**
@ -120,4 +138,16 @@ public final class SampleHolder {
}
}
private ByteBuffer createReplacementBuffer(int requiredCapacity) {
if (bufferReplacementMode == BUFFER_REPLACEMENT_MODE_NORMAL) {
return ByteBuffer.allocate(requiredCapacity);
} else if (bufferReplacementMode == BUFFER_REPLACEMENT_MODE_DIRECT) {
return ByteBuffer.allocateDirect(requiredCapacity);
} else {
int currentCapacity = data == null ? 0 : data.capacity();
throw new IllegalStateException("Buffer too small (" + currentCapacity + " < "
+ requiredCapacity + ")");
}
}
}

View File

@ -139,9 +139,7 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
sampleHolder.timeUs = 0;
sampleHolder.size = sampleSize;
sampleHolder.flags = C.SAMPLE_FLAG_SYNC;
if (sampleHolder.data == null || sampleHolder.data.capacity() < sampleSize) {
sampleHolder.replaceBuffer(sampleHolder.size);
}
sampleHolder.ensureSpaceForWrite(sampleHolder.size);
sampleHolder.data.put(sampleData, 0, sampleSize);
state = STATE_END_OF_STREAM;
return SAMPLE_READ;

View File

@ -186,12 +186,8 @@ import java.util.concurrent.LinkedBlockingDeque;
readEncryptionData(sampleHolder, extrasHolder);
}
// Write the sample data into the holder.
if (sampleHolder.data == null || sampleHolder.data.capacity() < sampleHolder.size) {
sampleHolder.replaceBuffer(sampleHolder.size);
}
if (sampleHolder.data != null) {
readData(extrasHolder.offset, sampleHolder.data, sampleHolder.size);
}
sampleHolder.ensureSpaceForWrite(sampleHolder.size);
readData(extrasHolder.offset, sampleHolder.data, sampleHolder.size);
// Advance the read head.
long nextOffset = infoQueue.moveToNextSample();
dropDownstreamTo(nextOffset);

View File

@ -114,6 +114,7 @@ public final class MetadataTrackRenderer<T> extends SampleSourceTrackRenderer im
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
continueBufferingSource(positionUs);
if (!inputStreamEnded && pendingMetadata == null) {
sampleHolder.clearData();
int result = readSource(positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) {
pendingMetadataTimestamp = sampleHolder.timeUs;
@ -122,7 +123,6 @@ public final class MetadataTrackRenderer<T> extends SampleSourceTrackRenderer im
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
sampleHolder.data.clear();
} else if (result == SampleSource.END_OF_STREAM) {
inputStreamEnded = true;
}