Catturare grandi quantità di output da Apache Commons-Exec
-
12-09-2019 - |
Domanda
Sto scrivendo un'applicazione video in Java eseguendo ffmpeg
e catturare il suo output sullo standard output. Ho deciso di usare Apache Commons-Exec invece di Runtime
di Java, perché sembra meglio. Tuttavia, io sono un momento difficile catturare tutto l'output.
Ho pensato che utilizzando tubi sarebbe la strada da percorrere, perché è un modo standard di comunicazione tra processi. Tuttavia, la mia messa a punto utilizzando PipedInputStream
e PipedOutputStream
è sbagliato. Sembra funzionare, ma solo per i primi 1042 byte del flusso, che risulta stranamente essere il valore di PipedInputStream.PIPE_SIZE
.
Non ho storia d'amore con l'utilizzo di tubi, ma voglio evitare l'utilizzo del disco I / O (se possibile), a causa della velocità e volume di dati (un video 20s 1m a 512x384 risoluzione produce 690M
dei dati pipe).
Pensieri sulla soluzione migliore per gestire grandi quantità di dati provenienti da un tubo? Il mio codice per i miei due classi sono al di sotto. (Sì, sleep
è male. Pensieri su questo? wait()
e 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();
}
}
}
Soluzione
Il problema è nel metodo run di classe YUV4MPEGPipeParser. Ci sono due cicli successivi. Il secondo ciclo termina immediatamente se ci sono dati attualmente disponibili sul flusso (ad esempio tutti gli input finora è stato elaborato dal parser e ffmpeg o pompa flusso non fosse abbastanza veloce per servire alcuni nuovi dati per esso -> disponibile () == 0 -> ciclo viene terminato -.> Rivestimenti filo pompa)
Proprio sbarazzarsi di questi due loop e il sonno e solo eseguire una semplice lettura di blocco () invece di controllare se tutti i dati sono disponibili per l'elaborazione. V'è anche probabilmente bisogno di wait () / notify () o anche dormire () perché il codice parser viene avviato in un thread separato.
MetodoÈ possibile riscrivere il codice di run () in questo modo:
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 ...
}
}