OpenCV处理RTSP帧后,如何用FFmpeg将JPEG帧转H.264 RTSP流?
问题描述
我有一个输入RTSP流,想用OpenCV逐帧处理,处理完成后基于这些帧创建独立的RTSP流。需通过标准输入(STDIN)将生成的JPEG图像管道传输给FFmpeg,且中间帧必须为JPEG格式,流程如下:
RTSP输入 → 生成JPEG作为输入 → 处理后输出JPEG → RTSP输出
当前遇到的问题与编解码器相关,FFmpeg输出错误信息显示连接失败,以下是我的代码和FFmpeg输出:
我的Python代码
def open_ffmpeg_stream_process(): args = ( "ffmpeg -re -stream_loop -1 " "-f jpeg_pipe " "-s 512x288 " "-i pipe:0 " "-c:v h264 " "-f rtsp " "rtsp://localhost:8100/out0" ).split() return subprocess.Popen(args, stdin=subprocess.PIPE) video_source = cv2.VideoCapture('rtsp://localhost:9100/in0') frame_cnt = 0 FRAME_SKIP = 30 ffmpeg_process = open_ffmpeg_stream_process() while video_source.isOpened(): frame_cnt += 1 if frame_cnt % FRAME_SKIP: continue else: frame_cnt = 0 _, frame = video_source.read() _, jpg = cv2.imencode('.jpg', frame) # 在这里对JPEG进行处理,输出仍是JPEG ffmpeg_process.stdin.write(jpg.astype(np.uint8).tobytes()) if cv2.waitKey(1) & 0xFF == ord('q'): break video_source.release()
FFmpeg输出信息
ffmpeg version 4.4.2-0ubuntu0.22.04.1 Copyright (c) 2000-2021 the FFmpeg developers built with gcc 11 (Ubuntu 11.2.0-19ubuntu1) configuration: ---- noisy configuration stuff ---- libavutil 56. 70.100 / 56. 70.100 libavcodec 58.134.100 / 58.134.100 libavformat 58. 76.100 / 58. 76.100 libavdevice 58. 13.100 / 58. 13.100 libavfilter 7.110.100 / 7.110.100 libswscale 5. 9.100 / 5. 9.100 libswresample 3. 9.100 / 3. 9.100 libpostproc 55. 9.100 / 55. 9.100 Input #0, jpeg_pipe, from 'pipe:0': Duration: N/A, bitrate: N/A Stream #0:0: Video: mjpeg, rgb24(bt470bg/unknown/unknown), 512x288, 25 fps, 25 tbr, 25 tbn, 25 tbc Stream mapping: Stream #0:0 -> #0:0 (mjpeg (native) -> h264 (libx264)) [libx264 @ 0x562a1ffc5840] using SAR=1/1 [libx264 @ 0x562a1ffc5840] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2 [libx264 @ 0x562a1ffc5840] profile High, level 3.1, 4:2:0, 8-bit [libx264 @ 0x562a1ffc5840] 264 - core 163 r3060 5db6aa6 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=18 lookahead_threads=3 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00 [tcp @ 0x562a20826c00] Connection to tcp://localhost:8100?timeout=0 failed: Connection refused Could not write header for output file #0 (incorrect codec parameters ?): Connection refused Error initializing output stream 0:0 -- Conversion failed!
我需要解决的问题:如何通过FFmpeg从一系列JPEG帧创建H.264编码的RTSP流?也欢迎对我的代码提出改进意见。
编辑:我发现单独执行FFmpeg命令也无法工作。
解决方案
1. 解决RTSP连接拒绝问题
错误中的Connection refused是核心问题:FFmpeg仅作为RTSP推流客户端,无法自行提供RTSP服务,必须先启动一个RTSP服务器来接收推流。推荐使用rtsp-simple-server:
- 安装后直接运行,默认监听
8554端口,可修改配置文件调整端口,或直接将推流地址改为rtsp://localhost:8554/out0
2. 优化FFmpeg命令
原命令存在多处不合理,调整后的命令如下:
ffmpeg -re -f jpeg_pipe -s 512x288 -r 25 -i pipe:0 -c:v libx264 -preset ultrafast -tune zerolatency -f rtsp rtsp://localhost:8554/out0
- 移除
-stream_loop -1:该参数用于循环读取文件,管道实时输入无需循环 - 添加
-r 25:明确指定帧率,避免FFmpeg自动猜测出错,需与输入JPEG的实际帧率匹配 - 替换
-c:v h264为-c:v libx264:明确指定使用libx264编码器,避免系统默认编码器不兼容 - 添加
-preset ultrafast和-tune zerolatency:针对实时推流优化,降低延迟,适配RTSP场景
3. 优化Python代码
(1)修正帧跳过逻辑
原代码的帧跳过逻辑本身没问题,但需注意:FRAME_SKIP=30会导致每30帧仅处理1帧,最终推流帧率极低,需根据需求调整该值。
(2)处理管道缓冲问题
直接写入管道可能因缓冲导致延迟,建议每次写入后调用flush():
ffmpeg_process.stdin.write(jpg.astype(np.uint8).tobytes()) ffmpeg_process.stdin.flush()
(3)完善资源清理与异常处理
添加异常捕获与资源释放逻辑,避免进程残留:
try: while video_source.isOpened(): frame_cnt += 1 if frame_cnt % FRAME_SKIP: continue else: frame_cnt = 0 ret, frame = video_source.read() if not ret: break _, jpg = cv2.imencode('.jpg', frame) # 在这里对JPEG进行处理,输出仍是JPEG ffmpeg_process.stdin.write(jpg.astype(np.uint8).tobytes()) ffmpeg_process.stdin.flush() if cv2.waitKey(1) & 0xFF == ord('q'): break except Exception as e: print(f"执行出错: {e}") finally: video_source.release() cv2.destroyAllWindows() ffmpeg_process.stdin.close() ffmpeg_process.wait()
(4)JPEG处理注意事项
若处理后的JPEG尺寸与原帧不同,需同步修改FFmpeg命令中的-s参数,否则会出现画面变形或编码错误。
4. 验证流程
- 启动RTSP服务器(如
rtsp-simple-server) - 运行修改后的Python代码
- 用VLC或其他RTSP播放器连接
rtsp://localhost:8554/out0,检查播放是否正常
内容的提问来源于stack exchange,提问作者Michael Schmidt
相关产品推荐
相关产品推荐

