summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md13
-rw-r--r--etc/nginx/modules-available/rtmp.conf48
-rw-r--r--etc/nginx/nginx.conf87
-rw-r--r--etc/systemd/system/obsproxy.service2
-rwxr-xr-xopt/obsproxy/server.py9
-rwxr-xr-xpush.sh19
6 files changed, 171 insertions, 7 deletions
diff --git a/README.md b/README.md
index 6bc954d..5832972 100644
--- a/README.md
+++ b/README.md
@@ -2,18 +2,19 @@ Shitty service to proxy data from OBS into an HTTP Live Streaming (HLS) feed VRC
## Streamer instructions
-1. Configure OBS with a custom server pointing at `rtmp://<your-domain>/live`
- and in the stream server. Enter your stream key.
+1. Configure OBS with a custom server pointing at `rtmps://<your-domain>:1935/live`
+ in the stream settings and enter your stream key.
2. Press stream. Check status on bottom right of obs.
## Dev instructions
-1. Get ssh perms to yummers.dev.
+1. Get ssh perms to yummers.dev. `ssh yummers.dev` should log you in as yum if
+ not, you'll have to update the ssh commands below.
2. Run ./push.sh
3. Add this to your .bashrc:
```bash
function check_live {
- ssh yummers.dev 'sudo systemctl status obsproxy'
+ ssh yummers.dev 'sudo journalctl -u obsproxy -f'
}
function start_live {
ssh yummers.dev 'sudo systemctl start obsproxy'
@@ -38,10 +39,14 @@ function get_live {
3. Start the Python service (see `etc/systemd/system/obsproxy.service` for a sample unit). The bundled entrypoint now runs under [Waitress](https://docs.pylonsproject.org/projects/waitress/en/stable/) so you get a production-grade WSGI server out of the box.
4. When the service starts it prints a session-specific playlist URL like `https://<your-domain>/hls/<session-hex>/stream.m3u8`; share that exact URL with your VRChat video player. Multiple viewers can consume the feed concurrently.
+RTMPS termination happens at nginx (default port `1935`) via the `ngx_stream_module`, which proxies plain RTMP to the local nginx-rtmp listener on `127.0.0.1:1936`. Update the `INGEST_RTMP_*` settings if you run the backend elsewhere, and make sure the stream module is installed/enabled (on Debian/Ubuntu install `libnginx-mod-stream`).
+
Environmental knobs:
- `OBS_STREAM_KEY` / `STREAM_PSK`: required PSK for the single ingest client.
- `INGEST_THREAD_QUEUE_SIZE`: RTMP demux queue depth before frames are dropped (defaults to `4096`).
+- `INGEST_RTMP_HOST`: host FFmpeg pulls RTMP from (defaults to `127.0.0.1`).
+- `INGEST_RTMP_PORT`: port FFmpeg pulls RTMP from (defaults to `1936`).
- `HLS_SEGMENT_TIME`: length (in seconds) of the `.ts` segments emitted by FFmpeg (defaults to `2`).
- `HLS_PLAYLIST_SIZE`: number of segments retained in the rolling playlist (defaults to `6`).
- `HLS_DELETE_THRESHOLD`: additional historical HLS segments to keep on disk before pruning (defaults to `2`).
diff --git a/etc/nginx/modules-available/rtmp.conf b/etc/nginx/modules-available/rtmp.conf
new file mode 100644
index 0000000..2e852a1
--- /dev/null
+++ b/etc/nginx/modules-available/rtmp.conf
@@ -0,0 +1,48 @@
+# RTMP ingest pipeline with TLS termination via the stream module.
+# - External publishers connect over RTMPS on tcp/1935.
+# - The stream module terminates TLS and forwards plain RTMP to nginx-rtmp on 127.0.0.1:1936.
+# - nginx-rtmp still triggers publish callbacks consumed by obsproxy.
+
+rtmp {
+ server {
+ listen 1936; # internal plain RTMP listener
+ chunk_size 4096;
+
+ application live {
+ live on;
+ record off;
+
+ # Allow publish/play; obsproxy enforces the ingest PSK.
+ allow publish all;
+ allow play all;
+
+ on_publish http://127.0.0.1:5000/rtmp_callbacks/on_publish;
+ on_publish_done http://127.0.0.1:5000/rtmp_callbacks/on_publish_done;
+ }
+ }
+}
+
+stream {
+ log_format stream_basic '$remote_addr:$remote_port -> $server_addr:$server_port '
+ 'sent=$bytes_sent received=$bytes_received '
+ 'time=$session_time';
+
+ upstream rtmp_backend {
+ server 127.0.0.1:1936;
+ }
+
+ server {
+ listen 1935 ssl;
+ proxy_pass rtmp_backend;
+ access_log /var/log/nginx/rtmp_stream_access.log stream_basic;
+ error_log /var/log/nginx/rtmp_stream_error.log debug;
+
+ ssl_certificate /etc/letsencrypt/live/yummers.dev/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/yummers.dev/privkey.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+ ssl_session_cache shared:rtmp_stream_cache:10m;
+ ssl_session_timeout 10m;
+ proxy_timeout 5m;
+ }
+}
diff --git a/etc/nginx/nginx.conf b/etc/nginx/nginx.conf
new file mode 100644
index 0000000..3eab3a5
--- /dev/null
+++ b/etc/nginx/nginx.conf
@@ -0,0 +1,87 @@
+user www-data;
+worker_processes auto;
+pid /run/nginx.pid;
+error_log /var/log/nginx/error.log;
+include /etc/nginx/modules-enabled/*.conf;
+
+events {
+ worker_connections 768;
+ # multi_accept on;
+}
+
+http {
+
+ ##
+ # Basic Settings
+ ##
+
+ sendfile on;
+ tcp_nopush on;
+ types_hash_max_size 2048;
+ # server_tokens off;
+
+ # server_names_hash_bucket_size 64;
+ # server_name_in_redirect off;
+
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ ##
+ # SSL Settings
+ ##
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
+ ssl_prefer_server_ciphers on;
+
+ ##
+ # Logging Settings
+ ##
+
+ access_log /var/log/nginx/access.log;
+
+ ##
+ # Gzip Settings
+ ##
+
+ # If a .gz version of any given file exists, serve it instead.
+ gzip on;
+
+ # gzip_vary on;
+ # gzip_proxied any;
+ # gzip_comp_level 6;
+ # gzip_buffers 16 8k;
+ # gzip_http_version 1.1;
+ # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
+
+ ##
+ # Virtual Host Configs
+ ##
+
+ include /etc/nginx/conf.d/*.conf;
+ include /etc/nginx/sites-enabled/*;
+
+ # If a .gz suffixed version of a file exists, serve it instead.
+ gzip_static on;
+}
+
+
+#mail {
+# # See sample authentication script at:
+# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
+#
+# # auth_http localhost/auth.php;
+# # pop3_capabilities "TOP" "USER";
+# # imap_capabilities "IMAP4rev1" "UIDPLUS";
+#
+# server {
+# listen localhost:110;
+# protocol pop3;
+# proxy on;
+# }
+#
+# server {
+# listen localhost:143;
+# protocol imap;
+# proxy on;
+# }
+#}
diff --git a/etc/systemd/system/obsproxy.service b/etc/systemd/system/obsproxy.service
index f60177d..b145a17 100644
--- a/etc/systemd/system/obsproxy.service
+++ b/etc/systemd/system/obsproxy.service
@@ -21,6 +21,8 @@ Environment=STREAM_DIR=/var/www/streams
Environment=PORT=5000
Environment=STREAM_PSK=your_pre_shared_key
Environment=LOG_LEVEL=INFO
+Environment=INGEST_RTMP_HOST=127.0.0.1
+Environment=INGEST_RTMP_PORT=1936
# Security settings
NoNewPrivileges=true
diff --git a/opt/obsproxy/server.py b/opt/obsproxy/server.py
index 422b483..0c32979 100755
--- a/opt/obsproxy/server.py
+++ b/opt/obsproxy/server.py
@@ -17,6 +17,11 @@ load_dotenv()
# Configuration
INGEST_PSK = os.environ.get('OBS_STREAM_KEY') or os.environ.get('STREAM_PSK')
+INGEST_RTMP_HOST = os.environ.get('INGEST_RTMP_HOST', '127.0.0.1')
+try:
+ INGEST_RTMP_PORT = int(os.environ.get('INGEST_RTMP_PORT', '1936'))
+except ValueError as exc:
+ raise ValueError('INGEST_RTMP_PORT must be an integer') from exc
INGEST_THREAD_QUEUE_SIZE = int(os.environ.get('INGEST_THREAD_QUEUE_SIZE', '4096'))
HLS_SEGMENT_TIME = float(os.environ.get('HLS_SEGMENT_TIME', '2'))
HLS_PLAYLIST_SIZE = int(os.environ.get('HLS_PLAYLIST_SIZE', '20'))
@@ -81,7 +86,7 @@ def start_ffmpeg_process():
'-loglevel', os.environ.get('FFMPEG_LOGLEVEL', 'warning'),
'-fflags', '+genpts',
'-thread_queue_size', str(INGEST_THREAD_QUEUE_SIZE),
- '-i', f'rtmp://localhost/live/{INGEST_PSK}',
+ '-i', f'rtmp://{INGEST_RTMP_HOST}:{INGEST_RTMP_PORT}/live/{INGEST_PSK}',
'-map', '0:v:0?',
'-map', '0:a:0?',
'-c:v', 'copy',
@@ -237,7 +242,7 @@ def health_check():
def print_instructions():
"""Print usage instructions"""
- obs_url = f"rtmp://{SERVER_DOMAIN}/live"
+ obs_url = f"rtmps://{SERVER_DOMAIN}:1935/live"
hls_url = f"https://{SERVER_DOMAIN}{HLS_ROUTE_PREFIX}/stream.m3u8"
print("\n" + "="*80)
diff --git a/push.sh b/push.sh
index bb49b3c..cba5a07 100755
--- a/push.sh
+++ b/push.sh
@@ -19,7 +19,23 @@ cd ~/obsproxy
# Install files to their final destinations
sudo cp etc/systemd/system/obsproxy.service /etc/systemd/system/
+sudo rm -f /etc/nginx/modules-enabled/stream.conf /etc/nginx/modules-enabled/00-stream.conf
+if ! sudo test -e /etc/nginx/modules-enabled/50-mod-stream.conf && \
+ ! sudo test -e /etc/nginx/modules-enabled/00-stream.conf; then
+ if sudo test -e /usr/lib/nginx/modules/ngx_stream_module.so; then
+ echo "Enabling nginx stream module..."
+ echo 'load_module /usr/lib/nginx/modules/ngx_stream_module.so;' | \
+ sudo tee /etc/nginx/modules-enabled/00-stream.conf >/dev/null
+ else
+ echo "nginx stream module is missing (install libnginx-mod-stream)" >&2
+ exit 1
+ fi
+fi
+
sudo cp etc/nginx/modules-available/rtmp.conf /etc/nginx/modules-available/
+sudo ln -sf /etc/nginx/modules-available/rtmp.conf /etc/nginx/modules-enabled/rtmp.conf
+# Ship the sanitized nginx.conf so only the TLS stream listener owns :1935.
+sudo cp etc/nginx/nginx.conf /etc/nginx/nginx.conf
sudo cp etc/nginx/sites-available/yummers.dev /etc/nginx/sites-available/
sudo ln -sf /etc/nginx/sites-available/yummers.dev /etc/nginx/sites-enabled/yummers.dev
sudo cp opt/obsproxy/server.py /opt/obsproxy/
@@ -32,7 +48,8 @@ sudo systemctl daemon-reload
sudo systemctl restart obsproxy
# Reload nginx
-sudo nginx -t && sudo systemctl reload nginx
+sudo nginx -t
+sudo systemctl reload nginx
echo "Deployment complete!"
EOF