update rtmp-client
This commit is contained in:
167
rtmp-client.ps1
167
rtmp-client.ps1
@@ -8,17 +8,7 @@ param(
|
|||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string[]]$Files,
|
[string[]]$Files,
|
||||||
|
|
||||||
[switch]$Loop,
|
[switch]$Loop
|
||||||
|
|
||||||
[string]$Preset = "veryfast",
|
|
||||||
|
|
||||||
[string]$VideoBitrate = "2500k",
|
|
||||||
|
|
||||||
[string]$VideoBufsize = "5000k",
|
|
||||||
|
|
||||||
[string]$AudioBitrate = "128k",
|
|
||||||
|
|
||||||
[int]$KeyframeInterval = 60
|
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
@@ -34,71 +24,21 @@ function Write-Log {
|
|||||||
Write-Host "[$time] $Message"
|
Write-Host "[$time] $Message"
|
||||||
}
|
}
|
||||||
|
|
||||||
function Test-Ffmpeg {
|
function Test-FFmpeg {
|
||||||
try {
|
try {
|
||||||
Get-Command ffmpeg -ErrorAction Stop | Out-Null
|
Get-Command ffmpeg -ErrorAction Stop | Out-Null
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Error "ffmpeg is not installed or not in PATH."
|
Write-Error "ffmpeg not found in PATH."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Build-TargetUrl {
|
|
||||||
$base = $RtmpUrl.TrimEnd('/')
|
|
||||||
$key = $StreamKey.TrimStart('/')
|
|
||||||
|
|
||||||
return "$base/$key"
|
|
||||||
}
|
|
||||||
|
|
||||||
function Stream-File {
|
|
||||||
param(
|
|
||||||
[string]$File,
|
|
||||||
[string]$TargetUrl
|
|
||||||
)
|
|
||||||
|
|
||||||
Write-Log "Streaming file: $File"
|
|
||||||
|
|
||||||
$arguments = @(
|
|
||||||
"-hide_banner"
|
|
||||||
"-loglevel", "info"
|
|
||||||
"-re"
|
|
||||||
"-i", $File
|
|
||||||
"-map", "0:v:0"
|
|
||||||
"-map", "0:a?"
|
|
||||||
"-c:v", "libx264"
|
|
||||||
"-preset", $Preset
|
|
||||||
"-tune", "zerolatency"
|
|
||||||
"-pix_fmt", "yuv420p"
|
|
||||||
"-profile:v", "baseline"
|
|
||||||
"-level", "3.1"
|
|
||||||
"-b:v", $VideoBitrate
|
|
||||||
"-maxrate", $VideoBitrate
|
|
||||||
"-bufsize", $VideoBufsize
|
|
||||||
"-g", $KeyframeInterval
|
|
||||||
"-c:a", "aac"
|
|
||||||
"-b:a", $AudioBitrate
|
|
||||||
"-ar", "44100"
|
|
||||||
"-ac", "2"
|
|
||||||
"-f", "flv"
|
|
||||||
$TargetUrl
|
|
||||||
)
|
|
||||||
|
|
||||||
$process = Start-Process `
|
|
||||||
-FilePath "ffmpeg" `
|
|
||||||
-ArgumentList $arguments `
|
|
||||||
-NoNewWindow `
|
|
||||||
-PassThru `
|
|
||||||
-Wait
|
|
||||||
|
|
||||||
return $process.ExitCode
|
|
||||||
}
|
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
# Validation
|
# Validation
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
Test-Ffmpeg
|
Test-FFmpeg
|
||||||
|
|
||||||
foreach ($file in $Files) {
|
foreach ($file in $Files) {
|
||||||
if (-not (Test-Path $file)) {
|
if (-not (Test-Path $file)) {
|
||||||
@@ -108,31 +48,98 @@ foreach ($file in $Files) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
# Main
|
# Build playlist
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
$targetUrl = Build-TargetUrl
|
$playlistFile = Join-Path $env:TEMP "ffmpeg_playlist.txt"
|
||||||
|
|
||||||
Write-Log "RTMP target: $targetUrl"
|
if (Test-Path $playlistFile) {
|
||||||
|
Remove-Item $playlistFile -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($file in $Files) {
|
||||||
|
|
||||||
|
$fullPath = (Resolve-Path $file).Path.Replace("'", "''")
|
||||||
|
|
||||||
|
Add-Content -Path $playlistFile -Value "file '$fullPath'"
|
||||||
|
}
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# RTMP URL
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
$targetUrl = "$($RtmpUrl.TrimEnd('/'))/$($StreamKey.TrimStart('/'))"
|
||||||
|
|
||||||
|
Write-Log "RTMP Target: $targetUrl"
|
||||||
|
Write-Log "Playlist: $playlistFile"
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# FFmpeg args
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
$arguments = @(
|
||||||
|
"-hide_banner"
|
||||||
|
"-loglevel", "info"
|
||||||
|
|
||||||
|
# Real-time mode
|
||||||
|
"-re"
|
||||||
|
|
||||||
|
# Infinite loop playlist
|
||||||
|
"-stream_loop", "-1"
|
||||||
|
|
||||||
|
# Concat playlist
|
||||||
|
"-f", "concat"
|
||||||
|
"-safe", "0"
|
||||||
|
"-i", $playlistFile
|
||||||
|
|
||||||
|
# Video
|
||||||
|
"-c:v", "libx264"
|
||||||
|
"-preset", "veryfast"
|
||||||
|
"-tune", "zerolatency"
|
||||||
|
"-pix_fmt", "yuv420p"
|
||||||
|
"-profile:v", "baseline"
|
||||||
|
"-level", "3.1"
|
||||||
|
"-b:v", "2500k"
|
||||||
|
"-maxrate", "2500k"
|
||||||
|
"-bufsize", "5000k"
|
||||||
|
"-g", "60"
|
||||||
|
|
||||||
|
# Audio
|
||||||
|
"-c:a", "aac"
|
||||||
|
"-b:a", "128k"
|
||||||
|
"-ar", "44100"
|
||||||
|
"-ac", "2"
|
||||||
|
|
||||||
|
# Output
|
||||||
|
"-f", "flv"
|
||||||
|
$targetUrl
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not $Loop) {
|
||||||
|
# Remove infinite loop if user does not want looping
|
||||||
|
$arguments = $arguments | Where-Object { $_ -ne "-1" }
|
||||||
|
}
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# Run ffmpeg
|
||||||
|
##################################################
|
||||||
|
|
||||||
try {
|
try {
|
||||||
do {
|
|
||||||
foreach ($file in $Files) {
|
|
||||||
|
|
||||||
$exitCode = Stream-File `
|
Write-Log "Starting stream..."
|
||||||
-File $file `
|
|
||||||
-TargetUrl $targetUrl
|
|
||||||
|
|
||||||
if ($exitCode -ne 0) {
|
Start-Process `
|
||||||
Write-Log "ffmpeg exited with code $exitCode"
|
-FilePath "ffmpeg" `
|
||||||
}
|
-ArgumentList $arguments `
|
||||||
}
|
-NoNewWindow `
|
||||||
|
-Wait
|
||||||
} while ($Loop)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch {
|
finally {
|
||||||
Write-Log "Streaming interrupted."
|
|
||||||
|
if (Test-Path $playlistFile) {
|
||||||
|
Remove-Item $playlistFile -Force
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Log "Finished."
|
Write-Log "Finished."
|
||||||
|
|||||||
176
rtmp-client.sh
176
rtmp-client.sh
@@ -3,7 +3,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# Simple RTMP Streaming Client
|
# RTMP Playlist Streamer (Single Session)
|
||||||
# Requires: ffmpeg
|
# Requires: ffmpeg
|
||||||
########################################
|
########################################
|
||||||
|
|
||||||
@@ -11,14 +11,14 @@ RTMP_URL=""
|
|||||||
STREAM_KEY=""
|
STREAM_KEY=""
|
||||||
LOOP=false
|
LOOP=false
|
||||||
|
|
||||||
|
FILES=()
|
||||||
|
|
||||||
VIDEO_BITRATE="2500k"
|
VIDEO_BITRATE="2500k"
|
||||||
VIDEO_BUFSIZE="5000k"
|
VIDEO_BUFSIZE="5000k"
|
||||||
AUDIO_BITRATE="128k"
|
AUDIO_BITRATE="128k"
|
||||||
PRESET="veryfast"
|
PRESET="veryfast"
|
||||||
KEYFRAME_INTERVAL="60"
|
KEYFRAME_INTERVAL="60"
|
||||||
|
|
||||||
FILES=()
|
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# Helpers
|
# Helpers
|
||||||
########################################
|
########################################
|
||||||
@@ -29,34 +29,15 @@ Usage:
|
|||||||
$0 --rtmp-url URL --stream-key KEY --files file1.mp4 [file2.mp4 ...]
|
$0 --rtmp-url URL --stream-key KEY --files file1.mp4 [file2.mp4 ...]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--rtmp-url RTMP server URL
|
--rtmp-url Example: rtmp://server/live
|
||||||
Example: rtmp://server/live
|
|
||||||
|
|
||||||
--stream-key Stream key
|
--stream-key Stream key
|
||||||
|
|
||||||
--files One or more video files
|
--files One or more video files
|
||||||
|
--loop Infinite playlist loop
|
||||||
--loop Loop playlist forever
|
|
||||||
|
|
||||||
--preset x264 preset
|
|
||||||
Default: veryfast
|
|
||||||
|
|
||||||
--video-bitrate Video bitrate
|
|
||||||
Default: 2500k
|
|
||||||
|
|
||||||
--video-bufsize Video buffer size
|
|
||||||
Default: 5000k
|
|
||||||
|
|
||||||
--audio-bitrate Audio bitrate
|
|
||||||
Default: 128k
|
|
||||||
|
|
||||||
--keyframe-interval GOP size
|
|
||||||
Default: 60
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
$0 \
|
$0 \
|
||||||
--rtmp-url rtmp://live.example.com/app \
|
--rtmp-url rtmp://server/live \
|
||||||
--stream-key abc123 \
|
--stream-key mystream \
|
||||||
--files intro.mp4 movie.mp4 \
|
--files intro.mp4 movie.mp4 \
|
||||||
--loop
|
--loop
|
||||||
EOF
|
EOF
|
||||||
@@ -67,7 +48,9 @@ log() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
log "Stopping stream..."
|
log "Cleaning up..."
|
||||||
|
|
||||||
|
[[ -f "$PLAYLIST_FILE" ]] && rm -f "$PLAYLIST_FILE"
|
||||||
|
|
||||||
if [[ -n "${FFMPEG_PID:-}" ]]; then
|
if [[ -n "${FFMPEG_PID:-}" ]]; then
|
||||||
kill "$FFMPEG_PID" 2>/dev/null || true
|
kill "$FFMPEG_PID" 2>/dev/null || true
|
||||||
@@ -106,31 +89,6 @@ while [[ $# -gt 0 ]]; do
|
|||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--preset)
|
|
||||||
PRESET="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
|
|
||||||
--video-bitrate)
|
|
||||||
VIDEO_BITRATE="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
|
|
||||||
--video-bufsize)
|
|
||||||
VIDEO_BUFSIZE="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
|
|
||||||
--audio-bitrate)
|
|
||||||
AUDIO_BITRATE="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
|
|
||||||
--keyframe-interval)
|
|
||||||
KEYFRAME_INTERVAL="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
|
|
||||||
-h|--help)
|
-h|--help)
|
||||||
usage
|
usage
|
||||||
exit 0
|
exit 0
|
||||||
@@ -182,54 +140,88 @@ done
|
|||||||
trap cleanup SIGINT SIGTERM
|
trap cleanup SIGINT SIGTERM
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# Main
|
# Build playlist
|
||||||
|
########################################
|
||||||
|
|
||||||
|
PLAYLIST_FILE="$(mktemp)"
|
||||||
|
|
||||||
|
for file in "${FILES[@]}"; do
|
||||||
|
ABS_PATH="$(realpath "$file")"
|
||||||
|
|
||||||
|
# Escape single quotes
|
||||||
|
ESCAPED_PATH="${ABS_PATH//\'/\'\\\'\'}"
|
||||||
|
|
||||||
|
echo "file '$ESCAPED_PATH'" >> "$PLAYLIST_FILE"
|
||||||
|
done
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Build target URL
|
||||||
########################################
|
########################################
|
||||||
|
|
||||||
TARGET_URL="${RTMP_URL%/}/${STREAM_KEY#/}"
|
TARGET_URL="${RTMP_URL%/}/${STREAM_KEY#/}"
|
||||||
|
|
||||||
log "RTMP target: $TARGET_URL"
|
log "RTMP Target: $TARGET_URL"
|
||||||
|
log "Playlist File: $PLAYLIST_FILE"
|
||||||
|
|
||||||
stream_file() {
|
########################################
|
||||||
local file="$1"
|
# FFmpeg command
|
||||||
|
########################################
|
||||||
|
|
||||||
log "Streaming file: $file"
|
FFMPEG_ARGS=(
|
||||||
|
-hide_banner
|
||||||
|
-loglevel info
|
||||||
|
|
||||||
ffmpeg \
|
# Real-time playback
|
||||||
-hide_banner \
|
-re
|
||||||
-loglevel info \
|
|
||||||
-re \
|
|
||||||
-i "$file" \
|
|
||||||
-map 0:v:0 \
|
|
||||||
-map 0:a? \
|
|
||||||
-c:v libx264 \
|
|
||||||
-preset "$PRESET" \
|
|
||||||
-tune zerolatency \
|
|
||||||
-pix_fmt yuv420p \
|
|
||||||
-profile:v baseline \
|
|
||||||
-level 3.1 \
|
|
||||||
-b:v "$VIDEO_BITRATE" \
|
|
||||||
-maxrate "$VIDEO_BITRATE" \
|
|
||||||
-bufsize "$VIDEO_BUFSIZE" \
|
|
||||||
-g "$KEYFRAME_INTERVAL" \
|
|
||||||
-c:a aac \
|
|
||||||
-b:a "$AUDIO_BITRATE" \
|
|
||||||
-ar 44100 \
|
|
||||||
-ac 2 \
|
|
||||||
-f flv \
|
|
||||||
"$TARGET_URL" &
|
|
||||||
|
|
||||||
FFMPEG_PID=$!
|
# Concat playlist
|
||||||
wait "$FFMPEG_PID"
|
-f concat
|
||||||
}
|
-safe 0
|
||||||
|
-i "$PLAYLIST_FILE"
|
||||||
|
|
||||||
while true; do
|
# Video
|
||||||
for file in "${FILES[@]}"; do
|
-c:v libx264
|
||||||
stream_file "$file"
|
-preset "$PRESET"
|
||||||
done
|
-tune zerolatency
|
||||||
|
-pix_fmt yuv420p
|
||||||
|
-profile:v baseline
|
||||||
|
-level 3.1
|
||||||
|
-b:v "$VIDEO_BITRATE"
|
||||||
|
-maxrate "$VIDEO_BITRATE"
|
||||||
|
-bufsize "$VIDEO_BUFSIZE"
|
||||||
|
-g "$KEYFRAME_INTERVAL"
|
||||||
|
|
||||||
if [[ "$LOOP" != true ]]; then
|
# Audio
|
||||||
break
|
-c:a aac
|
||||||
fi
|
-b:a "$AUDIO_BITRATE"
|
||||||
done
|
-ar 44100
|
||||||
|
-ac 2
|
||||||
|
|
||||||
log "Finished."
|
# Output
|
||||||
|
-f flv
|
||||||
|
"$TARGET_URL"
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Infinite loop
|
||||||
|
########################################
|
||||||
|
|
||||||
|
if [[ "$LOOP" == true ]]; then
|
||||||
|
FFMPEG_ARGS=(
|
||||||
|
-stream_loop -1
|
||||||
|
"${FFMPEG_ARGS[@]}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Start streaming
|
||||||
|
########################################
|
||||||
|
|
||||||
|
log "Starting stream..."
|
||||||
|
|
||||||
|
ffmpeg "${FFMPEG_ARGS[@]}" &
|
||||||
|
FFMPEG_PID=$!
|
||||||
|
|
||||||
|
wait "$FFMPEG_PID"
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
|||||||
Reference in New Issue
Block a user