java - 从命令行调用 ffmpeg 不会等到文件完全写入硬盘驱动器

标签 java command-line ffmpeg

我目前正在开发一项允许转换音频文件的服务。我正在使用 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/

相关文章:

java - Java中int类型的ArrayList的ArrayList?

java - getClass().getClassLoader().getResourceAsStream 抛出 NullPointerException

linux - 如何比较bash中的IP值?

c - 多个 fork() 并发

ffmpeg:如何在 m3u8 播放列表中包含动态路径? (播放列表和片段位于不同的目录中。)

python - 将 FFMpeg AVFrame 对象从 C++ 应用程序流式传输到 Python 的最佳方法?

java - 如何在所有包启动后调用 osgi 应用程序的启动方法?

java - 在子类中抛出错误、异常和运行时异常

macos - 在 Mac 和流媒体上使用 ffmpeg 捕获麦克风输入

ffmpeg - 从编码视频文件中提取运动向量