test(smoke): add live srs forward smoke script

This commit is contained in:
2026-03-11 14:47:21 +08:00
parent f59abb2a32
commit 058226fea2
2 changed files with 323 additions and 165 deletions
+90 -165
View File
@@ -4,201 +4,126 @@
**OPTIONAL / NON-BLOCKING** **OPTIONAL / NON-BLOCKING**
This document provides optional interoperability checks for SRS (Simple Realtime Server). These checks are not mandatory for acceptance. If the SRS environment is unavailable, skip these tests without failing the mandatory acceptance criteria. Use these checks when you want to verify RTMP interoperability or a real cvmmap-to-SRS forward path. They are not part of the mandatory acceptance matrix.
--- ## Covered Paths
## Purpose - Synthetic RTMP smoke using `rtmp_output_tester`
- Live cvmmap ingest -> FFmpeg encoder -> RTMP -> SRS HTTP-FLV forward
Validate RTMP streaming interoperability with SRS, specifically for:
- RTMP H.264 publishing and playback
- Enhanced RTMP HEVC (H.265) via [Enhanced RTMP spec](https://github.com/veovera/enhanced-rtmp)
- Low-latency streaming configurations
---
## Prerequisites ## Prerequisites
- Docker (recommended) or SRS built from source - Project built under `./build`
- FFmpeg or the project streaming binary - Local SRS checkout at `~/Code/srs`, or override `SRS_ROOT`
- Optional: ffplay, VLC for playback verification - `ffprobe`, `ffmpeg`, and `curl` available in `PATH`
- For the live test, a running cvmmap producer. Pass the URI explicitly.
--- ## Reproducible Test: Synthetic RTMP Matrix
## Quick Start (Docker) This is the fast interoperability smoke for the RTMP sink implementations:
```bash ```bash
# Run SRS with RTMP support cmake -B build -S .
docker run --rm -it -p 1935:1935 -p 8080:8080 ossrs/srs:6 \ cmake --build build
./objs/srs -c conf/rtmp.conf ./scripts/rtmp_srs_smoke.sh
# For HEVC support, use hevc.flv.conf or hevc.ts.conf
docker run --rm -it -p 1935:1935 -p 8080:8080 ossrs/srs:6 \
./objs/srs -c conf/hevc.flv.conf
``` ```
--- Useful overrides:
## Smoke Commands
### 1. Basic RTMP H.264 Stream Test
```bash ```bash
# Publish test stream to SRS ENCODER_DEVICE=nvidia ./scripts/rtmp_srs_smoke.sh
ffmpeg -re -f lavfi -i testsrc=duration=60:size=1280x720:rate=30 \ SRS_ROOT=~/Code/srs ./scripts/rtmp_srs_smoke.sh
-f lavfi -i sine=frequency=1000:duration=60 \
-pix_fmt yuv420p -c:v libx264 -preset fast -b:v 3000k \
-c:a aac -b:a 128k -f flv rtmp://localhost/live/smoke_test
``` ```
What it verifies:
- `libavformat` RTMP output with `h264`
- `ffmpeg_process` RTMP output with `h264`
- `libavformat` RTMP output with `h265`
- `ffmpeg_process` RTMP output with `h265`
- Pullback from SRS HTTP-FLV with `ffprobe`
## Reproducible Test: Live cvmmap Forward
This is the end-to-end live test for a real cvmmap source:
```bash ```bash
# Playback verification cmake -B build -S .
ffplay rtmp://localhost/live/smoke_test cmake --build build
./scripts/live_srs_forward_smoke.sh 'cvmmap://zed@/tmp/cvmmap'
``` ```
### 2. Enhanced RTMP HEVC (H.265) Test The script defaults to:
**Prerequisites:** - `CODEC=h264`
- SRS 6.0.4+ with HEVC configuration - `ENCODER_BACKEND=ffmpeg`
- FFmpeg 6.0+ with libx265 and enhanced RTMP support (or use `ossrs/srs:encoder` Docker image) - `ENCODER_DEVICE=nvidia`
- `RTMP_TRANSPORT=libavformat`
- `RTMP_MODE=enhanced`
- `INGEST_MAX_FRAMES=120`
- `STREAM_NAME=<instance>_live`, derived from `INPUT_URI`
Useful overrides:
```bash ```bash
# Publish HEVC stream (Enhanced RTMP) INPUT_URI='cvmmap://zed@/tmp/cvmmap' ./scripts/live_srs_forward_smoke.sh
ffmpeg -re -f lavfi -i testsrc=duration=60:size=1280x720:rate=30 \ ./scripts/live_srs_forward_smoke.sh 'cvmmap://front_cam@/tmp/cvmmap' front_cam_smoke
-f lavfi -i sine=frequency=1000:duration=60 \ ENCODER_DEVICE=software ./scripts/live_srs_forward_smoke.sh 'cvmmap://zed@/tmp/cvmmap'
-pix_fmt yuv420p -c:v libx265 -preset fast -b:v 2000k \ STREAM_NAME=zed_test INGEST_MAX_FRAMES=300 ./scripts/live_srs_forward_smoke.sh 'cvmmap://zed@/tmp/cvmmap'
-c:a aac -b:a 128k -f flv rtmp://localhost/live/smoke_hevc SRS_ROOT=~/Code/srs ./scripts/live_srs_forward_smoke.sh 'cvmmap://zed@/tmp/cvmmap'
# Alternative using project binary (if HEVC enabled)
# ./cvmmap-streamer --output rtmp://localhost/live/smoke_hevc --codec hevc
``` ```
```bash What it does:
# Playback HEVC stream
ffplay rtmp://localhost/live/smoke_hevc 1. Reuses a healthy local SRS instance if one is already listening on `127.0.0.1:1985`
2. Otherwise starts SRS from `~/Code/srs`
3. Writes a temporary SRS config with:
- `daemon off`
- `max_connections` reduced to avoid the common local `ulimit -n 1024` failure
4. Publishes the cvmmap stream to `rtmp://127.0.0.1/live/<stream>`
5. Verifies the forwarded HTTP-FLV mount at `http://127.0.0.1:8080/live/<stream>.flv`
6. Attempts to decode a short sample through `ffmpeg`
7. Stores logs and probe output under `build/live_srs_forward_smoke_*`
Expected success signals:
- The script exits `0`
- `ffprobe_httpflv.log` reports the expected codec and dimensions
- `streamer.log` shows `RTMP_OUTPUT_READY`
- `streamer.log` ends with `PIPELINE_METRICS` and `RTMP_OUTPUT_METRICS`
The HTTP-FLV probe is the authoritative pass/fail signal. The optional `ffmpeg` decode sample is best-effort because a very short bounded publish can disappear before the second client attaches.
## Example Live Result
For a ZED source running at `cvmmap://zed@/tmp/cvmmap`, the successful forward probe looked like:
```text
index=0
codec_name=h264
width=1280
height=720
avg_frame_rate=30/1
``` ```
### 3. Low-Latency Configuration ## Artifacts
For minimal latency with SRS, use this configuration snippet: Both smoke scripts write evidence under `build/`:
```bash - `srs-smoke.log`
# rtmp.conf with low-latency settings - `live_srs_forward_smoke_*/streamer.log`
listen 1935; - `live_srs_forward_smoke_*/ffprobe_httpflv.log`
chunk_size 128; - `live_srs_forward_smoke_*/ffmpeg_decode_httpflv.log`
- `live_srs_forward_smoke_*/srs_api_streams.json`
vhost __defaultVhost__ {
# Disable merged-read for lower latency
publish {
mr off;
mr_latency 100;
}
play {
# Disable GOP cache for lower latency (may cause startup delay)
gop_cache off;
# Reduce queue length
queue_length 5;
# Enable TCP_NODELAY
tcp_nodelay on;
# Reduce merged-write latency
mw_latency 100;
mw_msgs 1;
}
}
```
**Caveats for low-latency mode:**
- `gop_cache off` means players wait for next I-frame (startup delay)
- Lower `mw_latency` increases CPU usage
- Network jitter may cause more visible artifacts
---
## HEVC Compatibility Notes
### Enhanced RTMP (Standard)
SRS 6.0+ supports HEVC via the [Enhanced RTMP](https://github.com/veovera/enhanced-rtmp) specification:
- Uses FourCC `hvc1` for HEVC
- Supported by FFmpeg 6.0+ without patches
- Supported by OBS 29+
### Domestic HEVC Extension
Some older systems use the domestic HEVC FLV extension (code 12):
- Requires patched FFmpeg for some versions
- SRS supports both formats but prefers Enhanced RTMP
- When using FFmpeg 4.x/5.x, you may need the [runner365 patch](https://github.com/runner365/ffmpeg_rtmp_h265)
**Recommendation:** Use FFmpeg 6.0+ or the `ossrs/srs:encoder` Docker image to avoid compatibility issues.
---
## Environment Variable Overrides
SRS supports environment variable configuration:
```bash
# Override listen port
docker run --rm -it -p 1935:1935 -e SRS_LISTEN=1935 ossrs/srs:6 \
./objs/srs -c conf/rtmp.conf
# Enable low latency for all vhosts
docker run --rm -it -p 1935:1935 \
-e SRS_VHOST_PUBLISH_MR=off \
-e SRS_VHOST_PLAY_GOP_CACHE=off \
-e SRS_VHOST_PLAY_MW_LATENCY=100 \
ossrs/srs:6 \
./objs/srs -c conf/rtmp.conf
```
---
## Health Check
```bash
# Check SRS HTTP API (if enabled)
curl http://localhost:8080/api/v1/versions
# Check stream statistics
curl http://localhost:8080/api/v1/streams
```
---
## Missing Server Environment Behavior
If SRS is not available or the Docker container fails to start:
1. **SKIP** - Do not fail mandatory acceptance
2. **Log** - Document the environment issue
3. **Continue** - Proceed with other tests
Example skip condition:
```bash
if ! docker run --rm -p 1935:1935 ossrs/srs:6 true 2>/dev/null; then
echo "SRS environment unavailable - skipping smoke tests"
exit 0
fi
```
---
## Troubleshooting ## Troubleshooting
| Issue | Solution | | Issue | Cause | Action |
|-------|----------| |-------|-------|--------|
| Connection refused | Check port 1935 is not in use; verify SRS started | | SRS exits immediately | `max_connections` too high for local `ulimit` | Use the script-generated config or raise `ulimit -n` |
| Stream not found | Verify app/stream name matches; check SRS logs | | HTTP-FLV never mounts | Publish failed or source is idle | Check `streamer.log` and confirm the cvmmap URI is active |
| HEVC playback fails | Ensure player supports HEVC (Chrome 105+, ffplay, VLC 3.0+) | | NVENC unavailable | FFmpeg cannot open `h264_nvenc` / `hevc_nvenc` | Set `ENCODER_DEVICE=software` |
| High latency | Disable gop_cache, reduce mw_latency/mr_latency | | RTMP probe hangs | Live RTMP playback probe can block on some builds | Use the HTTP-FLV verification path; this is what the script treats as authoritative |
| Artifacts in stream | Check encoder settings; verify network stability |
---
## References ## References
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
BUILD_DIR="${BUILD_DIR:-$ROOT_DIR/build}"
STREAMER_BIN="${STREAMER_BIN:-$BUILD_DIR/cvmmap_streamer}"
SRS_ROOT="${SRS_ROOT:-$HOME/Code/srs}"
SRS_BIN="${SRS_BIN:-$SRS_ROOT/trunk/objs/srs}"
SRS_CONF="${SRS_CONF:-$SRS_ROOT/trunk/conf/srs.conf}"
FFPROBE_BIN="${FFPROBE_BIN:-ffprobe}"
FFMPEG_BIN="${FFMPEG_BIN:-ffmpeg}"
CURL_BIN="${CURL_BIN:-curl}"
INPUT_URI="${INPUT_URI:-}"
STREAM_NAME="${STREAM_NAME:-}"
CODEC="${CODEC:-h264}"
ENCODER_BACKEND="${ENCODER_BACKEND:-ffmpeg}"
ENCODER_DEVICE="${ENCODER_DEVICE:-nvidia}"
RTMP_TRANSPORT="${RTMP_TRANSPORT:-libavformat}"
RTMP_MODE="${RTMP_MODE:-enhanced}"
INGEST_MAX_FRAMES="${INGEST_MAX_FRAMES:-120}"
PROBE_TIMEOUT_S="${PROBE_TIMEOUT_S:-20}"
DECODE_FRAMES="${DECODE_FRAMES:-15}"
SRS_MAX_CONNECTIONS="${SRS_MAX_CONNECTIONS:-64}"
RUN_DIR="${RUN_DIR:-$BUILD_DIR/live_srs_forward_smoke_$(date +%Y%m%d_%H%M%S)}"
mkdir -p "$RUN_DIR"
SRS_LOG="$RUN_DIR/srs.log"
STREAMER_LOG="$RUN_DIR/streamer.log"
HTTP_PROBE_LOG="$RUN_DIR/ffprobe_httpflv.log"
DECODE_LOG="$RUN_DIR/ffmpeg_decode_httpflv.log"
API_LOG="$RUN_DIR/srs_api_streams.json"
SRS_TEMP_CONF="$RUN_DIR/srs.conf"
RTMP_URL="rtmp://127.0.0.1/live/${STREAM_NAME}"
HTTP_FLV_URL="http://127.0.0.1:8080/live/${STREAM_NAME}.flv"
SRS_PID=""
STREAMER_PID=""
STARTED_SRS=0
require_bin() {
local path="$1"
local label="$2"
if ! command -v "$path" >/dev/null 2>&1 && [[ ! -x "$path" ]]; then
echo "$label not found: $path" >&2
exit 1
fi
}
usage() {
cat <<'EOF'
Usage:
live_srs_forward_smoke.sh <input-uri> [stream-name]
Environment overrides:
INPUT_URI cvmmap source URI, if positional argument is omitted
STREAM_NAME RTMP/HTTP-FLV stream name, default derived from INPUT_URI
CODEC h264|h265
ENCODER_BACKEND ffmpeg|gstreamer_legacy
ENCODER_DEVICE auto|nvidia|software
RTMP_TRANSPORT libavformat|ffmpeg_process|legacy_custom
RTMP_MODE enhanced|domestic
INGEST_MAX_FRAMES bounded frame count for the smoke
DECODE_FRAMES frames to decode from HTTP-FLV after probe
SRS_ROOT local SRS checkout, default ~/Code/srs
EOF
}
derive_stream_name() {
local uri="$1"
local body="${uri#cvmmap://}"
local instance="${body%%@*}"
instance="${instance%%\?*}"
instance="${instance%%/*}"
if [[ -z "$instance" || "$instance" == "$uri" ]]; then
instance="stream"
fi
instance="${instance//[^a-zA-Z0-9_-]/_}"
printf '%s_live' "$instance"
}
wait_for_http() {
local url="$1"
local attempts="$2"
for _ in $(seq 1 "$attempts"); do
if "${CURL_BIN}" -fsS "$url" >/dev/null 2>&1; then
return 0
fi
sleep 0.25
done
return 1
}
wait_for_http_flv() {
local attempts="$1"
for _ in $(seq 1 "$attempts"); do
if "${FFPROBE_BIN}" \
-v error \
-select_streams v:0 \
-show_entries stream=index,codec_name,width,height,avg_frame_rate \
-of default=noprint_wrappers=1 \
"$HTTP_FLV_URL" >"$HTTP_PROBE_LOG" 2>&1; then
return 0
fi
if [[ -n "$STREAMER_PID" ]] && ! kill -0 "$STREAMER_PID" >/dev/null 2>&1; then
return 1
fi
sleep 0.25
done
return 1
}
print_failure_context() {
echo "--- artifacts ---" >&2
echo "RUN_DIR=$RUN_DIR" >&2
echo "--- streamer log tail ---" >&2
tail -n 120 "$STREAMER_LOG" >&2 || true
echo "--- srs log tail ---" >&2
tail -n 120 "$SRS_LOG" >&2 || true
}
cleanup() {
if [[ -n "$STREAMER_PID" ]] && kill -0 "$STREAMER_PID" >/dev/null 2>&1; then
kill "$STREAMER_PID" >/dev/null 2>&1 || true
wait "$STREAMER_PID" >/dev/null 2>&1 || true
fi
if [[ "$STARTED_SRS" -eq 1 ]] && [[ -n "$SRS_PID" ]] && kill -0 "$SRS_PID" >/dev/null 2>&1; then
kill "$SRS_PID" >/dev/null 2>&1 || true
wait "$SRS_PID" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
require_bin "$STREAMER_BIN" "streamer binary"
require_bin "$SRS_BIN" "SRS binary"
require_bin "$FFPROBE_BIN" "ffprobe"
require_bin "$FFMPEG_BIN" "ffmpeg"
require_bin "$CURL_BIN" "curl"
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
if [[ -n "${1:-}" ]]; then
INPUT_URI="$1"
fi
if [[ -n "${2:-}" ]]; then
STREAM_NAME="$2"
fi
if [[ -z "$INPUT_URI" ]]; then
echo "input URI is required" >&2
usage >&2
exit 1
fi
if [[ -z "$STREAM_NAME" ]]; then
STREAM_NAME="$(derive_stream_name "$INPUT_URI")"
fi
RTMP_URL="rtmp://127.0.0.1/live/${STREAM_NAME}"
HTTP_FLV_URL="http://127.0.0.1:8080/live/${STREAM_NAME}.flv"
if [[ ! -f "$SRS_CONF" ]]; then
echo "SRS config not found: $SRS_CONF" >&2
exit 1
fi
if wait_for_http "http://127.0.0.1:1985/api/v1/versions" 4; then
echo "Reusing running SRS on 127.0.0.1:1935"
else
sed \
-e 's/^daemon[[:space:]]\+on;/daemon off;/' \
-e "s/^max_connections[[:space:]]\\+[0-9]\\+;/max_connections ${SRS_MAX_CONNECTIONS};/" \
"$SRS_CONF" >"$SRS_TEMP_CONF"
(
cd "$SRS_ROOT/trunk"
"$SRS_BIN" -c "$SRS_TEMP_CONF" >"$SRS_LOG" 2>&1 &
echo $! >"$RUN_DIR/srs.pid"
)
SRS_PID="$(cat "$RUN_DIR/srs.pid")"
STARTED_SRS=1
if ! wait_for_http "http://127.0.0.1:1985/api/v1/versions" 40; then
echo "SRS failed to become ready on 127.0.0.1:1985" >&2
print_failure_context
exit 1
fi
fi
"$STREAMER_BIN" \
--run-mode pipeline \
--input-uri "$INPUT_URI" \
--codec "$CODEC" \
--encoder-backend "$ENCODER_BACKEND" \
--encoder-device "$ENCODER_DEVICE" \
--rtmp \
--rtmp-url "$RTMP_URL" \
--rtmp-transport "$RTMP_TRANSPORT" \
--rtmp-mode "$RTMP_MODE" \
--ingest-max-frames "$INGEST_MAX_FRAMES" \
>"$STREAMER_LOG" 2>&1 &
STREAMER_PID=$!
if ! wait_for_http_flv $((PROBE_TIMEOUT_S * 4)); then
echo "HTTP-FLV stream never became probeable: $HTTP_FLV_URL" >&2
print_failure_context
exit 1
fi
if ! "$FFMPEG_BIN" -v error -i "$HTTP_FLV_URL" -an -frames:v "$DECODE_FRAMES" -f null - >"$DECODE_LOG" 2>&1; then
echo "warning: HTTP-FLV decode sample failed, see $DECODE_LOG" >&2
fi
"$CURL_BIN" -fsSL "http://127.0.0.1:1985/api/v1/streams/" >"$API_LOG" 2>/dev/null || true
wait "$STREAMER_PID"
STREAMER_RC=$?
STREAMER_PID=""
echo "RUN_DIR=$RUN_DIR"
echo "STREAMER_RC=$STREAMER_RC"
echo "INPUT_URI=$INPUT_URI"
echo "RTMP_URL=$RTMP_URL"
echo "HTTP_FLV_URL=$HTTP_FLV_URL"
echo "--- http-flv ffprobe ---"
cat "$HTTP_PROBE_LOG"
echo "--- http-flv decode log ---"
cat "$DECODE_LOG"
echo "--- srs api streams ---"
cat "$API_LOG" 2>/dev/null || true
echo "--- streamer log tail ---"
tail -n 80 "$STREAMER_LOG"