Refactor nullness checks in renderers.
`checkNotNull` should be avoided where possible. This change adds `@EnsuresNonNull` or `@EnsuresNonNullIf` to configuration methods for fields they initialize. `checkNotNull` is now avoided for the `@MonotonicNonNull` formats by adding `@RequiresNonNull` annotations. `checkNotNull` is now avoided for the encoder and decoder in `feedMuxerFromEncoder()`, `feedEncoderFromDecoder()`, `feedDecoderFromInput()`, etc. by creating local variables for `encoder` and `decoder` in `render` after the configuration method calls and passing these as non-null parameters. PiperOrigin-RevId: 405893824
This commit is contained in:
parent
60a68f8891
commit
399172d63f
@ -36,6 +36,9 @@ import androidx.media3.exoplayer.audio.SonicAudioProcessor;
|
|||||||
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
@RequiresApi(18)
|
@RequiresApi(18)
|
||||||
/* package */ final class TransformerAudioRenderer extends TransformerBaseRenderer {
|
/* package */ final class TransformerAudioRenderer extends TransformerBaseRenderer {
|
||||||
@ -51,8 +54,8 @@ import java.nio.ByteBuffer;
|
|||||||
@Nullable private MediaCodecAdapterWrapper decoder;
|
@Nullable private MediaCodecAdapterWrapper decoder;
|
||||||
@Nullable private MediaCodecAdapterWrapper encoder;
|
@Nullable private MediaCodecAdapterWrapper encoder;
|
||||||
@Nullable private SpeedProvider speedProvider;
|
@Nullable private SpeedProvider speedProvider;
|
||||||
@Nullable private Format decoderInputFormat;
|
private @MonotonicNonNull Format decoderInputFormat;
|
||||||
@Nullable private AudioFormat encoderInputAudioFormat;
|
private @MonotonicNonNull AudioFormat encoderInputAudioFormat;
|
||||||
|
|
||||||
private ByteBuffer sonicOutputBuffer;
|
private ByteBuffer sonicOutputBuffer;
|
||||||
private long nextEncoderInputBufferTimeUs;
|
private long nextEncoderInputBufferTimeUs;
|
||||||
@ -100,8 +103,6 @@ import java.nio.ByteBuffer;
|
|||||||
encoder = null;
|
encoder = null;
|
||||||
}
|
}
|
||||||
speedProvider = null;
|
speedProvider = null;
|
||||||
decoderInputFormat = null;
|
|
||||||
encoderInputAudioFormat = null;
|
|
||||||
sonicOutputBuffer = AudioProcessor.EMPTY_BUFFER;
|
sonicOutputBuffer = AudioProcessor.EMPTY_BUFFER;
|
||||||
nextEncoderInputBufferTimeUs = 0;
|
nextEncoderInputBufferTimeUs = 0;
|
||||||
currentSpeed = SPEED_UNSET;
|
currentSpeed = SPEED_UNSET;
|
||||||
@ -117,16 +118,18 @@ import java.nio.ByteBuffer;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ensureDecoderConfigured()) {
|
if (ensureDecoderConfigured()) {
|
||||||
|
MediaCodecAdapterWrapper decoder = this.decoder;
|
||||||
if (ensureEncoderAndAudioProcessingConfigured()) {
|
if (ensureEncoderAndAudioProcessingConfigured()) {
|
||||||
while (feedMuxerFromEncoder()) {}
|
MediaCodecAdapterWrapper encoder = this.encoder;
|
||||||
|
while (feedMuxerFromEncoder(encoder)) {}
|
||||||
if (sonicAudioProcessor.isActive()) {
|
if (sonicAudioProcessor.isActive()) {
|
||||||
while (feedEncoderFromSonic()) {}
|
while (feedEncoderFromSonic(decoder, encoder)) {}
|
||||||
while (feedSonicFromDecoder()) {}
|
while (feedSonicFromDecoder(decoder)) {}
|
||||||
} else {
|
} else {
|
||||||
while (feedEncoderFromDecoder()) {}
|
while (feedEncoderFromDecoder(decoder, encoder)) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (feedDecoderFromInput()) {}
|
while (feedDecoderFromInput(decoder)) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,8 +137,7 @@ import java.nio.ByteBuffer;
|
|||||||
* Attempts to write encoder output data to the muxer, and returns whether it may be possible to
|
* Attempts to write encoder output data to the muxer, and returns whether it may be possible to
|
||||||
* write more data immediately by calling this method again.
|
* write more data immediately by calling this method again.
|
||||||
*/
|
*/
|
||||||
private boolean feedMuxerFromEncoder() {
|
private boolean feedMuxerFromEncoder(MediaCodecAdapterWrapper encoder) {
|
||||||
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
|
||||||
if (!hasEncoderOutputFormat) {
|
if (!hasEncoderOutputFormat) {
|
||||||
@Nullable Format encoderOutputFormat = encoder.getOutputFormat();
|
@Nullable Format encoderOutputFormat = encoder.getOutputFormat();
|
||||||
if (encoderOutputFormat == null) {
|
if (encoderOutputFormat == null) {
|
||||||
@ -170,15 +172,15 @@ import java.nio.ByteBuffer;
|
|||||||
* Attempts to pass decoder output data to the encoder, and returns whether it may be possible to
|
* Attempts to pass decoder output data to the encoder, and returns whether it may be possible to
|
||||||
* pass more data immediately by calling this method again.
|
* pass more data immediately by calling this method again.
|
||||||
*/
|
*/
|
||||||
private boolean feedEncoderFromDecoder() {
|
@RequiresNonNull({"encoderInputAudioFormat"})
|
||||||
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
|
private boolean feedEncoderFromDecoder(
|
||||||
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
MediaCodecAdapterWrapper decoder, MediaCodecAdapterWrapper encoder) {
|
||||||
if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) {
|
if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decoder.isEnded()) {
|
if (decoder.isEnded()) {
|
||||||
queueEndOfStreamToEncoder();
|
queueEndOfStreamToEncoder(encoder);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +192,7 @@ import java.nio.ByteBuffer;
|
|||||||
flushSonicAndSetSpeed(currentSpeed);
|
flushSonicAndSetSpeed(currentSpeed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
feedEncoder(decoderOutputBuffer);
|
feedEncoder(encoder, decoderOutputBuffer);
|
||||||
if (!decoderOutputBuffer.hasRemaining()) {
|
if (!decoderOutputBuffer.hasRemaining()) {
|
||||||
decoder.releaseOutputBuffer();
|
decoder.releaseOutputBuffer();
|
||||||
}
|
}
|
||||||
@ -201,8 +203,9 @@ import java.nio.ByteBuffer;
|
|||||||
* Attempts to pass audio processor output data to the encoder, and returns whether it may be
|
* Attempts to pass audio processor output data to the encoder, and returns whether it may be
|
||||||
* possible to pass more data immediately by calling this method again.
|
* possible to pass more data immediately by calling this method again.
|
||||||
*/
|
*/
|
||||||
private boolean feedEncoderFromSonic() {
|
@RequiresNonNull({"encoderInputAudioFormat"})
|
||||||
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
private boolean feedEncoderFromSonic(
|
||||||
|
MediaCodecAdapterWrapper decoder, MediaCodecAdapterWrapper encoder) {
|
||||||
if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) {
|
if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -210,14 +213,14 @@ import java.nio.ByteBuffer;
|
|||||||
if (!sonicOutputBuffer.hasRemaining()) {
|
if (!sonicOutputBuffer.hasRemaining()) {
|
||||||
sonicOutputBuffer = sonicAudioProcessor.getOutput();
|
sonicOutputBuffer = sonicAudioProcessor.getOutput();
|
||||||
if (!sonicOutputBuffer.hasRemaining()) {
|
if (!sonicOutputBuffer.hasRemaining()) {
|
||||||
if (checkNotNull(decoder).isEnded() && sonicAudioProcessor.isEnded()) {
|
if (decoder.isEnded() && sonicAudioProcessor.isEnded()) {
|
||||||
queueEndOfStreamToEncoder();
|
queueEndOfStreamToEncoder(encoder);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
feedEncoder(sonicOutputBuffer);
|
feedEncoder(encoder, sonicOutputBuffer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,9 +228,7 @@ import java.nio.ByteBuffer;
|
|||||||
* Attempts to process decoder output data, and returns whether it may be possible to process more
|
* Attempts to process decoder output data, and returns whether it may be possible to process more
|
||||||
* data immediately by calling this method again.
|
* data immediately by calling this method again.
|
||||||
*/
|
*/
|
||||||
private boolean feedSonicFromDecoder() {
|
private boolean feedSonicFromDecoder(MediaCodecAdapterWrapper decoder) {
|
||||||
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
|
|
||||||
|
|
||||||
if (drainingSonicForSpeedChange) {
|
if (drainingSonicForSpeedChange) {
|
||||||
if (sonicAudioProcessor.isEnded() && !sonicOutputBuffer.hasRemaining()) {
|
if (sonicAudioProcessor.isEnded() && !sonicOutputBuffer.hasRemaining()) {
|
||||||
flushSonicAndSetSpeed(currentSpeed);
|
flushSonicAndSetSpeed(currentSpeed);
|
||||||
@ -268,8 +269,7 @@ import java.nio.ByteBuffer;
|
|||||||
* Attempts to pass input data to the decoder, and returns whether it may be possible to pass more
|
* Attempts to pass input data to the decoder, and returns whether it may be possible to pass more
|
||||||
* data immediately by calling this method again.
|
* data immediately by calling this method again.
|
||||||
*/
|
*/
|
||||||
private boolean feedDecoderFromInput() {
|
private boolean feedDecoderFromInput(MediaCodecAdapterWrapper decoder) {
|
||||||
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
|
|
||||||
if (!decoder.maybeDequeueInputBuffer(decoderInputBuffer)) {
|
if (!decoder.maybeDequeueInputBuffer(decoderInputBuffer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -295,9 +295,8 @@ import java.nio.ByteBuffer;
|
|||||||
* Feeds as much data as possible between the current position and limit of the specified {@link
|
* Feeds as much data as possible between the current position and limit of the specified {@link
|
||||||
* ByteBuffer} to the encoder, and advances its position by the number of bytes fed.
|
* ByteBuffer} to the encoder, and advances its position by the number of bytes fed.
|
||||||
*/
|
*/
|
||||||
private void feedEncoder(ByteBuffer inputBuffer) {
|
@RequiresNonNull({"encoderInputAudioFormat"})
|
||||||
AudioFormat encoderInputAudioFormat = checkNotNull(this.encoderInputAudioFormat);
|
private void feedEncoder(MediaCodecAdapterWrapper encoder, ByteBuffer inputBuffer) {
|
||||||
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
|
||||||
ByteBuffer encoderInputBufferData = checkNotNull(encoderInputBuffer.data);
|
ByteBuffer encoderInputBufferData = checkNotNull(encoderInputBuffer.data);
|
||||||
int bufferLimit = inputBuffer.limit();
|
int bufferLimit = inputBuffer.limit();
|
||||||
inputBuffer.limit(min(bufferLimit, inputBuffer.position() + encoderInputBufferData.capacity()));
|
inputBuffer.limit(min(bufferLimit, inputBuffer.position() + encoderInputBufferData.capacity()));
|
||||||
@ -314,8 +313,7 @@ import java.nio.ByteBuffer;
|
|||||||
encoder.queueInputBuffer(encoderInputBuffer);
|
encoder.queueInputBuffer(encoderInputBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueEndOfStreamToEncoder() {
|
private void queueEndOfStreamToEncoder(MediaCodecAdapterWrapper encoder) {
|
||||||
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
|
||||||
checkState(checkNotNull(encoderInputBuffer.data).position() == 0);
|
checkState(checkNotNull(encoderInputBuffer.data).position() == 0);
|
||||||
encoderInputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
encoderInputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
encoderInputBuffer.flip();
|
encoderInputBuffer.flip();
|
||||||
@ -327,11 +325,15 @@ import java.nio.ByteBuffer;
|
|||||||
* Attempts to configure the {@link #encoder} and Sonic (if applicable), if they have not been
|
* Attempts to configure the {@link #encoder} and Sonic (if applicable), if they have not been
|
||||||
* configured yet, and returns whether they have been configured.
|
* configured yet, and returns whether they have been configured.
|
||||||
*/
|
*/
|
||||||
|
@RequiresNonNull({"decoder", "decoderInputFormat"})
|
||||||
|
@EnsuresNonNullIf(
|
||||||
|
expression = {"encoder", "encoderInputAudioFormat"},
|
||||||
|
result = true)
|
||||||
private boolean ensureEncoderAndAudioProcessingConfigured() throws ExoPlaybackException {
|
private boolean ensureEncoderAndAudioProcessingConfigured() throws ExoPlaybackException {
|
||||||
if (encoder != null) {
|
if (encoder != null && encoderInputAudioFormat != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
|
MediaCodecAdapterWrapper decoder = this.decoder;
|
||||||
@Nullable Format decoderOutputFormat = decoder.getOutputFormat();
|
@Nullable Format decoderOutputFormat = decoder.getOutputFormat();
|
||||||
if (decoderOutputFormat == null) {
|
if (decoderOutputFormat == null) {
|
||||||
return false;
|
return false;
|
||||||
@ -352,7 +354,7 @@ import java.nio.ByteBuffer;
|
|||||||
}
|
}
|
||||||
String audioMimeType =
|
String audioMimeType =
|
||||||
transformation.audioMimeType == null
|
transformation.audioMimeType == null
|
||||||
? checkNotNull(decoderInputFormat).sampleMimeType
|
? decoderInputFormat.sampleMimeType
|
||||||
: transformation.audioMimeType;
|
: transformation.audioMimeType;
|
||||||
try {
|
try {
|
||||||
encoder =
|
encoder =
|
||||||
@ -375,8 +377,11 @@ import java.nio.ByteBuffer;
|
|||||||
* Attempts to configure the {@link #decoder} if it has not been configured yet, and returns
|
* Attempts to configure the {@link #decoder} if it has not been configured yet, and returns
|
||||||
* whether the decoder has been configured.
|
* whether the decoder has been configured.
|
||||||
*/
|
*/
|
||||||
|
@EnsuresNonNullIf(
|
||||||
|
expression = {"decoderInputFormat", "decoder"},
|
||||||
|
result = true)
|
||||||
private boolean ensureDecoderConfigured() throws ExoPlaybackException {
|
private boolean ensureDecoderConfigured() throws ExoPlaybackException {
|
||||||
if (decoder != null) {
|
if (decoder != null && decoderInputFormat != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,6 +391,7 @@ import java.nio.ByteBuffer;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
decoderInputFormat = checkNotNull(formatHolder.format);
|
decoderInputFormat = checkNotNull(formatHolder.format);
|
||||||
|
MediaCodecAdapterWrapper decoder;
|
||||||
try {
|
try {
|
||||||
decoder = MediaCodecAdapterWrapper.createForAudioDecoding(decoderInputFormat);
|
decoder = MediaCodecAdapterWrapper.createForAudioDecoding(decoderInputFormat);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -394,6 +400,7 @@ import java.nio.ByteBuffer;
|
|||||||
}
|
}
|
||||||
speedProvider = new SegmentSpeedProvider(decoderInputFormat);
|
speedProvider = new SegmentSpeedProvider(decoderInputFormat);
|
||||||
currentSpeed = speedProvider.getSpeed(0);
|
currentSpeed = speedProvider.getSpeed(0);
|
||||||
|
this.decoder = decoder;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,10 @@ import androidx.media3.exoplayer.source.SampleStream;
|
|||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
@RequiresApi(18)
|
@RequiresApi(18)
|
||||||
/* package */ final class TransformerTranscodingVideoRenderer extends TransformerBaseRenderer {
|
/* package */ final class TransformerTranscodingVideoRenderer extends TransformerBaseRenderer {
|
||||||
@ -101,14 +104,26 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ensureEncoderConfigured();
|
ensureEncoderConfigured();
|
||||||
|
MediaCodecAdapterWrapper encoder = this.encoder;
|
||||||
ensureOpenGlConfigured();
|
ensureOpenGlConfigured();
|
||||||
|
EGLDisplay eglDisplay = this.eglDisplay;
|
||||||
|
EGLSurface eglSurface = this.eglSurface;
|
||||||
|
GlUtil.Uniform decoderTextureTransformUniform = this.decoderTextureTransformUniform;
|
||||||
if (!ensureDecoderConfigured()) {
|
if (!ensureDecoderConfigured()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
MediaCodecAdapterWrapper decoder = this.decoder;
|
||||||
|
SurfaceTexture decoderSurfaceTexture = this.decoderSurfaceTexture;
|
||||||
|
|
||||||
while (feedMuxerFromEncoder()) {}
|
while (feedMuxerFromEncoder(encoder)) {}
|
||||||
while (feedEncoderFromDecoder()) {}
|
while (feedEncoderFromDecoder(
|
||||||
while (feedDecoderFromInput()) {}
|
decoder,
|
||||||
|
encoder,
|
||||||
|
decoderSurfaceTexture,
|
||||||
|
eglDisplay,
|
||||||
|
eglSurface,
|
||||||
|
decoderTextureTransformUniform)) {}
|
||||||
|
while (feedDecoderFromInput(decoder)) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -150,6 +165,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
muxerWrapperTrackEnded = false;
|
muxerWrapperTrackEnded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnsuresNonNullIf(expression = "decoderInputFormat", result = true)
|
||||||
private boolean ensureInputFormatRead() {
|
private boolean ensureInputFormatRead() {
|
||||||
if (decoderInputFormat != null) {
|
if (decoderInputFormat != null) {
|
||||||
return true;
|
return true;
|
||||||
@ -166,6 +182,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresNonNull({"decoderInputFormat"})
|
||||||
|
@EnsuresNonNull({"encoder"})
|
||||||
private void ensureEncoderConfigured() throws ExoPlaybackException {
|
private void ensureEncoderConfigured() throws ExoPlaybackException {
|
||||||
if (encoder != null) {
|
if (encoder != null) {
|
||||||
return;
|
return;
|
||||||
@ -175,7 +193,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
encoder =
|
encoder =
|
||||||
MediaCodecAdapterWrapper.createForVideoEncoding(
|
MediaCodecAdapterWrapper.createForVideoEncoding(
|
||||||
new Format.Builder()
|
new Format.Builder()
|
||||||
.setWidth(checkNotNull(decoderInputFormat).width)
|
.setWidth(decoderInputFormat.width)
|
||||||
.setHeight(decoderInputFormat.height)
|
.setHeight(decoderInputFormat.height)
|
||||||
.setSampleMimeType(
|
.setSampleMimeType(
|
||||||
transformation.videoMimeType != null
|
transformation.videoMimeType != null
|
||||||
@ -186,18 +204,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw createRendererException(
|
throw createRendererException(
|
||||||
// TODO(claincly): should be "ENCODER_INIT_FAILED"
|
// TODO(claincly): should be "ENCODER_INIT_FAILED"
|
||||||
e,
|
e, decoderInputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
|
||||||
checkNotNull(this.decoder).getOutputFormat(),
|
|
||||||
PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresNonNull({"encoder", "decoderInputFormat"})
|
||||||
|
@EnsuresNonNull({"eglDisplay", "eglSurface", "decoderTextureTransformUniform"})
|
||||||
private void ensureOpenGlConfigured() {
|
private void ensureOpenGlConfigured() {
|
||||||
if (eglDisplay != null) {
|
if (eglDisplay != null && eglSurface != null && decoderTextureTransformUniform != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
eglDisplay = GlUtil.createEglDisplay();
|
MediaCodecAdapterWrapper encoder = this.encoder;
|
||||||
|
EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
||||||
EGLContext eglContext;
|
EGLContext eglContext;
|
||||||
try {
|
try {
|
||||||
eglContext = GlUtil.createEglContext(eglDisplay);
|
eglContext = GlUtil.createEglContext(eglDisplay);
|
||||||
@ -205,14 +224,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
} catch (GlUtil.UnsupportedEglVersionException e) {
|
} catch (GlUtil.UnsupportedEglVersionException e) {
|
||||||
throw new IllegalStateException("EGL version is unsupported", e);
|
throw new IllegalStateException("EGL version is unsupported", e);
|
||||||
}
|
}
|
||||||
eglSurface =
|
EGLSurface eglSurface =
|
||||||
GlUtil.getEglSurface(eglDisplay, checkNotNull(checkNotNull(encoder).getInputSurface()));
|
GlUtil.getEglSurface(eglDisplay, checkNotNull(encoder.getInputSurface()));
|
||||||
GlUtil.focusSurface(
|
GlUtil.focusSurface(
|
||||||
eglDisplay,
|
eglDisplay, eglContext, eglSurface, decoderInputFormat.width, decoderInputFormat.height);
|
||||||
eglContext,
|
|
||||||
eglSurface,
|
|
||||||
checkNotNull(decoderInputFormat).width,
|
|
||||||
decoderInputFormat.height);
|
|
||||||
decoderTextureId = GlUtil.createExternalTexture();
|
decoderTextureId = GlUtil.createExternalTexture();
|
||||||
String vertexShaderCode;
|
String vertexShaderCode;
|
||||||
String fragmentShaderCode;
|
String fragmentShaderCode;
|
||||||
@ -262,31 +277,36 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
throw new IllegalStateException("Unexpected uniform name.");
|
throw new IllegalStateException("Unexpected uniform name.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
checkNotNull(decoderTextureTransformUniform);
|
||||||
|
this.eglDisplay = eglDisplay;
|
||||||
|
this.eglSurface = eglSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresNonNull({"decoderInputFormat"})
|
||||||
|
@EnsuresNonNullIf(
|
||||||
|
expression = {"decoder", "decoderSurfaceTexture"},
|
||||||
|
result = true)
|
||||||
private boolean ensureDecoderConfigured() throws ExoPlaybackException {
|
private boolean ensureDecoderConfigured() throws ExoPlaybackException {
|
||||||
if (decoder != null) {
|
if (decoder != null && decoderSurfaceTexture != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkState(decoderTextureId != GlUtil.TEXTURE_ID_UNSET);
|
checkState(decoderTextureId != GlUtil.TEXTURE_ID_UNSET);
|
||||||
decoderSurfaceTexture = new SurfaceTexture(decoderTextureId);
|
SurfaceTexture decoderSurfaceTexture = new SurfaceTexture(decoderTextureId);
|
||||||
decoderSurfaceTexture.setOnFrameAvailableListener(
|
decoderSurfaceTexture.setOnFrameAvailableListener(
|
||||||
surfaceTexture -> isDecoderSurfacePopulated = true);
|
surfaceTexture -> isDecoderSurfacePopulated = true);
|
||||||
decoderSurface = new Surface(decoderSurfaceTexture);
|
decoderSurface = new Surface(decoderSurfaceTexture);
|
||||||
try {
|
try {
|
||||||
decoder =
|
decoder = MediaCodecAdapterWrapper.createForVideoDecoding(decoderInputFormat, decoderSurface);
|
||||||
MediaCodecAdapterWrapper.createForVideoDecoding(
|
|
||||||
checkNotNull(decoderInputFormat), decoderSurface);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw createRendererException(
|
throw createRendererException(
|
||||||
e, decoderInputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
|
e, decoderInputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
|
||||||
}
|
}
|
||||||
|
this.decoderSurfaceTexture = decoderSurfaceTexture;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean feedDecoderFromInput() {
|
private boolean feedDecoderFromInput(MediaCodecAdapterWrapper decoder) {
|
||||||
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
|
|
||||||
if (!decoder.maybeDequeueInputBuffer(decoderInputBuffer)) {
|
if (!decoder.maybeDequeueInputBuffer(decoderInputBuffer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -294,7 +314,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
decoderInputBuffer.clear();
|
decoderInputBuffer.clear();
|
||||||
@SampleStream.ReadDataResult
|
@SampleStream.ReadDataResult
|
||||||
int result = readSource(getFormatHolder(), decoderInputBuffer, /* readFlags= */ 0);
|
int result = readSource(getFormatHolder(), decoderInputBuffer, /* readFlags= */ 0);
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case C.RESULT_FORMAT_READ:
|
case C.RESULT_FORMAT_READ:
|
||||||
throw new IllegalStateException("Format changes are not supported.");
|
throw new IllegalStateException("Format changes are not supported.");
|
||||||
@ -310,8 +329,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean feedEncoderFromDecoder() {
|
private boolean feedEncoderFromDecoder(
|
||||||
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
|
MediaCodecAdapterWrapper decoder,
|
||||||
|
MediaCodecAdapterWrapper encoder,
|
||||||
|
SurfaceTexture decoderSurfaceTexture,
|
||||||
|
EGLDisplay eglDisplay,
|
||||||
|
EGLSurface eglSurface,
|
||||||
|
GlUtil.Uniform decoderTextureTransformUniform) {
|
||||||
if (decoder.isEnded()) {
|
if (decoder.isEnded()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -323,23 +347,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
waitingForPopulatedDecoderSurface = true;
|
waitingForPopulatedDecoderSurface = true;
|
||||||
}
|
}
|
||||||
if (decoder.isEnded()) {
|
if (decoder.isEnded()) {
|
||||||
checkNotNull(encoder).signalEndOfInputStream();
|
encoder.signalEndOfInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
waitingForPopulatedDecoderSurface = false;
|
waitingForPopulatedDecoderSurface = false;
|
||||||
SurfaceTexture decoderSurfaceTexture = checkNotNull(this.decoderSurfaceTexture);
|
|
||||||
decoderSurfaceTexture.updateTexImage();
|
decoderSurfaceTexture.updateTexImage();
|
||||||
decoderSurfaceTexture.getTransformMatrix(decoderTextureTransformMatrix);
|
decoderSurfaceTexture.getTransformMatrix(decoderTextureTransformMatrix);
|
||||||
GlUtil.Uniform decoderTextureTransformUniform =
|
|
||||||
checkNotNull(this.decoderTextureTransformUniform);
|
|
||||||
decoderTextureTransformUniform.setFloats(decoderTextureTransformMatrix);
|
decoderTextureTransformUniform.setFloats(decoderTextureTransformMatrix);
|
||||||
decoderTextureTransformUniform.bind();
|
decoderTextureTransformUniform.bind();
|
||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||||
EGLDisplay eglDisplay = checkNotNull(this.eglDisplay);
|
|
||||||
EGLSurface eglSurface = checkNotNull(this.eglSurface);
|
|
||||||
long decoderSurfaceTextureTimestampNs = decoderSurfaceTexture.getTimestamp();
|
long decoderSurfaceTextureTimestampNs = decoderSurfaceTexture.getTimestamp();
|
||||||
EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, decoderSurfaceTextureTimestampNs);
|
EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, decoderSurfaceTextureTimestampNs);
|
||||||
EGL14.eglSwapBuffers(eglDisplay, eglSurface);
|
EGL14.eglSwapBuffers(eglDisplay, eglSurface);
|
||||||
@ -347,8 +366,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean feedMuxerFromEncoder() {
|
private boolean feedMuxerFromEncoder(MediaCodecAdapterWrapper encoder) {
|
||||||
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
|
||||||
if (!hasEncoderActualOutputFormat) {
|
if (!hasEncoderActualOutputFormat) {
|
||||||
@Nullable Format encoderOutputFormat = encoder.getOutputFormat();
|
@Nullable Format encoderOutputFormat = encoder.getOutputFormat();
|
||||||
if (encoderOutputFormat == null) {
|
if (encoderOutputFormat == null) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user