Захват больших объемов вывода из Apache Commons-Exec
-
12-09-2019 - |
Вопрос
Я пишу видеоприложение на Java, выполняя ffmpeg
и захват его вывода в стандартный вывод.Я решил использовать Apache Commons-Exec вместо Java. Runtime
, потому что так кажется лучше.Однако мне трудно зафиксировать весь результат.
Я подумал, что лучше всего использовать каналы, потому что это стандартный способ межпроцессного взаимодействия.Однако моя установка с использованием PipedInputStream
и PipedOutputStream
неправильно.Кажется, это работает, но только для первых 1042 байт потока, что, как ни странно, соответствует значению PipedInputStream.PIPE_SIZE
.
У меня нет любви к использованию каналов, но я хочу избегать использования дискового ввода-вывода (если это возможно) из-за скорости и объема данных (видео продолжительностью 1 мин 20 с разрешением 512x384 выдает 690M
передаваемых данных).
Мысли о лучшем решении для обработки больших объемов данных, поступающих по каналу?Мой код для моих двух классов приведен ниже.(да, 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.Есть два последовательных цикла.Второй цикл завершается немедленно, если в данный момент в потоке нет данных (например,все входные данные до сих пор обрабатывались парсером, а ffmpeg или потоковая помпа были недостаточно быстрыми, чтобы обработать для него некоторые новые данные ->available() == 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 ...
}
}