
In order to eliminate the WebRTC Subtree mirror in Chromium, WebRTC is moving the content of the src/webrtc directory up to the src/ directory. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true TBR=tommi@webrtc.org Bug: chromium:611808 Change-Id: Iac59c5b51b950f174119565bac87955a7994bc38 Reviewed-on: https://webrtc-review.googlesource.com/1560 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Henrik Kjellander <kjellander@webrtc.org> Cr-Commit-Position: refs/heads/master@{#19845}
216 lines
6.1 KiB
Java
216 lines
6.1 KiB
Java
/*
|
|
* 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.util.concurrent.TimeUnit;
|
|
import java.util.Timer;
|
|
import java.util.TimerTask;
|
|
import java.io.RandomAccessFile;
|
|
import java.io.IOException;
|
|
|
|
public class FileVideoCapturer implements VideoCapturer {
|
|
static {
|
|
System.loadLibrary("jingle_peerconnection_so");
|
|
}
|
|
|
|
private interface VideoReader {
|
|
int getFrameWidth();
|
|
int getFrameHeight();
|
|
byte[] getNextFrame();
|
|
void close();
|
|
}
|
|
|
|
/**
|
|
* 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 Y4M_FRAME_DELIMETER = "FRAME";
|
|
|
|
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();
|
|
for (;;) {
|
|
int c = mediaFileStream.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 = mediaFileStream.getFilePointer();
|
|
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;
|
|
frameSize = w * h * 3 / 2;
|
|
Logging.d(TAG, "frame dim: (" + w + ", " + h + ") frameSize: " + frameSize);
|
|
}
|
|
|
|
public byte[] getNextFrame() {
|
|
byte[] frame = new byte[frameSize];
|
|
try {
|
|
byte[] frameDelim = new byte[Y4M_FRAME_DELIMETER.length() + 1];
|
|
if (mediaFileStream.read(frameDelim) < frameDelim.length) {
|
|
// We reach end of file, loop
|
|
mediaFileStream.seek(videoStart);
|
|
if (mediaFileStream.read(frameDelim) < frameDelim.length) {
|
|
throw new RuntimeException("Error looping video");
|
|
}
|
|
}
|
|
String frameDelimStr = new String(frameDelim);
|
|
if (!frameDelimStr.equals(Y4M_FRAME_DELIMETER + "\n")) {
|
|
throw new RuntimeException(
|
|
"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;
|
|
} catch (IOException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
try {
|
|
mediaFileStream.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();
|
|
}
|
|
};
|
|
|
|
private int getFrameWidth() {
|
|
return videoReader.getFrameWidth();
|
|
}
|
|
|
|
private int getFrameHeight() {
|
|
return videoReader.getFrameHeight();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
@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;
|
|
}
|
|
|
|
public static native void nativeI420ToNV21(byte[] src, int width, int height, byte[] dst);
|
|
}
|