التقاط كميات كبيرة من الإخراج من Apache Commons-EXEC
-
12-09-2019 - |
سؤال
أنا أكتب تطبيقا فيديو في جافا عن طريق التنفيذ ffmpeg
والتقاط إخراجها إلى الإخراج القياسي. قررت استخدام Apache Commons-EXEC بدلا من جافا Runtime
, ، لأنه يبدو أفضل. ومع ذلك، لدي صعوبة في التقاط كل الإخراج.
اعتقدت أن استخدام الأنابيب ستكون هي الطريقة التي يجب أن تذهب، لأنها طريقة قياسية للاتصالات بين العملية. ومع ذلك، فإن الإعداد الخاص بي باستخدام PipedInputStream
و PipedOutputStream
خطأ. يبدو أن العمل، ولكن فقط لأول 1042 بايت من الدفق، والتي تحدث بفضول هي قيمة PipedInputStream.PIPE_SIZE
.
ليس لدي علاقة بحب مع استخدام الأنابيب، ولكن أريد تجنب استخدام القرص I / O (إذا كان ذلك ممكنا)، بسبب السرعة وحجم البيانات (فيديو رقم 20s عند 512 × 384 القرار ينتج 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();
}
}
}
المحلول
المشكلة هي في طريقة تشغيل فئة yuv4mpegpippippiperser. هناك اثنين من الحلقات المتعاقبة. ينتهي الحلقة الثانية على الفور إذا لم تكن هناك بيانات متوفرة حاليا على دفق (على سبيل المثال، لم تكن جميع الإدخال حتى الآن عن طريق المحلل، ولم تكن مضخة FFMPEG أو Stream سريعة بما يكفي لخدمة بعض البيانات الجديدة الخاصة به -> متاح () == 0 -> يتم إنهاء الحلقة -> التشطيبات الخيط المضخة).
فقط تخلص من هاتين الحلقتين والنوم فقط وأداء قراءة حظر بسيط () بدلا من التحقق مما إذا كانت أي بيانات متاحة للمعالجة. من المحتمل أن تكون هناك حاجة أيضا للانتظار () / إعلام () أو حتى النوم () لأن رمز المحلل يتم تشغيل رمز المحلل في مؤشر ترابط منفصل.
يمكنك إعادة كتابة طريقة رمز التشغيل () مثل هذا:
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 ...
}
}