From ed4f83979e38a3f624f5ac08dc6ed460250e1920 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Mar 2016 05:48:54 -0700 Subject: [PATCH] Add extensions package to V2. - Not doing any renaming for now. It'll be easier to wait until after the extensions themselves are brought across. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=117438338 --- .../exoplayer/util/extensions/Buffer.java | 50 ++++ .../exoplayer/util/extensions/Decoder.java | 63 ++++ .../util/extensions/InputBuffer.java | 37 +++ .../util/extensions/OutputBuffer.java | 33 +++ .../util/extensions/SimpleDecoder.java | 271 ++++++++++++++++++ 5 files changed, 454 insertions(+) create mode 100644 library/src/main/java/com/google/android/exoplayer/util/extensions/Buffer.java create mode 100644 library/src/main/java/com/google/android/exoplayer/util/extensions/Decoder.java create mode 100644 library/src/main/java/com/google/android/exoplayer/util/extensions/InputBuffer.java create mode 100644 library/src/main/java/com/google/android/exoplayer/util/extensions/OutputBuffer.java create mode 100644 library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java diff --git a/library/src/main/java/com/google/android/exoplayer/util/extensions/Buffer.java b/library/src/main/java/com/google/android/exoplayer/util/extensions/Buffer.java new file mode 100644 index 0000000000..241a02327b --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/util/extensions/Buffer.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.util.extensions; + +/** + * Base class for {@link Decoder} buffers with flags. + */ +public abstract class Buffer { + + /** + * Flag for empty input/output buffers that signal that the end of the stream was reached. + */ + public static final int FLAG_END_OF_STREAM = 1; + /** + * Flag for non-empty input buffers which signals that the decoder must be reset before decoding. + */ + public static final int FLAG_RESET = 2; + /** + * Flag for non-empty input/output buffers that should only be decoded (not rendered). + */ + public static final int FLAG_DECODE_ONLY = 4; + + private int flags; + + public void reset() { + flags = 0; + } + + public final void setFlag(int flag) { + flags |= flag; + } + + public final boolean getFlag(int flag) { + return (flags & flag) == flag; + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/util/extensions/Decoder.java b/library/src/main/java/com/google/android/exoplayer/util/extensions/Decoder.java new file mode 100644 index 0000000000..55f7727ab3 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/util/extensions/Decoder.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.util.extensions; + +/** + * A media decoder. + * + * @param The type of buffer input to the decoder. + * @param The type of buffer output from the decoder. + * @param The type of exception thrown from the decoder. + */ +public interface Decoder { + + /** + * Dequeues the next input buffer to be filled and queued to the decoder. + * + * @return The input buffer, or null if an input buffer isn't available. + * @throws E If a decoder error has occurred. + */ + I dequeueInputBuffer() throws E; + + /** + * Queues an input buffer to the decoder. + * + * @param inputBuffer The input buffer. + * @throws E If a decoder error has occurred. + */ + void queueInputBuffer(I inputBuffer) throws E; + + /** + * Dequeues the next output buffer from the decoder. + * + * @return The output buffer, or null if an output buffer isn't available. + * @throws E If a decoder error has occurred. + */ + O dequeueOutputBuffer() throws E; + + /** + * Flushes input/output buffers that have not been dequeued yet and returns ownership of any + * dequeued input buffer to the decoder. Flushes any pending output currently in the decoder. The + * caller is still responsible for releasing any dequeued output buffers. + */ + void flush(); + + /** + * Releases the decoder. Must be called when the decoder is no longer needed. + */ + void release(); + +} diff --git a/library/src/main/java/com/google/android/exoplayer/util/extensions/InputBuffer.java b/library/src/main/java/com/google/android/exoplayer/util/extensions/InputBuffer.java new file mode 100644 index 0000000000..917e90bad7 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/util/extensions/InputBuffer.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.util.extensions; + +import com.google.android.exoplayer.SampleHolder; + +/** + * Input buffer to be decoded by a {@link Decoder}. + */ +public class InputBuffer extends Buffer { + + public final SampleHolder sampleHolder; + + public InputBuffer() { + sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT); + } + + @Override + public void reset() { + super.reset(); + sampleHolder.clearData(); + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/util/extensions/OutputBuffer.java b/library/src/main/java/com/google/android/exoplayer/util/extensions/OutputBuffer.java new file mode 100644 index 0000000000..c5ec800003 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/util/extensions/OutputBuffer.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.util.extensions; + +/** + * Output buffer decoded by a {@link Decoder}. + */ +public abstract class OutputBuffer extends Buffer { + + /** + * The presentation timestamp for the buffer, in microseconds. + */ + public long timestampUs; + + /** + * Releases the output buffer for reuse. Must be called when the buffer is no longer needed. + */ + public abstract void release(); + +} diff --git a/library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java b/library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java new file mode 100644 index 0000000000..c6fe12eb24 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.util.extensions; + +import com.google.android.exoplayer.util.Assertions; + +import java.util.LinkedList; + +/** + * Base class for {@link Decoder}s that use their own decode thread. + */ +public abstract class SimpleDecoder extends Thread implements Decoder { + + /** + * Listener for {@link SimpleDecoder} events. + */ + public interface EventListener { + + /** + * Invoked when the decoder encounters an error. + * + * @param e The corresponding exception. + */ + void onDecoderError(E e); + + } + + private final Object lock; + private final LinkedList queuedInputBuffers; + private final LinkedList queuedOutputBuffers; + private final I[] availableInputBuffers; + private final O[] availableOutputBuffers; + + private int availableInputBufferCount; + private int availableOutputBufferCount; + private I dequeuedInputBuffer; + + private E exception; + private boolean flushDecodedOutputBuffer; + private boolean released; + + /** + * @param inputBuffers An array of nulls that will be used to store references to input buffers. + * @param outputBuffers An array of nulls that will be used to store references to output buffers. + */ + protected SimpleDecoder(I[] inputBuffers, O[] outputBuffers) { + lock = new Object(); + queuedInputBuffers = new LinkedList<>(); + queuedOutputBuffers = new LinkedList<>(); + availableInputBuffers = inputBuffers; + availableInputBufferCount = inputBuffers.length; + for (int i = 0; i < availableInputBufferCount; i++) { + availableInputBuffers[i] = createInputBuffer(); + } + availableOutputBuffers = outputBuffers; + availableOutputBufferCount = outputBuffers.length; + for (int i = 0; i < availableOutputBufferCount; i++) { + availableOutputBuffers[i] = createOutputBuffer(); + } + } + + /** + * Sets the initial size of each input buffer. + *

+ * This method should only be called before the decoder is used (i.e. before the first call to + * {@link #dequeueInputBuffer()}. + * + * @param size The required input buffer size. + */ + protected final void setInitialInputBufferSize(int size) { + Assertions.checkState(availableInputBufferCount == availableInputBuffers.length); + for (int i = 0; i < availableInputBuffers.length; i++) { + availableInputBuffers[i].sampleHolder.ensureSpaceForWrite(size); + } + } + + @Override + public final I dequeueInputBuffer() throws E { + synchronized (lock) { + maybeThrowException(); + Assertions.checkState(dequeuedInputBuffer == null); + if (availableInputBufferCount == 0) { + return null; + } + I inputBuffer = availableInputBuffers[--availableInputBufferCount]; + inputBuffer.reset(); + dequeuedInputBuffer = inputBuffer; + return inputBuffer; + } + } + + @Override + public final void queueInputBuffer(I inputBuffer) throws E { + synchronized (lock) { + maybeThrowException(); + Assertions.checkArgument(inputBuffer == dequeuedInputBuffer); + queuedInputBuffers.addLast(inputBuffer); + maybeNotifyDecodeLoop(); + dequeuedInputBuffer = null; + } + } + + @Override + public final O dequeueOutputBuffer() throws E { + synchronized (lock) { + maybeThrowException(); + if (queuedOutputBuffers.isEmpty()) { + return null; + } + return queuedOutputBuffers.removeFirst(); + } + } + + /** + * Releases an output buffer back to the decoder. + * + * @param outputBuffer The output buffer being released. + */ + protected void releaseOutputBuffer(O outputBuffer) { + synchronized (lock) { + availableOutputBuffers[availableOutputBufferCount++] = outputBuffer; + maybeNotifyDecodeLoop(); + } + } + + @Override + public final void flush() { + synchronized (lock) { + flushDecodedOutputBuffer = true; + if (dequeuedInputBuffer != null) { + availableInputBuffers[availableInputBufferCount++] = dequeuedInputBuffer; + dequeuedInputBuffer = null; + } + while (!queuedInputBuffers.isEmpty()) { + availableInputBuffers[availableInputBufferCount++] = queuedInputBuffers.removeFirst(); + } + while (!queuedOutputBuffers.isEmpty()) { + availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst(); + } + } + } + + @Override + public void release() { + synchronized (lock) { + released = true; + lock.notify(); + } + try { + join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + /** + * Throws a decode exception, if there is one. + * + * @throws E The decode exception. + */ + private void maybeThrowException() throws E { + if (exception != null) { + throw exception; + } + } + + /** + * Notifies the decode loop if there exists a queued input buffer and an available output buffer + * to decode into. + *

+ * Should only be called whilst synchronized on the lock object. + */ + private void maybeNotifyDecodeLoop() { + if (canDecodeBuffer()) { + lock.notify(); + } + } + + @Override + public final void run() { + try { + while (decode()) { + // Do nothing. + } + } catch (InterruptedException e) { + // Not expected. + throw new IllegalStateException(e); + } + } + + private boolean decode() throws InterruptedException { + I inputBuffer; + O outputBuffer; + + // Wait until we have an input buffer to decode, and an output buffer to decode into. + synchronized (lock) { + while (!released && !canDecodeBuffer()) { + lock.wait(); + } + if (released) { + return false; + } + inputBuffer = queuedInputBuffers.removeFirst(); + outputBuffer = availableOutputBuffers[--availableOutputBufferCount]; + flushDecodedOutputBuffer = false; + } + + exception = decode(inputBuffer, outputBuffer); + if (exception != null) { + // Memory barrier to ensure that the decoder exception is visible from the playback thread. + synchronized (lock) {} + return false; + } + + boolean decodeOnly = outputBuffer.getFlag(Buffer.FLAG_DECODE_ONLY); + synchronized (lock) { + if (flushDecodedOutputBuffer || decodeOnly) { + // If a flush occurred while decoding or the buffer was only for decoding (not presentation) + // then make the output buffer available again rather than queueing it to be consumed. + availableOutputBuffers[availableOutputBufferCount++] = outputBuffer; + } else { + // Queue the decoded output buffer to be consumed. + queuedOutputBuffers.addLast(outputBuffer); + } + // Make the input buffer available again. + availableInputBuffers[availableInputBufferCount++] = inputBuffer; + } + + return true; + } + + private boolean canDecodeBuffer() { + return !queuedInputBuffers.isEmpty() && availableOutputBufferCount > 0; + } + + /** + * Creates a new input buffer. + */ + protected abstract I createInputBuffer(); + + /** + * Creates a new output buffer. + */ + protected abstract O createOutputBuffer(); + + /** + * Decodes the {@code inputBuffer} and stores any decoded output in {@code outputBuffer}. + * + * @param inputBuffer The buffer to decode. + * @param outputBuffer The output buffer to store decoded data. If the flag + * {@link Buffer#FLAG_DECODE_ONLY} is set after this method returns, any output should not be + * presented. + * @return A decode exception if an error occurred, or null if the decode was successful. + */ + protected abstract E decode(I inputBuffer, O outputBuffer); + +}