Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(607)

Unified Diff: webrtc/api/android/java/src/org/webrtc/FileVideoCapturer.java

Issue 2273573003: Support for video file instead of camera and output video out to file (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Added . to comments Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/api/android/java/src/org/webrtc/FileVideoCapturer.java
diff --git a/webrtc/api/android/java/src/org/webrtc/FileVideoCapturer.java b/webrtc/api/android/java/src/org/webrtc/FileVideoCapturer.java
new file mode 100644
index 0000000000000000000000000000000000000000..9acc31ae7ca361aeecc8a82675e06c04034b195b
--- /dev/null
+++ b/webrtc/api/android/java/src/org/webrtc/FileVideoCapturer.java
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
sakal 2016/09/27 07:54:27 nit: add empty line
+package org.webrtc;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import java.util.concurrent.TimeUnit;
+import java.util.Timer;
+import java.util.TimerTask;
+
sakal 2016/09/27 07:54:27 nit: remove empty line
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+
+public class FileVideoCapturer implements VideoCapturer {
sakal 2016/09/27 07:54:27 nit: it would be nice to have a simple javadoc abo
+ private interface VideoReader {
+ int getFrameWidth();
+ int getFrameHeight();
+ byte[] getNextFrame();
+ void close();
+ }
+
+ class VideoReaderY4M implements VideoReader {
magjed_webrtc 2016/09/26 11:40:01 Make this class static.
sakal 2016/09/27 07:54:27 And private?
mandermo 2016/10/04 14:56:55 Done.
mandermo 2016/10/04 14:56:56 Made it private static
+ private final static String TAG = "VideoReaderY4M";
+ private int frameWidth;
magjed_webrtc 2016/09/26 11:40:02 make these variables final
mandermo 2016/10/04 14:56:55 Done.
+ private int frameHeight;
+ private int frameSize;
+
+ // First char after header
+ private int videoStart;
+
+ final String Y4M_FRAME_DELIMETER = "FRAME";
magjed_webrtc 2016/09/26 11:40:02 make this private static
mandermo 2016/10/04 14:56:56 Done.
+
+ private RandomAccessFile mediaFileStream;
sakal 2016/09/27 07:54:27 Make this final.
+
+ public int getFrameWidth() {
+ return frameWidth;
+ }
+
+ public int getFrameHeight() {
+ return frameHeight;
+ }
+
+ VideoReaderY4M(String file) throws IOException {
sakal 2016/09/27 07:54:27 public?
mandermo 2016/10/04 14:56:56 Done.
+ mediaFileStream = new RandomAccessFile(file, "r");
+ byte[] startData = new byte[1000];
magjed_webrtc 2016/09/26 11:40:02 This is a bit ugly. Is there no elegant way of fin
mandermo 2016/10/04 14:56:56 Done.
mandermo 2016/10/04 14:56:56 Done.
+ mediaFileStream.read(startData);
+
+ String headerAndMore = new String(startData);
+ videoStart = headerAndMore.indexOf(Y4M_FRAME_DELIMETER);
+ if (videoStart == -1) {
+ //throw new RuntimeException("Error reading file");
magjed_webrtc 2016/09/26 11:40:01 You should probably throw here.
mandermo 2016/10/04 14:56:55 Done.
mandermo 2016/10/04 14:56:55 Done.
+ return;
+ }
+ String header = headerAndMore.substring(0, videoStart);
+ mediaFileStream.seek(videoStart);
+ String[] headerTokens = header.split("[\n ]");
+ Logging.d(TAG, "header: " + header + ", headerTokens" + headerTokens);
+ int w = 0;
+ int h = 0;
+ 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;
+ }
+ }
+ frameWidth = w;
+ frameHeight = h;
+ frameSize = w * h * 3 / 2;
magjed_webrtc 2016/09/26 11:40:01 have you tried frames with odd width? I think you
sakal 2016/09/27 07:54:27 The length of the frame depends on the color space
mandermo 2016/10/04 14:56:56 https://wiki.multimedia.cx/index.php?title=YUV4MPE
+ Logging.d(TAG, "frame dim: (" + w + ", " + h + ") frameSize: " + frameSize);
+ }
+
+ public byte[] getNextFrame() {
+ byte[] frame = new byte[frameSize];
+ try {
+ if (mediaFileStream.skipBytes(Y4M_FRAME_DELIMETER.length()+1) <
magjed_webrtc 2016/09/26 11:40:02 this doesn't follow the style guide
+ Y4M_FRAME_DELIMETER.length()+1)
+ {
+ // We reach end of file, loop
+ mediaFileStream.seek(videoStart + Y4M_FRAME_DELIMETER.length()+1);
+ }
+ mediaFileStream.readFully(frame);
sakal 2016/09/27 07:54:27 Frames can also have parameters. We should take th
mandermo 2016/10/04 14:56:56 Checks that the frame does not contain any paramet
+ byte[] nv12Frame = new byte[frameSize];
+ nativeI420ToNV21(
sakal 2016/09/27 07:54:27 Please add comment why it is called nv12Frame when
mandermo 2016/10/04 14:56:56 Renamed to NV21. Thought they where in NV12 format
+ frame, frameWidth, frameHeight, nv12Frame);
+ return nv12Frame;
+ }
+ catch (IOException e) {
sakal 2016/09/27 07:54:27 nit: move to the same line with the closing bracke
+ return null;
+ }
+ }
+
+ public void close() {
+ try {
+ mediaFileStream.close();
+ }
+ catch (IOException e) {
+ Logging.d(TAG, "Problem closing file");
magjed_webrtc 2016/09/26 11:40:01 Log error here instead, i.e. Logging.e.
mandermo 2016/10/04 14:56:55 Done.
mandermo 2016/10/04 14:56:55 Done.
+ }
+ }
+ }
+
+ private final static String TAG = "FileVideoCapturer";
+
+ private VideoReader videoReader;
+
+ private CapturerObserver capturerObserver;
+
+ private Timer timer = new Timer();
+
+ private TimerTask tickTask = new TimerTask() {
+ @Override
+ public void run() {
+ tick(capturerObserver);
+ }
+ };
+
+ private int getFrameWidth() {
+ return videoReader.getFrameWidth();
+ }
+
+ private int getFrameHeight() {
+ return videoReader.getFrameHeight();
+ }
+
+ public FileVideoCapturer(String inputFile) throws IOException {
+ openMediaFile(inputFile);
+ }
+
+ public static FileVideoCapturer create(String inputFile) {
+ try {
+ return new FileVideoCapturer(
+ inputFile);
+ } catch (IOException e) {
+ Logging.e(TAG, "Error opening input file.", e);
+ return null;
+ }
+ }
+
+ private void openMediaFile(String file)
+ throws IOException
+ {
+ try {
+ videoReader = new VideoReaderY4M(file);
magjed_webrtc 2016/09/26 11:40:01 just inline this in the ctor
mandermo 2016/10/04 14:56:56 Done.
+ }
+ catch (IOException e) {
+ Logging.d(TAG, "Could not open video file: " + file);
magjed_webrtc 2016/09/26 11:40:02 We already log in FileVideoCapturer.create so you
mandermo 2016/10/04 14:56:56 Done.
+ throw e;
+ }
+ }
+
+ private byte[] getNextFrame() {
+ return videoReader.getNextFrame();
+ }
+
+ public void tick(VideoCapturer.CapturerObserver capturerObserver) {
+ final long captureTimeNs =
+ TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
+
+ byte[] frameData = getNextFrame();
+ capturerObserver.onByteBufferFrameCaptured(
+ frameData,
+ getFrameWidth(),
+ getFrameHeight(),
+ 0,
+ captureTimeNs);
+ }
+
+ @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 onOutputFormatRequest(int width, int height, int framerate) {
+ // Empty on purpose
+ }
+
+ @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;
+ }
+
+ public static native void nativeI420ToNV21(byte[] src, int width,
+ int height, byte[] dst);
+}

Powered by Google App Engine
This is Rietveld 408576698