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

如何实现流中断后10秒自动重启?含现有脚本优化需求

问题与解决方案

问题背景

现有Bash脚本通过监控FFmpeg日志中的特定错误来重启HLS推流,但仍存在流中断无法自动恢复的情况,需要实现包括TS文件中断场景在内的流中断后10秒自动重启。

原监控脚本与FFmpeg推流命令如下:

原监控脚本

#!/bin/bash
while true;do
grep -c "Non-monotonous DTS in output stream" file.txt >nonmonotonus.txt
grep -c "Timestamps are unset in a packet for stream" file.txt >timestamp.txt
grep -c "PES packet size mismatch" file.txt >pespacket.txt
grep -c "Error while decoding stream" file.txt >errordecoding.txt
grep -c "Circular buffer overrun" file.txt >circularbuffer.txt
grep -c "Header missing" file.txt >header.txt
grep -c "Conversion failed" file.txt >conversion.txt

file=nonmonotonus.txt
file1=timestamp.txt
file2=pespacket.txt
file3=errordecoding.txt
file4=circularbuffer.txt
file5=header.txt
file6=conversion.txt

if (($(<"$file">=3000)) || (($(<"$file1">=500)) || (($(<"$file2">=100)) || (($(<"$file3">=1000)) || (($(<"$file4">=500)) || (($(<"$file5">=6)) || (($(<"$file6">=1)); then
stream1 restart > restart.txt
sleep 1
fi
done

原FFmpeg推流命令

FFmpeg -re -threads 3 -c:s webvtt -i "$INPUT_URL?source=null&overrun_nonfatal=1&fifo_size=1000000" \
  -c:v copy \
  -map 0:0 -map 0:1  \
  -c:a aac -b:a 128k -ar 48000 \
  -threads 4 -f hls -hls_time 2 -hls_wrap 15 \
  "manifest.m3u8" \
</dev/null > /dev/null 2>&1 2>file.txt & echo $! > $STREAM_PID_PATH

优化方案

针对原有脚本的不足,从进程存活监控、TS文件更新检测、错误日志优化、延迟重启逻辑四个维度改进:

1. 核心改进点

  • 进程存活检测:直接检查FFmpeg的PID是否存在,避免进程崩溃后日志监控失效的情况。
  • TS文件更新监控:HLS流正常时会持续生成新的.ts文件,若最近10秒无新TS文件生成,判定为流中断。
  • 优化错误日志检测:改为实时跟踪日志新增错误,避免重复统计历史错误,同时保留原有错误阈值触发逻辑。
  • 10秒延迟重启:触发重启条件后等待10秒,确认问题未自行恢复再执行重启,减少不必要的重启操作。

2. 优化后的完整脚本

#!/bin/bash
# 配置参数
LOG_FILE="file.txt"
STREAM_PID_PATH="/path/to/stream.pid"
HLS_OUTPUT_DIR="./"  # 替换为你的m3u8和ts文件所在目录
RESTART_DELAY=10     # 中断后延迟重启时间(秒)

# 重启FFmpeg流的函数
restart_stream() {
    echo "$(date): 检测到流中断,${RESTART_DELAY}秒后重启..."
    sleep ${RESTART_DELAY}
    
    # 停止原有进程(如果存在)
    if [ -f ${STREAM_PID_PATH} ]; then
        OLD_PID=$(cat ${STREAM_PID_PATH})
        kill ${OLD_PID} 2>/dev/null
        sleep 2
        # 确保进程被杀死
        kill -9 ${OLD_PID} 2>/dev/null
        rm -f ${STREAM_PID_PATH}
    fi

    # 启动新的FFmpeg推流
    ffmpeg -re -threads 3 -c:s webvtt -i "$INPUT_URL?source=null&overrun_nonfatal=1&fifo_size=1000000" \
      -c:v copy \
      -map 0:0 -map 0:1  \
      -c:a aac -b:a 128k -ar 48000 \
      -threads 4 -f hls -hls_time 2 -hls_wrap 15 \
      "${HLS_OUTPUT_DIR}/manifest.m3u8" \
      </dev/null > /dev/null 2>&1 2>${LOG_FILE} &
    echo $! > ${STREAM_PID_PATH}
    echo "$(date): 流已重启,新PID为$(cat ${STREAM_PID_PATH})"
}

# 初始化错误计数器
declare -A error_counts=(
    ["Non-monotonous DTS in output stream"]=0
    ["Timestamps are unset in a packet for stream"]=0
    ["PES packet size mismatch"]=0
    ["Error while decoding stream"]=0
    ["Circular buffer overrun"]=0
    ["Header missing"]=0
    ["Conversion failed"]=0
)

# 错误阈值配置
declare -A error_thresholds=(
    ["Non-monotonous DTS in output stream"]=3000
    ["Timestamps are unset in a packet for stream"]=500
    ["PES packet size mismatch"]=100
    ["Error while decoding stream"]=1000
    ["Circular buffer overrun"]=500
    ["Header missing"]=6
    ["Conversion failed"]=1
)

# 实时监控逻辑
while true; do
    restart_triggered=0

    # 1. 检查FFmpeg进程是否存活
    if [ -f ${STREAM_PID_PATH} ]; then
        PID=$(cat ${STREAM_PID_PATH})
        if ! kill -0 ${PID} 2>/dev/null; then
            restart_triggered=1
        fi
    else
        restart_triggered=1
    fi

    # 2. 检查TS文件是否在最近10秒更新
    if [ ${restart_triggered} -eq 0 ]; then
        latest_ts=$(ls -t ${HLS_OUTPUT_DIR}/*.ts 2>/dev/null | head -n1)
        if [ -z "${latest_ts}" ]; then
            # 无TS文件,判定中断
            restart_triggered=1
        else
            ts_modified=$(stat -c %Y "${latest_ts}")
            current_time=$(date +%s)
            if [ $((current_time - ts_modified)) -gt ${RESTART_DELAY} ]; then
                restart_triggered=1
            fi
        fi
    fi

    # 3. 检查日志错误是否超过阈值
    if [ ${restart_triggered} -eq 0 ]; then
        # 实时读取新增的日志行
        tail -n 0 -F ${LOG_FILE} | while read line; do
            for err_msg in "${!error_counts[@]}"; do
                if echo "${line}" | grep -q "${err_msg}"; then
                    error_counts["${err_msg}"]=$((error_counts["${err_msg}"] + 1))
                    # 检查是否超过阈值
                    if [ ${error_counts["${err_msg}"]} -ge ${error_thresholds["${err_msg}"]} ]; then
                        restart_triggered=1
                        break 2  # 跳出内层和外层循环
                    fi
                fi
            done
        done &
        TAIL_PID=$!
        sleep 5  # 每5秒检查一次错误计数
        kill ${TAIL_PID} 2>/dev/null
    fi

    # 如果触发重启条件,执行重启
    if [ ${restart_triggered} -eq 1 ]; then
        restart_stream
        # 重置错误计数器
        for err_msg in "${!error_counts[@]}"; do
            error_counts["${err_msg}"]=0
        done
    fi

    sleep 2  # 主循环间隔
done

3. 脚本说明

  • 配置参数:根据实际环境修改STREAM_PID_PATH、HLS_OUTPUT_DIR等路径参数。
  • 进程管理:重启前会先终止原有FFmpeg进程,避免僵尸进程。
  • TS文件检测:通过检查最新TS文件的修改时间,判断流是否正常生成片段。
  • 错误日志跟踪:采用tail -F实时读取新增日志行,仅统计新产生的错误,避免历史错误干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 16:30:49