mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00
207 lines
6.4 KiB
Java
207 lines
6.4 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.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
|
|
}
|
|
|
|
// RingRTC change to set frame orientation
|
|
@Override
|
|
public void setOrientation(Integer orientation) {
|
|
// Empty on purpose
|
|
}
|
|
|
|
@Override
|
|
public void dispose() {
|
|
videoReader.close();
|
|
}
|
|
|
|
@Override
|
|
public boolean isScreencast() {
|
|
return false;
|
|
}
|
|
}
|