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.ProcessBuilder
和 pb.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());
// ...
}
}
我无法将其发送至 运行,因为 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.ProcessBuilder
和 pb.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());
// ...
}
}