FFmpeg extension: Correctly pad input buffers
FFmpeg requires input buffers to be sized larger than the size of the data they contain. This is to allow optimized decoder implementations that read data in fixed size chunks, without the risk of such decoders reading beyond the end of the buffer. Issue: #2159 PiperOrigin-RevId: 310946866
This commit is contained in:
parent
dbff731d1f
commit
0d22d02df5
@ -20,6 +20,7 @@ import static java.lang.Runtime.getRuntime;
|
|||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.android.exoplayer2.video.VideoDecoderInputBuffer;
|
import com.google.android.exoplayer2.video.VideoDecoderInputBuffer;
|
||||||
@ -94,7 +95,7 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected VideoDecoderInputBuffer createInputBuffer() {
|
protected VideoDecoderInputBuffer createInputBuffer() {
|
||||||
return new VideoDecoderInputBuffer();
|
return new VideoDecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,10 +52,10 @@ import java.util.List;
|
|||||||
private volatile int sampleRate;
|
private volatile int sampleRate;
|
||||||
|
|
||||||
public FfmpegAudioDecoder(
|
public FfmpegAudioDecoder(
|
||||||
|
Format format,
|
||||||
int numInputBuffers,
|
int numInputBuffers,
|
||||||
int numOutputBuffers,
|
int numOutputBuffers,
|
||||||
int initialInputBufferSize,
|
int initialInputBufferSize,
|
||||||
Format format,
|
|
||||||
boolean outputFloat)
|
boolean outputFloat)
|
||||||
throws FfmpegDecoderException {
|
throws FfmpegDecoderException {
|
||||||
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
|
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
|
||||||
@ -82,7 +82,9 @@ import java.util.List;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DecoderInputBuffer createInputBuffer() {
|
protected DecoderInputBuffer createInputBuffer() {
|
||||||
return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
return new DecoderInputBuffer(
|
||||||
|
DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT,
|
||||||
|
FfmpegLibrary.getInputBufferPaddingSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,7 +125,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer {
|
|||||||
format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;
|
format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;
|
||||||
decoder =
|
decoder =
|
||||||
new FfmpegAudioDecoder(
|
new FfmpegAudioDecoder(
|
||||||
NUM_BUFFERS, NUM_BUFFERS, initialInputBufferSize, format, shouldUseFloatOutput(format));
|
format, NUM_BUFFERS, NUM_BUFFERS, initialInputBufferSize, shouldUseFloatOutput(format));
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
return decoder;
|
return decoder;
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,12 @@
|
|||||||
package com.google.android.exoplayer2.ext.ffmpeg;
|
package com.google.android.exoplayer2.ext.ffmpeg;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
||||||
import com.google.android.exoplayer2.util.LibraryLoader;
|
import com.google.android.exoplayer2.util.LibraryLoader;
|
||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures and queries the underlying native library.
|
* Configures and queries the underlying native library.
|
||||||
@ -35,6 +37,9 @@ public final class FfmpegLibrary {
|
|||||||
private static final LibraryLoader LOADER =
|
private static final LibraryLoader LOADER =
|
||||||
new LibraryLoader("avutil", "swresample", "avcodec", "ffmpeg");
|
new LibraryLoader("avutil", "swresample", "avcodec", "ffmpeg");
|
||||||
|
|
||||||
|
private static @MonotonicNonNull String version;
|
||||||
|
private static int inputBufferPaddingSize = C.LENGTH_UNSET;
|
||||||
|
|
||||||
private FfmpegLibrary() {}
|
private FfmpegLibrary() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +63,27 @@ public final class FfmpegLibrary {
|
|||||||
/** Returns the version of the underlying library if available, or null otherwise. */
|
/** Returns the version of the underlying library if available, or null otherwise. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String getVersion() {
|
public static String getVersion() {
|
||||||
return isAvailable() ? ffmpegGetVersion() : null;
|
if (!isAvailable()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (version == null) {
|
||||||
|
version = ffmpegGetVersion();
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the required amount of padding for input buffers in bytes, or {@link C#LENGTH_UNSET} if
|
||||||
|
* the underlying library is not available.
|
||||||
|
*/
|
||||||
|
public static int getInputBufferPaddingSize() {
|
||||||
|
if (!isAvailable()) {
|
||||||
|
return C.LENGTH_UNSET;
|
||||||
|
}
|
||||||
|
if (inputBufferPaddingSize == C.LENGTH_UNSET) {
|
||||||
|
inputBufferPaddingSize = ffmpegGetInputBufferPaddingSize();
|
||||||
|
}
|
||||||
|
return inputBufferPaddingSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,6 +155,8 @@ public final class FfmpegLibrary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static native String ffmpegGetVersion();
|
private static native String ffmpegGetVersion();
|
||||||
private static native boolean ffmpegHasDecoder(String codecName);
|
|
||||||
|
|
||||||
|
private static native int ffmpegGetInputBufferPaddingSize();
|
||||||
|
|
||||||
|
private static native boolean ffmpegHasDecoder(String codecName);
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,10 @@ LIBRARY_FUNC(jstring, ffmpegGetVersion) {
|
|||||||
return env->NewStringUTF(LIBAVCODEC_IDENT);
|
return env->NewStringUTF(LIBAVCODEC_IDENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LIBRARY_FUNC(jint, ffmpegGetInputBufferPaddingSize) {
|
||||||
|
return (jint)AV_INPUT_BUFFER_PADDING_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
|
LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
|
||||||
return getCodecByName(env, codecName) != NULL;
|
return getCodecByName(env, codecName) != NULL;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import android.view.Surface;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.decoder.CryptoInfo;
|
import com.google.android.exoplayer2.decoder.CryptoInfo;
|
||||||
|
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||||
import com.google.android.exoplayer2.drm.DecryptionException;
|
import com.google.android.exoplayer2.drm.DecryptionException;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||||
@ -97,7 +98,7 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected VideoDecoderInputBuffer createInputBuffer() {
|
protected VideoDecoderInputBuffer createInputBuffer() {
|
||||||
return new VideoDecoderInputBuffer();
|
return new VideoDecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -963,7 +963,7 @@ public final class C {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Mode specifying whether the player should hold a WakeLock and a WifiLock. One of {@link
|
* Mode specifying whether the player should hold a WakeLock and a WifiLock. One of {@link
|
||||||
* #WAKE_MODE_NONE}, {@link #WAKE_MODE_LOCAL} and {@link #WAKE_MODE_NETWORK}.
|
* #WAKE_MODE_NONE}, {@link #WAKE_MODE_LOCAL} or {@link #WAKE_MODE_NETWORK}.
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@ -30,7 +30,8 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
|||||||
public class DecoderInputBuffer extends Buffer {
|
public class DecoderInputBuffer extends Buffer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer replacement mode, which may disable replacement. One of {@link
|
* The buffer replacement mode. This controls how {@link #ensureSpaceForWrite} generates
|
||||||
|
* replacement buffers when the capacity of the existing buffer is insufficient. One of {@link
|
||||||
* #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} or {@link
|
* #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} or {@link
|
||||||
* #BUFFER_REPLACEMENT_MODE_DIRECT}.
|
* #BUFFER_REPLACEMENT_MODE_DIRECT}.
|
||||||
*/
|
*/
|
||||||
@ -83,6 +84,7 @@ public class DecoderInputBuffer extends Buffer {
|
|||||||
@Nullable public ByteBuffer supplementalData;
|
@Nullable public ByteBuffer supplementalData;
|
||||||
|
|
||||||
@BufferReplacementMode private final int bufferReplacementMode;
|
@BufferReplacementMode private final int bufferReplacementMode;
|
||||||
|
private final int paddingSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance for which {@link #isFlagsOnly()} will return true.
|
* Creates a new instance for which {@link #isFlagsOnly()} will return true.
|
||||||
@ -94,13 +96,28 @@ public class DecoderInputBuffer extends Buffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bufferReplacementMode Determines the behavior of {@link #ensureSpaceForWrite(int)}. One
|
* Creates a new instance.
|
||||||
* of {@link #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} and
|
*
|
||||||
* {@link #BUFFER_REPLACEMENT_MODE_DIRECT}.
|
* @param bufferReplacementMode The {@link BufferReplacementMode} replacement mode.
|
||||||
*/
|
*/
|
||||||
public DecoderInputBuffer(@BufferReplacementMode int bufferReplacementMode) {
|
public DecoderInputBuffer(@BufferReplacementMode int bufferReplacementMode) {
|
||||||
|
this(bufferReplacementMode, /* paddingSize= */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param bufferReplacementMode The {@link BufferReplacementMode} replacement mode.
|
||||||
|
* @param paddingSize If non-zero, {@link #ensureSpaceForWrite(int)} will ensure that the buffer
|
||||||
|
* is this number of bytes larger than the requested length. This can be useful for decoders
|
||||||
|
* that consume data in fixed size blocks, for efficiency. Setting the padding size to the
|
||||||
|
* decoder's fixed read size is necessary to prevent such a decoder from trying to read beyond
|
||||||
|
* the end of the buffer.
|
||||||
|
*/
|
||||||
|
public DecoderInputBuffer(@BufferReplacementMode int bufferReplacementMode, int paddingSize) {
|
||||||
this.cryptoInfo = new CryptoInfo();
|
this.cryptoInfo = new CryptoInfo();
|
||||||
this.bufferReplacementMode = bufferReplacementMode;
|
this.bufferReplacementMode = bufferReplacementMode;
|
||||||
|
this.paddingSize = paddingSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,24 +149,27 @@ public class DecoderInputBuffer extends Buffer {
|
|||||||
*/
|
*/
|
||||||
@EnsuresNonNull("data")
|
@EnsuresNonNull("data")
|
||||||
public void ensureSpaceForWrite(int length) {
|
public void ensureSpaceForWrite(int length) {
|
||||||
if (data == null) {
|
length += paddingSize;
|
||||||
|
@Nullable ByteBuffer currentData = data;
|
||||||
|
if (currentData == null) {
|
||||||
data = createReplacementByteBuffer(length);
|
data = createReplacementByteBuffer(length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check whether the current buffer is sufficient.
|
// Check whether the current buffer is sufficient.
|
||||||
int capacity = data.capacity();
|
int capacity = currentData.capacity();
|
||||||
int position = data.position();
|
int position = currentData.position();
|
||||||
int requiredCapacity = position + length;
|
int requiredCapacity = position + length;
|
||||||
if (capacity >= requiredCapacity) {
|
if (capacity >= requiredCapacity) {
|
||||||
|
data = currentData;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Instantiate a new buffer if possible.
|
// Instantiate a new buffer if possible.
|
||||||
ByteBuffer newData = createReplacementByteBuffer(requiredCapacity);
|
ByteBuffer newData = createReplacementByteBuffer(requiredCapacity);
|
||||||
newData.order(data.order());
|
newData.order(currentData.order());
|
||||||
// Copy data up to the current position from the old buffer to the new one.
|
// Copy data up to the current position from the old buffer to the new one.
|
||||||
if (position > 0) {
|
if (position > 0) {
|
||||||
data.flip();
|
currentData.flip();
|
||||||
newData.put(data);
|
newData.put(currentData);
|
||||||
}
|
}
|
||||||
// Set the new buffer.
|
// Set the new buffer.
|
||||||
data = newData;
|
data = newData;
|
||||||
@ -176,7 +196,9 @@ public class DecoderInputBuffer extends Buffer {
|
|||||||
* @see java.nio.Buffer#flip()
|
* @see java.nio.Buffer#flip()
|
||||||
*/
|
*/
|
||||||
public final void flip() {
|
public final void flip() {
|
||||||
data.flip();
|
if (data != null) {
|
||||||
|
data.flip();
|
||||||
|
}
|
||||||
if (supplementalData != null) {
|
if (supplementalData != null) {
|
||||||
supplementalData.flip();
|
supplementalData.flip();
|
||||||
}
|
}
|
||||||
@ -205,5 +227,4 @@ public class DecoderInputBuffer extends Buffer {
|
|||||||
+ requiredCapacity + ")");
|
+ requiredCapacity + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.exoplayer2.decoder;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Unit tests for {@link DecoderInputBuffer} */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class DecoderInputBufferTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureSpaceForWrite_replacementModeDisabled_doesNothingIfResizeNotNeeded() {
|
||||||
|
DecoderInputBuffer buffer =
|
||||||
|
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
|
ByteBuffer data = ByteBuffer.allocate(32);
|
||||||
|
buffer.data = data;
|
||||||
|
buffer.ensureSpaceForWrite(32);
|
||||||
|
assertThat(buffer.data).isSameInstanceAs(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureSpaceForWrite_replacementModeDisabled_failsIfResizeNeeded() {
|
||||||
|
DecoderInputBuffer buffer =
|
||||||
|
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
|
buffer.data = ByteBuffer.allocate(16);
|
||||||
|
assertThrows(IllegalStateException.class, () -> buffer.ensureSpaceForWrite(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureSpaceForWrite_usesPaddingSize() {
|
||||||
|
DecoderInputBuffer buffer =
|
||||||
|
new DecoderInputBuffer(
|
||||||
|
DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL, /* paddingSize= */ 16);
|
||||||
|
buffer.data = ByteBuffer.allocate(32);
|
||||||
|
buffer.ensureSpaceForWrite(32);
|
||||||
|
assertThat(buffer.data.capacity()).isEqualTo(32 + 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureSpaceForWrite_usesPosition() {
|
||||||
|
DecoderInputBuffer buffer =
|
||||||
|
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
|
||||||
|
buffer.data = ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7});
|
||||||
|
buffer.data.position(4);
|
||||||
|
buffer.ensureSpaceForWrite(12);
|
||||||
|
// The new capacity should be the current position (4) + the required space (12).
|
||||||
|
assertThat(buffer.data.capacity()).isEqualTo(4 + 12);
|
||||||
|
// The current position should have been retained.
|
||||||
|
assertThat(buffer.data.position()).isEqualTo(4);
|
||||||
|
// Data should have been copied up to the current position.
|
||||||
|
byte[] expectedData = Arrays.copyOf(new byte[] {0, 1, 2, 3}, 16);
|
||||||
|
assertThat(buffer.data.array()).isEqualTo(expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureSpaceForWrite_copiesByteOrder() {
|
||||||
|
DecoderInputBuffer buffer =
|
||||||
|
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
|
||||||
|
buffer.data = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
buffer.ensureSpaceForWrite(16);
|
||||||
|
assertThat(buffer.data.order()).isEqualTo(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
buffer.data = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
buffer.ensureSpaceForWrite(16);
|
||||||
|
assertThat(buffer.data.order()).isEqualTo(ByteOrder.BIG_ENDIAN);
|
||||||
|
}
|
||||||
|
}
|
@ -24,8 +24,31 @@ public class VideoDecoderInputBuffer extends DecoderInputBuffer {
|
|||||||
|
|
||||||
@Nullable public Format format;
|
@Nullable public Format format;
|
||||||
|
|
||||||
public VideoDecoderInputBuffer() {
|
/**
|
||||||
super(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @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 VideoDecoderInputBuffer(@BufferReplacementMode int bufferReplacementMode) {
|
||||||
|
super(bufferReplacementMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @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}.
|
||||||
|
* @param paddingSize If non-zero, {@link #ensureSpaceForWrite(int)} will ensure that the buffer
|
||||||
|
* is this number of bytes larger than the requested length. This can be useful for decoders
|
||||||
|
* that consume data in fixed size blocks, for efficiency. Setting the padding size to the
|
||||||
|
* decoder's fixed read size is necessary to prevent such a decoder from trying to read beyond
|
||||||
|
* the end of the buffer.
|
||||||
|
*/
|
||||||
|
public VideoDecoderInputBuffer(
|
||||||
|
@BufferReplacementMode int bufferReplacementMode, int paddingSize) {
|
||||||
|
super(bufferReplacementMode, paddingSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import com.google.android.exoplayer2.Format;
|
|||||||
import com.google.android.exoplayer2.RendererCapabilities;
|
import com.google.android.exoplayer2.RendererCapabilities;
|
||||||
import com.google.android.exoplayer2.RendererConfiguration;
|
import com.google.android.exoplayer2.RendererConfiguration;
|
||||||
import com.google.android.exoplayer2.decoder.DecoderException;
|
import com.google.android.exoplayer2.decoder.DecoderException;
|
||||||
|
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||||
import com.google.android.exoplayer2.testutil.FakeSampleStream;
|
import com.google.android.exoplayer2.testutil.FakeSampleStream;
|
||||||
@ -141,7 +142,8 @@ public final class DecoderVideoRendererTest {
|
|||||||
new VideoDecoderInputBuffer[10], new VideoDecoderOutputBuffer[10]) {
|
new VideoDecoderInputBuffer[10], new VideoDecoderOutputBuffer[10]) {
|
||||||
@Override
|
@Override
|
||||||
protected VideoDecoderInputBuffer createInputBuffer() {
|
protected VideoDecoderInputBuffer createInputBuffer() {
|
||||||
return new VideoDecoderInputBuffer();
|
return new VideoDecoderInputBuffer(
|
||||||
|
DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user