在Linux中执行Java代码中的FFmpeg命令的问题

我在我的java代码中执行这个ffmpeg命令有问题:

 ffmpeg -i sample.mp4 -i ad.mp4 -filter_complex "[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]" -map "[out]" output.mp4 

我使用了下面的getRuntime()方法,但是这对我不起作用。 即使我只是删除了" ,仍然不起作用,当我简单地复制粘贴等效的string在terminal,它的工作。

 String c1=" -i "+dir+"sample.mp4 "+"-i "+dir+"ad.mp4 -fi‌​lter_complex [0:v]‌​trim=0:15,setpts=PTS‌​-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out] -map [out] "+dir+"output.‌​mp4"; RunCommand("ffmpeg"+c1); 

使用这种方法:

 private static void RunCommand(String command) throws InterruptedException { try { // Execute command Process proc = Runtime.getRuntime().exec(command); System.out.println(proc.exitValue()); // Get output stream to write from it // Read the output BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); String line = ""; while((line = reader.readLine()) != null) { System.out.print(line + "\n"); // System.out.println(ads.get(0)); } proc.waitFor(); } catch (IOException e) { } } 

这个也不起作用,而打印出口值显示这个:

 Exception in thread "main" java.lang.IllegalThreadStateException: process hasn't exited at java.lang.UNIXProcess.exitValue(UNIXProcess.java:423) at parser.Parser.RunCommand(Parser.java:106) at parser.Parser.commandGenerator2(Parser.java:79) at parser.Parser.main(Parser.java:44) 

如果我移动proc.waitFor(); 在打印出口值之前,它是1

问题是什么? 为什么它不能在Java代码中运行?

你的代码有一些问题,首先,使用线程流入和内部进程的错误到控制台

创建一个管道流类如下所示:

 class PipeStream extends Thread { InputStream is; OutputStream os; public PipeStream(InputStream is, OutputStream os) { this.is = is; this.os = os; } public void run() { byte[] buffer=new byte[1024]; int len; try { while ((len=is.read(buffer))>=0){ os.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } } } 

然后调整运行时间部分:

 System.out.println("Launching command: "+command); ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", command); Process proc=pb.start(); PipeStream out=new PipeStream(proc.getInputStream(), System.out); PipeStream err=new PipeStream(proc.getErrorStream(), System.err); out.start(); err.start(); proc.waitFor(); System.out.println("Exit value is: "+proc.exitValue()); 

它将显示将要运行的命令,日志和潜在的错误。

你将能够复制粘贴命令来检查终端上发生了什么,如果需要的话。

编辑:这很有趣。 你的代码是缺少一些转义字符,并没有在你的代码中可见的字符。 当我复制粘贴代码行时,我看到了它们。 复制代码中粘贴以下行,它将删除错误:

 String command="ffmpeg -i "+dir+"sample.mp4 -i "+dir+"ad.mp4 -filter_complex '[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]' -map '[out]' "+dir+"output.mp4"; 

有几个问题需要解决(大多数是通过其他答案单独解决的,并且有不同程度的解释,但是您需要解决所有问题):

  • 你需要将参数传递给ffmpeg正如shell所要做的那样,这意味着你需要用单独的参数作为字符串数组来创建一个命令,或者为了使它更容易(和shell行为相同),引用你的参数: \"围绕着大的过滤器参数,那么你应该能够将命令传递给runCommand就像你在shell中写入一样。
  • 但是java不能解析这些引号(*)并隔离传递给ffmpeg ,但是/bin/sh可以为你做:用/bin/sh -c ...包装你的命令(为此我将使用ProcessBuilder下面)
  • 你需要消耗输出,否则你的进程可能永远阻塞。 ProcessBuilder来拯救:将stderr重定向到stdout只获得一个单独的流消耗,然后将stdout重定向到任何你想要的地方(下面我从父进程继承,所以它转到你的java进程本身的输出。
  • 你需要等待进程完成才能得到它的退出值(下面我使用waitFor() ,它会等待尽可能长的时间,但也有其他的选择)
  • [在@wargre之后添加] 不是为了窃取,但为了完整起见,您需要确保命令不会被隐形字符侵染;例如,您实际上正在传递-fi......lter_complex (十六进制中的点是e2 80 8c e2 80 8b ),但是在你的命令中有更多的分散。

从而:

 String c1 = " -i " + dir + "sample.mp4 -i " + dir + "ad.mp4 -filter_complex \"[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]\" -map \"[out]\" " + dir + "output.mp4"; // Notice additional quotes around filter & map above runCommand("ffmpeg" + c1); // ... static void runCommand(String command) throws IOException, InterruptedException { Process p = new ProcessBuilder("/bin/sh", "-c", command) .redirectErrorStream(true) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .start(); p.waitFor(); System.out.println("Exit value: " + p.exitValue()); } 

(*)在一个shell中,如果你想打印abecho "ab" ,但在Java中这是行不通的:

 Runtime.getRuntime().exec("echo \"ab\""); 

它所做的是天真地分割空间,它会传递2个参数而不是1 echo"a then b" 。 不是你想要的。


另类:单独传递参数。

 runCommandAsVarargs( "ffmpeg", "-i", dir + "sample.mp4", "-i", dir + "ad.mp4", "-filter_complex", "[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]", "-map", "[out]", dir + "output.mp4" ); // ... static void runCommandAsVarargs(String... command) throws IOException, InterruptedException { Process p = new ProcessBuilder(command) .redirectErrorStream(true) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .start(); p.waitFor(); System.out.println("Exit value: " + p.exitValue()); } 

似乎你错过了参数-filter_complex参数的引号。 Java将运行如下所示:

 ffmpeg -i ./sample.mp4 -i ./ad.mp4 -filter_complex [0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out] -map [out] output.mp4 

这从那以后就不起作用了; 意味着在bash中的命令结束。 把报价放回到java代码应该修复命令(确保正确地转义它们)。

 String c1=" -i "+dir+"sample.mp4 "+"-i "+dir+"ad.mp4 -fi‌​lter_complex \"[0:v]‌​trim=0:15,setpts=PTS‌​-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]\" -map [out] "+dir+"output.‌​mp4"; RunCommand("ffmpeg"+c1);