从命令行调用ffmpeg不会等到文件完全写入硬盘之后 [英] Calling ffmpeg from command line does not wait until file was fully written do hard drive
问题描述
我目前正在从事一项允许音频文件转换的服务.我在引擎盖下使用 ffmpeg
并使用 Runtime
进行呼叫.
I am currently working on a service to allower conversion of audio files. I am using ffmpeg
under the hood and use the Runtime
in order to make the call.
通话后,我读取了转换后的文件并将其上传到云存储中.
After the call I read the converted file and upload it to a cloud storage.
问题:
问题是,从驱动器读回文件仅给我几个字节.经过调查,它实际上在驱动器上有5 MB的空间,但是 readFileToByArray()
只能读取几个kb.我认为这是因为文件在我想读回该文件时并未完全保留.
The problem is, that reading the file back from the drive gives me only a few bytes. After investigating, it actually has like 5 MB on the drive but readFileToByArray()
reads only a few kb. I assume this is because the file was not completely persisted at the point where I want to read it back.
有什么方法可以确保将 ffmpeg
写入硬盘吗?看来 ffmpeg
所运行的主要进程在负责编写的并行进程之前完成了.也许吗?
Is there any way I can make sure that ffmpeg
is done writing to the hard drive? It seems that the main process that ffmpeg
was running in finishes before a parallel process that is responsible for writing. Maybe?
以下是将任意文件转换为AAC格式的相关代码:
Below is the relevant code that converts an arbitrary file to AAC-format:
File tempFile = File.createTempFile("input-", ".tmp", new File("/tmp"));
OutputStream outStream = new FileOutputStream(tempFile);
outStream.write(bytes);
String convertedFilePath = String.format("/tmp/output-%s.aac", UUID.randomUUID().toString());
String command = String.format(
"ffmpeg -i %s -c:a aac -b:a 256k %s",
tempFile.getAbsolutePath(),
convertedFilePath
);
LOGGER.debug(String.format("Converting file to AAC; Running %s", command));
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
try {
process.waitFor(200, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw new RuntimeException("Time out");
}
File convertedFile = new File(convertedFilePath);
byte[] result = FileUtils.readFileToByteArray(convertedFile);
// Upload "result" to cloud storage ..
推荐答案
我认为这里的问题是,您没有等到 ffmpeg
进程完成,而仅需200毫秒之后尝试在不检查 ffmpeg
进程是否正确退出的情况下读取转换后的文件.
I think the problem here is that you don't wait enough for ffmpeg
process to finish and after only 200 milliseconds you go on trying to read the converted file without checking whether ffmpeg
process exited correctly.
from waitFor(long timeout, TimeUnit unit)
docs (emphasis mine):
在必要时使当前线程等待,直到此Process对象表示的进程终止或或经过指定的等待时间.
...
返回:
如果进程已退出,则为true;如果如果进程已退出,则等待时间为.
true if the process has exited and false if the waiting time elapsed before the process has exited.
这里是利用 waitFor()
返回值来决定是否需要保持等待状态的代码的重写:
here a rewrite of your code that takes advantage of waitFor()
return value to decide if it needs to keep waiting or not:
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.Runtime;
import java.lang.RuntimeException;
import java.nio.file.Files;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class FFmpeg
{
public static void main(String[] args) throws Exception
{
/* just for testing purpose */
File fooFile = new File("foo.mp3");
byte[] bytes = Files.readAllBytes(fooFile.toPath());
File tempFile = File.createTempFile("input-", ".tmp", new File("/tmp"));
OutputStream outStream = new FileOutputStream(tempFile);
outStream.write(bytes);
outStream.close();
String convertedFilePath = String.format("output-%s.aac", UUID.randomUUID().toString());
String command = String.format(
"ffmpeg -nostdin -i %s -c:a aac -b:a 256k %s",
tempFile.getAbsolutePath(),
convertedFilePath
);
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
System.out.print("converting");
while ( ! process.waitFor(500, TimeUnit.MILLISECONDS)) {
/* here you have the opportunity to kill the process if
* it is taking too long, print something etc.. */
System.out.print(".");
}
System.out.print("\n");
if (process.exitValue() != 0) {
System.err.printf("ffmpeg failed with value %d\n", process.exitValue());
return;
}
File convertedFile = new File(convertedFilePath);
byte[] result = Files.readAllBytes(convertedFile.toPath());
System.out.println(result.length);
}
}
和一些演示:
$ ls -l
total 5368
-rw-r--r-- 1 marco marco 1557 Oct 10 17:46 FFmpeg.java
-rw-r--r-- 1 marco marco 5486341 Oct 10 17:09 foo.mp3
$ javac FFmpeg.java
$ java FFmpeg
converting.........
7329962
$ ls -l
total 12528
-rw-r--r-- 1 marco marco 1793 Oct 10 17:49 FFmpeg.class
-rw-r--r-- 1 marco marco 1557 Oct 10 17:46 FFmpeg.java
-rw-r--r-- 1 marco marco 5486341 Oct 10 17:09 foo.mp3
-rw-r--r-- 1 marco marco 7329962 Oct 10 17:50 output-176a2e73-82d6-483b-8a40-aec0819c749f.aac
$
(转换后的文件的长度显示在末尾,请注意它与 ls
输出中的文件匹配).
(length of converted file is printed at the end, notice how it matches the one in ls
output).
我添加的另一个重要内容是 ffmpeg
命令的 -nostdin
标志. ffmpeg
通常作为交互式程序运行,从 stdin
中获取用户输入,例如当询问您是否要覆盖目标文件时.当然,在这里我们没有机会回答是".或否"和 -nostdin
将使该过程失败并在需要用户交互时退出.如果没有该标志,它将无限期地等待用户输入(您可能也对 -y
和 -n
标志感兴趣).
another important thing I added is the -nostdin
flag to ffmpeg
command. ffmpeg
is often run as an interactive program taking user input from stdin
e.g. when it asks if you want to overwrite target file. here of course we don't have the chance to answer "yes" or "no" and -nostdin
will make the process to fail and exit when a user interaction is required. without that flag, it will wait for user input indefinitely (you maybe interested in -y
and -n
flags also).
这篇关于从命令行调用ffmpeg不会等到文件完全写入硬盘之后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!