Skip to content

Instantly share code, notes, and snippets.

@cohan
Created June 26, 2020 03:32
Show Gist options
  • Save cohan/7f676d3f561be62d0550785c015f00b3 to your computer and use it in GitHub Desktop.
Save cohan/7f676d3f561be62d0550785c015f00b3 to your computer and use it in GitHub Desktop.
thud.tv
## This is the config file for the stream ingest server
# We accept the stream (from OBS, xsplit, etc) here
# and turn it into a HLS stream (live video in fragments)
# served through HTTP. Reason we use this is HLS is very
# easy to serve using a Content Delivery Network (CDN)
# closer to individual viewers
# Generic guff. Run as the "nobody" user so if this service gets hacked
# they can't do much else
user nobody;
# Worker processes being set to auto means it'll spewn one worker per
# CPU we have available. It can be manually set but not really any
# point to do so right now
worker_processes auto;
# Log files for debugging issues. Here we output logs to a file
# in a full on service we'd probably have a central log server.
error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
# Process ID is stored in this file, generally used to reference it
# later e.g. to kill the process when we want it to stop.
pid logs/nginx.pid;
events {
# How many connections we can handle at the same time.
# The more the better but if the underlying system can't
# handle it we'll start seeing issues.
worker_connections 1024;
}
# RTMP - This is the realtime media protocol (i think) that OBS and the likes
# use to stream audio/video. It's a pretty robust protocol however it does
# require an open connection to keep streaming to it. This is why we convert
# to HLS rather than show RTMP to the viewer, as that doesn't require an active
# connection.
rtmp {
server {
# Listen on port 1935, the RTMP standard port
listen 1935;
# The bigger the chunk the less CPU we need. Comes at a cost of
# more stream delay I believe.
chunk_size 4096;
# Ping the RTMP connection every "ping" interval. Timeout the
# connection if we don't get a reply in "ping_timeout".
ping 10s;
ping_timeout 10s;
# How many streams we want this server to handle at the same time
# More CPU/RAM/etc (mostly cpu) resources are required for a higher number
max_streams 100;
# Generic name "app" this appears when the user inserts their stream settings
# e.g. rtmp://ingest.thud.tv/app
application app {
# When someone attempts to stream to this server we check against this URL
# On that side we'll also receive the stream key as configured in OBS.
# Essentially there (but not technically correct) we'll receive the data as
# http://thud.app.icnerd.com/api/streamauth?name=<stream key> which we can
# then use to authenticate and identify the incoming stream.
# In this case when we request and it's a legitimate stream we get a redirect
# from the main service. The redirect points us to the internal "live" app
# below. In the event it's not a legit stream we receive a "401" HTTP code
# and we drop the stream.
on_publish http://thud.app.icnerd.com/api/streamauth;
# If it aint live it aint workin!
live on;
}
# If we were to use this in OBS this would appear as rtmp://<this server IP>/live
# but that wont actually work without already being authed first. This is essentially
# internal to this server. In reality we're redirected to it quietly when our stream
# key is legit.
application live {
# Here's where it gets a bit confusing - we are authing again! This time however
# we've already had our stream key authenticated. Now we've been asked to stream
# to our channel name. If I was using the "ribbalicious" stream key I'd now be
# essentially streaming to rtmp://<this server IP>/live?name=ribbalicious
# Why bounce around all over the shop like this? If we didn't then we'd have to
# reference the stream key when playing the stream later on the site. That would
# be absolute chaos when people realised. They'd be taking over the stream every
# 5 seconds.
on_publish http://thud.app.icnerd.com/api/streamauth;
# If it aint live it aint workin!
live on;
# Convert the rtmp stream into an HLS stream.
hls on;
# Store the HLS fragments in this directory
hls_path /tmp/hls;
# Fragment into a new file every
hls_fragment 1s;
# Reference this length of the stream in the m3u8 file.
hls_playlist_length 10s;
}
}
}
# The serving side of things
http {
# These are just dealios that make the server faster. Not really important but help
sendfile off;
tcp_nopush on;
aio on;
directio 512;
server {
# The base URL. If this were set to /shibble then we'd reference http://ingest.thud.tv/shibble
# As it's / we don't need to specify a path before the stream name we're after. Keeps things tidy.
location / {
types {
# Sets up what type of files we're serving and what file/mime type they are
# Not required but it makes sure the CDN etc know what we're doing.
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
# Serve files from this directory. This is the same dir we set the HLS fragments to go into earlier.
root /tmp/hls;
# We set a no-cache header here so that people don't get served dead files. The m3u8 file in particular
# changes very frequently. The chunks of files will re-use names whenever the stream is re-started for
# the same channel. We don't want them caching else the stream will be chaos and might show chunks from
# the previous stream
add_header Cache-Control no-cache;
# Browser security thing. We have to tell the browser (where relevant, not so much behind a CDN)
# that it's OK to serve files from this server cross-origin - cross origin means from a different
# domain than where the site's running - e.g. it's OK to embed video from ingest.thud.tv on www.thud.tv
add_header Access-Control-Allow-Origin *;
}
}
}
Route::get("streamauth", function(Request $request) {
$streamKey = Key::where('key', $request->input('name'))->first();
$rtmpUrl = "rtmp://xx.yy.zz.aa/live";
return redirect($rtmpUrl."/".$streamKey->channel->slug);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment