From cc57ae65afb79ab262434ee8fdb549d84f04ce44 Mon Sep 17 00:00:00 2001 From: Meghdad Date: Fri, 15 May 2026 21:19:58 +0330 Subject: [PATCH] update rtmp-client --- rtmp-client.ps1 | 167 +++++++++++++++++++++++---------------------- rtmp-client.sh | 176 +++++++++++++++++++++++------------------------- 2 files changed, 171 insertions(+), 172 deletions(-) diff --git a/rtmp-client.ps1 b/rtmp-client.ps1 index 45070dd..afdadf9 100644 --- a/rtmp-client.ps1 +++ b/rtmp-client.ps1 @@ -8,17 +8,7 @@ param( [Parameter(Mandatory = $true)] [string[]]$Files, - [switch]$Loop, - - [string]$Preset = "veryfast", - - [string]$VideoBitrate = "2500k", - - [string]$VideoBufsize = "5000k", - - [string]$AudioBitrate = "128k", - - [int]$KeyframeInterval = 60 + [switch]$Loop ) $ErrorActionPreference = "Stop" @@ -34,71 +24,21 @@ function Write-Log { Write-Host "[$time] $Message" } -function Test-Ffmpeg { +function Test-FFmpeg { try { Get-Command ffmpeg -ErrorAction Stop | Out-Null } catch { - Write-Error "ffmpeg is not installed or not in PATH." + Write-Error "ffmpeg not found in PATH." 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 ################################################## -Test-Ffmpeg +Test-FFmpeg foreach ($file in $Files) { 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 { - do { - foreach ($file in $Files) { - $exitCode = Stream-File ` - -File $file ` - -TargetUrl $targetUrl + Write-Log "Starting stream..." - if ($exitCode -ne 0) { - Write-Log "ffmpeg exited with code $exitCode" - } - } - - } while ($Loop) + Start-Process ` + -FilePath "ffmpeg" ` + -ArgumentList $arguments ` + -NoNewWindow ` + -Wait } -catch { - Write-Log "Streaming interrupted." +finally { + + if (Test-Path $playlistFile) { + Remove-Item $playlistFile -Force + } } Write-Log "Finished." diff --git a/rtmp-client.sh b/rtmp-client.sh index 5c55cd2..50c2e7e 100644 --- a/rtmp-client.sh +++ b/rtmp-client.sh @@ -3,7 +3,7 @@ set -euo pipefail ######################################## -# Simple RTMP Streaming Client +# RTMP Playlist Streamer (Single Session) # Requires: ffmpeg ######################################## @@ -11,14 +11,14 @@ RTMP_URL="" STREAM_KEY="" LOOP=false +FILES=() + VIDEO_BITRATE="2500k" VIDEO_BUFSIZE="5000k" AUDIO_BITRATE="128k" PRESET="veryfast" KEYFRAME_INTERVAL="60" -FILES=() - ######################################## # Helpers ######################################## @@ -29,34 +29,15 @@ Usage: $0 --rtmp-url URL --stream-key KEY --files file1.mp4 [file2.mp4 ...] Options: - --rtmp-url RTMP server URL - Example: rtmp://server/live - + --rtmp-url Example: rtmp://server/live --stream-key Stream key - --files One or more video files - - --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 + --loop Infinite playlist loop Example: $0 \ - --rtmp-url rtmp://live.example.com/app \ - --stream-key abc123 \ + --rtmp-url rtmp://server/live \ + --stream-key mystream \ --files intro.mp4 movie.mp4 \ --loop EOF @@ -67,7 +48,9 @@ log() { } cleanup() { - log "Stopping stream..." + log "Cleaning up..." + + [[ -f "$PLAYLIST_FILE" ]] && rm -f "$PLAYLIST_FILE" if [[ -n "${FFMPEG_PID:-}" ]]; then kill "$FFMPEG_PID" 2>/dev/null || true @@ -106,31 +89,6 @@ while [[ $# -gt 0 ]]; do 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) usage exit 0 @@ -182,54 +140,88 @@ done 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#/}" -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 \ - -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 "$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" & + # Real-time playback + -re - FFMPEG_PID=$! - wait "$FFMPEG_PID" -} + # Concat playlist + -f concat + -safe 0 + -i "$PLAYLIST_FILE" -while true; do - for file in "${FILES[@]}"; do - stream_file "$file" - done + # Video + -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" - if [[ "$LOOP" != true ]]; then - break - fi -done + # Audio + -c:a aac + -b:a "$AUDIO_BITRATE" + -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