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:

committed by
Commit Bot

parent
4332d09028
commit
48b3c0272f
@ -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",
|
||||
|
@ -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
|
@ -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(),
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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});
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user