Skip to content

Instantly share code, notes, and snippets.

@ensean
Last active December 5, 2025 03:48
Show Gist options
  • Select an option

  • Save ensean/9be8fc6c8df1bfa28a3d22c19ec1ee0a to your computer and use it in GitHub Desktop.

Select an option

Save ensean/9be8fc6c8df1bfa28a3d22c19ec1ee0a to your computer and use it in GitHub Desktop.
SRS dynamic forwarding to amazon IVS guide

Arch

image

Setup guide

  1. checkout SRS and build it from source,
# Amazon Linux 2023
sudo yum groupinstall "Development Tools" -y
sudo yum install tcl cmake -y

git clone https://github.com/ossrs/srs.git
cd srs
git fetch --tags
git checkout v6.0.48
cd trunk
./configure
make

  1. revise the forward targets in the mock_backend.py file, start the mock backend api server
python3 mock_backend.py
  1. start the srs server
./objs/srs -c ./conf/forward.backend-mock.conf
  1. streaming to the SRS rtmp endpoint via ffmpeg or OBS, the rtmp url should be rtmp://srs-server-ip/live/stream. Please note that their is no restricion about the live and stream parameter

  2. check SRS logs, check live stream via IVS console

Refs

  1. SRS dynamic forwarding
  2. SRS RTMP URL
#!/usr/bin/env python3
"""
Mock Forward Backend Server for SRS Dynamic Forward
This server simulates the backend API that SRS calls to get dynamic forward destinations.
When SRS publishes a stream, it will call this server to get the list of RTMP URLs to forward to.
Usage:
python3 mock_forward_backend.py [port]
SRS Configuration:
vhost __defaultVhost__ {
forward {
enabled on;
backend http://127.0.0.1:8088/api/v1/forward;
}
}
Request from SRS (POST JSON):
{
"action": "on_forward",
"server_id": "...",
"service_id": "...",
"client_id": "...",
"ip": "192.168.1.100",
"vhost": "__defaultVhost__",
"app": "live",
"tcUrl": "rtmp://192.168.1.10/live",
"stream": "livestream",
"param": ""
}
Response (JSON):
{
"code": 0,
"data": {
"urls": [
"rtmp://server1:1935/live/stream1",
"rtmp://server2:1935/live/stream2"
]
}
}
"""
import json
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
from datetime import datetime
# Configure your forward destinations here
# Set via environment variable or edit directly
import os
# Get IVS endpoint from environment variable, or use default test destination
IVS_ENDPOINT = os.environ.get("IVS_ENDPOINT", "")
FORWARD_DESTINATIONS = {
# Default destinations for any stream
"default": [
# Add your destinations here, examples:
# "rtmp://backup-server:1935/live/backup_stream",
"rtmp://6127d8a38bxx.global-contribute.live-video.net/app/sk_ap-northeast-1_zMTV2vEM3xxxxxx",
"rtmp://6127d8a38axx.global-contribute.live-video.net/app/sk_ap-northeast-1_sFCvxmYAdyyyyyy",
],
# Stream-specific destinations (app/stream -> destinations)
# "live/important_stream": [
# "rtmp://primary:1935/live/main",
# ],
}
# If IVS_ENDPOINT is set, add it to default destinations
if IVS_ENDPOINT:
FORWARD_DESTINATIONS["default"].append(IVS_ENDPOINT)
print(f"[Config] Added IVS endpoint: {IVS_ENDPOINT[:50]}...")
class ForwardBackendHandler(BaseHTTPRequestHandler):
"""HTTP request handler for SRS forward backend API"""
def log_message(self, format, *args):
"""Custom log format with timestamp"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] {self.address_string()} - {format % args}")
def send_json_response(self, code, data):
"""Send JSON response to client"""
response = json.dumps(data, indent=2)
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", len(response))
self.end_headers()
self.wfile.write(response.encode("utf-8"))
def do_POST(self):
"""Handle POST request from SRS"""
# Read request body
content_length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_length).decode("utf-8")
print(f"\n{'='*60}")
print(f"Received request: {self.path}")
print(f"Body: {body}")
try:
req = json.loads(body) if body else {}
except json.JSONDecodeError:
req = {}
# Handle forward backend request
if self.path == "/api/v1/forward":
self.handle_forward(req)
else:
# Unknown endpoint
self.send_json_response(404, {"code": 404, "msg": "Not Found"})
def handle_forward(self, req):
"""Handle on_forward request from SRS"""
# dynamic forwarding targets can be determined by the requesting parameters, like app, stream and vhost etc.
action = req.get("action", "")
app = req.get("app", "live")
stream = req.get("stream", "")
vhost = req.get("vhost", "__defaultVhost__")
client_ip = req.get("ip", "unknown")
print(f"\n[Forward Request]")
print(f" Action: {action}")
print(f" Client IP: {client_ip}")
print(f" Vhost: {vhost}")
print(f" App: {app}")
print(f" Stream: {stream}")
# Determine forward destinations
stream_key = f"{app}/{stream}"
if stream_key in FORWARD_DESTINATIONS:
urls = FORWARD_DESTINATIONS[stream_key]
print(f" Using stream-specific destinations for '{stream_key}'")
else:
urls = FORWARD_DESTINATIONS.get("default", [])
print(f" Using default destinations")
# Send response
response = {
"code": 0,
"data": {
"urls": urls
}
}
self.send_json_response(200, response)
print(f"{'='*60}\n")
def do_GET(self):
"""Handle GET request - for health check"""
if self.path == "/health":
self.send_json_response(200, {"status": "ok"})
else:
# Return simple status page
html = """
<!DOCTYPE html>
<html>
<head><title>SRS Forward Backend Mock Server</title></head>
<body>
<h1>SRS Forward Backend Mock Server</h1>
<p>This server handles dynamic forward requests from SRS.</p>
<h2>API Endpoint</h2>
<pre>POST /api/v1/forward</pre>
<h2>Current Forward Destinations</h2>
<pre>%s</pre>
<h2>SRS Configuration Example</h2>
<pre>
vhost __defaultVhost__ {
forward {
enabled on;
backend http://127.0.0.1:%d/api/v1/forward;
}
}
</pre>
</body>
</html>
""" % (json.dumps(FORWARD_DESTINATIONS, indent=2), PORT)
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(html.encode("utf-8"))
def main():
global PORT
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8088
server = HTTPServer(("0.0.0.0", PORT), ForwardBackendHandler)
print(f"""
╔══════════════════════════════════════════════════════════════╗
║ SRS Forward Backend Mock Server ║
╠══════════════════════════════════════════════════════════════╣
║ Listening on: http://0.0.0.0:{PORT:<5} ║
║ API Endpoint: POST /api/v1/forward ║
║ Health Check: GET /health ║
╠══════════════════════════════════════════════════════════════╣
║ SRS Config: ║
║ forward {{ ║
║ enabled on; ║
║ backend http://127.0.0.1:{PORT}/api/v1/forward; ║
║ }} ║
╚══════════════════════════════════════════════════════════════╝
Edit FORWARD_DESTINATIONS in this script to configure forward URLs.
Waiting for requests...
""")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nShutting down server...")
server.shutdown()
if __name__ == "__main__":
main()
max_connections 1000;
pid ./objs/srs.backend.pid;
daemon off;
srs_log_tank console;
# RTMP server configuration
listen 1935;
vhost __defaultVhost__ {
chunk_size 128;
forward {
enabled on;
backend http://127.0.0.1:8088/api/v1/forward;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment