Commit 99b62a24 by olly Committed by Oliver Woodman

Rename VideoDecoderRenderer to VideoDecoderGLFrameRenderer

We currently have DecoderVideoRenderer and VideoDecoderRenderer, which
is very confusing! This one is package private, so we can rename it to
remove some of the confusion.

Also fix some nullness issues.

PiperOrigin-RevId: 309964088
parent 68881608
...@@ -20,16 +20,19 @@ import android.opengl.GLSurfaceView; ...@@ -20,16 +20,19 @@ import android.opengl.GLSurfaceView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.GlUtil;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* GLSurfaceView.Renderer implementation that can render YUV Frames returned by a video decoder * GLSurfaceView.Renderer implementation that can render YUV Frames returned by a video decoder
* after decoding. It does the YUV to RGB color conversion in the Fragment Shader. * after decoding. It does the YUV to RGB color conversion in the Fragment Shader.
*/ */
/* package */ class VideoDecoderRenderer /* package */ class VideoDecoderGLFrameRenderer
implements GLSurfaceView.Renderer, VideoDecoderOutputBufferRenderer { implements GLSurfaceView.Renderer, VideoDecoderOutputBufferRenderer {
private static final float[] kColorConversion601 = { private static final float[] kColorConversion601 = {
...@@ -86,7 +89,8 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -86,7 +89,8 @@ import javax.microedition.khronos.opengles.GL10;
GlUtil.createBuffer(new float[] {-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f}); GlUtil.createBuffer(new float[] {-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f});
private final GLSurfaceView surfaceView; private final GLSurfaceView surfaceView;
private final int[] yuvTextures = new int[3]; private final int[] yuvTextures = new int[3];
private final AtomicReference<VideoDecoderOutputBuffer> pendingOutputBufferReference; private final AtomicReference<@NullableType VideoDecoderOutputBuffer>
pendingOutputBufferReference;
// Kept in field rather than a local variable in order not to get garbage collected before // Kept in field rather than a local variable in order not to get garbage collected before
// glDrawArrays uses it. // glDrawArrays uses it.
...@@ -98,10 +102,10 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -98,10 +102,10 @@ import javax.microedition.khronos.opengles.GL10;
private int[] previousWidths; private int[] previousWidths;
private int[] previousStrides; private int[] previousStrides;
@Nullable // Accessed only from the GL thread.
private VideoDecoderOutputBuffer renderedOutputBuffer; // Accessed only from the GL thread. private @MonotonicNonNull VideoDecoderOutputBuffer renderedOutputBuffer;
public VideoDecoderRenderer(GLSurfaceView surfaceView) { public VideoDecoderGLFrameRenderer(GLSurfaceView surfaceView) {
this.surfaceView = surfaceView; this.surfaceView = surfaceView;
pendingOutputBufferReference = new AtomicReference<>(); pendingOutputBufferReference = new AtomicReference<>();
textureCoords = new FloatBuffer[3]; textureCoords = new FloatBuffer[3];
...@@ -119,7 +123,13 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -119,7 +123,13 @@ import javax.microedition.khronos.opengles.GL10;
GLES20.glUseProgram(program); GLES20.glUseProgram(program);
int posLocation = GLES20.glGetAttribLocation(program, "in_pos"); int posLocation = GLES20.glGetAttribLocation(program, "in_pos");
GLES20.glEnableVertexAttribArray(posLocation); GLES20.glEnableVertexAttribArray(posLocation);
GLES20.glVertexAttribPointer(posLocation, 2, GLES20.GL_FLOAT, false, 0, TEXTURE_VERTICES); GLES20.glVertexAttribPointer(
posLocation,
2,
GLES20.GL_FLOAT,
/* normalized= */ false,
/* stride= */ 0,
TEXTURE_VERTICES);
texLocations[0] = GLES20.glGetAttribLocation(program, "in_tc_y"); texLocations[0] = GLES20.glGetAttribLocation(program, "in_tc_y");
GLES20.glEnableVertexAttribArray(texLocations[0]); GLES20.glEnableVertexAttribArray(texLocations[0]);
texLocations[1] = GLES20.glGetAttribLocation(program, "in_tc_u"); texLocations[1] = GLES20.glGetAttribLocation(program, "in_tc_u");
...@@ -140,7 +150,9 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -140,7 +150,9 @@ import javax.microedition.khronos.opengles.GL10;
@Override @Override
public void onDrawFrame(GL10 unused) { public void onDrawFrame(GL10 unused) {
VideoDecoderOutputBuffer pendingOutputBuffer = pendingOutputBufferReference.getAndSet(null); @Nullable
VideoDecoderOutputBuffer pendingOutputBuffer =
pendingOutputBufferReference.getAndSet(/* newValue= */ null);
if (pendingOutputBuffer == null && renderedOutputBuffer == null) { if (pendingOutputBuffer == null && renderedOutputBuffer == null) {
// There is no output buffer to render at the moment. // There is no output buffer to render at the moment.
return; return;
...@@ -151,7 +163,9 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -151,7 +163,9 @@ import javax.microedition.khronos.opengles.GL10;
} }
renderedOutputBuffer = pendingOutputBuffer; renderedOutputBuffer = pendingOutputBuffer;
} }
VideoDecoderOutputBuffer outputBuffer = renderedOutputBuffer;
VideoDecoderOutputBuffer outputBuffer = Assertions.checkNotNull(renderedOutputBuffer);
// Set color matrix. Assume BT709 if the color space is unknown. // Set color matrix. Assume BT709 if the color space is unknown.
float[] colorConversion = kColorConversion709; float[] colorConversion = kColorConversion709;
switch (outputBuffer.colorspace) { switch (outputBuffer.colorspace) {
...@@ -163,9 +177,18 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -163,9 +177,18 @@ import javax.microedition.khronos.opengles.GL10;
break; break;
case VideoDecoderOutputBuffer.COLORSPACE_BT709: case VideoDecoderOutputBuffer.COLORSPACE_BT709:
default: default:
break; // Do nothing // Do nothing.
break;
} }
GLES20.glUniformMatrix3fv(colorMatrixLocation, 1, false, colorConversion, 0); GLES20.glUniformMatrix3fv(
colorMatrixLocation,
/* color= */ 1,
/* transpose= */ false,
colorConversion,
/* offset= */ 0);
int[] yuvStrides = Assertions.checkNotNull(outputBuffer.yuvStrides);
ByteBuffer[] yuvPlanes = Assertions.checkNotNull(outputBuffer.yuvPlanes);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
int h = (i == 0) ? outputBuffer.height : (outputBuffer.height + 1) / 2; int h = (i == 0) ? outputBuffer.height : (outputBuffer.height + 1) / 2;
...@@ -174,14 +197,14 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -174,14 +197,14 @@ import javax.microedition.khronos.opengles.GL10;
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
GLES20.glTexImage2D( GLES20.glTexImage2D(
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_2D,
0, /* level= */ 0,
GLES20.GL_LUMINANCE, GLES20.GL_LUMINANCE,
outputBuffer.yuvStrides[i], yuvStrides[i],
h, h,
0, /* border= */ 0,
GLES20.GL_LUMINANCE, GLES20.GL_LUMINANCE,
GLES20.GL_UNSIGNED_BYTE, GLES20.GL_UNSIGNED_BYTE,
outputBuffer.yuvPlanes[i]); yuvPlanes[i]);
} }
int[] widths = new int[3]; int[] widths = new int[3];
...@@ -192,28 +215,34 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -192,28 +215,34 @@ import javax.microedition.khronos.opengles.GL10;
widths[1] = widths[2] = (widths[0] + 1) / 2; widths[1] = widths[2] = (widths[0] + 1) / 2;
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
// Set cropping of stride if either width or stride has changed. // Set cropping of stride if either width or stride has changed.
if (previousWidths[i] != widths[i] || previousStrides[i] != outputBuffer.yuvStrides[i]) { if (previousWidths[i] != widths[i] || previousStrides[i] != yuvStrides[i]) {
Assertions.checkState(outputBuffer.yuvStrides[i] != 0); Assertions.checkState(yuvStrides[i] != 0);
float widthRatio = (float) widths[i] / outputBuffer.yuvStrides[i]; float widthRatio = (float) widths[i] / yuvStrides[i];
// These buffers are consumed during each call to glDrawArrays. They need to be member // These buffers are consumed during each call to glDrawArrays. They need to be member
// variables rather than local variables in order not to get garbage collected. // variables rather than local variables in order not to get garbage collected.
textureCoords[i] = textureCoords[i] =
GlUtil.createBuffer( GlUtil.createBuffer(
new float[] {0.0f, 0.0f, 0.0f, 1.0f, widthRatio, 0.0f, widthRatio, 1.0f}); new float[] {0.0f, 0.0f, 0.0f, 1.0f, widthRatio, 0.0f, widthRatio, 1.0f});
GLES20.glVertexAttribPointer( GLES20.glVertexAttribPointer(
texLocations[i], 2, GLES20.GL_FLOAT, false, 0, textureCoords[i]); texLocations[i],
/* size= */ 2,
GLES20.GL_FLOAT,
/* normalized= */ false,
/* stride= */ 0,
textureCoords[i]);
previousWidths[i] = widths[i]; previousWidths[i] = widths[i];
previousStrides[i] = outputBuffer.yuvStrides[i]; previousStrides[i] = yuvStrides[i];
} }
} }
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
GlUtil.checkGlError(); GlUtil.checkGlError();
} }
@Override @Override
public void setOutputBuffer(VideoDecoderOutputBuffer outputBuffer) { public void setOutputBuffer(VideoDecoderOutputBuffer outputBuffer) {
@Nullable
VideoDecoderOutputBuffer oldPendingOutputBuffer = VideoDecoderOutputBuffer oldPendingOutputBuffer =
pendingOutputBufferReference.getAndSet(outputBuffer); pendingOutputBufferReference.getAndSet(outputBuffer);
if (oldPendingOutputBuffer != null) { if (oldPendingOutputBuffer != null) {
...@@ -224,7 +253,7 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -224,7 +253,7 @@ import javax.microedition.khronos.opengles.GL10;
} }
private void setupTextures() { private void setupTextures() {
GLES20.glGenTextures(3, yuvTextures, 0); GLES20.glGenTextures(3, yuvTextures, /* offset= */ 0);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
GLES20.glUniform1i(GLES20.glGetUniformLocation(program, TEXTURE_UNIFORMS[i]), i); GLES20.glUniform1i(GLES20.glGetUniformLocation(program, TEXTURE_UNIFORMS[i]), i);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
......
...@@ -30,7 +30,7 @@ import androidx.annotation.Nullable; ...@@ -30,7 +30,7 @@ import androidx.annotation.Nullable;
*/ */
public class VideoDecoderGLSurfaceView extends GLSurfaceView { public class VideoDecoderGLSurfaceView extends GLSurfaceView {
private final VideoDecoderRenderer renderer; private final VideoDecoderGLFrameRenderer renderer;
/** @param context A {@link Context}. */ /** @param context A {@link Context}. */
public VideoDecoderGLSurfaceView(Context context) { public VideoDecoderGLSurfaceView(Context context) {
...@@ -41,9 +41,14 @@ public class VideoDecoderGLSurfaceView extends GLSurfaceView { ...@@ -41,9 +41,14 @@ public class VideoDecoderGLSurfaceView extends GLSurfaceView {
* @param context A {@link Context}. * @param context A {@link Context}.
* @param attrs Custom attributes. * @param attrs Custom attributes.
*/ */
@SuppressWarnings({
"nullness:assignment.type.incompatible",
"nullness:argument.type.incompatible",
"nullness:method.invocation.invalid"
})
public VideoDecoderGLSurfaceView(Context context, @Nullable AttributeSet attrs) { public VideoDecoderGLSurfaceView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs); super(context, attrs);
renderer = new VideoDecoderRenderer(this); renderer = new VideoDecoderGLFrameRenderer(/* surfaceView= */ this);
setPreserveEGLContextOnPause(true); setPreserveEGLContextOnPause(true);
setEGLContextClientVersion(2); setEGLContextClientVersion(2);
setRenderer(renderer); setRenderer(renderer);
......
...@@ -33,7 +33,7 @@ public class VideoDecoderOutputBuffer extends OutputBuffer { ...@@ -33,7 +33,7 @@ public class VideoDecoderOutputBuffer extends OutputBuffer {
// ../../../../../../../../../../extensions/vp9/src/main/jni/vpx_jni.cc // ../../../../../../../../../../extensions/vp9/src/main/jni/vpx_jni.cc
// ) // )
/** Decoder private data. */ /** Decoder private data. Used from native code. */
public int decoderPrivate; public int decoderPrivate;
/** Output mode. */ /** Output mode. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment