如何降低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
相关产品推荐
相关产品推荐

