【发布时间】:2017-11-24 06:44:38
【问题描述】:
我需要同时转换很多波形文件。并行大约 300 个文件。新文件不断出现。我使用运行在 CentOS 上的 Java 1.8 应用程序中的 ffmpeg 进程调用。我知道我必须读取错误和输入流才能使从 Java 创建的进程可以退出。
我的代码在几次过期后:
private void ffmpegconverter(String fileIn, String fileOut){
String[] comand = new String[]{"ffmpeg", "-v", "-8", "-i", fileIn, "-acodec", "pcm_s16le", fileOut};
Process process = null;
BufferedReader reader = null;
try {
ProcessBuilder pb = new ProcessBuilder(comand);
pb.redirectErrorStream(true);
process = pb.start();
//Reading from error and standard output console buffer of process. Or it could halts because of nobody
//reads its buffer
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s;
//noinspection StatementWithEmptyBody
while ((s = reader.readLine()) != null) {
log.info(Thread.currentThread().getName() + " with fileIn " + fileIn + " and fileOut " + fileOut + " writes " + s);
//Ignored as we just need to empty the output buffer from process
}
log.info(Thread.currentThread().getName() + " ffmpeg process will be waited for");
if (process.waitFor( 10, TimeUnit.SECONDS )) {
log.info(Thread.currentThread().getName() + " ffmpeg process exited normally");
} else {
log.info(Thread.currentThread().getName() + " ffmpeg process timed out and will be killed");
}
} catch (IOException | InterruptedException e) {
log.error(Thread.currentThread().getName() + "Error during ffmpeg process executing", e);
} finally {
if (process != null) {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
log.error("Error during closing the process streams reader", e);
}
}
try {
process.getOutputStream().close();
} catch (IOException e) {
log.error("Error during closing the process output stream", e);
}
process.destroyForcibly();
log.info(Thread.currentThread().getName() + " ffmpeg process " + process + " must be dead now");
}
}
}
如果我使用此代码运行单独的测试,它会正常运行。但是在我的应用程序中,我有数百个正在等待 ffmpeg 进程完成的 RUNNING 守护线程“进程收割机”。在我的真实应用程序中,ffpmeg 是从计时器线程开始的。另外我在单独的线程中有另一个活动,但我不认为这是问题所在。最大 CPU 消耗约为 10%。
这是我通常在线程转储中看到的:
"process reaper" #454 daemon prio=10 os_prio=0 tid=0x00007f641c007000 nid=0x5247 runnable [0x00007f63ec063000]
java.lang.Thread.State: RUNNABLE
at java.lang.UNIXProcess.waitForProcessExit(Native Method)
at java.lang.UNIXProcess.lambda$initStreams$3(UNIXProcess.java:289)
at java.lang.UNIXProcess$$Lambda$32/2113551491.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
我做错了什么?
统一更新: 我的应用程序接受大量语音流量连接。所以我每时每刻都有大约 300-500 个“好”线程。会不会是这个原因?守护线程具有低优先级。但我不相信他们真的不能在一小时内完成他们的工作。通常需要几十毫秒。
UPD2: 我的综合测试运行良好。我尝试使用新线程选项,而没有它只是直接调用 run 方法。
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
public class FFmpegConvert {
public static void main(String[] args) throws Exception {
FFmpegConvert f = new FFmpegConvert();
f.processDir(args[0], args[1], args.length > 2);
}
private void processDir(String dirPath, String dirOutPath, boolean isNewThread) {
File dir = new File(dirPath);
File dirOut = new File(dirOutPath);
if(!dirOut.exists()){
dirOut.mkdir();
}
for (int i = 0; i < 1000; i++) {
for (File f : dir.listFiles()) {
try {
System.out.println(f.getName());
FFmpegRunner fFmpegRunner = new FFmpegRunner(f.getAbsolutePath(), dirOut.getAbsolutePath() + "/" + System.currentTimeMillis() + f.getName());
if (isNewThread) {
new Thread(fFmpegRunner).start();
} else {
fFmpegRunner.run();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class FFmpegRunner implements Runnable {
private String fileIn;
private String fileOut;
FFmpegRunner(String fileIn, String fileOut) {
this.fileIn = fileIn;
this.fileOut = fileOut;
}
@Override
public void run() {
try {
ffmpegconverter(fileIn, fileOut);
} catch (Exception e) {
e.printStackTrace();
}
}
private void ffmpegconverter(String fileIn, String fileOut) throws Exception{
String[] comand = new String[]{"ffmpeg", "-i", fileIn, "-acodec", "pcm_s16le", fileOut};
Process process = null;
try {
ProcessBuilder pb = new ProcessBuilder(comand);
pb.redirectErrorStream(true);
process = pb.start();
//Reading from error and standard output console buffer of process. Or it could halts because of nobody
//reads its buffer
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
//noinspection StatementWithEmptyBody
while ((line = reader.readLine()) != null) {
System.out.println(line);
//Ignored as we just need to empty the output buffer from process
}
process.waitFor();
} catch (IOException | InterruptedException e) {
throw e;
} finally {
if (process != null)
process.destroy();
}
}
}
}
UPD3: 抱歉,我忘了注意到我看到了所有这些过程的工作 - 他们创建了新的转换文件,但无论如何都不会退出。
【问题讨论】:
-
不确定这些信息是否足以提供帮助。您似乎有一个非常复杂的“生产设置”;与您的工作测试设置非常不同。乍一看,您的代码看起来不错;所以我的猜测是你的问题在你刚刚向我们“解释”的那个领域的某个地方......
-
是的,看起来是这样。但我不知道它可能是什么。可能更多的细节会有所帮助。稍后我会提出问题。
-
按照您得到的答案:您是否尝试过简单地使用 shell 脚本来并行运行 300 次 ffmpeg - 没有 Java 开销。只是想看看会发生什么?我同意这个答案 - 可能你的线程都在等待 IO ...
-
好的,我将我的测试添加到问题中。请看一下。
标签: java linux multithreading ffmpeg centos