Java 使用 (pipe) "... -f nut - | ffmpeg -i - ..." 执行 ffmpeg 命令只是挂起

Java execute ffmpeg commands with (pipe) "... -f nut - | ffmpeg -i - ..." just hangs

我无法将其发送至 运行,因为 java 正在等待 ffmpeg。但是 ffmpeg 不提供输入,也不提供错误流。它只是 运行s,但什么也没做。

"System.out.println("命令的输出:..”插入bash只是运行很好,因为expected.Soffmpeg语法没有任何问题。

这是代码。

package mypackage;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;

/**
 *
 * @author test
 */
public class ffmpeg_hang {

        /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        String INPUT_FILE="/path/to/media";
        String FFMPEG_PATH="/path/to/ffmpegFolder/";

            for(int i=0;(i+4)<40;i+=4){                
            String[] ffmpeg_pipe = new String[]{
                FFMPEG_PATH + "ffmpeg_4.1.1",
                "-ss",(i+""),"-t", "4",             
                "-i", INPUT_FILE,                                        
                "-ac", "1", "-acodec", "pcm_s16le", "-ar", "16000", 
                "-f","nut","-","|",
                FFMPEG_PATH + "ffmpeg_4.1.1",
                "-i","-",
                "-lavfi", "showspectrumpic=s=128x75:legend=disabled:saturation=0:stop=8000",
                "-f","image2pipe","pipe:1"};

            System.out.println("command: "+String.join(" ", ffmpeg_pipe));

            Process p;
            //ffmpe wav->pipe->spectrogra->pipe->java
            p = Runtime.getRuntime().exec(ffmpeg_pipe);


            StringBuilder Boxbuffer = new StringBuilder();
            BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            String line = "";

            while ((line = reader.readLine()) != null) {
                Boxbuffer.append(line);
            }


            System.out.println("ffmpeg errors->> "+Boxbuffer.toString());
            p.waitFor();


            BufferedImage image = ImageIO.read(p.getInputStream());
            //do stuff with image
            }

    }

}

在我看来,您正在阻止正在关闭的 stderr 流。如果 ffmpeg 在退出前没有关闭它的 stderr(我不希望它那样做),那么你的程序就会死锁。

改用 java.lang.ProcessBuilderpb.redirectErrorStream(true);

在与调用 waitFor(); 的线程不同的线程上读取进程输出也是一个好主意,否则有死锁的风险。

以这种方式直接在命令中传递时,管道不会被解释,它只是命令开头第一个 ffmpeg 的另一个参数。考虑使用 /bin/sh -c "command1 | command2" 作为包装器(假设非 Windows OS...)。

考虑将 -nostdin 添加到第一个 ffmpeg 命令以避免 number of issues 时 ffmpeg 在您不期望它时尝试读取标准输入(显然不是在第二个命令中)。

考虑使用 String.format 构建带变量的复杂字符串。

考虑使用 ProcessBuilder 以更轻松地创建流程。在这里,我将错误重定向到您的 java 进程标准错误,这样您就可以在不使用线程的情况下读取您的子进程的标准输出。 See alternatives

所以这里有一个建议:

public static void main(String[] args) throws IOException, InterruptedException {
    String INPUT_FILE = "/path/to/media";
    String FFMPEG_PATH = "/path/to/ffmpegFolder";

    for (int i = 0; (i + 4) < 40; i += 4) {

        String command1 = String.format(
                "%s/ffmpeg_4.1.1 -nostdin -ss %d -t 4 -i '%s' -ac 1 -acodec pcm_s16le -ar 16000 -f nut -",
                FFMPEG_PATH, i, INPUT_FILE);

        String command2 = String.format(
                "%s/ffmpeg_4.1.1 -i - -lavfi showspectrumpic=s=128x75:legend=disabled:saturation=0:stop=8000",
                FFMPEG_PATH);

        Process process = new ProcessBuilder("sh", "-c", command1 + " | " + command2)
                .redirectError(ProcessBuilder.Redirect.INHERIT)
                .start();

        BufferedImage image = ImageIO.read(process.getInputStream());
        // ...
    }
}