树莓派5上如何监控ffplay输出并在其挂起时重启进程?
树莓派5上自动监控并重启ffplay RTSP流方案
我在树莓派5上开启4个终端窗口,每个窗口运行以下命令播放监控摄像头RTSP流:
ffplay -left 0 -top 0 -x 960 -y 540 -rtsp_transport tcp "rtsp://user:pass@camera-IP:port/videoSub"
已通过-rtsp_transport tcp解决了之前的流错误问题,但偶尔会出现单个或多个流挂起的情况,只能手动重启。现在需要实现两个需求:监控ffplay的输出、检测到挂起时自动杀死进程并重启,优先采用Shell脚本或Python方案,避开Mono和C#。
一、Shell脚本轻量方案(单流独立监控)
适合快速部署,每个流对应一个独立脚本,逻辑简单易修改。
1. 单流监控脚本示例(复制4份适配不同流)
创建monitor_ffplay_1.sh:
#!/bin/bash # 配置当前流的参数 WINDOW_LEFT=0 WINDOW_TOP=0 WINDOW_WIDTH=960 WINDOW_HEIGHT=540 RTSP_URL="rtsp://user:pass@camera-IP:port/videoSub" LOG_FILE="/tmp/ffplay_monitor_1.log" CHECK_INTERVAL=10 # 每10秒检查一次输出 TIMEOUT_THRESHOLD=30 # 超过30秒无输出判定为挂起 # 循环重启逻辑 while true; do # 启动ffplay并将输出写入日志 ffplay -left $WINDOW_LEFT -top $WINDOW_TOP -x $WINDOW_WIDTH -y $WINDOW_HEIGHT -rtsp_transport tcp "$RTSP_URL" 2>&1 | tee $LOG_FILE & PLAY_PID=$! # 监控输出更新 while kill -0 $PLAY_PID 2>/dev/null; do LAST_MODIFIED=$(stat -c %Y $LOG_FILE) CURRENT_TIME=$(date +%s) # 检查日志是否超时未更新 if [ $((CURRENT_TIME - LAST_MODIFIED)) -gt $TIMEOUT_THRESHOLD ]; then echo "[$(date)] ffplay进程 $PLAY_PID 无输出,重启中..." kill -9 $PLAY_PID break fi sleep $CHECK_INTERVAL done wait $PLAY_PID 2>/dev/null echo "[$(date)] ffplay进程已退出,准备重新启动..." sleep 2 done
2. 使用步骤
- 复制4份脚本,分别修改
WINDOW_LEFT/WINDOW_TOP(比如第二个窗口设为-left 960 -top 0)、RTSP_URL和LOG_FILE名称(如ffplay_monitor_2.log) - 给脚本添加执行权限:
chmod +x monitor_ffplay_*.sh - 后台运行脚本(避免终端关闭后进程终止):
nohup ./monitor_ffplay_1.sh > /dev/null 2>&1 &,其余3个流同理执行
二、Python统一监控方案(多流集中管理)
更灵活的集中管理方案,适合需要扩展功能(如告警、日志归档)的场景。
1. 统一监控脚本示例(ffplay_monitor.py)
import subprocess import time import os import signal # 配置4个流的参数,按需修改 STREAMS = [ {"left": 0, "top": 0, "width": 960, "height": 540, "url": "rtsp://user:pass@camera-IP1:port/videoSub", "name": "stream1"}, {"left": 960, "top": 0, "width": 960, "height": 540, "url": "rtsp://user:pass@camera-IP2:port/videoSub", "name": "stream2"}, {"left": 0, "top": 540, "width": 960, "height": 540, "url": "rtsp://user:pass@camera-IP3:port/videoSub", "name": "stream3"}, {"left": 960, "top": 540, "width": 960, "height": 540, "url": "rtsp://user:pass@camera-IP4:port/videoSub", "name": "stream4"}, ] CHECK_INTERVAL = 10 # 检查间隔(秒) TIMEOUT_THRESHOLD = 30 # 无输出超时阈值(秒) class StreamMonitor: def __init__(self, config): self.config = config self.process = None self.last_output_time = time.time() def start_stream(self): # 构建ffplay启动命令 cmd = [ "ffplay", "-left", str(self.config["left"]), "-top", str(self.config["top"]), "-x", str(self.config["width"]), "-y", str(self.config["height"]), "-rtsp_transport", "tcp", self.config["url"] ] # 启动子进程并捕获输出 self.process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, universal_newlines=True ) self.last_output_time = time.time() print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 启动流 {self.config['name']},PID: {self.process.pid}") def check_status(self): if not self.process: return False # 读取一行输出(非阻塞) try: line = self.process.stdout.readline() if line: self.last_output_time = time.time() # 可选:取消注释打印输出到控制台 # print(f"[{self.config['name']}] {line.strip()}") except: pass # 检查进程是否存活或超时无输出 if self.process.poll() is not None: print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 流 {self.config['name']} 进程退出,退出码: {self.process.returncode}") return False if time.time() - self.last_output_time > TIMEOUT_THRESHOLD: print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 流 {self.config['name']} 超时无输出,重启中...") self.kill_process() return False return True def kill_process(self): if self.process and self.process.poll() is None: os.kill(self.process.pid, signal.SIGKILL) self.process.wait() print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 已杀死流 {self.config['name']} 进程 {self.process.pid}") def main(): monitors = [StreamMonitor(stream) for stream in STREAMS] # 逐个启动流,避免资源瞬间占用过高 for monitor in monitors: monitor.start_stream() time.sleep(1) while True: for monitor in monitors: if not monitor.check_status(): time.sleep(2) monitor.start_stream() time.sleep(CHECK_INTERVAL) if __name__ == "__main__": main()
2. 使用步骤
- 修改
STREAMS列表中的窗口位置和RTSP地址 - 后台运行脚本:
nohup python3 ffplay_monitor.py > ffplay_monitor.log 2>&1 &
方案选择建议
- Shell脚本:轻量无依赖,适合快速部署,每个流独立管理,修改成本低
- Python脚本:集中管理所有流,逻辑扩展性强,方便后续添加告警、日志分析等功能
内容的提问来源于stack exchange,提问作者Deekay
相关产品推荐
相关产品推荐

