简体   繁体   English

在CentOS上从Java创建的FFMpeg进程不会退出

[英]FFMpeg process created from Java on CentOS doesn't exit

I need to convert a lot of wave files simultaneously. 我需要同时转换很多wave文件。 About 300 files in parallel. 并行约300个文件。 And new files come constantly. 并且新文件不断出现。 I use ffmpeg process call from my Java 1.8 app, which is running on CentOS. 我从CentOS上运行的Java 1.8应用程序使用ffmpeg进程调用。 I know that I have to read error and input streams for making created process from Java possible to exit. 我知道我必须读取错误和输入流,才能使从Java创建的进程退出。

My code after several expirements: 我的代码在几个到期后:

    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");
        }
    }
}

If I run separate test with this code it goes normally. 如果我用此代码运行单独的测试,它将正常进行。 But in my app I have hundreds of RUNNING deamon threads "process reaper" which are waiting for ffmpeg process finish. 但是在我的应用程序中,我有数百个正在运行的守护进程线程“进程收割者”,它们正在等待ffmpeg进程完成。 In my real app ffpmeg is started from timer thread. 在我的真实应用中,ffpmeg从计时器线程启动。 Also I have another activity in separate threads, but I don't think that this is the problem. 另外,我在单独的线程中还有另一个活动,但是我不认为这是问题所在。 Max CPU consume is about 10%. 最大CPU消耗约为10%。

Here is that I usual see in thread dump: 这是我通常在线程转储中看到的:

"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)

What am I doing wrong? 我究竟做错了什么?

UPD: My app accepts a lot of connects with voice traffic. UPD:我的应用接受语音流量的大量连接。 So I have about 300-500 another "good" threads in every moment. 因此,我每时每刻都有大约300-500个“好”线程。 Could it be the reason? 可能是原因吗? Deamon threads have low priority. 守护进程线程的优先级较低。 But I don't beleive that they really can't do their jobs in one hour. 但是我不相信他们真的不能在一小时内完成工作。 Ususally it takes some tens of millis. 通常,它需要几十毫秒。

UPD2: My synthetic test that runs fine. UPD2:我的综合测试运行良好。 I tried with new threads option and without it just with straigt calling of run method. 我尝试使用新的线程选项,而没有使用straigt调用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: Sorry, I forgot to notice that I see the work of all these process - they created new converted files but anyway don't exit. UPD3:对不起,我忘了注意到我看到了所有这些过程的工作-他们创建了新的转换文件,但是无论如何都不会退出。

Your application is I/O bound, not CPU bound. 您的应用程序是I / O绑定的,而不是CPU绑定的。 If all your files are in the same HDD, then opening simultaneously 300 files will definitely degrade the performance. 如果所有文件都在同一HDD中,则同时打开300个文件肯定会降低性能。 (that is a likely reason on why you have hundreds of processes waiting). (这可能是为什么您有数百个进程等待的原因)。

If I understood correctly, you mentioned that processing 1 file takes some tens of millis? 如果我理解正确,您提到处理1个文件需要花费数十毫秒吗?

(and this is doing sequential reads - the fastest that your HDD will read a file) (这是顺序读取,这是HDD读取文件的最快速度)

in this case, processing 300 files sequentially should take no more than 30 seconds. 在这种情况下,顺序处理300个文件应不超过30秒。

100 millis - process 1 file 100毫秒-处理1个档案

1 second - process 10 files 1秒-处理10个文件

30 second - process 300 files 30秒-处理300个文件

EDIT 编辑

I did 2 simple changes to your sample code (I removed the first loop, then changed the codec) finally I put one song in "ogg" format in "/tmp/origin" directory. 我对示例代码做了2次简单的更改(删除了第一个循环,然后更改了编解码器),最后我在“ / tmp / origin”目录中以“ ogg”格式放置了一首歌曲。 now the program works well). 现在该程序运行良好)。

see code below: 参见下面的代码:

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("/tmp/origin", "/tmp/destination", false);
    }

    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 (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", "copy", 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();
            }
        }

    }

}

Got it! 得到它了! In some cases ffmpeg wants to ask me should it override already existed file. 在某些情况下,ffmpeg想问我是否应覆盖已存在的文件。 In my code I close outputstream of this child process. 在我的代码中,我关闭了此子进程的输出流。 But as it appears this only closes outputstream for Java but not for the process. 但是看起来这只会关闭Java的输出流,而不会关闭该过程。

So my solution is to make ffmpeg silent at all: no output from this process with "-v -8", no asking question with default "Yes" "-y". 因此,我的解决方案是使ffmpeg完全保持静音:该进程没有使用“ -v -8”的输出,没有默认值为“ Yes”“ -y”的询问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM