Java调用ffmpeg处理视频,并记录下遇到的坑

2023年5月25日10:07:48

需求

给透明背景的视频自动叠加一张背景图片

基于JavaCV跨平台执行ffmpeg命令1

我测试发现的本需求的最小依赖:

<!-- Optional GPL builds with (almost) everything enabled -->
    <dependency>
      <groupId>org.bytedeco</groupId>
      <artifactId>ffmpeg-platform-gpl</artifactId>
      <version>5.0-1.5.7</version>
    </dependency>

核心代码:

  String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
  String tempFilePath = newVideoName;
  ProcessBuilder pb = new ProcessBuilder(ffmpeg,
      "-y", "-i", BACKGROUND_PNG, "-c:v", "libvpx-vp9", "-i", aliVideoUrl, "-filter_complex",
      "[0][1]overlay=0:0[video2];[video2]select=between(n\\,1\\,54000)", "-b:v", "2m", "-r",
      "30", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-profile:v", "high", "-preset:v",
      "veryslow", "-tune", "film", "-level", "4.2", tempFilePath);
  pb.inheritIO().start().waitFor();
  File tempFile = new File(tempFilePath);

坑一 内存不足

服务器内存不足导致合成视频实际失败,许多的合成文件只有48B,但是没有与之相关的错误日志。
每个服务器节点原来只有2G内存,扩容后解决。

坑二 多个ffmpeg进程并行导致IO负载大,进而导致io error?

这只是一种推测。现象是我会有多个Java线程,每个线程都来调用ffmpeg命令。合成的视频有概率缺失后面一段,并可以在日志中观察到io error, read error。源视频越大,Java线程数越多,出现这种情况的概率越大。当我把Java程序改为单线程,就没有这种问题了。但当我尝试用iostat命令去服务器节点观察IO状态时,并没有观察到所谓IO负载大的证据。2

坑三 使用Java操作ffmpeg时,有时会卡死

只发生在我本地Mac,在服务器没有出现过。我找了很久原因,最后发现是我的Mac跑了一段时间后自动休眠了。。不过也记录下很多人都遇到过的这个坑吧:

坑四 Process的waitFor死锁问题及解决办法

大意是:当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,而Java的waitfor()是阻塞等待子进程完成的。所以会最终造成阻塞在waitfor()这里。解决的主要思路是Java要及时消费这个缓冲区的io流,或者直接不让ffmpeg输出日志。一些参考:

使用Procees+Runtime操作ffmpeg时,执行比较大的文件操作会卡死,但cmd打开ffmpeg能正常执行

Java process.waitFor() 卡死问题里提到了,也可以直接给ffmpeg命令加上-loglevel quiet参数来不让输出日志。

Process的waitFor死锁问题及解决办法


  1. 如何跨平台调用ffmpeg?史上最简单基于JavaCV跨平台执行ffmpeg命令 ↩︎

  2. Linux IO实时监控iostat命令详解 ↩︎

  • 作者:qq_23204557
  • 原文链接:https://blog.csdn.net/qq_23204557/article/details/127104085
    更新时间:2023年5月25日10:07:48 ,共 1704 字。