Commit c685e279 by andrewlewis Committed by Andrew Lewis

Fix {Opus,Vpx}DecoderWrapper flush() buffer handling.

Currently, input/output buffers can 'leak' if they are dequeued then the decoder
is flush()ed. This happens if an input buffer is dequeued then a discontinuity
is read, or an output buffer is dequeued and is early. If this happens several
times, no more buffers are available.

This change makes flush() work like MediaCodec: it returns all dequeued
input/output buffers to the codec. Keeping the behavior in line with MediaCodec
might make it easier to factor out a common decoder interface in the future.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=109054178
parent 1855a5a9
...@@ -263,7 +263,7 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer ...@@ -263,7 +263,7 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
} }
if (inputBuffer == null) { if (inputBuffer == null) {
inputBuffer = decoder.getInputBuffer(); inputBuffer = decoder.dequeueInputBuffer();
if (inputBuffer == null) { if (inputBuffer == null) {
return false; return false;
} }
......
...@@ -36,8 +36,10 @@ import java.util.LinkedList; ...@@ -36,8 +36,10 @@ import java.util.LinkedList;
private final Object lock; private final Object lock;
private final OpusHeader opusHeader; private final OpusHeader opusHeader;
private final LinkedList<InputBuffer> dequeuedInputBuffers;
private final LinkedList<InputBuffer> queuedInputBuffers; private final LinkedList<InputBuffer> queuedInputBuffers;
private final LinkedList<OutputBuffer> queuedOutputBuffers; private final LinkedList<OutputBuffer> queuedOutputBuffers;
private final LinkedList<OutputBuffer> dequeuedOutputBuffers;
private final InputBuffer[] availableInputBuffers; private final InputBuffer[] availableInputBuffers;
private final OutputBuffer[] availableOutputBuffers; private final OutputBuffer[] availableOutputBuffers;
private int availableInputBufferCount; private int availableInputBufferCount;
...@@ -66,10 +68,14 @@ import java.util.LinkedList; ...@@ -66,10 +68,14 @@ import java.util.LinkedList;
long seekPreRollNs) throws OpusDecoderException { long seekPreRollNs) throws OpusDecoderException {
lock = new Object(); lock = new Object();
opusHeader = parseOpusHeader(headerBytes); opusHeader = parseOpusHeader(headerBytes);
skipSamples = (codecDelayNs == -1) ? opusHeader.skipSamples : nsToSamples(codecDelayNs); skipSamples = (codecDelayNs == -1) ? opusHeader.skipSamples
seekPreRoll = (seekPreRoll == -1) ? DEFAULT_SEEK_PRE_ROLL : nsToSamples(seekPreRollNs); : nsToSamples(opusHeader, codecDelayNs);
seekPreRoll = (seekPreRoll == -1) ? DEFAULT_SEEK_PRE_ROLL
: nsToSamples(opusHeader, seekPreRollNs);
dequeuedInputBuffers = new LinkedList<>();
queuedInputBuffers = new LinkedList<>(); queuedInputBuffers = new LinkedList<>();
queuedOutputBuffers = new LinkedList<>(); queuedOutputBuffers = new LinkedList<>();
dequeuedOutputBuffers = new LinkedList<>();
availableInputBuffers = new InputBuffer[NUM_BUFFERS]; availableInputBuffers = new InputBuffer[NUM_BUFFERS];
availableOutputBuffers = new OutputBuffer[NUM_BUFFERS]; availableOutputBuffers = new OutputBuffer[NUM_BUFFERS];
availableInputBufferCount = NUM_BUFFERS; availableInputBufferCount = NUM_BUFFERS;
...@@ -80,7 +86,7 @@ import java.util.LinkedList; ...@@ -80,7 +86,7 @@ import java.util.LinkedList;
} }
} }
public InputBuffer getInputBuffer() throws OpusDecoderException { public InputBuffer dequeueInputBuffer() throws OpusDecoderException {
synchronized (lock) { synchronized (lock) {
maybeThrowDecoderError(); maybeThrowDecoderError();
if (availableInputBufferCount == 0) { if (availableInputBufferCount == 0) {
...@@ -88,6 +94,7 @@ import java.util.LinkedList; ...@@ -88,6 +94,7 @@ import java.util.LinkedList;
} }
InputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount]; InputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount];
inputBuffer.reset(); inputBuffer.reset();
dequeuedInputBuffers.addLast(inputBuffer);
return inputBuffer; return inputBuffer;
} }
} }
...@@ -95,6 +102,7 @@ import java.util.LinkedList; ...@@ -95,6 +102,7 @@ import java.util.LinkedList;
public void queueInputBuffer(InputBuffer inputBuffer) throws OpusDecoderException { public void queueInputBuffer(InputBuffer inputBuffer) throws OpusDecoderException {
synchronized (lock) { synchronized (lock) {
maybeThrowDecoderError(); maybeThrowDecoderError();
dequeuedInputBuffers.remove(inputBuffer);
queuedInputBuffers.addLast(inputBuffer); queuedInputBuffers.addLast(inputBuffer);
maybeNotifyDecodeLoop(); maybeNotifyDecodeLoop();
} }
...@@ -106,7 +114,9 @@ import java.util.LinkedList; ...@@ -106,7 +114,9 @@ import java.util.LinkedList;
if (queuedOutputBuffers.isEmpty()) { if (queuedOutputBuffers.isEmpty()) {
return null; return null;
} }
return queuedOutputBuffers.removeFirst(); OutputBuffer outputBuffer = queuedOutputBuffers.removeFirst();
dequeuedOutputBuffers.add(outputBuffer);
return outputBuffer;
} }
} }
...@@ -114,6 +124,7 @@ import java.util.LinkedList; ...@@ -114,6 +124,7 @@ import java.util.LinkedList;
synchronized (lock) { synchronized (lock) {
maybeThrowDecoderError(); maybeThrowDecoderError();
outputBuffer.reset(); outputBuffer.reset();
dequeuedOutputBuffers.remove(outputBuffer);
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer; availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
maybeNotifyDecodeLoop(); maybeNotifyDecodeLoop();
} }
...@@ -122,12 +133,18 @@ import java.util.LinkedList; ...@@ -122,12 +133,18 @@ import java.util.LinkedList;
public void flush() { public void flush() {
synchronized (lock) { synchronized (lock) {
flushDecodedOutputBuffer = true; flushDecodedOutputBuffer = true;
while (!dequeuedInputBuffers.isEmpty()) {
availableInputBuffers[availableInputBufferCount++] = dequeuedInputBuffers.removeFirst();
}
while (!queuedInputBuffers.isEmpty()) { while (!queuedInputBuffers.isEmpty()) {
availableInputBuffers[availableInputBufferCount++] = queuedInputBuffers.removeFirst(); availableInputBuffers[availableInputBufferCount++] = queuedInputBuffers.removeFirst();
} }
while (!queuedOutputBuffers.isEmpty()) { while (!queuedOutputBuffers.isEmpty()) {
availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst(); availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst();
} }
while (!dequeuedOutputBuffers.isEmpty()) {
availableOutputBuffers[availableOutputBufferCount++] = dequeuedOutputBuffers.removeFirst();
}
} }
} }
...@@ -254,7 +271,7 @@ import java.util.LinkedList; ...@@ -254,7 +271,7 @@ import java.util.LinkedList;
return true; return true;
} }
private OpusHeader parseOpusHeader(byte[] headerBytes) throws OpusDecoderException { private static OpusHeader parseOpusHeader(byte[] headerBytes) throws OpusDecoderException {
final int maxChannelCount = 8; final int maxChannelCount = 8;
final int maxChannelCountWithDefaultLayout = 2; final int maxChannelCountWithDefaultLayout = 2;
final int headerSize = 19; final int headerSize = 19;
...@@ -300,13 +317,13 @@ import java.util.LinkedList; ...@@ -300,13 +317,13 @@ import java.util.LinkedList;
} }
} }
private int readLittleEndian16(byte[] input, int offset) { private static int readLittleEndian16(byte[] input, int offset) {
int value = input[offset]; int value = input[offset];
value |= input[offset + 1] << 8; value |= input[offset + 1] << 8;
return value; return value;
} }
private int nsToSamples(long ns) { private static int nsToSamples(OpusHeader opusHeader, long ns) {
return (int) (ns * opusHeader.sampleRate / 1000000000); return (int) (ns * opusHeader.sampleRate / 1000000000);
} }
......
...@@ -298,7 +298,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer { ...@@ -298,7 +298,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
} }
if (inputBuffer == null) { if (inputBuffer == null) {
inputBuffer = decoder.getInputBuffer(); inputBuffer = decoder.dequeueInputBuffer();
if (inputBuffer == null) { if (inputBuffer == null) {
return false; return false;
} }
......
...@@ -32,8 +32,10 @@ import java.util.LinkedList; ...@@ -32,8 +32,10 @@ import java.util.LinkedList;
private final Object lock; private final Object lock;
private final LinkedList<InputBuffer> dequeuedInputBuffers;
private final LinkedList<InputBuffer> queuedInputBuffers; private final LinkedList<InputBuffer> queuedInputBuffers;
private final LinkedList<OutputBuffer> queuedOutputBuffers; private final LinkedList<OutputBuffer> queuedOutputBuffers;
private final LinkedList<OutputBuffer> dequeuedOutputBuffers;
private final InputBuffer[] availableInputBuffers; private final InputBuffer[] availableInputBuffers;
private final OutputBuffer[] availableOutputBuffers; private final OutputBuffer[] availableOutputBuffers;
private int availableInputBufferCount; private int availableInputBufferCount;
...@@ -52,8 +54,10 @@ import java.util.LinkedList; ...@@ -52,8 +54,10 @@ import java.util.LinkedList;
public VpxDecoderWrapper(int outputMode) { public VpxDecoderWrapper(int outputMode) {
lock = new Object(); lock = new Object();
this.outputMode = outputMode; this.outputMode = outputMode;
dequeuedInputBuffers = new LinkedList<>();
queuedInputBuffers = new LinkedList<>(); queuedInputBuffers = new LinkedList<>();
queuedOutputBuffers = new LinkedList<>(); queuedOutputBuffers = new LinkedList<>();
dequeuedOutputBuffers = new LinkedList<>();
availableInputBuffers = new InputBuffer[NUM_BUFFERS]; availableInputBuffers = new InputBuffer[NUM_BUFFERS];
availableOutputBuffers = new OutputBuffer[NUM_BUFFERS]; availableOutputBuffers = new OutputBuffer[NUM_BUFFERS];
availableInputBufferCount = NUM_BUFFERS; availableInputBufferCount = NUM_BUFFERS;
...@@ -68,7 +72,7 @@ import java.util.LinkedList; ...@@ -68,7 +72,7 @@ import java.util.LinkedList;
this.outputMode = outputMode; this.outputMode = outputMode;
} }
public InputBuffer getInputBuffer() throws VpxDecoderException { public InputBuffer dequeueInputBuffer() throws VpxDecoderException {
synchronized (lock) { synchronized (lock) {
maybeThrowDecoderError(); maybeThrowDecoderError();
if (availableInputBufferCount == 0) { if (availableInputBufferCount == 0) {
...@@ -77,6 +81,7 @@ import java.util.LinkedList; ...@@ -77,6 +81,7 @@ import java.util.LinkedList;
InputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount]; InputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount];
inputBuffer.flags = 0; inputBuffer.flags = 0;
inputBuffer.sampleHolder.clearData(); inputBuffer.sampleHolder.clearData();
dequeuedInputBuffers.addLast(inputBuffer);
return inputBuffer; return inputBuffer;
} }
} }
...@@ -84,6 +89,7 @@ import java.util.LinkedList; ...@@ -84,6 +89,7 @@ import java.util.LinkedList;
public void queueInputBuffer(InputBuffer inputBuffer) throws VpxDecoderException { public void queueInputBuffer(InputBuffer inputBuffer) throws VpxDecoderException {
synchronized (lock) { synchronized (lock) {
maybeThrowDecoderError(); maybeThrowDecoderError();
dequeuedInputBuffers.remove(inputBuffer);
queuedInputBuffers.addLast(inputBuffer); queuedInputBuffers.addLast(inputBuffer);
maybeNotifyDecodeLoop(); maybeNotifyDecodeLoop();
} }
...@@ -95,13 +101,16 @@ import java.util.LinkedList; ...@@ -95,13 +101,16 @@ import java.util.LinkedList;
if (queuedOutputBuffers.isEmpty()) { if (queuedOutputBuffers.isEmpty()) {
return null; return null;
} }
return queuedOutputBuffers.removeFirst(); OutputBuffer outputBuffer = queuedOutputBuffers.removeFirst();
dequeuedOutputBuffers.add(outputBuffer);
return outputBuffer;
} }
} }
public void releaseOutputBuffer(OutputBuffer outputBuffer) throws VpxDecoderException { public void releaseOutputBuffer(OutputBuffer outputBuffer) throws VpxDecoderException {
synchronized (lock) { synchronized (lock) {
maybeThrowDecoderError(); maybeThrowDecoderError();
dequeuedOutputBuffers.remove(outputBuffer);
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer; availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
maybeNotifyDecodeLoop(); maybeNotifyDecodeLoop();
} }
...@@ -110,12 +119,18 @@ import java.util.LinkedList; ...@@ -110,12 +119,18 @@ import java.util.LinkedList;
public void flush() { public void flush() {
synchronized (lock) { synchronized (lock) {
flushDecodedOutputBuffer = true; flushDecodedOutputBuffer = true;
while (!dequeuedInputBuffers.isEmpty()) {
availableInputBuffers[availableInputBufferCount++] = dequeuedInputBuffers.removeFirst();
}
while (!queuedInputBuffers.isEmpty()) { while (!queuedInputBuffers.isEmpty()) {
availableInputBuffers[availableInputBufferCount++] = queuedInputBuffers.removeFirst(); availableInputBuffers[availableInputBufferCount++] = queuedInputBuffers.removeFirst();
} }
while (!queuedOutputBuffers.isEmpty()) { while (!queuedOutputBuffers.isEmpty()) {
availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst(); availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst();
} }
while (!dequeuedOutputBuffers.isEmpty()) {
availableOutputBuffers[availableOutputBufferCount++] = dequeuedOutputBuffers.removeFirst();
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment