Apache Commons-Exec에서 많은 양의 출력 캡처
-
12-09-2019 - |
문제
나는 실행하여 Java에서 비디오 응용 프로그램을 작성하고 있습니다. ffmpeg
출력을 표준 출력으로 캡처합니다. 나는 Java 대신 Apache Commons-Exec을 사용하기로 결정했습니다. Runtime
, 더 좋아 보이기 때문에. 그러나 모든 출력을 포착하는 데 어려움이 있습니다.
파이프를 사용하는 것이 진행되는 길이라고 생각했습니다. 왜냐하면 그것은 프로세스 간 통신의 표준 방법이기 때문입니다. 그러나 내 설정을 사용합니다 PipedInputStream
그리고 PipedOutputStream
잘못되었습니다. 그것은 효과가있는 것 같지만 하천의 첫 1042 바이트에 대해서만 호기심이 호기심이 있습니다. PipedInputStream.PIPE_SIZE
.
파이프 사용에 대한 연애는 없지만 데이터의 속도와 양 (512x384 해상도의 1m 20s 비디오 690) 때문에 디스크 I/O (가능한 경우)를 사용하지 않기를 원합니다.M
파이프 데이터).
파이프에서 나오는 다량의 데이터를 처리하기위한 최상의 솔루션에 대한 생각? 두 클래스에 대한 내 코드는 다음과 같습니다. (예, sleep
나쁘다. 그것에 대한 생각? wait()
그리고 notifyAll()
?)
WriteFrames.java
public class WriteFrames {
public static void main(String[] args) {
String commandName = "ffmpeg";
CommandLine commandLine = new CommandLine(commandName);
File filename = new File(args[0]);
String[] options = new String[] {
"-i",
filename.getAbsolutePath(),
"-an",
"-f",
"yuv4mpegpipe",
"-"};
for (String s : options) {
commandLine.addArgument(s);
}
PipedOutputStream output = new PipedOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(output, System.err);
DefaultExecutor executor = new DefaultExecutor();
try {
DataInputStream is = new DataInputStream(new PipedInputStream(output));
YUV4MPEGPipeParser p = new YUV4MPEGPipeParser(is);
p.start();
executor.setStreamHandler(streamHandler);
executor.execute(commandLine);
} catch (IOException e) {
e.printStackTrace();
}
}
}
YUV4MPEGPipeParser.java
public class YUV4MPEGPipeParser extends Thread {
private InputStream is;
int width, height;
public YUV4MPEGPipeParser(InputStream is) {
this.is = is;
}
public void run() {
try {
while (is.available() == 0) {
Thread.sleep(100);
}
while (is.available() != 0) {
// do stuff.... like write out YUV frames
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
해결책
문제는 yuv4mpegpipeparser 클래스의 실행 방법에 있습니다. 두 개의 연속 루프가 있습니다. 스트림에 현재 사용할 수있는 데이터가없는 경우 두 번째 루프는 즉시 종료됩니다 (예 : 지금까지 모든 입력은 Parser에 의해 처리되었으며 FFMPEG 또는 스트림 펌프는 새로운 데이터를 제공하기에 충분히 빠르지 않았습니다 -> usaval () == 0 -> 루프가 종료됩니다 -> 펌프 스레드 마감).
이 두 루프와 수면을 제거하고 처리 할 수있는 데이터가 있는지 확인하는 대신 간단한 차단 read ()를 수행하십시오. 파서 코드가 별도의 스레드에서 시작 되었기 때문에 Wait ()/notify () 또는 sleep ()가 필요하지 않을 수도 있습니다.
다음과 같은 run () 메소드 코드를 다시 작성할 수 있습니다.
public class YUV4MPEGPipeParser extends Thread {
...
// optimal size of buffer for reading from pipe stream :-)
private static final int BUFSIZE = PipedInputStream.PIPE_SIZE;
public void run() {
try {
byte buffer[] = new byte[BUFSIZE];
int len = 0;
while ((len = is.read(buffer, 0, BUFSIZE) != -1) {
// we have valid data available
// in first 'len' bytes of 'buffer' array.
// do stuff.... like write out YUV frames
}
} catch ...
}
}