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-Commit-Position: refs/heads/master@{#16167}
This commit is contained in:
mandermo 2017-01-19 09:02:29 -08:00 committed by Commit bot
parent 3626865be2
commit eef94d9995
2 changed files with 47 additions and 14 deletions

View File

@ -16,6 +16,7 @@ import java.io.FileOutputStream;
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 @@ public class VideoFileRenderer implements VideoRenderer.Callbacks {
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 @@ public class VideoFileRenderer implements VideoRenderer.Callbacks {
throw new IllegalArgumentException("Does not support uneven width or height");
}
this.outputFileName = outputFile;
this.outputFileWidth = outputFileWidth;
this.outputFileHeight = outputFileHeight;
@ -86,7 +90,7 @@ public class VideoFileRenderer implements VideoRenderer.Callbacks {
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 @@ public class VideoFileRenderer implements VideoRenderer.Callbacks {
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 @@ public class VideoFileRenderer implements VideoRenderer.Callbacks {
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 @@ public class VideoFileRenderer implements VideoRenderer.Callbacks {
}
});
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);
}

View File

@ -2237,6 +2237,19 @@ JOW(void, VideoFileRenderer_nativeI420Scale)(
}
}
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());