summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Buffer.java100
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/CryptoInfo.java146
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Decoder.java73
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderCounters.java105
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java209
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/OutputBuffer.java38
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleDecoder.java314
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java65
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/package-info.java19
9 files changed, 1069 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Buffer.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Buffer.java
new file mode 100644
index 0000000000..ac254fae96
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Buffer.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+
+/**
+ * Base class for buffers with flags.
+ */
+public abstract class Buffer {
+
+ @C.BufferFlags
+ private int flags;
+
+ /**
+ * Clears the buffer.
+ */
+ public void clear() {
+ flags = 0;
+ }
+
+ /**
+ * Returns whether the {@link C#BUFFER_FLAG_DECODE_ONLY} flag is set.
+ */
+ public final boolean isDecodeOnly() {
+ return getFlag(C.BUFFER_FLAG_DECODE_ONLY);
+ }
+
+ /**
+ * Returns whether the {@link C#BUFFER_FLAG_END_OF_STREAM} flag is set.
+ */
+ public final boolean isEndOfStream() {
+ return getFlag(C.BUFFER_FLAG_END_OF_STREAM);
+ }
+
+ /**
+ * Returns whether the {@link C#BUFFER_FLAG_KEY_FRAME} flag is set.
+ */
+ public final boolean isKeyFrame() {
+ return getFlag(C.BUFFER_FLAG_KEY_FRAME);
+ }
+
+ /** Returns whether the {@link C#BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA} flag is set. */
+ public final boolean hasSupplementalData() {
+ return getFlag(C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA);
+ }
+
+ /**
+ * Replaces this buffer's flags with {@code flags}.
+ *
+ * @param flags The flags to set, which should be a combination of the {@code C.BUFFER_FLAG_*}
+ * constants.
+ */
+ public final void setFlags(@C.BufferFlags int flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Adds the {@code flag} to this buffer's flags.
+ *
+ * @param flag The flag to add to this buffer's flags, which should be one of the
+ * {@code C.BUFFER_FLAG_*} constants.
+ */
+ public final void addFlag(@C.BufferFlags int flag) {
+ flags |= flag;
+ }
+
+ /**
+ * Removes the {@code flag} from this buffer's flags, if it is set.
+ *
+ * @param flag The flag to remove.
+ */
+ public final void clearFlag(@C.BufferFlags int flag) {
+ flags &= ~flag;
+ }
+
+ /**
+ * Returns whether the specified flag has been set on this buffer.
+ *
+ * @param flag The flag to check.
+ * @return Whether the flag is set.
+ */
+ protected final boolean getFlag(@C.BufferFlags int flag) {
+ return (flags & flag) == flag;
+ }
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/CryptoInfo.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/CryptoInfo.java
new file mode 100644
index 0000000000..1bfb0fb06e
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/CryptoInfo.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+import android.annotation.TargetApi;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Util;
+
+/**
+ * Compatibility wrapper for {@link android.media.MediaCodec.CryptoInfo}.
+ */
+public final class CryptoInfo {
+
+ /**
+ * The 16 byte initialization vector. If the initialization vector of the content is shorter than
+ * 16 bytes, 0 byte padding is appended to extend the vector to the required 16 byte length.
+ *
+ * @see android.media.MediaCodec.CryptoInfo#iv
+ */
+ public byte[] iv;
+ /**
+ * The 16 byte key id.
+ *
+ * @see android.media.MediaCodec.CryptoInfo#key
+ */
+ public byte[] key;
+ /**
+ * The type of encryption that has been applied. Must be one of the {@link C.CryptoMode} values.
+ *
+ * @see android.media.MediaCodec.CryptoInfo#mode
+ */
+ @C.CryptoMode public int mode;
+ /**
+ * The number of leading unencrypted bytes in each sub-sample. If null, all bytes are treated as
+ * encrypted and {@link #numBytesOfEncryptedData} must be specified.
+ *
+ * @see android.media.MediaCodec.CryptoInfo#numBytesOfClearData
+ */
+ public int[] numBytesOfClearData;
+ /**
+ * The number of trailing encrypted bytes in each sub-sample. If null, all bytes are treated as
+ * clear and {@link #numBytesOfClearData} must be specified.
+ *
+ * @see android.media.MediaCodec.CryptoInfo#numBytesOfEncryptedData
+ */
+ public int[] numBytesOfEncryptedData;
+ /**
+ * The number of subSamples that make up the buffer's contents.
+ *
+ * @see android.media.MediaCodec.CryptoInfo#numSubSamples
+ */
+ public int numSubSamples;
+ /**
+ * @see android.media.MediaCodec.CryptoInfo.Pattern
+ */
+ public int encryptedBlocks;
+ /**
+ * @see android.media.MediaCodec.CryptoInfo.Pattern
+ */
+ public int clearBlocks;
+
+ private final android.media.MediaCodec.CryptoInfo frameworkCryptoInfo;
+ private final PatternHolderV24 patternHolder;
+
+ public CryptoInfo() {
+ frameworkCryptoInfo = new android.media.MediaCodec.CryptoInfo();
+ patternHolder = Util.SDK_INT >= 24 ? new PatternHolderV24(frameworkCryptoInfo) : null;
+ }
+
+ /**
+ * @see android.media.MediaCodec.CryptoInfo#set(int, int[], int[], byte[], byte[], int)
+ */
+ public void set(int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
+ byte[] key, byte[] iv, @C.CryptoMode int mode, int encryptedBlocks, int clearBlocks) {
+ this.numSubSamples = numSubSamples;
+ this.numBytesOfClearData = numBytesOfClearData;
+ this.numBytesOfEncryptedData = numBytesOfEncryptedData;
+ this.key = key;
+ this.iv = iv;
+ this.mode = mode;
+ this.encryptedBlocks = encryptedBlocks;
+ this.clearBlocks = clearBlocks;
+ // Update frameworkCryptoInfo fields directly because CryptoInfo.set performs an unnecessary
+ // object allocation on Android N.
+ frameworkCryptoInfo.numSubSamples = numSubSamples;
+ frameworkCryptoInfo.numBytesOfClearData = numBytesOfClearData;
+ frameworkCryptoInfo.numBytesOfEncryptedData = numBytesOfEncryptedData;
+ frameworkCryptoInfo.key = key;
+ frameworkCryptoInfo.iv = iv;
+ frameworkCryptoInfo.mode = mode;
+ if (Util.SDK_INT >= 24) {
+ patternHolder.set(encryptedBlocks, clearBlocks);
+ }
+ }
+
+ /**
+ * Returns an equivalent {@link android.media.MediaCodec.CryptoInfo} instance.
+ *
+ * <p>Successive calls to this method on a single {@link CryptoInfo} will return the same
+ * instance. Changes to the {@link CryptoInfo} will be reflected in the returned object. The
+ * return object should not be modified directly.
+ *
+ * @return The equivalent {@link android.media.MediaCodec.CryptoInfo} instance.
+ */
+ public android.media.MediaCodec.CryptoInfo getFrameworkCryptoInfo() {
+ return frameworkCryptoInfo;
+ }
+
+ /** @deprecated Use {@link #getFrameworkCryptoInfo()}. */
+ @Deprecated
+ public android.media.MediaCodec.CryptoInfo getFrameworkCryptoInfoV16() {
+ return getFrameworkCryptoInfo();
+ }
+
+ @TargetApi(24)
+ private static final class PatternHolderV24 {
+
+ private final android.media.MediaCodec.CryptoInfo frameworkCryptoInfo;
+ private final android.media.MediaCodec.CryptoInfo.Pattern pattern;
+
+ private PatternHolderV24(android.media.MediaCodec.CryptoInfo frameworkCryptoInfo) {
+ this.frameworkCryptoInfo = frameworkCryptoInfo;
+ pattern = new android.media.MediaCodec.CryptoInfo.Pattern(0, 0);
+ }
+
+ private void set(int encryptedBlocks, int clearBlocks) {
+ pattern.set(encryptedBlocks, clearBlocks);
+ frameworkCryptoInfo.setPattern(pattern);
+ }
+
+ }
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Decoder.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Decoder.java
new file mode 100644
index 0000000000..8040c04ebe
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/Decoder.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A media decoder.
+ *
+ * @param <I> The type of buffer input to the decoder.
+ * @param <O> The type of buffer output from the decoder.
+ * @param <E> The type of exception thrown from the decoder.
+ */
+public interface Decoder<I, O, E extends Exception> {
+
+ /**
+ * Returns the name of the decoder.
+ *
+ * @return The name of the decoder.
+ */
+ String getName();
+
+ /**
+ * Dequeues the next input buffer to be filled and queued to the decoder.
+ *
+ * @return The input buffer, which will have been cleared, or null if a buffer isn't available.
+ * @throws E If a decoder error has occurred.
+ */
+ @Nullable
+ 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.
+ */
+ @Nullable
+ O dequeueOutputBuffer() throws E;
+
+ /**
+ * Flushes the decoder. Ownership of dequeued input buffers is returned to 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/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderCounters.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderCounters.java
new file mode 100644
index 0000000000..f8bdb9b29a
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderCounters.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+/**
+ * Maintains decoder event counts, for debugging purposes only.
+ * <p>
+ * Counters should be written from the playback thread only. Counters may be read from any thread.
+ * To ensure that the counter values are made visible across threads, users of this class should
+ * invoke {@link #ensureUpdated()} prior to reading and after writing.
+ */
+public final class DecoderCounters {
+
+ /**
+ * The number of times a decoder has been initialized.
+ */
+ public int decoderInitCount;
+ /**
+ * The number of times a decoder has been released.
+ */
+ public int decoderReleaseCount;
+ /**
+ * The number of queued input buffers.
+ */
+ public int inputBufferCount;
+ /**
+ * The number of skipped input buffers.
+ * <p>
+ * A skipped input buffer is an input buffer that was deliberately not sent to the decoder.
+ */
+ public int skippedInputBufferCount;
+ /**
+ * The number of rendered output buffers.
+ */
+ public int renderedOutputBufferCount;
+ /**
+ * The number of skipped output buffers.
+ * <p>
+ * A skipped output buffer is an output buffer that was deliberately not rendered.
+ */
+ public int skippedOutputBufferCount;
+ /**
+ * The number of dropped buffers.
+ * <p>
+ * A dropped buffer is an buffer that was supposed to be decoded/rendered, but was instead
+ * dropped because it could not be rendered in time.
+ */
+ public int droppedBufferCount;
+ /**
+ * The maximum number of dropped buffers without an interleaving rendered output buffer.
+ * <p>
+ * Skipped output buffers are ignored for the purposes of calculating this value.
+ */
+ public int maxConsecutiveDroppedBufferCount;
+ /**
+ * The number of times all buffers to a keyframe were dropped.
+ * <p>
+ * Each time buffers to a keyframe are dropped, this counter is increased by one, and the dropped
+ * buffer counters are increased by one (for the current output buffer) plus the number of buffers
+ * dropped from the source to advance to the keyframe.
+ */
+ public int droppedToKeyframeCount;
+
+ /**
+ * Should be called to ensure counter values are made visible across threads. The playback thread
+ * should call this method after updating the counter values. Any other thread should call this
+ * method before reading the counters.
+ */
+ public synchronized void ensureUpdated() {
+ // Do nothing. The use of synchronized ensures a memory barrier should another thread also
+ // call this method.
+ }
+
+ /**
+ * Merges the counts from {@code other} into this instance.
+ *
+ * @param other The {@link DecoderCounters} to merge into this instance.
+ */
+ public void merge(DecoderCounters other) {
+ decoderInitCount += other.decoderInitCount;
+ decoderReleaseCount += other.decoderReleaseCount;
+ inputBufferCount += other.inputBufferCount;
+ skippedInputBufferCount += other.skippedInputBufferCount;
+ renderedOutputBufferCount += other.renderedOutputBufferCount;
+ skippedOutputBufferCount += other.skippedOutputBufferCount;
+ droppedBufferCount += other.droppedBufferCount;
+ maxConsecutiveDroppedBufferCount = Math.max(maxConsecutiveDroppedBufferCount,
+ other.maxConsecutiveDroppedBufferCount);
+ droppedToKeyframeCount += other.droppedToKeyframeCount;
+ }
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java
new file mode 100644
index 0000000000..254ecfdec8
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
+import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
+
+/**
+ * Holds input for a decoder.
+ */
+public class DecoderInputBuffer extends Buffer {
+
+ /**
+ * The buffer replacement mode, which may disable replacement. One of {@link
+ * #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} or {@link
+ * #BUFFER_REPLACEMENT_MODE_DIRECT}.
+ */
+ @Documented
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ BUFFER_REPLACEMENT_MODE_DISABLED,
+ BUFFER_REPLACEMENT_MODE_NORMAL,
+ BUFFER_REPLACEMENT_MODE_DIRECT
+ })
+ public @interface BufferReplacementMode {}
+ /**
+ * Disallows buffer replacement.
+ */
+ public static final int BUFFER_REPLACEMENT_MODE_DISABLED = 0;
+ /**
+ * Allows buffer replacement using {@link ByteBuffer#allocate(int)}.
+ */
+ public static final int BUFFER_REPLACEMENT_MODE_NORMAL = 1;
+ /**
+ * Allows buffer replacement using {@link ByteBuffer#allocateDirect(int)}.
+ */
+ public static final int BUFFER_REPLACEMENT_MODE_DIRECT = 2;
+
+ /**
+ * {@link CryptoInfo} for encrypted data.
+ */
+ public final CryptoInfo cryptoInfo;
+
+ /** The buffer's data, or {@code null} if no data has been set. */
+ @Nullable public ByteBuffer data;
+
+ // TODO: Remove this temporary signaling once end-of-stream propagation for clips using content
+ // protection is fixed. See [Internal: b/153326944] for details.
+ /**
+ * Whether the last attempt to read a sample into this buffer failed due to not yet having the DRM
+ * keys associated with the next sample.
+ */
+ public boolean waitingForKeys;
+
+ /**
+ * The time at which the sample should be presented.
+ */
+ public long timeUs;
+
+ /**
+ * Supplemental data related to the buffer, if {@link #hasSupplementalData()} returns true. If
+ * present, the buffer is populated with supplemental data from position 0 to its limit.
+ */
+ @Nullable public ByteBuffer supplementalData;
+
+ @BufferReplacementMode private final int bufferReplacementMode;
+
+ /**
+ * Creates a new instance for which {@link #isFlagsOnly()} will return true.
+ *
+ * @return A new flags only input buffer.
+ */
+ public static DecoderInputBuffer newFlagsOnlyInstance() {
+ return new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
+ }
+
+ /**
+ * @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 DecoderInputBuffer(@BufferReplacementMode int bufferReplacementMode) {
+ this.cryptoInfo = new CryptoInfo();
+ this.bufferReplacementMode = bufferReplacementMode;
+ }
+
+ /**
+ * Clears {@link #supplementalData} and ensures that it's large enough to accommodate {@code
+ * length} bytes.
+ *
+ * @param length The length of the supplemental data that must be accommodated, in bytes.
+ */
+ @EnsuresNonNull("supplementalData")
+ public void resetSupplementalData(int length) {
+ if (supplementalData == null || supplementalData.capacity() < length) {
+ supplementalData = ByteBuffer.allocate(length);
+ } else {
+ supplementalData.clear();
+ }
+ }
+
+ /**
+ * 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 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}.
+ */
+ @EnsuresNonNull("data")
+ public void ensureSpaceForWrite(int length) {
+ if (data == null) {
+ data = createReplacementByteBuffer(length);
+ return;
+ }
+ // 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 = createReplacementByteBuffer(requiredCapacity);
+ newData.order(data.order());
+ // Copy data up to the current position from the old buffer to the new one.
+ if (position > 0) {
+ data.flip();
+ newData.put(data);
+ }
+ // Set the new buffer.
+ data = newData;
+ }
+
+ /**
+ * Returns whether the buffer is only able to hold flags, meaning {@link #data} is null and
+ * its replacement mode is {@link #BUFFER_REPLACEMENT_MODE_DISABLED}.
+ */
+ public final boolean isFlagsOnly() {
+ return data == null && bufferReplacementMode == BUFFER_REPLACEMENT_MODE_DISABLED;
+ }
+
+ /**
+ * Returns whether the {@link C#BUFFER_FLAG_ENCRYPTED} flag is set.
+ */
+ public final boolean isEncrypted() {
+ return getFlag(C.BUFFER_FLAG_ENCRYPTED);
+ }
+
+ /**
+ * Flips {@link #data} and {@link #supplementalData} in preparation for being queued to a decoder.
+ *
+ * @see java.nio.Buffer#flip()
+ */
+ public final void flip() {
+ data.flip();
+ if (supplementalData != null) {
+ supplementalData.flip();
+ }
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ if (data != null) {
+ data.clear();
+ }
+ if (supplementalData != null) {
+ supplementalData.clear();
+ }
+ waitingForKeys = false;
+ }
+
+ private ByteBuffer createReplacementByteBuffer(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 + ")");
+ }
+ }
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/OutputBuffer.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/OutputBuffer.java
new file mode 100644
index 0000000000..73a8a7d2fd
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/OutputBuffer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+/**
+ * Output buffer decoded by a {@link Decoder}.
+ */
+public abstract class OutputBuffer extends Buffer {
+
+ /**
+ * The presentation timestamp for the buffer, in microseconds.
+ */
+ public long timeUs;
+
+ /**
+ * The number of buffers immediately prior to this one that were skipped in the {@link Decoder}.
+ */
+ public int skippedOutputBufferCount;
+
+ /**
+ * Releases the output buffer for reuse. Must be called when the buffer is no longer needed.
+ */
+ public abstract void release();
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleDecoder.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleDecoder.java
new file mode 100644
index 0000000000..a193ad3c8e
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleDecoder.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;
+import java.util.ArrayDeque;
+
+/** Base class for {@link Decoder}s that use their own decode thread. */
+@SuppressWarnings("UngroupedOverloads")
+public abstract class SimpleDecoder<
+ I extends DecoderInputBuffer, O extends OutputBuffer, E extends Exception>
+ implements Decoder<I, O, E> {
+
+ private final Thread decodeThread;
+
+ private final Object lock;
+ private final ArrayDeque<I> queuedInputBuffers;
+ private final ArrayDeque<O> queuedOutputBuffers;
+ private final I[] availableInputBuffers;
+ private final O[] availableOutputBuffers;
+
+ private int availableInputBufferCount;
+ private int availableOutputBufferCount;
+ private I dequeuedInputBuffer;
+
+ private E exception;
+ private boolean flushed;
+ private boolean released;
+ private int skippedOutputBufferCount;
+
+ /**
+ * @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 ArrayDeque<>();
+ queuedOutputBuffers = new ArrayDeque<>();
+ 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();
+ }
+ decodeThread = new Thread() {
+ @Override
+ public void run() {
+ SimpleDecoder.this.run();
+ }
+ };
+ decodeThread.start();
+ }
+
+ /**
+ * Sets the initial size of each input buffer.
+ * <p>
+ * 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 (I inputBuffer : availableInputBuffers) {
+ inputBuffer.ensureSpaceForWrite(size);
+ }
+ }
+
+ @Override
+ @Nullable
+ public final I dequeueInputBuffer() throws E {
+ synchronized (lock) {
+ maybeThrowException();
+ Assertions.checkState(dequeuedInputBuffer == null);
+ dequeuedInputBuffer = availableInputBufferCount == 0 ? null
+ : availableInputBuffers[--availableInputBufferCount];
+ return dequeuedInputBuffer;
+ }
+ }
+
+ @Override
+ public final void queueInputBuffer(I inputBuffer) throws E {
+ synchronized (lock) {
+ maybeThrowException();
+ Assertions.checkArgument(inputBuffer == dequeuedInputBuffer);
+ queuedInputBuffers.addLast(inputBuffer);
+ maybeNotifyDecodeLoop();
+ dequeuedInputBuffer = null;
+ }
+ }
+
+ @Override
+ @Nullable
+ 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.
+ */
+ @CallSuper
+ protected void releaseOutputBuffer(O outputBuffer) {
+ synchronized (lock) {
+ releaseOutputBufferInternal(outputBuffer);
+ maybeNotifyDecodeLoop();
+ }
+ }
+
+ @Override
+ public final void flush() {
+ synchronized (lock) {
+ flushed = true;
+ skippedOutputBufferCount = 0;
+ if (dequeuedInputBuffer != null) {
+ releaseInputBufferInternal(dequeuedInputBuffer);
+ dequeuedInputBuffer = null;
+ }
+ while (!queuedInputBuffers.isEmpty()) {
+ releaseInputBufferInternal(queuedInputBuffers.removeFirst());
+ }
+ while (!queuedOutputBuffers.isEmpty()) {
+ queuedOutputBuffers.removeFirst().release();
+ }
+ exception = null;
+ }
+ }
+
+ @CallSuper
+ @Override
+ public void release() {
+ synchronized (lock) {
+ released = true;
+ lock.notify();
+ }
+ try {
+ decodeThread.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.
+ * <p>
+ * Should only be called whilst synchronized on the lock object.
+ */
+ private void maybeNotifyDecodeLoop() {
+ if (canDecodeBuffer()) {
+ lock.notify();
+ }
+ }
+
+ private 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;
+ boolean resetDecoder;
+
+ // 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];
+ resetDecoder = flushed;
+ flushed = false;
+ }
+
+ if (inputBuffer.isEndOfStream()) {
+ outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
+ } else {
+ if (inputBuffer.isDecodeOnly()) {
+ outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
+ }
+ @Nullable E exception;
+ try {
+ exception = decode(inputBuffer, outputBuffer, resetDecoder);
+ } catch (RuntimeException e) {
+ // This can occur if a sample is malformed in a way that the decoder is not robust against.
+ // We don't want the process to die in this case, but we do want to propagate the error.
+ exception = createUnexpectedDecodeException(e);
+ } catch (OutOfMemoryError e) {
+ // This can occur if a sample is malformed in a way that causes the decoder to think it
+ // needs to allocate a large amount of memory. We don't want the process to die in this
+ // case, but we do want to propagate the error.
+ exception = createUnexpectedDecodeException(e);
+ }
+ if (exception != null) {
+ synchronized (lock) {
+ this.exception = exception;
+ }
+ return false;
+ }
+ }
+
+ synchronized (lock) {
+ if (flushed) {
+ outputBuffer.release();
+ } else if (outputBuffer.isDecodeOnly()) {
+ skippedOutputBufferCount++;
+ outputBuffer.release();
+ } else {
+ outputBuffer.skippedOutputBufferCount = skippedOutputBufferCount;
+ skippedOutputBufferCount = 0;
+ queuedOutputBuffers.addLast(outputBuffer);
+ }
+ // Make the input buffer available again.
+ releaseInputBufferInternal(inputBuffer);
+ }
+
+ return true;
+ }
+
+ private boolean canDecodeBuffer() {
+ return !queuedInputBuffers.isEmpty() && availableOutputBufferCount > 0;
+ }
+
+ private void releaseInputBufferInternal(I inputBuffer) {
+ inputBuffer.clear();
+ availableInputBuffers[availableInputBufferCount++] = inputBuffer;
+ }
+
+ private void releaseOutputBufferInternal(O outputBuffer) {
+ outputBuffer.clear();
+ availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
+ }
+
+ /**
+ * Creates a new input buffer.
+ */
+ protected abstract I createInputBuffer();
+
+ /**
+ * Creates a new output buffer.
+ */
+ protected abstract O createOutputBuffer();
+
+ /**
+ * Creates an exception to propagate for an unexpected decode error.
+ *
+ * @param error The unexpected decode error.
+ * @return The exception to propagate.
+ */
+ protected abstract E createUnexpectedDecodeException(Throwable error);
+
+ /**
+ * 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. The flag {@link
+ * C#BUFFER_FLAG_DECODE_ONLY} will be set if the same flag is set on {@code inputBuffer}, but
+ * may be set/unset as required. If the flag is set when the call returns then the output
+ * buffer will not be made available to dequeue. The output buffer may not have been populated
+ * in this case.
+ * @param reset Whether the decoder must be reset before decoding.
+ * @return A decoder exception if an error occurred, or null if decoding was successful.
+ */
+ @Nullable
+ protected abstract E decode(I inputBuffer, O outputBuffer, boolean reset);
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java
new file mode 100644
index 0000000000..4b80d38e54
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+import androidx.annotation.Nullable;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Buffer for {@link SimpleDecoder} output.
+ */
+public class SimpleOutputBuffer extends OutputBuffer {
+
+ private final SimpleDecoder<?, SimpleOutputBuffer, ?> owner;
+
+ @Nullable public ByteBuffer data;
+
+ public SimpleOutputBuffer(SimpleDecoder<?, SimpleOutputBuffer, ?> owner) {
+ this.owner = owner;
+ }
+
+ /**
+ * Initializes the buffer.
+ *
+ * @param timeUs The presentation timestamp for the buffer, in microseconds.
+ * @param size An upper bound on the size of the data that will be written to the buffer.
+ * @return The {@link #data} buffer, for convenience.
+ */
+ public ByteBuffer init(long timeUs, int size) {
+ this.timeUs = timeUs;
+ if (data == null || data.capacity() < size) {
+ data = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+ }
+ data.position(0);
+ data.limit(size);
+ return data;
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ if (data != null) {
+ data.clear();
+ }
+ }
+
+ @Override
+ public void release() {
+ owner.releaseOutputBuffer(this);
+ }
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/package-info.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/package-info.java
new file mode 100644
index 0000000000..78a2c9f2e2
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+@NonNullApi
+package org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;
+
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.NonNullApi;