
Java 用 Process 启动 ffmpeg 为什么读不到进度?
因为 ffmpeg 默认把进度输出到 stderr,且带缓冲;Java 的 Process.getInputStream() 只能拿到 stdout,不接 stderr 就等于没监听。
- 必须显式调用
process.getErrorStream()并另起线程持续读取 - ffmpeg 要加
-progress pipe:1(不是-report或纯日志文件),才能把结构化进度推到 stdout/pipe,但注意:它仍默认往stderr打普通日志,所以实际要同时处理两个流 - 别用
Scanner.nextLine()直接阻塞读——遇到换行缺失或缓冲延迟会卡死,改用BufferedReader.readLine()+ 超时机制更稳
怎么从 ffmpeg 的 pipe:1 解析出当前秒数和总时长?
ffmpeg 的 -progress pipe:1 输出是 key=value 格式的纯文本行,每帧可能触发多行,关键字段是 out_time_ms 和 duration_us(单位微秒),不是 time 或 progress。
-
out_time_ms=123456表示已编码到第 123456 毫秒,除以 1000 得秒数 -
duration_us=120000000是总时长(微秒),除以 1e6 得秒数;但首次出现可能为空,需等几秒后才稳定,不能一上来就除零 - 别用正则全局匹配——单行只含一个 key=value,用
line.startsWith("out_time_ms=")判断再split("=")更轻量、不易错 - 注意:
out_time_ms可能跳变(如 seek 或丢帧),建议做单调递增校验,避免进度条倒退
用 ProcessBuilder 启动 ffmpeg 有哪些兼容性坑?
Windows 和 Linux 下路径、空格、参数分隔行为不同,直接拼字符串大概率在某平台失败。
- 命令必须拆成
List<string></string>传给ProcessBuilder,例如:Arrays.asList("ffmpeg", "-i", "/path/to/in.mp4", "-vcodec", "libx264", "-progress", "pipe:1", "/path/to/out.mp4") - Windows 上 ffmpeg 路径含空格(如
C:\Program Files\ffmpeg\bin\ffmpeg.exe)必须原样作为独立列表项,不能合并进字符串 - macOS/Linux 需确保
ffmpeg在$PATH中,或传绝对路径;别依赖当前工作目录,用pb.directory(new File("/work/dir"))显式指定 - 某些旧版 JRE 对
Process的销毁不彻底,转码未完成就调destroy()可能留僵尸进程,建议用destroyForcibly()+waitFor(5, TimeUnit.SECONDS)
进度监控线程怎么避免内存泄漏和假死?
长期运行的读流线程如果没设退出条件或异常兜底,很容易让整个应用卡住或 OOM。
立即学习“Java免费学习笔记(深入)”;
- 线程必须响应中断:
while (!Thread.currentThread().isInterrupted() && (line = br.readLine()) != null) - 别把所有进度行 accumulate 到
List<string></string>—— 每次只解析最新行,用 volatile 变量存currentMs和totalSec即可 - 捕获
IOException(如管道关闭)后应主动 break,并通知主线程“转码结束”,而不是吞掉异常继续循环 - 如果 ffmpeg 崩溃,
Process.waitFor()会返回非 0 值,此时getErrorStream()可能还有残余错误行,记得在 wait 后再 flush 一次读取
真正麻烦的是 ffmpeg 输出节奏不均——有时连续十几行 out_time_ms,有时隔两秒才来一行。别指望“每秒更新一次 UI”,重点是别让解析逻辑拖慢主流程,用生产者-消费者模式隔离更稳妥,但初学者先从单线程轮询读起也够用。










