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

如何降低FFmpeg推流至RTMP服务器时的输出速度?

问题分析与解决方案

你的核心问题是没有控制帧的发送速度——代码会瞬间把所有短视频的帧写入ffmpeg的标准输入,导致推流速度远超过目标20fps,服务器无法承受这种突发流量而崩溃。摄像头推流正常是因为摄像头本身按固定帧率输出帧,天然有节奏控制。

关键修复点

  • 移除无用代码:开头的cap = cv2.VideoCapture('traffic.mp4')及后续参数获取完全未被使用,直接删除避免混淆。
  • 添加帧发送节奏控制:按20fps计算每帧间隔(0.05秒),用time.sleep()强制控制发送速度,确保推流稳定在目标帧率。
  • 优化ffmpeg参数:输入端明确指定帧率-r 20,添加-vsync cfr强制恒定帧率输出,让ffmpeg严格按目标速率处理帧。

修改后的完整代码

import cv2
import os
import subprocess
import time

url = "rtmp://..."
key = "..."
rtmp_url = url + "/" + key

# 目标推流帧率
TARGET_FPS = 20
FRAME_DELAY = 1.0 / TARGET_FPS  # 每帧间隔时间(秒)

# 优化后的ffmpeg命令模板
command = [
    'ffmpeg',
    '-y',
    '-f', 'rawvideo',
    '-vcodec', 'rawvideo',
    '-pix_fmt', 'bgr24',
    '-r', str(TARGET_FPS),  # 明确输入帧率
    '-s', "{}x{}",  # 动态填充视频分辨率
    '-i', '-',
    '-vsync', 'cfr',  # 强制恒定帧率输出
    '-c:v', 'libx264',
    '-pix_fmt', 'yuv420p',
    '-preset', 'slow',
    '-f', 'flv',
    rtmp_url
]

received_video_path = 'ReceivedRecording'
while True:
    # 获取目录下的视频文件,按逆序排序
    video_files = sorted(os.listdir(received_video_path), reverse=True)
    # 保留最新3个文件,处理其余旧文件
    for filename in video_files[:-3]:
        video_path = os.path.join(received_video_path, filename)
        video = cv2.VideoCapture(video_path)
        
        # 获取当前视频的宽高,更新ffmpeg命令中的分辨率参数
        width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
        command[7] = f"{width}x{height}"
        
        # 启动ffmpeg子进程(若未启动或已结束则重启)
        if 'p' not in locals() or p.poll() is not None:
            p = subprocess.Popen(command, stdin=subprocess.PIPE, shell=False)
        
        # 记录上一帧发送时间,控制节奏
        last_frame_time = time.time()
        
        while True:
            ret, frame = video.read()
            if not ret:
                video.release()
                break
            
            # 控制发送速度,确保每帧间隔符合目标帧率
            current_time = time.time()
            elapsed_time = current_time - last_frame_time
            if elapsed_time < FRAME_DELAY:
                time.sleep(FRAME_DELAY - elapsed_time)
            last_frame_time = time.time()
            
            # 写入帧数据到ffmpeg管道
            p.stdin.write(frame.tobytes())
        
        os.remove(video_path)

额外注意事项

  • 改用shell=False避免shell注入风险,同时提升跨平台兼容性;
  • 代码支持动态适配不同分辨率的短视频,避免分辨率不匹配导致的推流错误;
  • 若服务器仍有崩溃情况,可降低ffmpeg编码预设(如将slow改为medium),减少CPU负载,避免数据堆积。

内容的提问来源于stack exchange,提问作者Loc Bui Nhien

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 04:15:29