You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过NiceGUI调用子进程时FFmpeg崩溃问题排查

问题:NiceGUI触发FFmpeg后台进程失败,直接运行正常

我希望通过NiceGUI按钮触发一个长时间运行的FFmpeg进程,编写了如下代码:

from concurrent.futures import ProcessPoolExecutor
import shlex, subprocess
import argparse
import asyncio
from nicegui import ui, app

def run_cmd():
  cmd = "ffmpeg -y -i data/video.mp4 -acodec libmp3lame -ab 128000 data/video.mp3"
  subprocess.call(shlex.split(cmd))

pool = ProcessPoolExecutor()
async def async_run():
  loop = asyncio.get_running_loop()
  await loop.run_in_executor(pool, run_cmd)

args = argparse.ArgumentParser()
args.add_argument("--webui", action="store_true")
args = args.parse_args()

if args.webui:
  ui.button('Translate', on_click=async_run)
  app.on_shutdown(pool.shutdown)
  ui.run()
else:
  run_cmd()

直接运行Python脚本时FFmpeg工作正常,但通过NiceGUI启动(执行python3 ./main.py --webui)时,FFmpeg出现错误并崩溃,错误信息如下:

...
Error while decoding stream #0:1: Invalid argument
[aac @ 0x555e54100440] Sample rate index in program config element does not match the sample rate index configured by the container.
[aac @ 0x555e54100440] Inconsistent channel configuration.
[aac @ 0x555e54100440] get_buffer() failed
...
[aac @ 0x555e54100440] Error decoding AAC frame header.
Error while decoding stream #0:1: Error number -50531338 occurred
[aac @ 0x555e54100440] channel element 2.8 is not allocated
Error while decoding stream #0:1: Invalid data found when processing input
[aac @ 0x555e54100440] Pulse data corrupt or invalid.
Error while decoding stream #0:1: Invalid data found when processing input
ffmpeg: psymodel.c:576: calc_energy: Assertion `el >= 0' failed.
...

请问我哪里操作有误?或者是否有更简便的方式在NiceGUI中启动后台长进程?


解答

问题原因

  1. ProcessPoolExecutor环境冲突:ProcessPoolExecutor创建的子进程会复制NiceGUI Web服务的运行环境,而Web服务的IO资源、进程上下文和直接运行脚本时不同,导致FFmpeg解码时出现数据错乱。
  2. 未隔离FFmpeg的IO流:默认subprocess.call会继承主进程的stdin/stdout/stderr,Web服务的IO流被占用后,会干扰FFmpeg的解码逻辑,引发缓冲区错误或数据解析异常。

修正方案

方案1:改用线程池+隔离IO流

线程池足够处理外部进程调用,避免进程池的环境隔离问题,同时重定向FFmpeg的IO流,避免干扰Web服务:

from concurrent.futures import ThreadPoolExecutor
import shlex, subprocess
import argparse
import asyncio
from nicegui import ui, app

def run_cmd():
    cmd = "ffmpeg -y -i data/video.mp4 -acodec libmp3lame -ab 128000 data/video.mp3"
    # 将FFmpeg输出重定向到/dev/null,避免占用Web进程IO
    with open('/dev/null', 'w') as dev_null:
        subprocess.call(
            shlex.split(cmd),
            stdout=dev_null,
            stderr=dev_null,
            stdin=subprocess.DEVNULL
        )

pool = ThreadPoolExecutor()
async def async_run():
    loop = asyncio.get_running_loop()
    await loop.run_in_executor(pool, run_cmd)

args = argparse.ArgumentParser()
args.add_argument("--webui", action="store_true")
args = args.parse_args()

if args.webui:
    ui.button('Convert to MP3', on_click=async_run)
    app.on_shutdown(pool.shutdown)
    ui.run()
else:
    run_cmd()

方案2:用asyncio.to_thread简化代码(Python3.9+)

无需手动创建线程池,直接用Python内置的asyncio.to_thread运行同步函数,代码更简洁:

import shlex, subprocess
import argparse
import asyncio
from nicegui import ui, app

def run_cmd():
    cmd = "ffmpeg -y -i data/video.mp4 -acodec libmp3lame -ab 128000 data/video.mp3"
    with open('/dev/null', 'w') as dev_null:
        subprocess.call(
            shlex.split(cmd),
            stdout=dev_null,
            stderr=dev_null,
            stdin=subprocess.DEVNULL
        )

async def async_run():
    await asyncio.to_thread(run_cmd)

args = argparse.ArgumentParser()
args.add_argument("--webui", action="store_true")
args = args.parse_args()

if args.webui:
    ui.button('Convert to MP3', on_click=async_run)
    ui.run()
else:
    run_cmd()

核心注意事项

  • 必须隔离FFmpeg的IO流:要么重定向到空设备/日志文件,要么通过subprocess.PIPE读取输出,绝对不能让FFmpeg复用Web服务的IO流。
  • 优先选择线程池:FFmpeg本身是独立进程,Python侧不需要额外创建进程,线程池更轻量且不会引发环境冲突。

内容的提问来源于stack exchange,提问作者user1113159

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:57:48