diff --git a/rtmp-client.ps1 b/rtmp-client.ps1 new file mode 100644 index 0000000..45070dd --- /dev/null +++ b/rtmp-client.ps1 @@ -0,0 +1,138 @@ +param( + [Parameter(Mandatory = $true)] + [string]$RtmpUrl, + + [Parameter(Mandatory = $true)] + [string]$StreamKey, + + [Parameter(Mandatory = $true)] + [string[]]$Files, + + [switch]$Loop, + + [string]$Preset = "veryfast", + + [string]$VideoBitrate = "2500k", + + [string]$VideoBufsize = "5000k", + + [string]$AudioBitrate = "128k", + + [int]$KeyframeInterval = 60 +) + +$ErrorActionPreference = "Stop" + +################################################## +# Helpers +################################################## + +function Write-Log { + param([string]$Message) + + $time = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Host "[$time] $Message" +} + +function Test-Ffmpeg { + try { + Get-Command ffmpeg -ErrorAction Stop | Out-Null + } + catch { + Write-Error "ffmpeg is not installed or not 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 + +foreach ($file in $Files) { + if (-not (Test-Path $file)) { + Write-Error "File not found: $file" + exit 1 + } +} + +################################################## +# Main +################################################## + +$targetUrl = Build-TargetUrl + +Write-Log "RTMP target: $targetUrl" + +try { + do { + foreach ($file in $Files) { + + $exitCode = Stream-File ` + -File $file ` + -TargetUrl $targetUrl + + if ($exitCode -ne 0) { + Write-Log "ffmpeg exited with code $exitCode" + } + } + + } while ($Loop) + +} +catch { + Write-Log "Streaming interrupted." +} + +Write-Log "Finished." diff --git a/rtmp-client.sh b/rtmp-client.sh new file mode 100644 index 0000000..5c55cd2 --- /dev/null +++ b/rtmp-client.sh @@ -0,0 +1,235 @@ +#!/usr/bin/env bash + +set -euo pipefail + +######################################## +# Simple RTMP Streaming Client +# Requires: ffmpeg +######################################## + +RTMP_URL="" +STREAM_KEY="" +LOOP=false + +VIDEO_BITRATE="2500k" +VIDEO_BUFSIZE="5000k" +AUDIO_BITRATE="128k" +PRESET="veryfast" +KEYFRAME_INTERVAL="60" + +FILES=() + +######################################## +# Helpers +######################################## + +usage() { + cat </dev/null || true + fi + + exit 0 +} + +######################################## +# Parse args +######################################## + +while [[ $# -gt 0 ]]; do + case "$1" in + --rtmp-url) + RTMP_URL="$2" + shift 2 + ;; + + --stream-key) + STREAM_KEY="$2" + shift 2 + ;; + + --files) + shift + + while [[ $# -gt 0 && ! "$1" =~ ^-- ]]; do + FILES+=("$1") + shift + done + ;; + + --loop) + LOOP=true + 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 + ;; + + *) + echo "Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +######################################## +# Validation +######################################## + +if ! command -v ffmpeg >/dev/null 2>&1; then + echo "ffmpeg is not installed." + exit 1 +fi + +if [[ -z "$RTMP_URL" ]]; then + echo "--rtmp-url is required" + exit 1 +fi + +if [[ -z "$STREAM_KEY" ]]; then + echo "--stream-key is required" + exit 1 +fi + +if [[ ${#FILES[@]} -eq 0 ]]; then + echo "At least one file is required" + exit 1 +fi + +for file in "${FILES[@]}"; do + if [[ ! -f "$file" ]]; then + echo "File not found: $file" + exit 1 + fi +done + +######################################## +# Signals +######################################## + +trap cleanup SIGINT SIGTERM + +######################################## +# Main +######################################## + +TARGET_URL="${RTMP_URL%/}/${STREAM_KEY#/}" + +log "RTMP target: $TARGET_URL" + +stream_file() { + local file="$1" + + log "Streaming file: $file" + + 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" & + + FFMPEG_PID=$! + wait "$FFMPEG_PID" +} + +while true; do + for file in "${FILES[@]}"; do + stream_file "$file" + done + + if [[ "$LOOP" != true ]]; then + break + fi +done + +log "Finished."