2021-01-11 17:34:38 +00:00
|
|
|
---
|
|
|
|
title: "Custom Streaming Setup"
|
|
|
|
description: "My second post of 100 Days To Offload details my custom streaming setup"
|
2021-11-10 08:07:08 +00:00
|
|
|
author: Amolith
|
2021-01-11 17:34:38 +00:00
|
|
|
date: 2020-04-26T20:24:38-04:00
|
|
|
|
cover: /assets/pngs/stream.png
|
|
|
|
categories:
|
|
|
|
- Technology
|
|
|
|
tags:
|
|
|
|
- Gaming
|
|
|
|
- Streaming
|
|
|
|
- NGINX
|
|
|
|
- OBS
|
|
|
|
- 100 Days To Offload
|
|
|
|
toc: true
|
|
|
|
---
|
2021-06-26 20:51:30 +00:00
|
|
|
|
|
|
|
The other day, I decided that I wanted to start streaming. I'll
|
|
|
|
definitely be playing some games but I might also stream some other
|
|
|
|
things like me playing music. We'll see where that goes. In any case, I
|
|
|
|
don't like relying on third parties for things and didn't want to use
|
|
|
|
Twitch so I started figuring out how to build my own open source and
|
|
|
|
privacy-friendly "platform" (which is really just a [page.](/live))
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
## The search for a platform
|
2021-06-26 20:51:30 +00:00
|
|
|
Before settling on my own custom thing, I did some digging into
|
|
|
|
ready-made platforms I could just throw on one of my servers and run.
|
|
|
|
Two of the ones I found were
|
|
|
|
[OpenStreamingPlatform](https://openstreamingplatform.com/) and
|
|
|
|
[Restreamer.](https://datarhei.github.io/restreamer/) The latter isn't
|
|
|
|
exactly what I was looking for but it could have worked quite well. The
|
|
|
|
former, at first glance, was absolutely *perfect*. On a functional
|
|
|
|
level, it still is. However, take a look at [the installation
|
|
|
|
guide.](https://wiki.openstreamingplatform.com/Install/Manual)
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
`<rant>`
|
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
Steps 3 and 7 are unnecessary unless you feel like manually compiling
|
|
|
|
your web server; it's already available in the [Debian
|
|
|
|
repos](https://packages.debian.org/buster/libnginx-mod-rtmp) and, by
|
|
|
|
extension, Ubuntu's. It's even been backported to Stretch. In step 4, he
|
|
|
|
has `sed -i 's/appendfsync everysec/appendfsync no/'`. Like so many
|
|
|
|
application developers, he's assuming that this is the only project that
|
|
|
|
will be installed on the system. If someone is already using redis in
|
|
|
|
production and they have a different value there, that command will
|
|
|
|
fail. In step 9, the commands are copying the SystemD service files to
|
|
|
|
`/lib/systemd/` but this is where the package manager, `apt`, stores its
|
|
|
|
services. When you have your own that you're writing or copying from
|
|
|
|
somewhere else, best practise is to put them in `/etc/systemd/system`.
|
|
|
|
In addition, all of this is scripted for the "standard" install. Yes,
|
|
|
|
you're always supposed to review scripts before running them but who
|
|
|
|
really does that? When I see a project whose only supported installation
|
|
|
|
method is a script, I nope right on out of there for exactly this
|
|
|
|
reason. I know how my system *is* set up and I know how I *want* it set
|
|
|
|
up. I can't stand it when they assume they know what's best. Just tell
|
|
|
|
me what you *recommend* and I'll make decisions from there.
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
`</rant>`
|
|
|
|
|
|
|
|
## NGINX & RTMP
|
2021-06-26 20:51:30 +00:00
|
|
|
RTMP stands for [Real-Time Messaging
|
|
|
|
Protocol](https://wikipedia.org/wiki/Real-Time_Messaging_Protocol) and
|
|
|
|
facilitates streaming audio, video, and other data over the internet in
|
|
|
|
real-time. The NGINX module mentioned above adds functionality to NGINX
|
|
|
|
that allows it to handle RTMP streams and turn them into something a
|
|
|
|
browser or media streaming client can use. Connecting directly via
|
|
|
|
`rtmp://example.com/live/stream` is not very widely supported so
|
|
|
|
protocols such as
|
|
|
|
[MPEG-DASH](https://wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP)
|
|
|
|
and [HLS](https://wikipedia.org/wiki/HTTP_Live_Streaming) are used
|
|
|
|
instead.
|
|
|
|
|
|
|
|
On Debian-based systems, adding RTMP functionality to NGINX is as simple
|
|
|
|
as `apt install libnginx-mod-rtmp`. After that, you'll need to add some
|
|
|
|
things to your `nginx.conf` and whatever host file you're using for your
|
|
|
|
website.
|
|
|
|
|
|
|
|
``` c
|
2021-01-11 17:34:38 +00:00
|
|
|
rtmp {
|
|
|
|
server {
|
|
|
|
listen 1935;
|
|
|
|
application live {
|
|
|
|
deny publish all;
|
|
|
|
allow publish 127.0.0.1;
|
|
|
|
live on;
|
|
|
|
interleave on;
|
|
|
|
hls on;
|
|
|
|
hls_path /tmp/hls;
|
|
|
|
hls_fragment 15s;
|
|
|
|
dash on;
|
|
|
|
dash_path /tmp/dash;
|
|
|
|
dash_fragment 15s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
`1935` is the default RTMP port. `deny publish all` means you are
|
|
|
|
denying *anyone* from publishing a stream (that includes you. `allow
|
|
|
|
publish 127.0.0.1` allows *local* connections to publish content. I'm
|
|
|
|
using this as a form of authentication---before streaming anything, I
|
|
|
|
have to tunnel my connection to my server via SSH or a VPN. At the
|
|
|
|
moment, I'm using SSH:
|
|
|
|
|
|
|
|
``` text
|
2021-01-11 17:34:38 +00:00
|
|
|
ssh -L 1935:localhost:1935 user@example.com
|
|
|
|
```
|
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
The other options are just the basics needed to get DASH and HLS to
|
|
|
|
work. The only other thing to do is use NGINX as a reverse proxy (sort
|
|
|
|
of) to serve the streams. Add this to your site's virtual host.
|
2021-01-11 17:34:38 +00:00
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
``` c
|
2021-01-11 17:34:38 +00:00
|
|
|
location /dash {
|
|
|
|
root /tmp;
|
|
|
|
}
|
|
|
|
location /hls {
|
|
|
|
root /tmp;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
That's it! Now you'll need to test your stream and verify that it
|
|
|
|
actually works.
|
2021-01-11 17:34:38 +00:00
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
``` bash
|
2021-01-11 17:34:38 +00:00
|
|
|
ffmpeg -re -i video.mp4 -vcodec copy -loop -1 -c:a aac -b:a 160k -ar 44100 -strict -2 -f flv rtmp://example.com/live/stream
|
|
|
|
```
|
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
This command has FFmpeg play the video and stream it to the server. You
|
|
|
|
should then be able to open the stream in something like
|
|
|
|
[VLC](https://www.videolan.org/) or [MPV](https://mpv.io/) and watch it
|
|
|
|
from anywhere.
|
|
|
|
|
|
|
|
``` bash
|
2021-01-11 17:34:38 +00:00
|
|
|
mpv https://example.com/dash/stream.mpd
|
|
|
|
```
|
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
However, I also wanted to embed it in a website and this is where it
|
|
|
|
gets a little unstable.
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
## Browser playback
|
2021-06-26 20:51:30 +00:00
|
|
|
`dash.js` is currently one of the best ways to play a live stream in a
|
|
|
|
browser plus it's pretty easy to work with. The code can be found [on
|
|
|
|
GitHub.](https://github.com/Dash-Industry-Forum/dash.js) Using the setup
|
|
|
|
with NGINX I detailed above, this should work perfectly fine out of the
|
|
|
|
box.
|
|
|
|
|
|
|
|
``` js
|
2021-01-11 17:34:38 +00:00
|
|
|
<div>
|
|
|
|
<video id="videoPlayer" poster="/assets/jpgs/stream.jpg" controls></video>
|
|
|
|
</div>
|
|
|
|
<script src="/assets/js/dash.all.min.js"></script>
|
|
|
|
<script>
|
|
|
|
(function(){
|
|
|
|
var url = "/dash/stream.mpd";
|
|
|
|
var player = dashjs.MediaPlayer().create();
|
|
|
|
player.initialize(document.querySelector("#videoPlayer"), url, true);
|
|
|
|
})();
|
|
|
|
</script>
|
|
|
|
```
|
|
|
|
|
|
|
|
## Web chat
|
2021-06-26 20:51:30 +00:00
|
|
|
The last thing every stream needs is something for web chat. I tried a
|
|
|
|
few different solutions and had mixed results. The first was
|
|
|
|
[KiwiIRC](https://kiwiirc.com/) but the iframe wouldn't even finish
|
|
|
|
loading because it connected to so many third parties with a lot of
|
|
|
|
tracking. It functions very well and I might set it up on my own site
|
|
|
|
eventually but it was a bit much to go through at the time. As an
|
|
|
|
intermediate solution, I embedded [my
|
|
|
|
instance](https://irc.nixnet.services) of [The
|
|
|
|
Lounge,](https://thelounge.chat) a fully-functional web-based IRC
|
|
|
|
client. This loaded perfectly right out of the box but it wasn't quite
|
|
|
|
what I wanted; there were *too* many options and the friends of mine who
|
|
|
|
tested it got frustrated because some of the essential UI elements were
|
|
|
|
hidden due to the small viewport. It's just not quite suitable for
|
|
|
|
embedded webchat.
|
|
|
|
|
|
|
|
Finally, I landed on [qwebirc](https://qwebirc.org/) and it was pretty
|
|
|
|
much *exactly* what I wanted. When the iframe loads, you're prompted to
|
|
|
|
enter a nick, you click connect, wait a minute, and done! My one
|
|
|
|
complaint is that the theme is very bright but I'll work on that later
|
2021-11-10 08:07:08 +00:00
|
|
|
on. It's good enough for now :wink:
|
2021-06-26 20:51:30 +00:00
|
|
|
|
|
|
|
**EDIT:** Since the time of writing, I have switched to hosting
|
|
|
|
[KiwiIRC](https://kiwiirc.com/) on
|
|
|
|
[Secluded.Site](https://chat.secluded.site) so all of the trackers and
|
|
|
|
third parties aren't in use. My configs are below but I recommend going
|
|
|
|
through [the
|
|
|
|
wiki](https://github.com/kiwiirc/kiwiirc/wiki/Configuration-Options) and
|
|
|
|
making your own decisions.
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
`/etc/kiwiirc/config.conf`
|
2021-06-26 20:51:30 +00:00
|
|
|
|
|
|
|
``` ini
|
2021-01-11 17:34:38 +00:00
|
|
|
logLevel = 3
|
|
|
|
identd = false
|
|
|
|
gateway_name = "webircgateway"
|
|
|
|
secret = "changeme"
|
|
|
|
|
|
|
|
[verify]
|
|
|
|
recaptcha_secret = ""
|
|
|
|
recaptcha_key = ""
|
|
|
|
|
|
|
|
[clients]
|
|
|
|
username = "%i"
|
|
|
|
realname = "KiwiIRC on secluded.site"
|
|
|
|
|
|
|
|
[server.1]
|
|
|
|
bind = "0.0.0.0"
|
|
|
|
port = 7264
|
|
|
|
|
|
|
|
[fileserving]
|
|
|
|
enabled = true
|
|
|
|
webroot = /usr/share/kiwiirc/
|
|
|
|
|
|
|
|
[transports]
|
|
|
|
websocket
|
|
|
|
sockjs
|
|
|
|
kiwiirc
|
|
|
|
|
|
|
|
[reverse_proxies]
|
|
|
|
127.0.0.0/8
|
|
|
|
10.0.0.0/8
|
|
|
|
172.16.0.0/12
|
|
|
|
192.168.0.0/16
|
|
|
|
"::1/128"
|
|
|
|
"fd00::/8"
|
|
|
|
|
|
|
|
[upstream.1]
|
|
|
|
hostname = "irc.nixnet.services"
|
|
|
|
port = 6697
|
|
|
|
tls = true
|
|
|
|
timeout = 5
|
|
|
|
throttle = 2
|
|
|
|
webirc = ""
|
|
|
|
```
|
|
|
|
|
|
|
|
`/etc/kiwiirc/client.json`
|
2021-06-26 20:51:30 +00:00
|
|
|
|
|
|
|
``` json
|
2021-01-11 17:34:38 +00:00
|
|
|
{
|
|
|
|
"windowTitle": "Secluded.Site Chat",
|
|
|
|
"startupScreen": "welcome",
|
|
|
|
"kiwiServer": "/webirc/kiwiirc/",
|
|
|
|
"restricted": true,
|
|
|
|
"hideAdvanced": true,
|
|
|
|
"showAutoComplete": true,
|
|
|
|
"showSendButton": true,
|
|
|
|
"sidebarDefault": "nicklist",
|
|
|
|
"theme": "dark",
|
|
|
|
"themes": [
|
|
|
|
{ "name": "Default", "url": "static/themes/default" },
|
|
|
|
{ "name": "Dark", "url": "static/themes/dark" },
|
|
|
|
{ "name": "Coffee", "url": "static/themes/coffee" },
|
|
|
|
{ "name": "GrayFox", "url": "static/themes/grayfox" },
|
|
|
|
{ "name": "Nightswatch", "url": "static/themes/nightswatch" },
|
|
|
|
{ "name": "Osprey", "url": "static/themes/osprey" },
|
|
|
|
{ "name": "Radioactive", "url": "static/themes/radioactive" },
|
|
|
|
{ "name": "Sky", "url": "static/themes/sky" }
|
|
|
|
],
|
|
|
|
"buffers" : {
|
|
|
|
"messageLayout": "compact",
|
|
|
|
"show_timestamps": false,
|
|
|
|
"show_hostnames": false,
|
|
|
|
"show_joinparts": false,
|
|
|
|
"show_topics": true,
|
|
|
|
"show_nick_changes": true,
|
|
|
|
"show_mode_changes": false,
|
|
|
|
"traffic_as_activity": false,
|
|
|
|
"coloured_nicklist": true,
|
|
|
|
"colour_nicknames_in_messages": true,
|
|
|
|
"block_pms": true,
|
|
|
|
"show_emoticons": true,
|
|
|
|
"extra_formatting": true,
|
|
|
|
"mute_sound": false,
|
|
|
|
"hide_message_counts": false,
|
|
|
|
"show_realnames": false,
|
|
|
|
"default_kick_reason": "Your behaviour is not conducive to this environment.",
|
|
|
|
"shared_input": false,
|
|
|
|
"show_message_info": true,
|
|
|
|
"share_typing": true,
|
|
|
|
"flash_title": "off",
|
|
|
|
"nicklist_avatars": true,
|
|
|
|
"show_link_previews": true,
|
|
|
|
"inline_link_previews": true,
|
|
|
|
"inline_link_auto_preview_whitelist": "secluded.site|nixnet.services",
|
|
|
|
"channel": "#secluded"
|
|
|
|
},
|
|
|
|
"startupOptions" : {
|
|
|
|
"server": "irc.nixnet.services",
|
|
|
|
"port": 6697,
|
|
|
|
"tls": true,
|
|
|
|
"direct": false,
|
|
|
|
"channel": "#secluded",
|
|
|
|
"nick": "viewer?",
|
|
|
|
"greetingText": "Welcome!",
|
|
|
|
"infoBackground": "",
|
|
|
|
"infoContent": ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## Actually streaming
|
2021-06-26 20:51:30 +00:00
|
|
|
Once you're ready to start streaming content, I recommend using [OBS
|
|
|
|
Studio.](https://github.com/obsproject/obs-studio/) If you're noticing
|
|
|
|
issues with stream performance, play around with your output resolution
|
|
|
|
and FPS---those are the biggest factors. To use OBS with NGINX, you'll
|
|
|
|
need to go to `Settings`, `Stream`, and set `Server` to
|
|
|
|
`rtmp://localhost/live/`. If you're using my configs as they are, the
|
|
|
|
key will need to be `stream`. Literally every component requires
|
|
|
|
specific paths so, unless you're careful, things will break and you'll
|
|
|
|
spend hours trying figure it out like I did. Also don't forget that the
|
|
|
|
connection *has* to be tunnelled if you want authentication as I
|
|
|
|
mentioned above. If you don't have `localhost:1935` on your streaming
|
|
|
|
machine tunnelled to port 1935 on your server, OBS is going to throw
|
|
|
|
errors about not being able to connect.
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
## Summary
|
2021-06-26 20:51:30 +00:00
|
|
|
I'm pretty proud of [the set up](/live) I have now but it could still do
|
|
|
|
with some improvements. For example, I plan to mess with the CSS and
|
|
|
|
make both the video and chat panes *much* wider as well as side-by-side
|
|
|
|
rather than on top of each other. Everything is crammed together and
|
|
|
|
it's not a very good experience.
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
## References
|
2021-06-26 20:51:30 +00:00
|
|
|
This post has pieces taken from a few other articles and sites that also
|
|
|
|
deserve a mention as well as a read. NGINX actually has an [official
|
|
|
|
blog
|
|
|
|
post](https://www.nginx.com/blog/video-streaming-for-remote-learning-with-nginx/)
|
|
|
|
on setting up RTMP streaming (though they compile NGINX from source as
|
|
|
|
well) that was a *massive* help. I also found another post that is very
|
|
|
|
similar to this one about [HTML5 Live Streaming with
|
|
|
|
MPEG-DASH.](https://www.isrv.pw/html5-live-streaming-with-mpeg-dash) A
|
|
|
|
good number of the parts are the same but I used the NGINX module in
|
|
|
|
Debian repos and they used a fork of it with additional features. My
|
|
|
|
NGINX setup was mostly from the NGINX blog post and the embedded stream
|
|
|
|
was primarily from Inanity's. I figured out some of the components I
|
|
|
|
could use for all of this from [Drew
|
|
|
|
DeVault.](https://live.drewdevault.com/)
|
2021-01-11 17:34:38 +00:00
|
|
|
|
|
|
|
---
|
|
|
|
|
2021-06-26 20:51:30 +00:00
|
|
|
This was posted as part of
|
|
|
|
[#100DaysToOffload,](https://100daystooffload.com/) an [awesome
|
|
|
|
idea](https://fosstodon.org/@kev/104053977554016690) from [Kev
|
|
|
|
Quirk.](https://kevq.uk/) If you want to participate, just write
|
|
|
|
something every day for 100 days and post a link on social media with
|
|
|
|
the hashtag!
|