From 06110652561298890b9ad4f0eb0f7315ca42325c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Kalliom=C3=A4ki?= Date: Tue, 20 Feb 2018 11:19:58 +0100 Subject: [PATCH] Update JavaI420Buffer.allocate to use native allocations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures memory is released timely and avoids problems with garbage collection. Native buffers don't support array operation, so FileVideoCapturer had to be update to use FileChannel to write ByteBuffers directly. Bug: None Change-Id: I3f63d2adc159e9f39f0c68dd0bd6b1747686584e Reviewed-on: https://webrtc-review.googlesource.com/55262 Commit-Queue: Sami Kalliomäki Reviewed-by: Anders Carlsson Cr-Commit-Position: refs/heads/master@{#22118} --- .../api/org/webrtc/FileVideoCapturer.java | 31 +++++++++++-------- .../api/org/webrtc/JavaI420Buffer.java | 7 +++-- .../src/org/webrtc/FileVideoCapturerTest.java | 6 ++++ .../org/webrtc/HardwareVideoDecoderTest.java | 4 ++- .../org/webrtc/HardwareVideoEncoderTest.java | 13 +++++++- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/sdk/android/api/org/webrtc/FileVideoCapturer.java b/sdk/android/api/org/webrtc/FileVideoCapturer.java index 88a0d30090..4efed36a5f 100644 --- a/sdk/android/api/org/webrtc/FileVideoCapturer.java +++ b/sdk/android/api/org/webrtc/FileVideoCapturer.java @@ -15,6 +15,7 @@ import android.os.SystemClock; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.util.Timer; import java.util.TimerTask; @@ -33,18 +34,21 @@ public class FileVideoCapturer implements VideoCapturer { private static class VideoReaderY4M implements VideoReader { private static final String TAG = "VideoReaderY4M"; private static final String Y4M_FRAME_DELIMETER = "FRAME"; + private static final int FRAME_DELIMETER_LENGTH = Y4M_FRAME_DELIMETER.length() + 1; private final int frameWidth; private final int frameHeight; // First char after header private final long videoStart; - private final RandomAccessFile mediaFileStream; + private final RandomAccessFile mediaFile; + private final FileChannel mediaFileChannel; public VideoReaderY4M(String file) throws IOException { - mediaFileStream = new RandomAccessFile(file, "r"); + mediaFile = new RandomAccessFile(file, "r"); + mediaFileChannel = mediaFile.getChannel(); StringBuilder builder = new StringBuilder(); for (;;) { - int c = mediaFileStream.read(); + int c = mediaFile.read(); if (c == -1) { // End of file reached. throw new RuntimeException("Found end of file before end of header for file: " + file); @@ -55,7 +59,7 @@ public class FileVideoCapturer implements VideoCapturer { } builder.append((char) c); } - videoStart = mediaFileStream.getFilePointer(); + videoStart = mediaFileChannel.position(); String header = builder.toString(); String[] headerTokens = header.split("[ ]"); int w = 0; @@ -101,24 +105,24 @@ public class FileVideoCapturer implements VideoCapturer { final int sizeV = chromaHeight * buffer.getStrideV(); try { - byte[] frameDelim = new byte[Y4M_FRAME_DELIMETER.length() + 1]; - if (mediaFileStream.read(frameDelim) < frameDelim.length) { + ByteBuffer frameDelim = ByteBuffer.allocate(FRAME_DELIMETER_LENGTH); + if (mediaFileChannel.read(frameDelim) < FRAME_DELIMETER_LENGTH) { // We reach end of file, loop - mediaFileStream.seek(videoStart); - if (mediaFileStream.read(frameDelim) < frameDelim.length) { + mediaFileChannel.position(videoStart); + if (mediaFileChannel.read(frameDelim) < FRAME_DELIMETER_LENGTH) { throw new RuntimeException("Error looping video"); } } - String frameDelimStr = new String(frameDelim, Charset.forName("US-ASCII")); + String frameDelimStr = new String(frameDelim.array(), Charset.forName("US-ASCII")); if (!frameDelimStr.equals(Y4M_FRAME_DELIMETER + "\n")) { throw new RuntimeException( "Frames should be delimited by FRAME plus newline, found delimter was: '" + frameDelimStr + "'"); } - mediaFileStream.readFully(dataY.array(), dataY.arrayOffset(), sizeY); - mediaFileStream.readFully(dataU.array(), dataU.arrayOffset(), sizeU); - mediaFileStream.readFully(dataV.array(), dataV.arrayOffset(), sizeV); + mediaFileChannel.read(dataY); + mediaFileChannel.read(dataU); + mediaFileChannel.read(dataV); } catch (IOException e) { throw new RuntimeException(e); } @@ -129,7 +133,8 @@ public class FileVideoCapturer implements VideoCapturer { @Override public void close() { try { - mediaFileStream.close(); + // Closing a file also closes the channel. + mediaFile.close(); } catch (IOException e) { Logging.e(TAG, "Problem closing file", e); } diff --git a/sdk/android/api/org/webrtc/JavaI420Buffer.java b/sdk/android/api/org/webrtc/JavaI420Buffer.java index 13ed63bf18..3498faf50e 100644 --- a/sdk/android/api/org/webrtc/JavaI420Buffer.java +++ b/sdk/android/api/org/webrtc/JavaI420Buffer.java @@ -85,7 +85,8 @@ public class JavaI420Buffer implements VideoFrame.I420Buffer { int uPos = yPos + width * height; int vPos = uPos + strideUV * chromaHeight; - ByteBuffer buffer = ByteBuffer.allocateDirect(width * height + 2 * strideUV * chromaHeight); + ByteBuffer buffer = + JniCommon.nativeAllocateByteBuffer(width * height + 2 * strideUV * chromaHeight); buffer.position(yPos); buffer.limit(uPos); @@ -99,8 +100,8 @@ public class JavaI420Buffer implements VideoFrame.I420Buffer { buffer.limit(vPos + strideUV * chromaHeight); ByteBuffer dataV = buffer.slice(); - return new JavaI420Buffer( - width, height, dataY, width, dataU, strideUV, dataV, strideUV, null /* releaseCallback */); + return new JavaI420Buffer(width, height, dataY, width, dataU, strideUV, dataV, strideUV, + () -> { JniCommon.nativeFreeByteBuffer(buffer); }); } @Override diff --git a/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java b/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java index a572b7e91f..667a343745 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java @@ -21,6 +21,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,6 +59,11 @@ public class FileVideoCapturerTest { } } + @Before + public void setUp() { + NativeLibrary.initialize(new NativeLibrary.DefaultLoader()); + } + @Test @SmallTest public void testVideoCaptureFromFile() throws InterruptedException, IOException { diff --git a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java index f680102a7c..09a8275fc1 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java @@ -59,7 +59,7 @@ public final class HardwareVideoDecoderTest { private static final int TEST_FRAME_COUNT = 10; private static final int TEST_FRAME_WIDTH = 640; private static final int TEST_FRAME_HEIGHT = 360; - private static final VideoFrame.I420Buffer[] TEST_FRAMES = generateTestFrames(); + private VideoFrame.I420Buffer[] TEST_FRAMES; private static final boolean ENABLE_INTEL_VP8_ENCODER = true; private static final boolean ENABLE_H264_HIGH_PROFILE = true; @@ -150,6 +150,8 @@ public final class HardwareVideoDecoderTest { public void setUp() { NativeLibrary.initialize(new NativeLibrary.DefaultLoader()); + TEST_FRAMES = generateTestFrames(); + eglBase = new EglBase14(null, EglBase.CONFIG_PLAIN); eglBase.createDummyPbufferSurface(); eglBase.makeCurrent(); diff --git a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java index 2c45a4f16b..ef0be1427f 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java @@ -211,7 +211,6 @@ public class HardwareVideoEncoderTest { 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 = JavaI420Buffer.allocate(width, height); } @@ -251,6 +250,18 @@ public class HardwareVideoEncoderTest { return this; } + @Override + public void retain() { + super.retain(); + realBuffer.retain(); + } + + @Override + public void release() { + super.release(); + realBuffer.release(); + } + @Override public VideoFrame.Buffer cropAndScale( int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {