在while循环中运行ffmpeg会导致内存泄漏

Posted

技术标签:

【中文标题】在while循环中运行ffmpeg会导致内存泄漏【英文标题】:Running ffmpeg in a while loop causes memory leak 【发布时间】:2022-01-21 18:05:08 【问题描述】:

我正在尝试同步while循环中运行以下ffmpeg命令。

每次循环时,我都会等待当前命令完成,然后再继续执行下一个命令。但是我得到了意想不到的结果,然后它崩溃了。

  import ffmpegPath from '@ffmpeg-installer/ffmpeg'
  import ffmpeg from 'fluent-ffmpeg'

  ffmpeg.setFfmpegPath(ffmpegPath.path)

  const command = ffmpeg()

  let VIDEOS = 1000
  let videoIdx = 1

  while (videoIdx <= VIDEOS) 
    await new Promise((resolve) => 
      command
        .on('end', () => 
          setTimeout(() => 
            console.log(`$videoIdx/$VIDEOS`)
            videoIdx++
            resolve()
          , 100)
        )
        .on('error', () => 
          console.log('error = ', error)
        )
        .input(`MyExernalHardDrive/input/video-$videoIdx-frame-%d.png`)
        .inputFPS(1/0.0425)
        .output(`MyExernalHardDrive/output/video-$videoIdx.mp4`)
        .outputFPS(24)
        .noAudio()
        .run()
    )
  

在我希望看到的控制台中:

1/1000

then

2/1000

then

3/1000

etc..

相反,我正在批量获取日志,例如:

1/1000
2/1000
3/1000

then

4/1000
5/1000
6/1000
7/1000

then it keeps incrementing but I get them in even bigger batches:

45/1000
46/1000
47/1000
48/1000
49/1000
50/1000
51/1000
52/1000
53/1000
54/1000
55/1000

然后我得到:

(node:14509) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 
11 end listeners added to [FfmpegCommand]. 
Use emitter.setMaxListeners() to increase limit

然后我退出该过程,这些是已创建的文件:

video-1.mp4
video-2.mp4
video-4.mp4
video-7.mp4
video-11.mp4
video-16.mp4
video-22.mp4
video-29.mp4
video-37.mp4
video-46.mp4
video-56.mp4
video-67.mp4

看来:

    时间安排到处都是,根本没有顺序 进程堆积,直到出现内存泄漏

为什么会发生这种情况,因为我在继续下一个流程之前使用的是await,我该如何解决这个问题?

【问题讨论】:

您是否尝试过在每次循环迭代中重新使用command 变量并使用ffmpeg() 的新实例?我无法想象重用同一个事件发射器是正确的方法。例如:ffmpeg().on('end', ...).....run(),而不是 command.on('end', ...)...run() 我一定会尝试的。谢谢! 您的建议解决了问题。你可以把它变成一个答案。 :) 再次感谢 我已经发布了我的评论作为答案。抱歉,它没有更深入,我已尝试使其对面临相同问题的其他人足够有用。 会的。没关系:) 【参考方案1】:
// ...
  const command = ffmpeg()

  let VIDEOS = 1000
  let videoIdx = 1

  while (videoIdx <= VIDEOS) 
    await new Promise((resolve) => 
      command
        .on('end', () => 
          setTimeout(() => 
            console.log(`$videoIdx/$VIDEOS`)
            videoIdx++
            resolve()
          , 100)
        )
// ...

变量command 中的ffmpeg 实例在每个循环中都被重用。它是一个事件发射器,不应该被重用,因为有些事件只触发一次,比如“结束”。此单个实例上的“结束”事件接收许多侦听器。 Node 运行时会发出警告,因为这通常不是您想要的(如果是,您可以选择禁用限制)。

解决办法是不要重复使用同一个实例:

// ...
  let VIDEOS = 1000
  let videoIdx = 1

  while (videoIdx <= VIDEOS) 
    await new Promise((resolve) => 
      ffmpeg()
        .on('end', () => 
          setTimeout(() => 
            console.log(`$videoIdx/$VIDEOS`)
            videoIdx++
            resolve()
          , 100)
        )
// ...

现在每次循环迭代都会创建一个全新的实例。

【讨论】:

以上是关于在while循环中运行ffmpeg会导致内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

Android App解决卡顿慢之内存抖动及内存泄漏(发现和定位)

循环 await Promise.all 内存泄漏

在 Handler 线程的队列中添加匿名可运行对象会导致内存泄漏吗?

循环引用导致内存泄漏?

内存泄漏和内存溢出的区别是啥

ByteBuffer.wrap(byte[]) 会导致长时间运行的应用程序内存泄漏吗?