summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/sdk/android/api/org/webrtc/FileVideoCapturer.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/sdk/android/api/org/webrtc/FileVideoCapturer.java')
-rw-r--r--third_party/libwebrtc/sdk/android/api/org/webrtc/FileVideoCapturer.java201
1 files changed, 201 insertions, 0 deletions
diff --git a/third_party/libwebrtc/sdk/android/api/org/webrtc/FileVideoCapturer.java b/third_party/libwebrtc/sdk/android/api/org/webrtc/FileVideoCapturer.java
new file mode 100644
index 0000000000..8270367970
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/api/org/webrtc/FileVideoCapturer.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2016 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.
+ */
+
+package org.webrtc;
+
+import android.content.Context;
+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;
+import java.util.concurrent.TimeUnit;
+
+public class FileVideoCapturer implements VideoCapturer {
+ private interface VideoReader {
+ VideoFrame getNextFrame();
+ void close();
+ }
+
+ /**
+ * Read video data from file for the .y4m container.
+ */
+ @SuppressWarnings("StringSplitter")
+ 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 mediaFile;
+ private final FileChannel mediaFileChannel;
+
+ public VideoReaderY4M(String file) throws IOException {
+ mediaFile = new RandomAccessFile(file, "r");
+ mediaFileChannel = mediaFile.getChannel();
+ StringBuilder builder = new StringBuilder();
+ for (;;) {
+ 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);
+ }
+ if (c == '\n') {
+ // End of header found.
+ break;
+ }
+ builder.append((char) c);
+ }
+ videoStart = mediaFileChannel.position();
+ String header = builder.toString();
+ String[] headerTokens = header.split("[ ]");
+ int w = 0;
+ int h = 0;
+ String colorSpace = "";
+ for (String tok : headerTokens) {
+ char c = tok.charAt(0);
+ switch (c) {
+ case 'W':
+ w = Integer.parseInt(tok.substring(1));
+ break;
+ case 'H':
+ h = Integer.parseInt(tok.substring(1));
+ break;
+ case 'C':
+ colorSpace = tok.substring(1);
+ break;
+ }
+ }
+ Logging.d(TAG, "Color space: " + colorSpace);
+ if (!colorSpace.equals("420") && !colorSpace.equals("420mpeg2")) {
+ throw new IllegalArgumentException(
+ "Does not support any other color space than I420 or I420mpeg2");
+ }
+ if ((w % 2) == 1 || (h % 2) == 1) {
+ throw new IllegalArgumentException("Does not support odd width or height");
+ }
+ frameWidth = w;
+ frameHeight = h;
+ Logging.d(TAG, "frame dim: (" + w + ", " + h + ")");
+ }
+
+ @Override
+ 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 {
+ ByteBuffer frameDelim = ByteBuffer.allocate(FRAME_DELIMETER_LENGTH);
+ if (mediaFileChannel.read(frameDelim) < FRAME_DELIMETER_LENGTH) {
+ // We reach end of file, loop
+ mediaFileChannel.position(videoStart);
+ if (mediaFileChannel.read(frameDelim) < FRAME_DELIMETER_LENGTH) {
+ throw new RuntimeException("Error looping video");
+ }
+ }
+ 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 + "'");
+ }
+
+ mediaFileChannel.read(dataY);
+ mediaFileChannel.read(dataU);
+ mediaFileChannel.read(dataV);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return new VideoFrame(buffer, 0 /* rotation */, captureTimeNs);
+ }
+
+ @Override
+ public void close() {
+ try {
+ // Closing a file also closes the channel.
+ mediaFile.close();
+ } catch (IOException e) {
+ Logging.e(TAG, "Problem closing file", e);
+ }
+ }
+ }
+
+ private final static String TAG = "FileVideoCapturer";
+ private final VideoReader videoReader;
+ private CapturerObserver capturerObserver;
+ private final Timer timer = new Timer();
+
+ private final TimerTask tickTask = new TimerTask() {
+ @Override
+ public void run() {
+ tick();
+ }
+ };
+
+ public FileVideoCapturer(String inputFile) throws IOException {
+ try {
+ videoReader = new VideoReaderY4M(inputFile);
+ } catch (IOException e) {
+ Logging.d(TAG, "Could not open video file: " + inputFile);
+ throw e;
+ }
+ }
+
+ public void tick() {
+ VideoFrame videoFrame = videoReader.getNextFrame();
+ capturerObserver.onFrameCaptured(videoFrame);
+ videoFrame.release();
+ }
+
+ @Override
+ public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context applicationContext,
+ CapturerObserver capturerObserver) {
+ this.capturerObserver = capturerObserver;
+ }
+
+ @Override
+ public void startCapture(int width, int height, int framerate) {
+ timer.schedule(tickTask, 0, 1000 / framerate);
+ }
+
+ @Override
+ public void stopCapture() throws InterruptedException {
+ timer.cancel();
+ }
+
+ @Override
+ public void changeCaptureFormat(int width, int height, int framerate) {
+ // Empty on purpose
+ }
+
+ @Override
+ public void dispose() {
+ videoReader.close();
+ }
+
+ @Override
+ public boolean isScreencast() {
+ return false;
+ }
+}