mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Allow custom VPX output buffer renderers.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=109832096
This commit is contained in:
parent
7bc341f385
commit
e487fe6fa7
@ -173,7 +173,7 @@ public class VideoPlayer extends Activity implements OnClickListener,
|
|||||||
TrackRenderer videoRenderer =
|
TrackRenderer videoRenderer =
|
||||||
new LibvpxVideoTrackRenderer(sampleSource, true, handler, this, 50);
|
new LibvpxVideoTrackRenderer(sampleSource, true, handler, this, 50);
|
||||||
if (useOpenGL) {
|
if (useOpenGL) {
|
||||||
player.sendMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_VPX_SURFACE_VIEW,
|
player.sendMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER,
|
||||||
vpxVideoSurfaceView);
|
vpxVideoSurfaceView);
|
||||||
surfaceView.setVisibility(View.GONE);
|
surfaceView.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
@ -202,7 +202,7 @@ public class VideoPlayer extends Activity implements OnClickListener,
|
|||||||
player.addListener(this);
|
player.addListener(this);
|
||||||
mediaController.setMediaPlayer(new PlayerControl(player));
|
mediaController.setMediaPlayer(new PlayerControl(player));
|
||||||
mediaController.setEnabled(true);
|
mediaController.setEnabled(true);
|
||||||
player.sendMessage(renderers[0], LibvpxVideoTrackRenderer.MSG_SET_VPX_SURFACE_VIEW,
|
player.sendMessage(renderers[0], LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER,
|
||||||
vpxVideoSurfaceView);
|
vpxVideoSurfaceView);
|
||||||
player.prepare(renderers);
|
player.prepare(renderers);
|
||||||
player.setPlayWhenReady(true);
|
player.setPlayWhenReady(true);
|
||||||
|
@ -23,8 +23,7 @@ import com.google.android.exoplayer.MediaFormatHolder;
|
|||||||
import com.google.android.exoplayer.SampleSource;
|
import com.google.android.exoplayer.SampleSource;
|
||||||
import com.google.android.exoplayer.SampleSourceTrackRenderer;
|
import com.google.android.exoplayer.SampleSourceTrackRenderer;
|
||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.InputBuffer;
|
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.VpxInputBuffer;
|
||||||
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer;
|
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -88,7 +87,12 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
* should be the target {@link Surface}, or null.
|
* should be the target {@link Surface}, or null.
|
||||||
*/
|
*/
|
||||||
public static final int MSG_SET_SURFACE = 1;
|
public static final int MSG_SET_SURFACE = 1;
|
||||||
public static final int MSG_SET_VPX_SURFACE_VIEW = 2;
|
/**
|
||||||
|
* The type of a message that can be passed to an instance of this class via
|
||||||
|
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||||
|
* should be the target {@link VpxOutputBufferRenderer}, or null.
|
||||||
|
*/
|
||||||
|
public static final int MSG_SET_OUTPUT_BUFFER_RENDERER = 2;
|
||||||
|
|
||||||
public final CodecCounters codecCounters = new CodecCounters();
|
public final CodecCounters codecCounters = new CodecCounters();
|
||||||
|
|
||||||
@ -100,14 +104,15 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
|
|
||||||
private MediaFormat format;
|
private MediaFormat format;
|
||||||
private VpxDecoderWrapper decoder;
|
private VpxDecoderWrapper decoder;
|
||||||
private InputBuffer inputBuffer;
|
private VpxInputBuffer inputBuffer;
|
||||||
private OutputBuffer outputBuffer;
|
private VpxOutputBuffer outputBuffer;
|
||||||
|
private VpxOutputBuffer renderedOutputBuffer;
|
||||||
|
|
||||||
private Bitmap bitmap;
|
private Bitmap bitmap;
|
||||||
private boolean drawnToSurface;
|
private boolean drawnToSurface;
|
||||||
private boolean renderedFirstFrame;
|
private boolean renderedFirstFrame;
|
||||||
private Surface surface;
|
private Surface surface;
|
||||||
private VpxVideoSurfaceView vpxVideoSurfaceView;
|
private VpxOutputBufferRenderer outputBufferRenderer;
|
||||||
private int outputMode;
|
private int outputMode;
|
||||||
|
|
||||||
private boolean inputStreamEnded;
|
private boolean inputStreamEnded;
|
||||||
@ -176,7 +181,13 @@ 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)) {
|
||||||
@ -215,7 +226,8 @@ 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();
|
releaseOutputBuffer(outputBuffer);
|
||||||
|
outputBuffer = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +241,8 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
||||||
notifyAndResetDroppedFrameCount();
|
notifyAndResetDroppedFrameCount();
|
||||||
}
|
}
|
||||||
releaseOutputBuffer();
|
releaseOutputBuffer(outputBuffer);
|
||||||
|
outputBuffer = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,18 +279,23 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
drawnToSurface = true;
|
drawnToSurface = true;
|
||||||
notifyDrawnToSurface(surface);
|
notifyDrawnToSurface(surface);
|
||||||
}
|
}
|
||||||
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && vpxVideoSurfaceView != null) {
|
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
||||||
vpxVideoSurfaceView.renderFrame(outputBuffer);
|
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
||||||
}
|
}
|
||||||
releaseOutputBuffer();
|
// Release the output buffer we rendered during the previous cycle, now that we delivered a new
|
||||||
}
|
// buffer.
|
||||||
|
releaseOutputBuffer(renderedOutputBuffer);
|
||||||
private void releaseOutputBuffer() throws VpxDecoderException {
|
renderedOutputBuffer = outputBuffer;
|
||||||
decoder.releaseOutputBuffer(outputBuffer);
|
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderRgbFrame(OutputBuffer outputBuffer, boolean scale) {
|
private void releaseOutputBuffer(VpxOutputBuffer buffer) throws VpxDecoderException {
|
||||||
|
if (buffer != null) {
|
||||||
|
decoder.releaseOutputBuffer(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
bitmap = Bitmap.createBitmap(outputBuffer.width, outputBuffer.height, Bitmap.Config.RGB_565);
|
bitmap = Bitmap.createBitmap(outputBuffer.width, outputBuffer.height, Bitmap.Config.RGB_565);
|
||||||
@ -332,7 +350,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForDiscontinuity(long positionUs) {
|
private void checkForDiscontinuity(long positionUs) throws VpxDecoderException {
|
||||||
if (decoder == null) {
|
if (decoder == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -342,9 +360,12 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushDecoder() {
|
private void flushDecoder() throws VpxDecoderException {
|
||||||
inputBuffer = null;
|
inputBuffer = null;
|
||||||
|
VpxOutputBuffer bufferToRelease = outputBuffer;
|
||||||
|
// Set this to null now because releaseOutputBuffer could throw an exception.
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
|
releaseOutputBuffer(bufferToRelease);
|
||||||
decoder.flush();
|
decoder.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,8 +438,8 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
||||||
if (messageType == MSG_SET_SURFACE) {
|
if (messageType == MSG_SET_SURFACE) {
|
||||||
setSurface((Surface) message);
|
setSurface((Surface) message);
|
||||||
} else if (messageType == MSG_SET_VPX_SURFACE_VIEW) {
|
} else if (messageType == MSG_SET_OUTPUT_BUFFER_RENDERER) {
|
||||||
setVpxVideoSurfaceView((VpxVideoSurfaceView) message);
|
setOutputBufferRenderer((VpxOutputBufferRenderer) message);
|
||||||
} else {
|
} else {
|
||||||
super.handleMessage(messageType, message);
|
super.handleMessage(messageType, message);
|
||||||
}
|
}
|
||||||
@ -429,7 +450,7 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.surface = surface;
|
this.surface = surface;
|
||||||
vpxVideoSurfaceView = null;
|
outputBufferRenderer = null;
|
||||||
outputMode = (surface != null) ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_UNKNOWN;
|
outputMode = (surface != null) ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_UNKNOWN;
|
||||||
if (decoder != null) {
|
if (decoder != null) {
|
||||||
decoder.setOutputMode(outputMode);
|
decoder.setOutputMode(outputMode);
|
||||||
@ -437,20 +458,20 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
|
|||||||
drawnToSurface = false;
|
drawnToSurface = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setVpxVideoSurfaceView(VpxVideoSurfaceView vpxVideoSurfaceView) {
|
private void setOutputBufferRenderer(VpxOutputBufferRenderer outputBufferRenderer) {
|
||||||
if (this.vpxVideoSurfaceView == vpxVideoSurfaceView) {
|
if (this.outputBufferRenderer == outputBufferRenderer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.vpxVideoSurfaceView = vpxVideoSurfaceView;
|
this.outputBufferRenderer = outputBufferRenderer;
|
||||||
surface = null;
|
surface = null;
|
||||||
outputMode =
|
outputMode = (outputBufferRenderer != null)
|
||||||
(vpxVideoSurfaceView != null) ? VpxDecoder.OUTPUT_MODE_YUV : VpxDecoder.OUTPUT_MODE_UNKNOWN;
|
? VpxDecoder.OUTPUT_MODE_YUV : VpxDecoder.OUTPUT_MODE_UNKNOWN;
|
||||||
if (decoder != null) {
|
if (decoder != null) {
|
||||||
decoder.setOutputMode(outputMode);
|
decoder.setOutputMode(outputMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyIfVideoSizeChanged(final OutputBuffer outputBuffer) {
|
private void notifyIfVideoSizeChanged(final VpxOutputBuffer outputBuffer) {
|
||||||
if (previousWidth == -1 || previousHeight == -1
|
if (previousWidth == -1 || previousHeight == -1
|
||||||
|| previousWidth != outputBuffer.width || previousHeight != outputBuffer.height) {
|
|| previousWidth != outputBuffer.width || previousHeight != outputBuffer.height) {
|
||||||
previousWidth = outputBuffer.width;
|
previousWidth = outputBuffer.width;
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.ext.vp9;
|
package com.google.android.exoplayer.ext.vp9;
|
||||||
|
|
||||||
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +62,7 @@ import java.nio.ByteBuffer;
|
|||||||
* @return 0 on success with a frame to render. 1 on success without a frame to render.
|
* @return 0 on success with a frame to render. 1 on success without a frame to render.
|
||||||
* @throws VpxDecoderException on decode failure.
|
* @throws VpxDecoderException on decode failure.
|
||||||
*/
|
*/
|
||||||
public int decode(ByteBuffer encoded, int size, OutputBuffer outputBuffer)
|
public int decode(ByteBuffer encoded, int size, VpxOutputBuffer outputBuffer)
|
||||||
throws VpxDecoderException {
|
throws VpxDecoderException {
|
||||||
if (vpxDecode(vpxDecContext, encoded, size) != 0) {
|
if (vpxDecode(vpxDecContext, encoded, size) != 0) {
|
||||||
throw new VpxDecoderException("libvpx decode error: " + vpxGetErrorMessage(vpxDecContext));
|
throw new VpxDecoderException("libvpx decode error: " + vpxGetErrorMessage(vpxDecContext));
|
||||||
@ -94,7 +92,7 @@ import java.nio.ByteBuffer;
|
|||||||
private native long vpxInit();
|
private native long vpxInit();
|
||||||
private native long vpxClose(long context);
|
private native long vpxClose(long context);
|
||||||
private native long vpxDecode(long context, ByteBuffer encoded, int length);
|
private native long vpxDecode(long context, ByteBuffer encoded, int length);
|
||||||
private native int vpxGetFrame(long context, OutputBuffer outputBuffer);
|
private native int vpxGetFrame(long context, VpxOutputBuffer outputBuffer);
|
||||||
private native String vpxGetErrorMessage(long context);
|
private native String vpxGetErrorMessage(long context);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,16 +28,19 @@ import java.util.LinkedList;
|
|||||||
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
|
||||||
|
* at a time so this value should be high enough considering LibvpxVideoTrackRenderer requirement.
|
||||||
|
*/
|
||||||
private static final int NUM_BUFFERS = 16;
|
private static final int NUM_BUFFERS = 16;
|
||||||
|
|
||||||
private final Object lock;
|
private final Object lock;
|
||||||
|
|
||||||
private final LinkedList<InputBuffer> dequeuedInputBuffers;
|
private final LinkedList<VpxInputBuffer> dequeuedInputBuffers;
|
||||||
private final LinkedList<InputBuffer> queuedInputBuffers;
|
private final LinkedList<VpxInputBuffer> queuedInputBuffers;
|
||||||
private final LinkedList<OutputBuffer> queuedOutputBuffers;
|
private final LinkedList<VpxOutputBuffer> queuedOutputBuffers;
|
||||||
private final LinkedList<OutputBuffer> dequeuedOutputBuffers;
|
private final VpxInputBuffer[] availableInputBuffers;
|
||||||
private final InputBuffer[] availableInputBuffers;
|
private final VpxOutputBuffer[] availableOutputBuffers;
|
||||||
private final OutputBuffer[] availableOutputBuffers;
|
|
||||||
private int availableInputBufferCount;
|
private int availableInputBufferCount;
|
||||||
private int availableOutputBufferCount;
|
private int availableOutputBufferCount;
|
||||||
|
|
||||||
@ -57,14 +60,13 @@ import java.util.LinkedList;
|
|||||||
dequeuedInputBuffers = new LinkedList<>();
|
dequeuedInputBuffers = new LinkedList<>();
|
||||||
queuedInputBuffers = new LinkedList<>();
|
queuedInputBuffers = new LinkedList<>();
|
||||||
queuedOutputBuffers = new LinkedList<>();
|
queuedOutputBuffers = new LinkedList<>();
|
||||||
dequeuedOutputBuffers = new LinkedList<>();
|
availableInputBuffers = new VpxInputBuffer[NUM_BUFFERS];
|
||||||
availableInputBuffers = new InputBuffer[NUM_BUFFERS];
|
availableOutputBuffers = new VpxOutputBuffer[NUM_BUFFERS];
|
||||||
availableOutputBuffers = new OutputBuffer[NUM_BUFFERS];
|
|
||||||
availableInputBufferCount = NUM_BUFFERS;
|
availableInputBufferCount = NUM_BUFFERS;
|
||||||
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 InputBuffer();
|
availableInputBuffers[i] = new VpxInputBuffer();
|
||||||
availableOutputBuffers[i] = new OutputBuffer();
|
availableOutputBuffers[i] = new VpxOutputBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,13 +74,13 @@ import java.util.LinkedList;
|
|||||||
this.outputMode = outputMode;
|
this.outputMode = outputMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputBuffer dequeueInputBuffer() throws VpxDecoderException {
|
public VpxInputBuffer dequeueInputBuffer() throws VpxDecoderException {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
maybeThrowDecoderError();
|
maybeThrowDecoderError();
|
||||||
if (availableInputBufferCount == 0) {
|
if (availableInputBufferCount == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
InputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount];
|
VpxInputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount];
|
||||||
inputBuffer.flags = 0;
|
inputBuffer.flags = 0;
|
||||||
inputBuffer.sampleHolder.clearData();
|
inputBuffer.sampleHolder.clearData();
|
||||||
dequeuedInputBuffers.addLast(inputBuffer);
|
dequeuedInputBuffers.addLast(inputBuffer);
|
||||||
@ -86,7 +88,7 @@ import java.util.LinkedList;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queueInputBuffer(InputBuffer inputBuffer) throws VpxDecoderException {
|
public void queueInputBuffer(VpxInputBuffer inputBuffer) throws VpxDecoderException {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
maybeThrowDecoderError();
|
maybeThrowDecoderError();
|
||||||
dequeuedInputBuffers.remove(inputBuffer);
|
dequeuedInputBuffers.remove(inputBuffer);
|
||||||
@ -95,22 +97,20 @@ import java.util.LinkedList;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputBuffer dequeueOutputBuffer() throws VpxDecoderException {
|
public VpxOutputBuffer dequeueOutputBuffer() throws VpxDecoderException {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
maybeThrowDecoderError();
|
maybeThrowDecoderError();
|
||||||
if (queuedOutputBuffers.isEmpty()) {
|
if (queuedOutputBuffers.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
OutputBuffer outputBuffer = queuedOutputBuffers.removeFirst();
|
VpxOutputBuffer outputBuffer = queuedOutputBuffers.removeFirst();
|
||||||
dequeuedOutputBuffers.add(outputBuffer);
|
|
||||||
return outputBuffer;
|
return outputBuffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseOutputBuffer(OutputBuffer outputBuffer) throws VpxDecoderException {
|
public void releaseOutputBuffer(VpxOutputBuffer outputBuffer) throws VpxDecoderException {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
maybeThrowDecoderError();
|
maybeThrowDecoderError();
|
||||||
dequeuedOutputBuffers.remove(outputBuffer);
|
|
||||||
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
|
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
|
||||||
maybeNotifyDecodeLoop();
|
maybeNotifyDecodeLoop();
|
||||||
}
|
}
|
||||||
@ -128,9 +128,6 @@ import java.util.LinkedList;
|
|||||||
while (!queuedOutputBuffers.isEmpty()) {
|
while (!queuedOutputBuffers.isEmpty()) {
|
||||||
availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst();
|
availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst();
|
||||||
}
|
}
|
||||||
while (!dequeuedOutputBuffers.isEmpty()) {
|
|
||||||
availableOutputBuffers[availableOutputBufferCount++] = dequeuedOutputBuffers.removeFirst();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,8 +184,8 @@ import java.util.LinkedList;
|
|||||||
|
|
||||||
private boolean decodeBuffer(VpxDecoder decoder) throws InterruptedException,
|
private boolean decodeBuffer(VpxDecoder decoder) throws InterruptedException,
|
||||||
VpxDecoderException {
|
VpxDecoderException {
|
||||||
InputBuffer inputBuffer;
|
VpxInputBuffer inputBuffer;
|
||||||
OutputBuffer outputBuffer;
|
VpxOutputBuffer outputBuffer;
|
||||||
|
|
||||||
// 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) {
|
||||||
@ -238,7 +235,7 @@ import java.util.LinkedList;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ static final class InputBuffer {
|
/* package */ static final class VpxInputBuffer {
|
||||||
|
|
||||||
public final SampleHolder sampleHolder;
|
public final SampleHolder sampleHolder;
|
||||||
|
|
||||||
@ -246,75 +243,11 @@ import java.util.LinkedList;
|
|||||||
public int height;
|
public int height;
|
||||||
public int flags;
|
public int flags;
|
||||||
|
|
||||||
public InputBuffer() {
|
public VpxInputBuffer() {
|
||||||
sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
|
sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
|
||||||
sampleHolder.data = ByteBuffer.allocateDirect(INPUT_BUFFER_SIZE);
|
sampleHolder.data = ByteBuffer.allocateDirect(INPUT_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ static final class OutputBuffer {
|
|
||||||
|
|
||||||
public ByteBuffer data;
|
|
||||||
public long timestampUs;
|
|
||||||
public int width;
|
|
||||||
public int height;
|
|
||||||
public int flags;
|
|
||||||
public ByteBuffer[] yuvPlanes;
|
|
||||||
public int[] yuvStrides;
|
|
||||||
public int mode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called from C++ through JNI after decoding is done. It will resize the
|
|
||||||
* buffer based on the given dimensions.
|
|
||||||
*/
|
|
||||||
public void initForRgbFrame(int width, int height) {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
int minimumRgbSize = width * height * 2;
|
|
||||||
if (data == null || data.capacity() < minimumRgbSize) {
|
|
||||||
data = ByteBuffer.allocateDirect(minimumRgbSize);
|
|
||||||
yuvPlanes = null;
|
|
||||||
}
|
|
||||||
data.position(0);
|
|
||||||
data.limit(minimumRgbSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called from C++ through JNI after decoding is done. It will resize the
|
|
||||||
* buffer based on the given stride.
|
|
||||||
*/
|
|
||||||
public void initForYuvFrame(int width, int height, int yStride, int uvStride) {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
int yLength = yStride * height;
|
|
||||||
int uvLength = uvStride * ((height + 1) / 2);
|
|
||||||
int minimumYuvSize = yLength + (uvLength * 2);
|
|
||||||
if (data == null || data.capacity() < minimumYuvSize) {
|
|
||||||
data = ByteBuffer.allocateDirect(minimumYuvSize);
|
|
||||||
}
|
|
||||||
data.limit(minimumYuvSize);
|
|
||||||
if (yuvPlanes == null) {
|
|
||||||
yuvPlanes = new ByteBuffer[3];
|
|
||||||
}
|
|
||||||
// Rewrapping has to be done on every frame since the stride might have changed.
|
|
||||||
data.position(0);
|
|
||||||
yuvPlanes[0] = data.slice();
|
|
||||||
yuvPlanes[0].limit(yLength);
|
|
||||||
data.position(yLength);
|
|
||||||
yuvPlanes[1] = data.slice();
|
|
||||||
yuvPlanes[1].limit(uvLength);
|
|
||||||
data.position(yLength + uvLength);
|
|
||||||
yuvPlanes[2] = data.slice();
|
|
||||||
yuvPlanes[2].limit(uvLength);
|
|
||||||
if (yuvStrides == null) {
|
|
||||||
yuvStrides = new int[3];
|
|
||||||
}
|
|
||||||
yuvStrides[0] = yStride;
|
|
||||||
yuvStrides[1] = uvStride;
|
|
||||||
yuvStrides[2] = uvStride;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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.exoplayer.ext.vp9;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OutputBuffer for storing the video frame.
|
||||||
|
*/
|
||||||
|
public final class VpxOutputBuffer {
|
||||||
|
|
||||||
|
public ByteBuffer data;
|
||||||
|
public long timestampUs;
|
||||||
|
public int width;
|
||||||
|
public int height;
|
||||||
|
public int flags;
|
||||||
|
public ByteBuffer[] yuvPlanes;
|
||||||
|
public int[] yuvStrides;
|
||||||
|
public int mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called from C++ through JNI after decoding is done. It will resize the
|
||||||
|
* buffer based on the given dimensions.
|
||||||
|
*/
|
||||||
|
public void initForRgbFrame(int width, int height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
int minimumRgbSize = width * height * 2;
|
||||||
|
if (data == null || data.capacity() < minimumRgbSize) {
|
||||||
|
data = ByteBuffer.allocateDirect(minimumRgbSize);
|
||||||
|
yuvPlanes = null;
|
||||||
|
}
|
||||||
|
data.position(0);
|
||||||
|
data.limit(minimumRgbSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called from C++ through JNI after decoding is done. It will resize the
|
||||||
|
* buffer based on the given stride.
|
||||||
|
*/
|
||||||
|
public void initForYuvFrame(int width, int height, int yStride, int uvStride) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
int yLength = yStride * height;
|
||||||
|
int uvLength = uvStride * ((height + 1) / 2);
|
||||||
|
int minimumYuvSize = yLength + (uvLength * 2);
|
||||||
|
if (data == null || data.capacity() < minimumYuvSize) {
|
||||||
|
data = ByteBuffer.allocateDirect(minimumYuvSize);
|
||||||
|
}
|
||||||
|
data.limit(minimumYuvSize);
|
||||||
|
if (yuvPlanes == null) {
|
||||||
|
yuvPlanes = new ByteBuffer[3];
|
||||||
|
}
|
||||||
|
// Rewrapping has to be done on every frame since the stride might have changed.
|
||||||
|
data.position(0);
|
||||||
|
yuvPlanes[0] = data.slice();
|
||||||
|
yuvPlanes[0].limit(yLength);
|
||||||
|
data.position(yLength);
|
||||||
|
yuvPlanes[1] = data.slice();
|
||||||
|
yuvPlanes[1].limit(uvLength);
|
||||||
|
data.position(yLength + uvLength);
|
||||||
|
yuvPlanes[2] = data.slice();
|
||||||
|
yuvPlanes[2].limit(uvLength);
|
||||||
|
if (yuvStrides == null) {
|
||||||
|
yuvStrides = new int[3];
|
||||||
|
}
|
||||||
|
yuvStrides[0] = yStride;
|
||||||
|
yuvStrides[1] = uvStride;
|
||||||
|
yuvStrides[2] = uvStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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.exoplayer.ext.vp9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the {@link VpxOutputBuffer}.
|
||||||
|
*/
|
||||||
|
public interface VpxOutputBufferRenderer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the output buffer to be rendered.
|
||||||
|
*/
|
||||||
|
void setOutputBuffer(VpxOutputBuffer outputBuffer);
|
||||||
|
|
||||||
|
}
|
@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.ext.vp9;
|
package com.google.android.exoplayer.ext.vp9;
|
||||||
|
|
||||||
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer;
|
|
||||||
|
|
||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
|
|
||||||
@ -67,7 +65,7 @@ import javax.microedition.khronos.opengles.GL10;
|
|||||||
private int program;
|
private int program;
|
||||||
private int texLocation;
|
private int texLocation;
|
||||||
private FloatBuffer textureCoords;
|
private FloatBuffer textureCoords;
|
||||||
private volatile OutputBuffer outputBuffer;
|
private VpxOutputBuffer outputBuffer;
|
||||||
private int previousWidth;
|
private int previousWidth;
|
||||||
private int previousStride;
|
private int previousStride;
|
||||||
|
|
||||||
@ -82,7 +80,7 @@ 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 void setFrame(OutputBuffer outputBuffer) {
|
public synchronized void setFrame(VpxOutputBuffer outputBuffer) {
|
||||||
this.outputBuffer = outputBuffer;
|
this.outputBuffer = outputBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +119,8 @@ import javax.microedition.khronos.opengles.GL10;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrawFrame(GL10 unused) {
|
public void onDrawFrame(GL10 unused) {
|
||||||
OutputBuffer outputBuffer = this.outputBuffer;
|
synchronized (this) {
|
||||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
VpxOutputBuffer outputBuffer = this.outputBuffer;
|
||||||
if (outputBuffer == null) {
|
if (outputBuffer == null) {
|
||||||
// Nothing to render yet.
|
// Nothing to render yet.
|
||||||
return;
|
return;
|
||||||
@ -132,8 +130,9 @@ import javax.microedition.khronos.opengles.GL10;
|
|||||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
||||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
||||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
|
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
|
||||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, outputBuffer.yuvStrides[i],
|
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
|
||||||
h, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, outputBuffer.yuvPlanes[i]);
|
outputBuffer.yuvStrides[i], h, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
|
||||||
|
outputBuffer.yuvPlanes[i]);
|
||||||
}
|
}
|
||||||
// Set cropping of stride if either width or stride has changed.
|
// Set cropping of stride if either width or stride has changed.
|
||||||
if (previousWidth != outputBuffer.width || previousStride != outputBuffer.yuvStrides[0]) {
|
if (previousWidth != outputBuffer.width || previousStride != outputBuffer.yuvStrides[0]) {
|
||||||
@ -148,6 +147,8 @@ 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.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||||
checkNoGLES2Error();
|
checkNoGLES2Error();
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.ext.vp9;
|
package com.google.android.exoplayer.ext.vp9;
|
||||||
|
|
||||||
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
@ -26,7 +24,7 @@ import android.util.AttributeSet;
|
|||||||
* A GLSurfaceView extension that scales itself to the given aspect ratio.
|
* A GLSurfaceView extension that scales itself to the given aspect ratio.
|
||||||
*/
|
*/
|
||||||
@TargetApi(11)
|
@TargetApi(11)
|
||||||
public class VpxVideoSurfaceView extends GLSurfaceView {
|
public class VpxVideoSurfaceView extends GLSurfaceView implements VpxOutputBufferRenderer {
|
||||||
|
|
||||||
private final VpxRenderer renderer;
|
private final VpxRenderer renderer;
|
||||||
|
|
||||||
@ -43,7 +41,8 @@ public class VpxVideoSurfaceView extends GLSurfaceView {
|
|||||||
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderFrame(OutputBuffer outputBuffer) {
|
@Override
|
||||||
|
public void setOutputBuffer(VpxOutputBuffer outputBuffer) {
|
||||||
renderer.setFrame(outputBuffer);
|
renderer.setFrame(outputBuffer);
|
||||||
requestRender();
|
requestRender();
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
Java_com_google_android_exoplayer_ext_vp9_VpxDecoder_ ## NAME \
|
Java_com_google_android_exoplayer_ext_vp9_VpxDecoder_ ## NAME \
|
||||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||||
|
|
||||||
// JNI references for OutputBuffer class.
|
// JNI references for VpxOutputBuffer class.
|
||||||
static jmethodID initForRgbFrame;
|
static jmethodID initForRgbFrame;
|
||||||
static jmethodID initForYuvFrame;
|
static jmethodID initForYuvFrame;
|
||||||
static jfieldID dataField;
|
static jfieldID dataField;
|
||||||
@ -69,7 +69,7 @@ FUNC(jlong, vpxInit) {
|
|||||||
|
|
||||||
// Populate JNI References.
|
// Populate JNI References.
|
||||||
const jclass outputBufferClass = env->FindClass(
|
const jclass outputBufferClass = env->FindClass(
|
||||||
"com/google/android/exoplayer/ext/vp9/VpxDecoderWrapper$OutputBuffer");
|
"com/google/android/exoplayer/ext/vp9/VpxOutputBuffer");
|
||||||
initForYuvFrame = env->GetMethodID(outputBufferClass, "initForYuvFrame",
|
initForYuvFrame = env->GetMethodID(outputBufferClass, "initForYuvFrame",
|
||||||
"(IIII)V");
|
"(IIII)V");
|
||||||
initForRgbFrame = env->GetMethodID(outputBufferClass, "initForRgbFrame",
|
initForRgbFrame = env->GetMethodID(outputBufferClass, "initForRgbFrame",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user