Chromium Code Reviews| 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..9953960c83d73049b7e30b14728171e145437ccf |
| --- /dev/null |
| +++ b/webrtc/api/android/java/src/org/webrtc/FileVideoCapturer.java |
| @@ -0,0 +1,280 @@ |
| +/* |
| + * 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 java.util.List; |
| + |
| +import java.io.File; |
| +import java.io.RandomAccessFile; |
| +import java.io.IOException; |
| + |
| +interface VideoReader { |
| + int getFrameWidth(); |
| + int getFrameHeight(); |
| + byte[] getNextFrame(); |
| + void close(); |
| +} |
| + |
| +class VideoReaderYuv implements VideoReader { |
|
magjed_webrtc
2016/08/31 13:08:03
Make this a private class in FileVideoCapturer ins
mandermo
2016/09/16 12:32:30
Done.
|
| + private final static String TAG = "VideoReaderYuv"; |
| + private int frameWidth; |
| + private int frameHeight; |
| + private int frameSize; |
| + private RandomAccessFile mediaFileStream; |
| + |
| + public int getFrameWidth() { |
| + return frameWidth; |
| + } |
|
magjed_webrtc
2016/08/31 13:08:03
You should have an empty line between functions.
mandermo
2016/09/16 12:32:29
Done.
Does it apply to interfaces?
magjed_webrtc
2016/09/16 13:46:25
No.
|
| + public int getFrameHeight() { |
| + return frameHeight; |
| + } |
| + |
| + VideoReaderYuv(File file, int width, int height) throws IOException { |
| + mediaFileStream = new RandomAccessFile(file, "r"); |
| + this.frameWidth = width; |
| + this.frameHeight = height; |
| + this.frameSize = width*height*3/2; |
|
magjed_webrtc
2016/08/31 13:08:03
You need to insert spaces here.
mandermo
2016/09/16 12:32:30
Done.
|
| + } |
| + public byte[] getNextFrame() { |
| + byte[] frame = new byte[frameSize]; |
| + try { |
| + if (mediaFileStream.length() - mediaFileStream.getFilePointer() < frameSize) { |
| + mediaFileStream.seek(0); |
| + } |
| + mediaFileStream.readFully(frame); |
| + |
| + return frame; |
| + } |
| + catch (IOException e) { |
| + Logging.d(TAG, "Problem reading file"); |
| + return null; |
| + } |
| + } |
| + public void close() { |
| + try { |
| + mediaFileStream.close(); |
| + } |
| + catch (IOException e) { |
| + Logging.d(TAG, "Problem closing file"); |
| + } |
| + } |
| +} |
| + |
| +class VideoReaderY4M implements VideoReader { |
|
magjed_webrtc
2016/08/31 13:08:03
Is this class unused?
mandermo
2016/09/16 12:32:30
Used earlier, removed now.
|
| + private final static String TAG = "VideoReaderY4M"; |
| + private int frameWidth; |
| + private int frameHeight; |
| + private int frameSize; |
| + // First char after header |
| + private int videoStart; |
| + final String Y4M_FRAME_DELIMETER = "FRAME"; |
| + private RandomAccessFile mediaFileStream; |
| + |
| + public int getFrameWidth() { |
| + return frameWidth; |
| + } |
| + public int getFrameHeight() { |
| + return frameHeight; |
| + } |
| + |
| + VideoReaderY4M(File file) throws IOException { |
| + mediaFileStream = new RandomAccessFile(file, "r"); |
| + byte[] startData = new byte[1000]; |
| + mediaFileStream.read(startData); |
| + |
| + String headerAndMore = new String(startData); |
| + videoStart = headerAndMore.indexOf(Y4M_FRAME_DELIMETER); |
| + if (videoStart == -1) { |
| + //throw new RuntimeException("Error reading file"); |
| + 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; |
| + 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) < |
| + Y4M_FRAME_DELIMETER.length()+1) |
| + { |
| + // We reach end of file, loop |
| + mediaFileStream.seek(videoStart + Y4M_FRAME_DELIMETER.length()+1); |
| + } |
| + mediaFileStream.readFully(frame); |
| + return frame; |
| + } |
| + catch (IOException e) { |
| + return null; |
| + } |
| + } |
| + public void close() { |
| + try { |
| + mediaFileStream.close(); |
| + } |
| + catch (IOException e) { |
| + Logging.d(TAG, "Problem closing file"); |
| + } |
| + } |
| +} |
| +public class FileVideoCapturer implements CameraVideoCapturer { |
|
magjed_webrtc
2016/08/31 13:08:03
Implement VideoCapturer instead of CameraVideoCapt
mandermo
2016/09/16 12:32:30
PeerConnectionClient uses switchCamera(), which is
magjed_webrtc
2016/09/16 13:46:25
We talked about it offline, but I think the right
mandermo
2016/09/23 15:12:03
Done.
|
| + private final static String TAG = "FileVideoCapturer"; |
| + |
| + private VideoReader videoReader; |
| + |
| + private CameraVideoCapturer physicalCameraCapturer; |
|
magjed_webrtc
2016/08/31 13:08:03
This is strange, why does the File capturer need a
mandermo
2016/09/16 12:32:30
Refactored with a timer thread controlling when to
|
| + |
| + private int getFrameWidth() { |
| + return videoReader.getFrameWidth(); |
| + } |
| + |
| + private int getFrameHeight() { |
| + return videoReader.getFrameHeight(); |
| + } |
| + |
| + class CapturerObserverHijacker implements CapturerObserver { |
| + private CapturerObserver sink; |
| + |
| + CapturerObserverHijacker(CapturerObserver sink) { |
| + this.sink = sink; |
| + } |
| + |
| + @Override |
| + public void onCapturerStarted(boolean success) { |
| + sink.onCapturerStarted(success); |
| + } |
| + |
| + @Override |
| + public void onCapturerStopped() { |
| + sink.onCapturerStopped(); |
| + } |
| + |
| + @Override |
| + public void onByteBufferFrameCaptured(byte[] data, int width, int height, int rotation, |
| + long timeStamp) { |
| + // Hijack and send our frame instead |
| + byte[] frameData = getNextFrame(); |
| + sink.onByteBufferFrameCaptured(frameData, getFrameWidth(), getFrameHeight(), 0, timeStamp); |
| + } |
| + |
| + @Override |
| + public void onTextureFrameCaptured( |
| + int width, int height, int oesTextureId, float[] transformMatrix, int rotation, |
| + long timestamp) |
| + { |
| + sink.onTextureFrameCaptured( |
| + width, height, oesTextureId, transformMatrix, rotation, timestamp); |
| + } |
| + |
| + @Override |
| + public void onOutputFormatRequest(int width, int height, int framerate) { |
| + sink.onOutputFormatRequest(width, height, framerate); |
| + } |
| + } |
| + |
| + public FileVideoCapturer(File inputFile, int fileVideoWidth, int fileVideoHeight, |
| + CameraVideoCapturer physicalCameraCapturer) throws IOException { |
| + this.physicalCameraCapturer = physicalCameraCapturer; |
| + openMediaFile(inputFile, fileVideoWidth, fileVideoHeight); |
| + } |
| + |
| + public static FileVideoCapturer create(File inputFile, int fileVideoWidth, |
| + int fileVideoHeight, CameraVideoCapturer physicalCameraCapturer) { |
| + try { |
| + return new FileVideoCapturer( |
| + inputFile, fileVideoWidth, fileVideoHeight, physicalCameraCapturer); |
| + } catch (IOException e) { |
| + Logging.e(TAG, "Error opening input file.", e); |
| + return null; |
| + } |
| + } |
| + |
| + @Override |
| + public List<CameraEnumerationAndroid.CaptureFormat> getSupportedFormats() { |
| + return physicalCameraCapturer.getSupportedFormats(); |
| + } |
| + |
| + @Override |
| + public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context applicationContext, |
| + CapturerObserver capturerObserver) { |
| + CapturerObserverHijacker hijacker = new CapturerObserverHijacker(capturerObserver); |
| + // Decorate with own frameObserver and call parent |
| + // The decorated frame observer throws away the frame it got and takes from file |
| + physicalCameraCapturer.initialize(surfaceTextureHelper, applicationContext, hijacker); |
| + } |
| + |
| + @Override |
| + public void startCapture( |
| + int width, int height, int framerate) { |
| + physicalCameraCapturer.startCapture(width, height, framerate); |
| + } |
| + |
| + @Override |
| + public void stopCapture() throws InterruptedException { |
| + physicalCameraCapturer.stopCapture(); |
| + } |
| + |
| + @Override |
| + public void onOutputFormatRequest(int width, int height, int framerate) { |
| + physicalCameraCapturer.onOutputFormatRequest(width, height, framerate); |
| + } |
| + |
| + @Override |
| + public void changeCaptureFormat(int width, int height, int framerate) { |
| + physicalCameraCapturer.changeCaptureFormat(width, height, framerate); |
| + } |
| + |
| + @Override |
| + public void dispose() { |
| + physicalCameraCapturer.dispose(); |
| + videoReader.close(); |
| + } |
| + |
| + @Override |
| + public void switchCamera(CameraSwitchHandler switchEventsHandler) { |
| + physicalCameraCapturer.switchCamera(switchEventsHandler); |
| + } |
| + |
| + private void openMediaFile(File file, int fileVideoWidth, int fileVideoHeight) |
| + throws IOException |
| + { |
| + try { |
| + videoReader = new VideoReaderYuv(file, fileVideoWidth, fileVideoHeight); |
| + } |
| + catch (IOException e) { |
| + Logging.d(TAG, "Could not open video file: " + file); |
| + } |
| + } |
| + |
| + private byte[] getNextFrame() { |
| + return videoReader.getNextFrame(); |
| + } |
| +} |
| + |