diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index 9f770761a3..322104bf04 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -97,7 +97,6 @@ rtc_static_library("video_jni") {
"src/jni/androidvideotracksource.cc",
"src/jni/androidvideotracksource.h",
"src/jni/androidvideotracksource_jni.cc",
- "src/jni/filevideocapturer_jni.cc",
"src/jni/native_handle_impl.cc",
"src/jni/native_handle_impl.h",
"src/jni/nv12buffer_jni.cc",
diff --git a/sdk/android/api/org/webrtc/FileVideoCapturer.java b/sdk/android/api/org/webrtc/FileVideoCapturer.java
index a71e9327ae..2cff9e0142 100644
--- a/sdk/android/api/org/webrtc/FileVideoCapturer.java
+++ b/sdk/android/api/org/webrtc/FileVideoCapturer.java
@@ -12,12 +12,12 @@ package org.webrtc;
import android.content.Context;
import android.os.SystemClock;
-
-import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
import java.util.Timer;
import java.util.TimerTask;
-import java.io.RandomAccessFile;
-import java.io.IOException;
+import java.util.concurrent.TimeUnit;
public class FileVideoCapturer implements VideoCapturer {
static {
@@ -25,9 +25,7 @@ public class FileVideoCapturer implements VideoCapturer {
}
private interface VideoReader {
- int getFrameWidth();
- int getFrameHeight();
- byte[] getNextFrame();
+ VideoFrame getNextFrame();
void close();
}
@@ -35,26 +33,15 @@ public class FileVideoCapturer implements VideoCapturer {
* Read video data from file for the .y4m container.
*/
private static class VideoReaderY4M implements VideoReader {
- private final static String TAG = "VideoReaderY4M";
- private final int frameWidth;
- private final int frameHeight;
- private final int frameSize;
-
- // First char after header
- private final long videoStart;
-
+ private static final String TAG = "VideoReaderY4M";
private static final String Y4M_FRAME_DELIMETER = "FRAME";
+ private final int frameWidth;
+ private final int frameHeight;
+ // First char after header
+ private final long videoStart;
private final RandomAccessFile mediaFileStream;
- public int getFrameWidth() {
- return frameWidth;
- }
-
- public int getFrameHeight() {
- return frameHeight;
- }
-
public VideoReaderY4M(String file) throws IOException {
mediaFileStream = new RandomAccessFile(file, "r");
StringBuilder builder = new StringBuilder();
@@ -100,12 +87,20 @@ public class FileVideoCapturer implements VideoCapturer {
}
frameWidth = w;
frameHeight = h;
- frameSize = w * h * 3 / 2;
- Logging.d(TAG, "frame dim: (" + w + ", " + h + ") frameSize: " + frameSize);
+ Logging.d(TAG, "frame dim: (" + w + ", " + h + ")");
}
- public byte[] getNextFrame() {
- byte[] frame = new byte[frameSize];
+ public VideoFrame getNextFrame() {
+ final long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
+ final JavaI420Buffer buffer = JavaI420Buffer.allocate(frameWidth, frameHeight);
+ final ByteBuffer dataY = buffer.getDataY();
+ final ByteBuffer dataU = buffer.getDataU();
+ final ByteBuffer dataV = buffer.getDataV();
+ final int chromaHeight = (frameHeight + 1) / 2;
+ final int sizeY = frameHeight * buffer.getStrideY();
+ final int sizeU = chromaHeight * buffer.getStrideU();
+ final int sizeV = chromaHeight * buffer.getStrideV();
+
try {
byte[] frameDelim = new byte[Y4M_FRAME_DELIMETER.length() + 1];
if (mediaFileStream.read(frameDelim) < frameDelim.length) {
@@ -121,13 +116,15 @@ public class FileVideoCapturer implements VideoCapturer {
"Frames should be delimited by FRAME plus newline, found delimter was: '"
+ frameDelimStr + "'");
}
- mediaFileStream.readFully(frame);
- byte[] nv21Frame = new byte[frameSize];
- nativeI420ToNV21(frame, frameWidth, frameHeight, nv21Frame);
- return nv21Frame;
+
+ mediaFileStream.readFully(dataY.array(), dataY.arrayOffset(), sizeY);
+ mediaFileStream.readFully(dataU.array(), dataU.arrayOffset(), sizeU);
+ mediaFileStream.readFully(dataV.array(), dataV.arrayOffset(), sizeV);
} catch (IOException e) {
throw new RuntimeException(e);
}
+
+ return new VideoFrame(buffer, 0 /* rotation */, captureTimeNs);
}
public void close() {
@@ -151,14 +148,6 @@ public class FileVideoCapturer implements VideoCapturer {
}
};
- private int getFrameWidth() {
- return videoReader.getFrameWidth();
- }
-
- private int getFrameHeight() {
- return videoReader.getFrameHeight();
- }
-
public FileVideoCapturer(String inputFile) throws IOException {
try {
videoReader = new VideoReaderY4M(inputFile);
@@ -168,16 +157,8 @@ public class FileVideoCapturer implements VideoCapturer {
}
}
- private byte[] getNextFrame() {
- return videoReader.getNextFrame();
- }
-
public void tick() {
- final long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
-
- byte[] frameData = getNextFrame();
- capturerObserver.onByteBufferFrameCaptured(
- frameData, getFrameWidth(), getFrameHeight(), 0, captureTimeNs);
+ capturerObserver.onFrameCaptured(videoReader.getNextFrame());
}
@Override
@@ -210,6 +191,4 @@ public class FileVideoCapturer implements VideoCapturer {
public boolean isScreencast() {
return false;
}
-
- public static native void nativeI420ToNV21(byte[] src, int width, int height, byte[] dst);
}
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java b/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java
index fd8f3b4102..627e7770ff 100644
--- a/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java
+++ b/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java
@@ -14,11 +14,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.os.Environment;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
import android.support.test.filters.SmallTest;
import java.io.IOException;
-import java.lang.Thread;
+import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,14 +26,8 @@ import org.junit.runner.RunWith;
@RunWith(BaseJUnit4ClassRunner.class)
public class FileVideoCapturerTest {
- private static class Frame {
- public byte[] data;
- public int width;
- public int height;
- }
-
public class MockCapturerObserver implements VideoCapturer.CapturerObserver {
- private final ArrayList frameDatas = new ArrayList();
+ private final ArrayList frames = new ArrayList();
@Override
public void onCapturerStarted(boolean success) {
@@ -50,13 +42,7 @@ public class FileVideoCapturerTest {
@Override
public synchronized void onByteBufferFrameCaptured(
byte[] data, int width, int height, int rotation, long timeStamp) {
- Frame frame = new Frame();
- frame.data = data;
- frame.width = width;
- frame.height = height;
- assertTrue(data.length != 0);
- frameDatas.add(frame);
- notify();
+ // Empty on purpose.
}
@Override
@@ -66,16 +52,17 @@ public class FileVideoCapturerTest {
}
@Override
- public void onFrameCaptured(VideoFrame frame) {
- // Empty on purpose.
+ public synchronized void onFrameCaptured(VideoFrame frame) {
+ frames.add(frame);
+ notify();
}
- public synchronized ArrayList getMinimumFramesBlocking(int minFrames)
+ public synchronized ArrayList getMinimumFramesBlocking(int minFrames)
throws InterruptedException {
- while (frameDatas.size() < minFrames) {
+ while (frames.size() < minFrames) {
wait();
}
- return new ArrayList(frameDatas);
+ return new ArrayList(frames);
}
}
@@ -84,37 +71,63 @@ public class FileVideoCapturerTest {
public void testVideoCaptureFromFile() throws InterruptedException, IOException {
final int FRAME_WIDTH = 4;
final int FRAME_HEIGHT = 4;
+ final int FRAME_CHROMA_WIDTH = (FRAME_WIDTH + 1) / 2;
+ final int FRAME_CHROMA_HEIGHT = (FRAME_HEIGHT + 1) / 2;
+ final int FRAME_SIZE_Y = FRAME_WIDTH * FRAME_HEIGHT;
+ final int FRAME_SIZE_CHROMA = FRAME_CHROMA_WIDTH * FRAME_CHROMA_HEIGHT;
+
final FileVideoCapturer fileVideoCapturer =
new FileVideoCapturer(Environment.getExternalStorageDirectory().getPath()
+ "/chromium_tests_root/sdk/android/instrumentationtests/src/org/webrtc/"
+ "capturetestvideo.y4m");
final MockCapturerObserver capturerObserver = new MockCapturerObserver();
- fileVideoCapturer.initialize(null, null, capturerObserver);
- fileVideoCapturer.startCapture(FRAME_WIDTH, FRAME_HEIGHT, 33);
+ fileVideoCapturer.initialize(
+ null /* surfaceTextureHelper */, null /* applicationContext */, capturerObserver);
+ fileVideoCapturer.startCapture(FRAME_WIDTH, FRAME_HEIGHT, 33 /* fps */);
final String[] expectedFrames = {
"THIS IS JUST SOME TEXT x", "THE SECOND FRAME qwerty.", "HERE IS THE THRID FRAME!"};
- final ArrayList frameDatas;
- frameDatas = capturerObserver.getMinimumFramesBlocking(expectedFrames.length);
-
- assertEquals(expectedFrames.length, frameDatas.size());
+ final ArrayList frames =
+ capturerObserver.getMinimumFramesBlocking(expectedFrames.length);
+ assertEquals(expectedFrames.length, frames.size());
fileVideoCapturer.stopCapture();
fileVideoCapturer.dispose();
+ // Check the content of the frames.
for (int i = 0; i < expectedFrames.length; ++i) {
- Frame frame = frameDatas.get(i);
+ final VideoFrame frame = frames.get(i);
+ final VideoFrame.Buffer buffer = frame.getBuffer();
+ assertTrue(buffer instanceof VideoFrame.I420Buffer);
+ final VideoFrame.I420Buffer i420Buffer = (VideoFrame.I420Buffer) buffer;
- assertEquals(FRAME_WIDTH, frame.width);
- assertEquals(FRAME_HEIGHT, frame.height);
- assertEquals(FRAME_WIDTH * FRAME_HEIGHT * 3 / 2, frame.data.length);
+ assertEquals(FRAME_WIDTH, i420Buffer.getWidth());
+ assertEquals(FRAME_HEIGHT, i420Buffer.getHeight());
- byte[] expectedNV12Bytes = new byte[frame.data.length];
- FileVideoCapturer.nativeI420ToNV21(expectedFrames[i].getBytes(Charset.forName("US-ASCII")),
- FRAME_WIDTH, FRAME_HEIGHT, expectedNV12Bytes);
+ final ByteBuffer dataY = i420Buffer.getDataY();
+ final ByteBuffer dataU = i420Buffer.getDataU();
+ final ByteBuffer dataV = i420Buffer.getDataV();
- assertTrue(Arrays.equals(expectedNV12Bytes, frame.data));
+ assertEquals(FRAME_SIZE_Y, dataY.remaining());
+ assertEquals(FRAME_SIZE_CHROMA, dataU.remaining());
+ assertEquals(FRAME_SIZE_CHROMA, dataV.remaining());
+
+ ByteBuffer frameContents = ByteBuffer.allocate(FRAME_SIZE_Y + 2 * FRAME_SIZE_CHROMA);
+ frameContents.put(dataY);
+ frameContents.put(dataU);
+ frameContents.put(dataV);
+ frameContents.rewind(); // Move back to the beginning.
+
+ assertByteBufferContents(
+ expectedFrames[i].getBytes(Charset.forName("US-ASCII")), frameContents);
+ }
+ }
+
+ private static void assertByteBufferContents(byte[] expected, ByteBuffer actual) {
+ assertEquals("Unexpected ByteBuffer size.", expected.length, actual.remaining());
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals("Unexpected byte at index: " + i, expected[i], actual.get());
}
}
}
diff --git a/sdk/android/src/jni/filevideocapturer_jni.cc b/sdk/android/src/jni/filevideocapturer_jni.cc
deleted file mode 100644
index b5b5c552c2..0000000000
--- a/sdk/android/src/jni/filevideocapturer_jni.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2017 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include
-
-#include "third_party/libyuv/include/libyuv/convert_from.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/logging.h"
-
-namespace webrtc {
-namespace jni {
-
-extern "C" JNIEXPORT void JNICALL
-Java_org_webrtc_FileVideoCapturer_nativeI420ToNV21(JNIEnv* jni,
- jclass,
- jbyteArray j_src_buffer,
- jint width,
- jint height,
- jbyteArray j_dst_buffer) {
- size_t src_size = jni->GetArrayLength(j_src_buffer);
- size_t dst_size = jni->GetArrayLength(j_dst_buffer);
- int src_stride = width;
- int dst_stride = width;
- RTC_CHECK_GE(src_size, src_stride * height * 3 / 2);
- RTC_CHECK_GE(dst_size, dst_stride * height * 3 / 2);
-
- jbyte* src_bytes = jni->GetByteArrayElements(j_src_buffer, 0);
- uint8_t* src = reinterpret_cast(src_bytes);
- jbyte* dst_bytes = jni->GetByteArrayElements(j_dst_buffer, 0);
- uint8_t* dst = reinterpret_cast(dst_bytes);
-
- uint8_t* src_y = src;
- size_t src_stride_y = src_stride;
- uint8_t* src_u = src + src_stride * height;
- size_t src_stride_u = src_stride / 2;
- uint8_t* src_v = src + src_stride * height * 5 / 4;
- size_t src_stride_v = src_stride / 2;
-
- uint8_t* dst_y = dst;
- size_t dst_stride_y = dst_stride;
- size_t dst_stride_uv = dst_stride;
- uint8_t* dst_uv = dst + dst_stride * height;
-
- int ret = libyuv::I420ToNV21(src_y, src_stride_y, src_u, src_stride_u, src_v,
- src_stride_v, dst_y, dst_stride_y, dst_uv,
- dst_stride_uv, width, height);
- jni->ReleaseByteArrayElements(j_src_buffer, src_bytes, 0);
- jni->ReleaseByteArrayElements(j_dst_buffer, dst_bytes, 0);
- if (ret) {
- LOG(LS_ERROR) << "Error converting I420 frame to NV21: " << ret;
- }
-}
-
-} // namespace jni
-} // namespace webrtc