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

如何用Python多进程启动FFmpeg并从主进程发送帧字符串?

老哥,你的问题我太懂了——单进程里Python的GIL把FFmpeg绑得死死的,明明有3个空闲核心却用不上,CPU拉满还导致直播卡顿,简直头疼。刚好我之前处理过类似的场景,给你一套实打实的解决方案:用Python把FFmpeg放到独立的子进程里跑,主进程专门负责生成帧,再通过管道把帧数据传给FFmpeg,彻底绕开GIL的限制,让FFmpeg能自由啃剩下的CPU核心。

核心思路

本质就是把FFmpeg从Python的GIL枷锁里解放出来:用subprocess模块启动FFmpeg作为系统级子进程(完全脱离Python解释器的控制),主进程专注于帧生成,两者通过管道做数据传输。这样FFmpeg可以随便用空闲的CPU核心,和主进程的帧处理并行执行,CPU利用率直接拉满,卡顿自然就没了。

具体实现步骤&代码示例

我直接给你写好可复用的代码,你可以照着改:

1. 基础版:主进程生成帧 + FFmpeg子进程编码推流

import subprocess
import sys

# 替换成你自己的帧生成逻辑,返回帧的字节/字符串数据
def get_frame():
    # 示例:生成模拟的YUV420p格式帧(1920x1080),你换成自己的帧字符串即可
    width, height = 1920, 1080
    frame_size = width * height * 3 // 2  # YUV420p的帧大小计算
    return b'\x00' * frame_size  # 用全黑帧模拟,实际替换成你的帧数据

def main():
    # 这里是FFmpeg的命令行参数,根据你的直播需求调整
    # 核心是`-i -`表示从stdin读取输入,也就是我们主进程传过去的帧
    ffmpeg_cmd = [
        'ffmpeg',
        '-f', 'rawvideo',          # 输入格式是原始视频帧
        '-pix_fmt', 'yuv420p',     # 你的帧格式,要和实际生成的一致
        '-s', '1920x1080',         # 分辨率,和你的帧匹配
        '-r', '30',                # 帧率
        '-i', '-',                 # 从stdin读取输入
        '-c:v', 'libx264',         # 视频编码器
        '-preset', 'veryfast',     # 编码速度优先,适合直播
        '-f', 'flv',               # 输出格式,比如FLV对应RTMP
        'rtmp://your-live-server-url'  # 你的直播推流地址
    ]

    try:
        # 启动FFmpeg子进程,创建可写入的stdin管道
        ffmpeg_proc = subprocess.Popen(
            ffmpeg_cmd,
            stdin=subprocess.PIPE,
            stderr=subprocess.PIPE,
            stdout=subprocess.PIPE,
            text=False  # 帧是二进制数据,用二进制模式传输
        )

        print("FFmpeg子进程启动成功,开始推流!")

        # 循环生成并发送帧
        while True:
            frame_data = get_frame()
            # 把帧数据写入FFmpeg的输入管道
            ffmpeg_proc.stdin.write(frame_data)
            ffmpeg_proc.stdin.flush()  # 强制刷新缓存,避免数据积压导致卡顿

    except KeyboardInterrupt:
        print("\n收到中断信号,正在停止推流...")
    except Exception as e:
        print(f"出问题了: {str(e)}")
        # 打印FFmpeg的错误日志,方便调试
        if ffmpeg_proc:
            print("\nFFmpeg错误输出:")
            print(ffmpeg_proc.stderr.read().decode('utf-8', errors='ignore'))
    finally:
        # 优雅关闭子进程,避免资源泄漏
        if ffmpeg_proc:
            ffmpeg_proc.stdin.close()
            ffmpeg_proc.terminate()
            ffmpeg_proc.wait()
        print("推流已停止")

if __name__ == "__main__":
    main()

2. 进阶版:帧生成也放到子进程(适合帧生成CPU密集的场景)

如果你的帧生成逻辑本身也很吃CPU,那可以再用multiprocessing把帧生成单独放到一个子进程,让帧生成、FFmpeg编码两个任务分别占不同核心,彻底榨干CPU性能:

import subprocess
from multiprocessing import Process, Queue
import sys

def frame_generator(queue):
    # 帧生成逻辑,持续把帧放到队列里
    while True:
        width, height = 1920, 1080
        frame_size = width * height * 3 // 2
        frame_data = b'\x00' * frame_size  # 替换成你的实际帧数据
        queue.put(frame_data)

def main():
    # 创建队列,用于帧生成进程和主进程之间传数据
    frame_queue = Queue(maxsize=10)  # 设置队列大小,防止内存爆了

    # 启动帧生成子进程
    gen_proc = Process(target=frame_generator, args=(frame_queue,))
    gen_proc.start()

    # FFmpeg命令行参数,和基础版一致
    ffmpeg_cmd = [
        'ffmpeg', '-f', 'rawvideo', '-pix_fmt', 'yuv420p',
        '-s', '1920x1080', '-r', '30', '-i', '-',
        '-c:v', 'libx264', '-preset', 'veryfast', '-f', 'flv',
        'rtmp://your-live-server-url'
    ]

    try:
        ffmpeg_proc = subprocess.Popen(
            ffmpeg_cmd,
            stdin=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=False
        )

        print("双进程启动成功,开始推流!")

        # 从队列取帧,传给FFmpeg
        while True:
            frame_data = frame_queue.get()
            ffmpeg_proc.stdin.write(frame_data)
            ffmpeg_proc.stdin.flush()

    except KeyboardInterrupt:
        print("\n正在停止所有进程...")
    except Exception as e:
        print(f"出错了: {str(e)}")
        if ffmpeg_proc:
            print("\nFFmpeg错误输出:")
            print(ffmpeg_proc.stderr.read().decode('utf-8', errors='ignore'))
    finally:
        # 先关帧生成进程,再关FFmpeg
        gen_proc.terminate()
        gen_proc.join()
        if ffmpeg_proc:
            ffmpeg_proc.stdin.close()
            ffmpeg_proc.terminate()
            ffmpeg_proc.wait()
        print("所有进程已关闭")

if __name__ == "__main__":
    main()

关键注意点

  • GIL彻底拜拜:FFmpeg是独立的系统进程,和Python解释器没关系,完全不受GIL限制,能用上所有空闲CPU核心。
  • 管道要flush:每次写完帧一定要调用flush(),不然数据会存在缓存里,导致FFmpeg拿不到数据,直播卡顿。
  • 格式要匹配:FFmpeg的输入参数(比如-pix_fmt、-s、-r)一定要和你生成的帧格式完全一致,不然FFmpeg会报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:36:23