mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Don't hold a lock while rendering in VpxRenderer.
This commit is contained in:
parent
b9607321fe
commit
f4c10dc560
5 changed files with 98 additions and 79 deletions
|
|
@ -106,7 +106,6 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
private VpxDecoderWrapper decoder;
|
private VpxDecoderWrapper decoder;
|
||||||
private VpxInputBuffer inputBuffer;
|
private VpxInputBuffer inputBuffer;
|
||||||
private VpxOutputBuffer outputBuffer;
|
private VpxOutputBuffer outputBuffer;
|
||||||
private VpxOutputBuffer renderedOutputBuffer;
|
|
||||||
|
|
||||||
private Bitmap bitmap;
|
private Bitmap bitmap;
|
||||||
private boolean drawnToSurface;
|
private boolean drawnToSurface;
|
||||||
|
|
@ -181,13 +180,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sourceIsReady = continueBufferingSource(positionUs);
|
sourceIsReady = continueBufferingSource(positionUs);
|
||||||
try {
|
|
||||||
checkForDiscontinuity(positionUs);
|
checkForDiscontinuity(positionUs);
|
||||||
} catch (VpxDecoderException e) {
|
|
||||||
notifyDecoderError(e);
|
|
||||||
throw new ExoPlaybackException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Try and read a format if we don't have one already.
|
// Try and read a format if we don't have one already.
|
||||||
if (format == null && !readFormat(positionUs)) {
|
if (format == null && !readFormat(positionUs)) {
|
||||||
|
|
@ -226,7 +219,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
|
|
||||||
if (outputBuffer.flags == VpxDecoderWrapper.FLAG_END_OF_STREAM) {
|
if (outputBuffer.flags == VpxDecoderWrapper.FLAG_END_OF_STREAM) {
|
||||||
outputStreamEnded = true;
|
outputStreamEnded = true;
|
||||||
releaseOutputBuffer(outputBuffer);
|
decoder.releaseOutputBuffer(outputBuffer);
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +234,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
||||||
notifyAndResetDroppedFrameCount();
|
notifyAndResetDroppedFrameCount();
|
||||||
}
|
}
|
||||||
releaseOutputBuffer(outputBuffer);
|
decoder.releaseOutputBuffer(outputBuffer);
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -270,7 +263,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
renderBuffer();
|
renderBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderBuffer() throws VpxDecoderException {
|
private void renderBuffer() {
|
||||||
codecCounters.renderedOutputBufferCount++;
|
codecCounters.renderedOutputBufferCount++;
|
||||||
notifyIfVideoSizeChanged(outputBuffer);
|
notifyIfVideoSizeChanged(outputBuffer);
|
||||||
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
||||||
|
|
@ -279,22 +272,16 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
drawnToSurface = true;
|
drawnToSurface = true;
|
||||||
notifyDrawnToSurface(surface);
|
notifyDrawnToSurface(surface);
|
||||||
}
|
}
|
||||||
|
outputBuffer.release();
|
||||||
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
||||||
|
// The renderer will release the buffer.
|
||||||
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
||||||
|
} else {
|
||||||
|
outputBuffer.release();
|
||||||
}
|
}
|
||||||
// Release the output buffer we rendered during the previous cycle, now that we delivered a new
|
|
||||||
// buffer.
|
|
||||||
releaseOutputBuffer(renderedOutputBuffer);
|
|
||||||
renderedOutputBuffer = outputBuffer;
|
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseOutputBuffer(VpxOutputBuffer buffer) throws VpxDecoderException {
|
|
||||||
if (buffer != null) {
|
|
||||||
decoder.releaseOutputBuffer(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderRgbFrame(VpxOutputBuffer outputBuffer, boolean scale) {
|
private void renderRgbFrame(VpxOutputBuffer outputBuffer, boolean scale) {
|
||||||
if (bitmap == null || bitmap.getWidth() != outputBuffer.width
|
if (bitmap == null || bitmap.getWidth() != outputBuffer.width
|
||||||
|| bitmap.getHeight() != outputBuffer.height) {
|
|| bitmap.getHeight() != outputBuffer.height) {
|
||||||
|
|
@ -350,7 +337,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForDiscontinuity(long positionUs) throws VpxDecoderException {
|
private void checkForDiscontinuity(long positionUs) {
|
||||||
if (decoder == null) {
|
if (decoder == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -360,12 +347,12 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushDecoder() throws VpxDecoderException {
|
private void flushDecoder() {
|
||||||
inputBuffer = null;
|
inputBuffer = null;
|
||||||
VpxOutputBuffer bufferToRelease = outputBuffer;
|
if (outputBuffer != null) {
|
||||||
// Set this to null now because releaseOutputBuffer could throw an exception.
|
decoder.releaseOutputBuffer(outputBuffer);
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
releaseOutputBuffer(bufferToRelease);
|
}
|
||||||
decoder.flush();
|
decoder.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,15 @@ import java.util.LinkedList;
|
||||||
/**
|
/**
|
||||||
* Wraps {@link VpxDecoder}, exposing a higher level decoder interface.
|
* Wraps {@link VpxDecoder}, exposing a higher level decoder interface.
|
||||||
*/
|
*/
|
||||||
/* package */ class VpxDecoderWrapper extends Thread {
|
/* package */ final class VpxDecoderWrapper extends Thread {
|
||||||
|
|
||||||
public static final int FLAG_END_OF_STREAM = 1;
|
public static final int FLAG_END_OF_STREAM = 1;
|
||||||
|
|
||||||
private static final int INPUT_BUFFER_SIZE = 768 * 1024; // Value based on cs/SoftVpx.cpp.
|
private static final int INPUT_BUFFER_SIZE = 768 * 1024; // Value based on cs/SoftVpx.cpp.
|
||||||
/**
|
/**
|
||||||
* The total number of output buffers. {@link LibvpxVideoTrackRenderer} may hold on to 2 buffers
|
* The number of input buffers and the number of output buffers. The track renderer may limit the
|
||||||
* at a time so this value should be high enough considering LibvpxVideoTrackRenderer requirement.
|
* minimum possible value due to requiring multiple output buffers to be dequeued at a time for it
|
||||||
|
* to make progress.
|
||||||
*/
|
*/
|
||||||
private static final int NUM_BUFFERS = 16;
|
private static final int NUM_BUFFERS = 16;
|
||||||
|
|
||||||
|
|
@ -66,7 +67,7 @@ import java.util.LinkedList;
|
||||||
availableOutputBufferCount = NUM_BUFFERS;
|
availableOutputBufferCount = NUM_BUFFERS;
|
||||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||||
availableInputBuffers[i] = new VpxInputBuffer();
|
availableInputBuffers[i] = new VpxInputBuffer();
|
||||||
availableOutputBuffers[i] = new VpxOutputBuffer();
|
availableOutputBuffers[i] = new VpxOutputBuffer(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,19 +106,22 @@ import java.util.LinkedList;
|
||||||
if (queuedOutputBuffers.isEmpty()) {
|
if (queuedOutputBuffers.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
VpxOutputBuffer outputBuffer = queuedOutputBuffers.removeFirst();
|
return queuedOutputBuffers.removeFirst();
|
||||||
return outputBuffer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseOutputBuffer(VpxOutputBuffer outputBuffer) throws VpxDecoderException {
|
public void releaseOutputBuffer(VpxOutputBuffer outputBuffer) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
maybeThrowDecoderError();
|
|
||||||
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
|
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
|
||||||
maybeNotifyDecodeLoop();
|
maybeNotifyDecodeLoop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes input/output buffers that have not been dequeued yet and returns ownership of any
|
||||||
|
* dequeued input buffer to the decoder. Flushes any pending output currently in the decoder. The
|
||||||
|
* caller is still responsible for releasing any dequeued output buffers.
|
||||||
|
*/
|
||||||
public void flush() {
|
public void flush() {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
flushDecodedOutputBuffer = true;
|
flushDecodedOutputBuffer = true;
|
||||||
|
|
@ -159,7 +163,7 @@ import java.util.LinkedList;
|
||||||
* Should only be called whilst synchronized on the lock object.
|
* Should only be called whilst synchronized on the lock object.
|
||||||
*/
|
*/
|
||||||
private void maybeNotifyDecodeLoop() {
|
private void maybeNotifyDecodeLoop() {
|
||||||
if (!queuedInputBuffers.isEmpty() && availableOutputBufferCount > 0) {
|
if (canDecodeBuffer()) {
|
||||||
lock.notify();
|
lock.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +196,7 @@ import java.util.LinkedList;
|
||||||
|
|
||||||
// Wait until we have an input buffer to decode, and an output buffer to decode into.
|
// Wait until we have an input buffer to decode, and an output buffer to decode into.
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
while (!released && (queuedInputBuffers.isEmpty() || availableOutputBufferCount == 0)) {
|
while (!released && !canDecodeBuffer()) {
|
||||||
lock.wait();
|
lock.wait();
|
||||||
}
|
}
|
||||||
if (released) {
|
if (released) {
|
||||||
|
|
@ -238,6 +242,10 @@ import java.util.LinkedList;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canDecodeBuffer() {
|
||||||
|
return !queuedInputBuffers.isEmpty() && availableOutputBufferCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* package */ static final class VpxInputBuffer {
|
/* package */ static final class VpxInputBuffer {
|
||||||
|
|
||||||
public final SampleHolder sampleHolder;
|
public final SampleHolder sampleHolder;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ public final class VpxOutputBuffer {
|
||||||
public static final int COLORSPACE_BT601 = 1;
|
public static final int COLORSPACE_BT601 = 1;
|
||||||
public static final int COLORSPACE_BT709 = 2;
|
public static final int COLORSPACE_BT709 = 2;
|
||||||
|
|
||||||
|
private final VpxDecoderWrapper decoder;
|
||||||
|
|
||||||
|
/* package */ VpxOutputBuffer(VpxDecoderWrapper decoder) {
|
||||||
|
this.decoder = decoder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RGB buffer for RGB mode.
|
* RGB buffer for RGB mode.
|
||||||
*/
|
*/
|
||||||
|
|
@ -43,10 +49,16 @@ public final class VpxOutputBuffer {
|
||||||
public int colorspace;
|
public int colorspace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from C++ through JNI after decoding is done. It will resize the
|
* Releases the buffer back to the decoder, allowing it to be reused.
|
||||||
* buffer based on the given dimensions.
|
|
||||||
*/
|
*/
|
||||||
public void initForRgbFrame(int width, int height) {
|
public void release() {
|
||||||
|
decoder.releaseOutputBuffer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes the buffer based on the given dimensions. Called via JNI after decoding completes.
|
||||||
|
*/
|
||||||
|
/* package */ void initForRgbFrame(int width, int height) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
int minimumRgbSize = width * height * 2;
|
int minimumRgbSize = width * height * 2;
|
||||||
|
|
@ -59,10 +71,10 @@ public final class VpxOutputBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from C++ through JNI after decoding is done. It will resize the
|
* Resizes the buffer based on the given stride. Called via JNI after decoding completes.
|
||||||
* buffer based on the given stride.
|
|
||||||
*/
|
*/
|
||||||
public void initForYuvFrame(int width, int height, int yStride, int uvStride, int colorspace) {
|
/* package */ void initForYuvFrame(int width, int height, int yStride, int uvStride,
|
||||||
|
int colorspace) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.colorspace = colorspace;
|
this.colorspace = colorspace;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ package com.google.android.exoplayer.ext.vp9;
|
||||||
public interface VpxOutputBufferRenderer {
|
public interface VpxOutputBufferRenderer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the output buffer to be rendered.
|
* Sets the output buffer to be rendered. The renderer is responsible for releasing the buffer.
|
||||||
*/
|
*/
|
||||||
void setOutputBuffer(VpxOutputBuffer outputBuffer);
|
void setOutputBuffer(VpxOutputBuffer outputBuffer);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import android.opengl.GLSurfaceView;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
|
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;
|
||||||
|
|
@ -72,18 +73,21 @@ import javax.microedition.khronos.opengles.GL10;
|
||||||
1.0f, 1.0f,
|
1.0f, 1.0f,
|
||||||
1.0f, -1.0f);
|
1.0f, -1.0f);
|
||||||
private final int[] yuvTextures = new int[3];
|
private final int[] yuvTextures = new int[3];
|
||||||
|
private final AtomicReference<VpxOutputBuffer> pendingOutputBufferReference;
|
||||||
|
|
||||||
private int program;
|
private int program;
|
||||||
private int texLocation;
|
private int texLocation;
|
||||||
private int colorMatrixLocation;
|
private int colorMatrixLocation;
|
||||||
private FloatBuffer textureCoords;
|
private FloatBuffer textureCoords;
|
||||||
private VpxOutputBuffer outputBuffer;
|
|
||||||
private int previousWidth;
|
private int previousWidth;
|
||||||
private int previousStride;
|
private int previousStride;
|
||||||
|
|
||||||
|
private VpxOutputBuffer renderedOutputBuffer; // Accessed only from the GL thread.
|
||||||
|
|
||||||
public VpxRenderer() {
|
public VpxRenderer() {
|
||||||
previousWidth = -1;
|
previousWidth = -1;
|
||||||
previousStride = -1;
|
previousStride = -1;
|
||||||
|
pendingOutputBufferReference = new AtomicReference<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -92,8 +96,12 @@ import javax.microedition.khronos.opengles.GL10;
|
||||||
*
|
*
|
||||||
* @param outputBuffer OutputBuffer containing the YUV Frame to be rendered
|
* @param outputBuffer OutputBuffer containing the YUV Frame to be rendered
|
||||||
*/
|
*/
|
||||||
public synchronized void setFrame(VpxOutputBuffer outputBuffer) {
|
public void setFrame(VpxOutputBuffer outputBuffer) {
|
||||||
this.outputBuffer = outputBuffer;
|
VpxOutputBuffer oldPendingOutputBuffer = pendingOutputBufferReference.getAndSet(outputBuffer);
|
||||||
|
if (oldPendingOutputBuffer != null) {
|
||||||
|
// The old pending output buffer will never be used for rendering, so release it now.
|
||||||
|
oldPendingOutputBuffer.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -134,13 +142,18 @@ import javax.microedition.khronos.opengles.GL10;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrawFrame(GL10 unused) {
|
public void onDrawFrame(GL10 unused) {
|
||||||
synchronized (this) {
|
VpxOutputBuffer pendingOutputBuffer = pendingOutputBufferReference.getAndSet(null);
|
||||||
VpxOutputBuffer outputBuffer = this.outputBuffer;
|
if (pendingOutputBuffer == null && renderedOutputBuffer == null) {
|
||||||
if (outputBuffer == null) {
|
// There is no output buffer to render at the moment.
|
||||||
// Nothing to render yet.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (pendingOutputBuffer != null) {
|
||||||
|
if (renderedOutputBuffer != null) {
|
||||||
|
renderedOutputBuffer.release();
|
||||||
|
}
|
||||||
|
renderedOutputBuffer = pendingOutputBuffer;
|
||||||
|
}
|
||||||
|
VpxOutputBuffer outputBuffer = 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 = outputBuffer.colorspace == VpxOutputBuffer.COLORSPACE_BT601
|
float[] colorConversion = outputBuffer.colorspace == VpxOutputBuffer.COLORSPACE_BT601
|
||||||
? kColorConversion601 : kColorConversion709;
|
? kColorConversion601 : kColorConversion709;
|
||||||
|
|
@ -168,7 +181,6 @@ import javax.microedition.khronos.opengles.GL10;
|
||||||
previousWidth = outputBuffer.width;
|
previousWidth = outputBuffer.width;
|
||||||
previousStride = outputBuffer.yuvStrides[0];
|
previousStride = outputBuffer.yuvStrides[0];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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, 0, 4);
|
||||||
checkNoGLES2Error();
|
checkNoGLES2Error();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue