Add JavaI420Buffer to the API.

This renames I420BufferImpl to JavaI420Buffer and moves it as part of
the API.

Bug: webrtc:7749
Change-Id: I70726f248ba4601b4922996712bdfdafbfa4a1e1
Reviewed-on: https://webrtc-review.googlesource.com/5381
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Commit-Queue: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20145}
This commit is contained in:
Sami Kalliomäki
2017-10-04 17:01:00 +02:00
committed by Commit Bot
parent 4332d09028
commit 48b3c0272f
11 changed files with 87 additions and 45 deletions

View File

@ -395,6 +395,7 @@ android_library("libjingle_peerconnection_java") {
"api/org/webrtc/HardwareVideoDecoderFactory.java",
"api/org/webrtc/HardwareVideoEncoderFactory.java",
"api/org/webrtc/IceCandidate.java",
"api/org/webrtc/JavaI420Buffer.java",
"api/org/webrtc/MediaCodecVideoDecoder.java",
"api/org/webrtc/MediaCodecVideoEncoder.java",
"api/org/webrtc/MediaConstraints.java",
@ -449,7 +450,6 @@ android_library("libjingle_peerconnection_java") {
"src/java/org/webrtc/HardwareVideoDecoder.java",
"src/java/org/webrtc/HardwareVideoEncoder.java",
"src/java/org/webrtc/Histogram.java",
"src/java/org/webrtc/I420BufferImpl.java",
"src/java/org/webrtc/JniCommon.java",
"src/java/org/webrtc/MediaCodecUtils.java",
"src/java/org/webrtc/NativeLibrary.java",

View File

@ -13,8 +13,8 @@ package org.webrtc;
import java.nio.ByteBuffer;
import org.webrtc.VideoFrame.I420Buffer;
/** Implementation of an I420 VideoFrame buffer. */
class I420BufferImpl implements VideoFrame.I420Buffer {
/** Implementation of VideoFrame.I420Buffer backed by Java direct byte buffers. */
public class JavaI420Buffer implements VideoFrame.I420Buffer {
private final int width;
private final int height;
private final ByteBuffer dataY;
@ -28,8 +28,7 @@ class I420BufferImpl implements VideoFrame.I420Buffer {
private int refCount;
/** Constructs an I420Buffer backed by existing data. */
I420BufferImpl(int width, int height, ByteBuffer dataY, int strideY, ByteBuffer dataU,
private JavaI420Buffer(int width, int height, ByteBuffer dataY, int strideY, ByteBuffer dataU,
int strideU, ByteBuffer dataV, int strideV, Runnable releaseCallback) {
this.width = width;
this.height = height;
@ -44,8 +43,42 @@ class I420BufferImpl implements VideoFrame.I420Buffer {
this.refCount = 1;
}
/** Wraps existing ByteBuffers into JavaI420Buffer object without copying the contents. */
public static JavaI420Buffer wrap(int width, int height, ByteBuffer dataY, int strideY,
ByteBuffer dataU, int strideU, ByteBuffer dataV, int strideV, Runnable releaseCallback) {
if (dataY == null || dataU == null || dataV == null) {
throw new IllegalArgumentException("Data buffers cannot be null.");
}
if (!dataY.isDirect() || !dataU.isDirect() || !dataV.isDirect()) {
throw new IllegalArgumentException("Data buffers must be direct byte buffers.");
}
// Slice the buffers to prevent external modifications to the position / limit of the buffer.
// Note that this doesn't protect the contents of the buffers from modifications.
dataY = dataY.slice();
dataU = dataU.slice();
dataV = dataV.slice();
final int chromaHeight = (height + 1) / 2;
final int minCapacityY = strideY * height;
final int minCapacityU = strideU * chromaHeight;
final int minCapacityV = strideV * chromaHeight;
if (dataY.capacity() < minCapacityY) {
throw new IllegalArgumentException("Y-buffer must be at least " + minCapacityY + " bytes.");
}
if (dataU.capacity() < minCapacityU) {
throw new IllegalArgumentException("U-buffer must be at least " + minCapacityU + " bytes.");
}
if (dataV.capacity() < minCapacityV) {
throw new IllegalArgumentException("V-buffer must be at least " + minCapacityV + " bytes.");
}
return new JavaI420Buffer(
width, height, dataY, strideY, dataU, strideU, dataV, strideV, releaseCallback);
}
/** Allocates an empty I420Buffer suitable for an image of the given dimensions. */
static I420BufferImpl allocate(int width, int height) {
public static JavaI420Buffer allocate(int width, int height) {
int chromaHeight = (height + 1) / 2;
int strideUV = (width + 1) / 2;
int yPos = 0;
@ -66,7 +99,8 @@ class I420BufferImpl implements VideoFrame.I420Buffer {
buffer.limit(vPos + strideUV * chromaHeight);
ByteBuffer dataV = buffer.slice();
return new I420BufferImpl(width, height, dataY, width, dataU, strideUV, dataV, strideUV, null);
return new JavaI420Buffer(
width, height, dataY, width, dataU, strideUV, dataV, strideUV, null /* releaseCallback */);
}
@Override

View File

@ -173,17 +173,12 @@ public class VideoFrame {
dataV.position(cropX / 2 + cropY / 2 * buffer.getStrideV());
buffer.retain();
return new I420BufferImpl(buffer.getWidth(), buffer.getHeight(), dataY.slice(),
return JavaI420Buffer.wrap(buffer.getWidth(), buffer.getHeight(), dataY.slice(),
buffer.getStrideY(), dataU.slice(), buffer.getStrideU(), dataV.slice(),
buffer.getStrideV(), new Runnable() {
@Override
public void run() {
buffer.release();
}
});
buffer.getStrideV(), buffer::release);
}
I420BufferImpl newBuffer = I420BufferImpl.allocate(scaleWidth, scaleHeight);
JavaI420Buffer newBuffer = JavaI420Buffer.allocate(scaleWidth, scaleHeight);
nativeCropAndScaleI420(buffer.getDataY(), buffer.getStrideY(), buffer.getDataU(),
buffer.getStrideU(), buffer.getDataV(), buffer.getStrideV(), cropX, cropY, cropWidth,
cropHeight, newBuffer.getDataY(), newBuffer.getStrideY(), newBuffer.getDataU(),

View File

@ -163,7 +163,7 @@ public class VideoRenderer {
VideoRenderer.renderFrameDone(this);
buffer = backingBuffer;
} else if (yuvFrame) {
buffer = new I420BufferImpl(width, height, yuvPlanes[0], yuvStrides[0], yuvPlanes[1],
buffer = JavaI420Buffer.wrap(width, height, yuvPlanes[0], yuvStrides[0], yuvPlanes[1],
yuvStrides[1], yuvPlanes[2], yuvStrides[2],
() -> { VideoRenderer.renderFrameDone(this); });
} else {

View File

@ -37,33 +37,32 @@ import org.junit.runner.RunWith;
// EmptyActivity is needed for the surface.
@RunWith(BaseJUnit4ClassRunner.class)
public class EglRendererTest {
final static String TAG = "EglRendererTest";
final static int RENDER_WAIT_MS = 1000;
final static int SURFACE_WAIT_MS = 1000;
final static int TEST_FRAME_WIDTH = 4;
final static int TEST_FRAME_HEIGHT = 4;
final static int REMOVE_FRAME_LISTENER_RACY_NUM_TESTS = 10;
private final static String TAG = "EglRendererTest";
private final static int RENDER_WAIT_MS = 1000;
private final static int SURFACE_WAIT_MS = 1000;
private final static int TEST_FRAME_WIDTH = 4;
private final static int TEST_FRAME_HEIGHT = 4;
private final static int REMOVE_FRAME_LISTENER_RACY_NUM_TESTS = 10;
// Some arbitrary frames.
final static ByteBuffer[][] TEST_FRAMES = {
private final static byte[][][] TEST_FRAMES_DATA = {
{
ByteBuffer.wrap(new byte[] {
11, -12, 13, -14, -15, 16, -17, 18, 19, -110, 111, -112, -113, 114, -115, 116}),
ByteBuffer.wrap(new byte[] {117, 118, 119, 120}),
ByteBuffer.wrap(new byte[] {121, 122, 123, 124}),
new byte[] {
11, -12, 13, -14, -15, 16, -17, 18, 19, -110, 111, -112, -113, 114, -115, 116},
new byte[] {117, 118, 119, 120}, new byte[] {121, 122, 123, 124},
},
{
ByteBuffer.wrap(new byte[] {-11, -12, -13, -14, -15, -16, -17, -18, -19, -110, -111, -112,
-113, -114, -115, -116}),
ByteBuffer.wrap(new byte[] {-121, -122, -123, -124}),
ByteBuffer.wrap(new byte[] {-117, -118, -119, -120}),
new byte[] {-11, -12, -13, -14, -15, -16, -17, -18, -19, -110, -111, -112, -113, -114,
-115, -116},
new byte[] {-121, -122, -123, -124}, new byte[] {-117, -118, -119, -120},
},
{
ByteBuffer.wrap(new byte[] {-11, -12, -13, -14, -15, -16, -17, -18, -19, -110, -111, -112,
-113, -114, -115, -116}),
ByteBuffer.wrap(new byte[] {117, 118, 119, 120}),
ByteBuffer.wrap(new byte[] {121, 122, 123, 124}),
new byte[] {-11, -12, -13, -14, -15, -16, -17, -18, -19, -110, -111, -112, -113, -114,
-115, -116},
new byte[] {117, 118, 119, 120}, new byte[] {121, 122, 123, 124},
},
};
private final static ByteBuffer[][] TEST_FRAMES =
copyTestDataToDirectByteBuffers(TEST_FRAMES_DATA);
private class TestFrameListener implements EglRenderer.FrameListener {
final private ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
@ -335,4 +334,18 @@ public class EglRendererTest {
feedFrame(1);
assertFalse(testFrameListener.waitForBitmap(RENDER_WAIT_MS));
}
private static ByteBuffer[][] copyTestDataToDirectByteBuffers(byte[][][] testData) {
final ByteBuffer[][] result = new ByteBuffer[testData.length][];
for (int i = 0; i < testData.length; i++) {
result[i] = new ByteBuffer[testData[i].length];
for (int j = 0; j < testData[i].length; j++) {
result[i][j] = ByteBuffer.allocateDirect(testData[i][j].length);
result[i][j].put(testData[i][j]);
result[i][j].rewind();
}
}
return result;
}
}

View File

@ -122,7 +122,7 @@ public final class HardwareVideoDecoderTest {
VideoCodecStatus.OK);
// First, encode a frame.
VideoFrame.I420Buffer buffer = I420BufferImpl.allocate(SETTINGS.width, SETTINGS.height);
VideoFrame.I420Buffer buffer = JavaI420Buffer.allocate(SETTINGS.width, SETTINGS.height);
VideoFrame frame = new VideoFrame(buffer, rotation, presentationTimestampUs * 1000);
VideoEncoder.EncodeInfo info = new VideoEncoder.EncodeInfo(
new EncodedImage.FrameType[] {EncodedImage.FrameType.VideoFrameKey});
@ -197,7 +197,7 @@ public final class HardwareVideoDecoderTest {
VideoCodecStatus.OK);
// First, encode a frame.
VideoFrame.I420Buffer buffer = I420BufferImpl.allocate(SETTINGS.width, SETTINGS.height);
VideoFrame.I420Buffer buffer = JavaI420Buffer.allocate(SETTINGS.width, SETTINGS.height);
VideoFrame frame = new VideoFrame(buffer, rotation, presentationTimestampUs * 1000);
VideoEncoder.EncodeInfo info = new VideoEncoder.EncodeInfo(
new EncodedImage.FrameType[] {EncodedImage.FrameType.VideoFrameKey});

View File

@ -179,7 +179,7 @@ public class HardwareVideoEncoderTest {
@Override
public VideoFrame.I420Buffer toI420() {
return I420BufferImpl.allocate(width, height);
return JavaI420Buffer.allocate(width, height);
}
@Override
@ -191,12 +191,12 @@ public class HardwareVideoEncoderTest {
}
private static class MockI420Buffer extends MockBufferBase implements VideoFrame.I420Buffer {
private final I420BufferImpl realBuffer;
private final JavaI420Buffer realBuffer;
public MockI420Buffer(int width, int height, Runnable releaseCallback) {
super(width, height, releaseCallback);
// We never release this but it is not a problem in practice because the release is a no-op.
realBuffer = I420BufferImpl.allocate(width, height);
realBuffer = JavaI420Buffer.allocate(width, height);
}
@Override

View File

@ -535,7 +535,7 @@ class HardwareVideoDecoder
final int vPos = uPos + uvStride * sliceHeight / 2;
final int vEnd = vPos + uvStride * (sliceHeight / 2);
VideoFrame.I420Buffer frameBuffer = I420BufferImpl.allocate(width, height);
VideoFrame.I420Buffer frameBuffer = JavaI420Buffer.allocate(width, height);
ByteBuffer dataY = frameBuffer.getDataY();
dataY.position(0); // Ensure we are in the beginning.
@ -605,7 +605,7 @@ class HardwareVideoDecoder
buffer.limit(vEnd);
ByteBuffer dataV = buffer.slice();
return new I420BufferImpl(
return JavaI420Buffer.wrap(
width, height, dataY, stride, dataU, uvStride, dataV, uvStride, releaseCallback);
}

View File

@ -69,7 +69,7 @@ public class NV12Buffer implements VideoFrame.Buffer {
@Override
public VideoFrame.Buffer cropAndScale(
int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
I420BufferImpl newBuffer = I420BufferImpl.allocate(scaleWidth, scaleHeight);
JavaI420Buffer newBuffer = JavaI420Buffer.allocate(scaleWidth, scaleHeight);
nativeCropAndScale(cropX, cropY, cropWidth, cropHeight, scaleWidth, scaleHeight, buffer, width,
height, stride, sliceHeight, newBuffer.getDataY(), newBuffer.getStrideY(),
newBuffer.getDataU(), newBuffer.getStrideU(), newBuffer.getDataV(), newBuffer.getStrideV());

View File

@ -64,7 +64,7 @@ public class NV21Buffer implements VideoFrame.Buffer {
@Override
public VideoFrame.Buffer cropAndScale(
int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
I420BufferImpl newBuffer = I420BufferImpl.allocate(scaleWidth, scaleHeight);
JavaI420Buffer newBuffer = JavaI420Buffer.allocate(scaleWidth, scaleHeight);
nativeCropAndScale(cropX, cropY, cropWidth, cropHeight, scaleWidth, scaleHeight, data, width,
height, newBuffer.getDataY(), newBuffer.getStrideY(), newBuffer.getDataU(),
newBuffer.getStrideU(), newBuffer.getDataV(), newBuffer.getStrideV());

View File

@ -101,7 +101,7 @@ class TextureBufferImpl implements VideoFrame.TextureBuffer {
ByteBuffer dataV = buffer.slice();
// SurfaceTextureHelper uses the same stride for Y, U, and V data.
return new I420BufferImpl(width, height, dataY, stride, dataU, stride, dataV, stride, null);
return JavaI420Buffer.wrap(width, height, dataY, stride, dataU, stride, dataV, stride, null);
}
@Override