我目前正在开发一项允许转换音频文件的服务。我正在使用 ffmpeg
在引擎盖下使用 Runtime
为了调用电话。
通话后,我读取转换后的文件并将其上传到云存储。
问题:
问题是,从驱动器读回文件只给了我几个字节。经过调查,它实际上在驱动器上有大约 5 MB 但 readFileToByArray()
仅读取几 kb。我认为这是因为文件在我想读回的时候没有完全保存。
有什么办法可以确保ffmpeg
写入硬盘完成了吗?似乎 ffmpeg
的主要进程在负责写入的并行进程之前运行完成。也许?
以下是将任意文件转换为 AAC 格式的相关代码:
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
进程正确退出。
来自 waitFor(long timeout, TimeUnit unit)
docs (强调我的):
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated, or the specified waiting time elapses.
...
Returns:
true if the process has exited and false if the waiting time elapsed before the process has exited.
这里重写你的代码,利用
waitFor()
返回值来决定是否需要继续等待: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
输出中的长度匹配)。我添加的另一个重要内容是
-nostdin
标记为 ffmpeg
命令。 ffmpeg
通常作为交互式程序运行,从 stdin
获取用户输入例如当它询问您是否要覆盖目标文件时。这里我们当然没有机会回答"is"或“否”和-nostdin
。当需要用户交互时,将使进程失败并退出。如果没有该标志,它将无限期地等待用户输入(您可能也对 -y
和 -n
标志感兴趣)。
关于java - 从命令行调用 ffmpeg 不会等到文件完全写入硬盘驱动器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64292363/