79616305

Date: 2025-05-11 08:54:02
Score: 2
Natty:
Report link

I've been facing the same issue for a long time, and unfortunately the only working resolution was to disable HLS generation by nginx-rtmp-module itself and switch to ffmpeg instead (previously suggested by @sasuke-uchiha).

Relevant excerpts from nginx configuration:

rtmp {
    server {
        listen 127.0.0.1:1935;
        application cctv {
            allow publish 127.0.0.0/24;
            deny publish all;
            allow play all;

            live on;

            # HLS fragments generation
            hls off;

            # maintain HLS via ffmpeg instead of nginx-rtmp-module
            exec_push /home/cctv/bin/rtmp2hls $name;
            exec_kill_signal term;

            # rest of RTMP configuration (recorders etc)
        }
    }
}

The rtmp2hls script is a simple shell script which is intended to run ffmpeg for every connected publisher and ensure to kill it once SIGTERM is received from nginx (pasting here with minor edits):

#!/bin/bash -e

SPOOL="/var/spool/cctv"
LOGS="/var/log/cctv"

SELF=$(basename "$0")
_ffmpeg_pid=

log () {
    logger --id $$ "$SELF: $*"
}

_teardown() {
    if [ -n "${_ffmpeg_pid}" ]; then
        if ! kill "${_ffmpeg_pid}"; then
            log "ffmpeg (${_ffmpeg_pid}) was not running"
        else
            log "ffmpeg ${_ffmpeg_pid} killed"
        fi
        _ffmpeg_pid=
    fi
    log "stopped rtmp-to-hls ffmpeg for ${CAMERA_ID}"
    exit 0
}

if [ $# -lt 1 ]; then
    echo "usage: $(basename "$0") camera_id" >&2
    exit 1
fi

CAMERA_ID="$1"
PULL_URL="rtmp://127.0.0.1/cctv/${CAMERA_ID}"

while read -r fname; do
    log "removing stale stream: ${fname}"
    rm -f "${fname}"
done < <(find "${SPOOL}/hls" -type f -name "${CAMERA_ID}*.ts" | sort)

# see also:
# https://ffmpeg.org/ffmpeg-formats.html#hls-2
# https://www.martin-riedl.de/2020/04/17/using-ffmpeg-as-a-hls-streaming-server-overview/

ffmpeg \
    -i "${PULL_URL}" \
    -nostats \
    -c copy \
    -sc_threshold 0 \
    -f hls \
    -hls_time 6 \
    -hls_list_size 6 \
    -hls_delete_threshold 1 \
    -hls_start_number_source epoch \
    -hls_flags independent_segments+delete_segments+program_date_time \
    "${SPOOL}/hls/${CAMERA_ID}.m3u8" 2>>"${LOGS}/${CAMERA_ID}.ffmpeg-hls.log" &

_ffmpeg_pid=$!

log "started rtmp-to-hls ffmpeg (pid=${_ffmpeg_pid}) for ${CAMERA_ID} from ${PULL_URL} to ${SPOOL}/hls/${CAMERA_ID}.m3u8"

trap "_teardown" INT TERM

wait "${_ffmpeg_pid}"

I've been testing this with nginx/1.27.1, nginx-rtmp-module/1.2.2, and ffmpeg/5.1.6 for a few weeks, and everything works just fine. With this setup I was able to get almost clean report from Apple's mediastreamvalidator tool:

enter image description here

For comparison, here's what I was getting with plain nginx-rtmp-module:

enter image description here

Interestingly enough, even with nginx-rtmp-module's HLS browsers (Safari on macOS or iPad) were regularly fetching playlists and stream data, but never show anything except a spinning wheel.

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Me too answer (2.5): facing the same issue
  • User mentioned (1): @sasuke-uchiha
Posted by: Andrei Belov