Video collected by VideoFileRenderer is first saved on the native heap, then saved to disk during release.
BUG=webrtc:6545
Review-Url: https://codereview.webrtc.org/2576283004
Cr-Original-Commit-Position: refs/heads/master@{#16167}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: eef94d99952f64e543075a417e54d476067f32d2
diff --git a/sdk/android/api/org/webrtc/VideoFileRenderer.java b/sdk/android/api/org/webrtc/VideoFileRenderer.java
index f4ffbca..02a4a3f 100644
--- a/sdk/android/api/org/webrtc/VideoFileRenderer.java
+++ b/sdk/android/api/org/webrtc/VideoFileRenderer.java
@@ -16,6 +16,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
+import java.util.ArrayList;
/**
* Can be used to save the video frames to file.
@@ -27,12 +28,14 @@
private final Object handlerLock = new Object();
private final Handler renderThreadHandler;
private final FileOutputStream videoOutFile;
+ private final String outputFileName;
private final int outputFileWidth;
private final int outputFileHeight;
private final int outputFrameSize;
private final ByteBuffer outputFrameBuffer;
private EglBase eglBase;
private YuvConverter yuvConverter;
+ private ArrayList<ByteBuffer> rawFrames = new ArrayList<>();
public VideoFileRenderer(String outputFile, int outputFileWidth, int outputFileHeight,
final EglBase.Context sharedContext) throws IOException {
@@ -40,6 +43,7 @@
throw new IllegalArgumentException("Does not support uneven width or height");
}
+ this.outputFileName = outputFile;
this.outputFileWidth = outputFileWidth;
this.outputFileHeight = outputFileHeight;
@@ -86,7 +90,7 @@
final float[] texMatrix = RendererCommon.multiplyMatrices(rotatedSamplingMatrix, layoutMatrix);
try {
- videoOutFile.write("FRAME\n".getBytes());
+ ByteBuffer buffer = nativeCreateNativeByteBuffer(outputFrameSize);
if (!frame.yuvFrame) {
yuvConverter.convert(outputFrameBuffer, outputFileWidth, outputFileHeight, outputFileWidth,
frame.textureId, texMatrix);
@@ -96,27 +100,26 @@
int offset = outputFrameBuffer.arrayOffset();
// Write Y
- videoOutFile.write(data, offset, outputFileWidth * outputFileHeight);
+ buffer.put(data, offset, outputFileWidth * outputFileHeight);
// Write U
for (int r = outputFileHeight; r < outputFileHeight * 3 / 2; ++r) {
- videoOutFile.write(data, offset + r * stride, stride / 2);
+ buffer.put(data, offset + r * stride, stride / 2);
}
// Write V
for (int r = outputFileHeight; r < outputFileHeight * 3 / 2; ++r) {
- videoOutFile.write(data, offset + r * stride + stride / 2, stride / 2);
+ buffer.put(data, offset + r * stride + stride / 2, stride / 2);
}
} else {
nativeI420Scale(frame.yuvPlanes[0], frame.yuvStrides[0], frame.yuvPlanes[1],
frame.yuvStrides[1], frame.yuvPlanes[2], frame.yuvStrides[2], frame.width, frame.height,
outputFrameBuffer, outputFileWidth, outputFileHeight);
- videoOutFile.write(
- outputFrameBuffer.array(), outputFrameBuffer.arrayOffset(), outputFrameSize);
+
+ buffer.put(outputFrameBuffer.array(), outputFrameBuffer.arrayOffset(), outputFrameSize);
}
- } catch (IOException e) {
- Logging.e(TAG, "Failed to write to file for video out");
- throw new RuntimeException(e);
+ buffer.rewind();
+ rawFrames.add(buffer);
} finally {
VideoRenderer.renderFrameDone(frame);
}
@@ -130,11 +133,6 @@
renderThreadHandler.post(new Runnable() {
@Override
public void run() {
- try {
- videoOutFile.close();
- } catch (IOException e) {
- Logging.d(TAG, "Error closing output video file");
- }
yuvConverter.release();
eglBase.release();
renderThread.quit();
@@ -142,9 +140,31 @@
}
});
ThreadUtils.awaitUninterruptibly(cleanupBarrier);
+ try {
+ for (ByteBuffer buffer : rawFrames) {
+ videoOutFile.write("FRAME\n".getBytes());
+
+ byte[] data = new byte[outputFrameSize];
+ buffer.get(data);
+
+ videoOutFile.write(data);
+
+ nativeFreeNativeByteBuffer(buffer);
+ }
+ videoOutFile.close();
+ Logging.d(TAG, "Video written to disk as " + outputFileName + ". Number frames are "
+ + rawFrames.size() + " and the dimension of the frames are " + outputFileWidth + "x"
+ + outputFileHeight + ".");
+ } catch (IOException e) {
+ Logging.e(TAG, "Error writing video to disk", e);
+ }
}
public static native void nativeI420Scale(ByteBuffer srcY, int strideY, ByteBuffer srcU,
int strideU, ByteBuffer srcV, int strideV, int width, int height, ByteBuffer dst,
int dstWidth, int dstHeight);
+
+ public static native ByteBuffer nativeCreateNativeByteBuffer(int size);
+
+ public static native void nativeFreeNativeByteBuffer(ByteBuffer buffer);
}
diff --git a/sdk/android/src/jni/peerconnection_jni.cc b/sdk/android/src/jni/peerconnection_jni.cc
index 4d98414..8c38a46 100644
--- a/sdk/android/src/jni/peerconnection_jni.cc
+++ b/sdk/android/src/jni/peerconnection_jni.cc
@@ -2237,6 +2237,19 @@
}
}
+JOW(jobject, VideoFileRenderer_nativeCreateNativeByteBuffer)
+(JNIEnv* jni, jclass, jint size) {
+ void* new_data = ::operator new(size);
+ jobject byte_buffer = jni->NewDirectByteBuffer(new_data, size);
+ return byte_buffer;
+}
+
+JOW(void, VideoFileRenderer_nativeFreeNativeByteBuffer)
+(JNIEnv* jni, jclass, jobject byte_buffer) {
+ void* data = jni->GetDirectBufferAddress(byte_buffer);
+ ::operator delete(data);
+}
+
JOW(jstring, MediaStreamTrack_nativeId)(JNIEnv* jni, jclass, jlong j_p) {
return JavaStringFromStdString(
jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->id());