ffmpeg在某些情况下取消初始化线程

时间:2020-10-16 07:04:09

标签: java multithreading ffmpeg wildfly

由于我之前发布的问题已被关闭,因此我将再次发布

我有一个在wildfly中运行的JAVA服务,该服务正在调用外部ffmpeg二进制文件以将.au文件转换为.wav文件。正在执行的实际命令如下:

ffmpeg -y -i INPUT.au OUTPUT.wav

它运行平稳,但偶尔会由于以下错误而创建一个空的.wav文件:

Error: ffmpeg version c6710aa Copyright (c) 2000-2017 the FFmpeg 
developers
built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.4) 20160609
configuration: --prefix=/tmp/ffmpeg-static/target --pkg-config-flags=- 
-static --extra-cflags=-I/tmp/ffmpeg-static/target/include --extra- 
ldflags=-L/tmp/ffmpeg-static/target/lib --extra-ldexeflags=-static -- 
bindir=/tmp/ffmpeg-static/bin --enable-pic --enable-ffplay --enable- 
ffserver --enable-fontconfig --enable-frei0r --enable-gpl --enable- 
version3 --enable-libass --enable-libfribidi --enable-libfdk-aac -- 
enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb -- 
enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus -- 
enable-librtmp --enable-libsoxr --enable-libspeex --enable-libtheora - 
-enable-libvidstab --enable-libvo-amrwbenc --enable-libvorbis -- 
enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 -- 
enable-libxvid --enable-libzimg --enable-nonfree --enable-openssl
libavutil      55. 34.101 / 55. 34.101
libavcodec     57. 64.101 / 57. 64.101
libavformat    57. 56.101 / 57. 56.101
libavdevice    57.  1.100 / 57.  1.100
libavfilter     6. 65.100 /  6. 65.100
libswscale      4.  2.100 /  4.  2.100
libswresample   2.  3.100 /  2.  3.100
libpostproc    54.  1.100 / 54.  1.100

Input #0, ogg, from 'INPUT.au'
Duration: 00:00:34.08, start: 0.01500, bitrate: 15kb/s
Stream: #0.0: Audio: speex, 8000Hz, mono, s16, 15kb/s

[AVFilterGraph @ 0x43ec6e0] Error initializing threading.
[AVFilterGraph @ 0x43ec6e0] Error creating filter 'anull'

如果我尝试从命令行手动转换文件,它将起作用。简短的Internet搜索(this)表明,这可能是由于ffmpeg无法创建供内部使用的线程而造成的。有人可以详细说明吗?

我面临问题的服务器负载相对较高。我已经看到wildfly正在创建接近1800个线程。

谢谢

P.s。我设法重现了问题。下面是代码:

SystemCommandExecutor.java

    import java.io.*;
    import java.util.List;
    public class SystemCommandExecutor {
        private List<String> commandInformation;
        private String adminPassword;
        private ThreadedStreamHandler inputStreamHandler;
        private ThreadedStreamHandler errorStreamHandler;

        public SystemCommandExecutor(final List<String> commandInformation)
        {
        if (commandInformation==null) throw new NullPointerException("The commandInformation is required.");
        this.commandInformation = commandInformation;
        this.adminPassword = null;
        }

    public int executeCommand()
            throws IOException, InterruptedException
    {
        int exitValue = -99;

        try
        {
            ProcessBuilder pb = new ProcessBuilder(commandInformation);
            Process process = pb.start();
            OutputStream stdOutput = process.getOutputStream();
            InputStream inputStream = process.getInputStream();
            InputStream errorStream = process.getErrorStream();
            inputStreamHandler = new ThreadedStreamHandler(inputStream, stdOutput, adminPassword);
            errorStreamHandler = new ThreadedStreamHandler(errorStream);
            inputStreamHandler.start();
            errorStreamHandler.start();
            exitValue = process.waitFor();
            inputStreamHandler.interrupt();
            errorStreamHandler.interrupt();
            inputStreamHandler.join();
            errorStreamHandler.join();
        }
        catch (IOException e)
        {
            throw e;
        }
        catch (InterruptedException e)
        {
            throw e;
        }
        finally
        {
            return exitValue;
        }
    }

    public StringBuilder getStandardOutputFromCommand()
    {
        return inputStreamHandler.getOutputBuffer();
    }

    public StringBuilder getStandardErrorFromCommand()
    {
        return errorStreamHandler.getOutputBuffer();
    }
}

