diff options
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | etc/nginx/modules-available/rtmp.conf | 48 | ||||
| -rw-r--r-- | etc/nginx/nginx.conf | 87 | ||||
| -rw-r--r-- | etc/systemd/system/obsproxy.service | 2 | ||||
| -rwxr-xr-x | opt/obsproxy/server.py | 9 | ||||
| -rwxr-xr-x | push.sh | 19 |
6 files changed, 171 insertions, 7 deletions
@@ -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) @@ -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 |
