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:
parent
3626865be2
commit
eef94d9995
@ -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);
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user