Apache Commons-Execからの大量の出力をキャプチャします
-
12-09-2019 - |
質問
私は実行してJavaでビデオアプリケーションを書いています ffmpeg
出力を標準出力にキャプチャします。 Javaの代わりにApache Commons-Execを使用することにしました Runtime
, 、それはより良いように見えるからです。しかし、私はすべての出力をキャプチャするのに苦労しています。
パイプの使用は、プロセス間通信の標準的な方法であるため、行く方法だと思いました。ただし、使用したセットアップ PipedInputStream
と PipedOutputStream
間違っている。それは機能しているようですが、ストリームの最初の1042バイトの場合にのみ、不思議なことに、 PipedInputStream.PIPE_SIZE
.
私はパイプを使用することに恋をしていませんが、データの速度と量のためにディスクI/O(可能であれば)の使用を避けたいです(512x384の1m 20秒のビデオは690を生成しますM
パイプデータの)。
パイプから来る大量のデータを処理するための最良のソリューションについての考え?私の2つのクラスの私のコードは以下にあります。 (はい、 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クラスの実行方法です。 2つの連続したループがあります。ストリームに現在使用可能なデータがない場合、2番目のループはすぐに終了します(例えば、これまでのすべての入力はパーサーによって処理され、FFMPEGまたはストリームポンプは、新しいデータを提供するのに十分な速度ではありませんでした - >利用可能()== 0 - >ループが終了します - >ポンプスレッド仕上げ)。
これら2つのループを取り除いてスリープし、処理にデータが利用可能かどうかを確認する代わりに、単純なブロッキング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 ...
}
}