ThreadedStreamHandler.java

import java.io.*;

class ThreadedStreamHandler extends Thread
{
    InputStream inputStream;
    String adminPassword;
    OutputStream outputStream;
    PrintWriter printWriter;
    StringBuilder outputBuffer = new StringBuilder();
    private boolean sudoIsRequested = false;

    
    ThreadedStreamHandler(InputStream inputStream)
    {
        this.inputStream = inputStream;
    }

    
    ThreadedStreamHandler(InputStream inputStream, OutputStream outputStream, String adminPassword)
    {
        this.inputStream = inputStream;
        this.outputStream = outputStream;
        this.printWriter = new PrintWriter(outputStream);
        this.adminPassword = adminPassword;
        this.sudoIsRequested = true;
    }

    public void run()
    {
        
        if (sudoIsRequested)
        {
            printWriter.println(adminPassword);
            printWriter.flush();
        }

        BufferedReader bufferedReader = null;
        try
        {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = null;
            while ((line = bufferedReader.readLine()) != null)
            {
                outputBuffer.append(line + "\n");
            }
        }
        catch (IOException ioe)
        {
            ioe.printStackTrace();
        }
        catch (Throwable t)
        {
            t.printStackTrace();
        }
        finally
        {
            try
            {
                bufferedReader.close();
            }
            catch (IOException e)
            {
                // ignore this one
            }
        }
    }

    private void doSleep(long millis)
    {
        try
        {
            Thread.sleep(millis);
        }
        catch (InterruptedException e)
        {
            // ignore
        }
    }

    public StringBuilder getOutputBuffer()
    {
        return outputBuffer;
    }

}

FfmpegRunnable.java

import java.io.IOException;
import java.util.List;

public class FfmpegRunnable implements Runnable {
    private List<String> command;
    SystemCommandExecutor executor;

    public FfmpegRunnable(List<String> command) {
        this.command = command;
        this.executor = new SystemCommandExecutor(command);
    }

    @Override
    public void run() {
        try {
            int id = (int) Thread.currentThread().getId();
            int result = executor.executeCommand();
            if(result != 0) {
                StringBuilder err = executor.getStandardErrorFromCommand();
                System.out.println("[" + id + "]" + "[ERROR] " + err);
            } else {
                System.out.println("[" + id + "]" + "[SUCCESS]");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

FfmpegMain.java

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class FfmpegMain {
    public static void main(String[] args) {
        //boolean threading = false;
        System.out.println(args[0]);
        int nrThread = Integer.parseInt(args[0]);
        boolean threading = Boolean.parseBoolean(args[1]);
        System.out.println("nrThread : " + nrThread + ", threading : " + threading);
        if(threading) {
            System.out.println("ffmpeg threading enabled");
        } else {
            System.out.println("ffmpeg threading not enabled");
        }
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(nrThread);
        for(int i=0; i<nrThread*2; i++) {
            List<String> cmd = new ArrayList<String>();
            String dest = "/tmp/OUTPUT/output_" + (Math.random()*1000) + ".wav";
            String cmdStr = "/tmp/FFMPEG/ffmpeg" + (threading ? " -threads 1 " : " ")
                    + "-y -i /tmp/input.au " + dest;
            cmd.add("/bin/sh");
            cmd.add("-c");
            cmd.add(cmdStr);

            executor.submit(new FfmpegRunnable(cmd));
        }
        executor.shutdown();
    }
}

我已经用类文件创建了一个jar,并使用以下命令从两个单独的终端运行jar

java -jar JAR.jar 40 true

这里40是线程数,用于模拟访问系统的各种用户。偶尔我会遇到上述错误。

0 个答案:

没有答案