custom-streaming-setup.md

  1---
  2title: "Custom Streaming Setup"
  3description: "My second post of 100 Days To Offload details my custom streaming setup"
  4author: Amolith
  5date: 2020-04-26T20:24:38-04:00
  6draft: false
  7cover: /assets/pngs/stream.png
  8categories:
  9  - Technology
 10tags:
 11  - Gaming
 12  - Streaming
 13  - NGINX
 14  - OBS
 15  - 100 Days To Offload
 16toc: true
 17---
 18
 19The other day, I decided that I wanted to start streaming. I'll
 20definitely be playing some games but I might also stream some other
 21things like me playing music. We'll see where that goes. In any case, I
 22don't like relying on third parties for things and didn't want to use
 23Twitch so I started figuring out how to build my own open source and
 24privacy-friendly "platform" (which is really just a [page.](/live))
 25
 26## The search for a platform
 27Before settling on my own custom thing, I did some digging into
 28ready-made platforms I could just throw on one of my servers and run.
 29Two of the ones I found were
 30[OpenStreamingPlatform](https://openstreamingplatform.com/) and
 31[Restreamer.](https://datarhei.github.io/restreamer/) The latter isn't
 32exactly what I was looking for but it could have worked quite well. The
 33former, at first glance, was absolutely *perfect*. On a functional
 34level, it still is. However, take a look at [the installation
 35guide.](https://wiki.openstreamingplatform.com/Install/Manual)
 36
 37`<rant>`
 38
 39Steps 3 and 7 are unnecessary unless you feel like manually compiling
 40your web server; it's already available in the [Debian
 41repos](https://packages.debian.org/buster/libnginx-mod-rtmp) and, by
 42extension, Ubuntu's. It's even been backported to Stretch. In step 4, he
 43has `sed -i 's/appendfsync everysec/appendfsync no/'`. Like so many
 44application developers, he's assuming that this is the only project that
 45will be installed on the system. If someone is already using redis in
 46production and they have a different value there, that command will
 47fail. In step 9, the commands are copying the SystemD service files to
 48`/lib/systemd/` but this is where the package manager, `apt`, stores its
 49services. When you have your own that you're writing or copying from
 50somewhere else, best practise is to put them in `/etc/systemd/system`.
 51In addition, all of this is scripted for the "standard" install. Yes,
 52you're always supposed to review scripts before running them but who
 53really does that? When I see a project whose only supported installation
 54method is a script, I nope right on out of there for exactly this
 55reason. I know how my system *is* set up and I know how I *want* it set
 56up. I can't stand it when they assume they know what's best. Just tell
 57me what you *recommend* and I'll make decisions from there.
 58
 59`</rant>`
 60
 61## NGINX & RTMP
 62RTMP stands for [Real-Time Messaging
 63Protocol](https://wikipedia.org/wiki/Real-Time_Messaging_Protocol) and
 64facilitates streaming audio, video, and other data over the internet in
 65real-time. The NGINX module mentioned above adds functionality to NGINX
 66that allows it to handle RTMP streams and turn them into something a
 67browser or media streaming client can use. Connecting directly via
 68`rtmp://example.com/live/stream` is not very widely supported so
 69protocols such as
 70[MPEG-DASH](https://wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP)
 71and [HLS](https://wikipedia.org/wiki/HTTP_Live_Streaming) are used
 72instead.
 73
 74On Debian-based systems, adding RTMP functionality to NGINX is as simple
 75as `apt install libnginx-mod-rtmp`. After that, you'll need to add some
 76things to your `nginx.conf` and whatever host file you're using for your
 77website.
 78
 79``` c
 80rtmp {
 81  server {
 82    listen 1935;
 83    application live {
 84      deny publish all;
 85      allow publish 127.0.0.1;
 86      live on;
 87      interleave on;
 88      hls on;
 89      hls_path /tmp/hls;
 90      hls_fragment 15s;
 91      dash on;
 92      dash_path /tmp/dash;
 93      dash_fragment 15s;
 94    }
 95  }
 96}
 97```
 98
 99`1935` is the default RTMP port. `deny publish all` means you are
100denying *anyone* from publishing a stream (that includes you. `allow
101publish 127.0.0.1` allows *local* connections to publish content. I'm
102using this as a form of authentication---before streaming anything, I
103have to tunnel my connection to my server via SSH or a VPN. At the
104moment, I'm using SSH:
105
106``` text
107ssh -L 1935:localhost:1935 user@example.com
108```
109
110The other options are just the basics needed to get DASH and HLS to
111work. The only other thing to do is use NGINX as a reverse proxy (sort
112of) to serve the streams. Add this to your site's virtual host.
113
114``` c
115location /dash {
116  root /tmp;
117}
118location /hls {
119  root /tmp;
120}
121```
122
123That's it! Now you'll need to test your stream and verify that it
124actually works.
125
126``` bash
127ffmpeg -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
128```
129
130This command has FFmpeg play the video and stream it to the server. You
131should then be able to open the stream in something like
132[VLC](https://www.videolan.org/) or [MPV](https://mpv.io/) and watch it
133from anywhere.
134
135``` bash
136mpv https://example.com/dash/stream.mpd
137```
138
139However, I also wanted to embed it in a website and this is where it
140gets a little unstable.
141
142## Browser playback
143`dash.js` is currently one of the best ways to play a live stream in a
144browser plus it's pretty easy to work with. The code can be found [on
145GitHub.](https://github.com/Dash-Industry-Forum/dash.js) Using the setup
146with NGINX I detailed above, this should work perfectly fine out of the
147box.
148
149``` js
150<div>
151    <video id="videoPlayer" poster="/assets/jpgs/stream.jpg" controls></video>
152</div>
153<script src="/assets/js/dash.all.min.js"></script>
154<script>
155(function(){
156    var url = "/dash/stream.mpd";
157    var player = dashjs.MediaPlayer().create();
158    player.initialize(document.querySelector("#videoPlayer"), url, true);
159})();
160</script>
161```
162
163## Web chat
164The last thing every stream needs is something for web chat. I tried a
165few different solutions and had mixed results. The first was
166[KiwiIRC](https://kiwiirc.com/) but the iframe wouldn't even finish
167loading because it connected to so many third parties with a lot of
168tracking. It functions very well and I might set it up on my own site
169eventually but it was a bit much to go through at the time. As an
170intermediate solution, I embedded [my
171instance](https://irc.nixnet.services) of [The
172Lounge,](https://thelounge.chat) a fully-functional web-based IRC
173client. This loaded perfectly right out of the box but it wasn't quite
174what I wanted; there were *too* many options and the friends of mine who
175tested it got frustrated because some of the essential UI elements were
176hidden due to the small viewport. It's just not quite suitable for
177embedded webchat.
178
179Finally, I landed on [qwebirc](https://qwebirc.org/) and it was pretty
180much *exactly* what I wanted. When the iframe loads, you're prompted to
181enter a nick, you click connect, wait a minute, and done! My one
182complaint is that the theme is very bright but I'll work on that later
183on. It's good enough for now :wink:
184
185**EDIT:** Since the time of writing, I have switched to hosting
186[KiwiIRC](https://kiwiirc.com/) on
187[Secluded.Site](https://chat.secluded.site) so all of the trackers and
188third parties aren't in use. My configs are below but I recommend going
189through [the
190wiki](https://github.com/kiwiirc/kiwiirc/wiki/Configuration-Options) and
191making your own decisions.
192
193`/etc/kiwiirc/config.conf`
194
195``` ini
196logLevel = 3
197identd = false
198gateway_name = "webircgateway"
199secret = "changeme"
200
201[verify]
202recaptcha_secret = ""
203recaptcha_key = ""
204
205[clients]
206username = "%i"
207realname = "KiwiIRC on secluded.site"
208
209[server.1]
210bind = "0.0.0.0"
211port = 7264
212
213[fileserving]
214enabled = true
215webroot = /usr/share/kiwiirc/
216
217[transports]
218websocket
219sockjs
220kiwiirc
221
222[reverse_proxies]
223127.0.0.0/8
22410.0.0.0/8
225172.16.0.0/12
226192.168.0.0/16
227"::1/128"
228"fd00::/8"
229
230[upstream.1]
231hostname = "irc.nixnet.services"
232port = 6697
233tls = true
234timeout = 5
235throttle = 2
236webirc = ""
237```
238
239`/etc/kiwiirc/client.json`
240
241``` json
242{
243	"windowTitle": "Secluded.Site Chat",
244	"startupScreen": "welcome",
245	"kiwiServer": "/webirc/kiwiirc/",
246	"restricted": true,
247	"hideAdvanced": true,
248	"showAutoComplete": true,
249	"showSendButton": true,
250	"sidebarDefault": "nicklist",
251	"theme": "dark",
252	"themes": [
253		{ "name": "Default", "url": "static/themes/default" },
254		{ "name": "Dark", "url": "static/themes/dark" },
255		{ "name": "Coffee", "url": "static/themes/coffee" },
256		{ "name": "GrayFox", "url": "static/themes/grayfox" },
257		{ "name": "Nightswatch", "url": "static/themes/nightswatch" },
258		{ "name": "Osprey", "url": "static/themes/osprey" },
259		{ "name": "Radioactive", "url": "static/themes/radioactive" },
260		{ "name": "Sky", "url": "static/themes/sky" }
261	],
262  "buffers" : {
263    "messageLayout": "compact",
264    "show_timestamps": false,
265    "show_hostnames": false,
266    "show_joinparts": false,
267    "show_topics": true,
268    "show_nick_changes": true,
269    "show_mode_changes": false,
270    "traffic_as_activity": false,
271    "coloured_nicklist": true,
272    "colour_nicknames_in_messages": true,
273    "block_pms": true,
274    "show_emoticons": true,
275    "extra_formatting": true,
276    "mute_sound": false,
277    "hide_message_counts": false,
278    "show_realnames": false,
279    "default_kick_reason": "Your behaviour is not conducive to this environment.",
280    "shared_input": false,
281    "show_message_info": true,
282    "share_typing": true,
283    "flash_title": "off",
284    "nicklist_avatars": true,
285    "show_link_previews": true,
286    "inline_link_previews": true,
287    "inline_link_auto_preview_whitelist": "secluded.site|nixnet.services",
288    "channel": "#secluded"
289  },
290	"startupOptions" : {
291		"server": "irc.nixnet.services",
292		"port": 6697,
293		"tls": true,
294		"direct": false,
295		"channel": "#secluded",
296		"nick": "viewer?",
297		"greetingText": "Welcome!",
298		"infoBackground": "",
299		"infoContent": ""
300	}
301}
302```
303
304## Actually streaming
305Once you're ready to start streaming content, I recommend using [OBS
306Studio.](https://github.com/obsproject/obs-studio/) If you're noticing
307issues with stream performance, play around with your output resolution
308and FPS---those are the biggest factors. To use OBS with NGINX, you'll
309need to go to `Settings`, `Stream`, and set `Server` to
310`rtmp://localhost/live/`. If you're using my configs as they are, the
311key will need to be `stream`. Literally every component requires
312specific paths so, unless you're careful, things will break and you'll
313spend hours trying figure it out like I did. Also don't forget that the
314connection *has* to be tunnelled if you want authentication as I
315mentioned above. If you don't have `localhost:1935` on your streaming
316machine tunnelled to port 1935 on your server, OBS is going to throw
317errors about not being able to connect.
318
319## Summary
320I'm pretty proud of [the set up](/live) I have now but it could still do
321with some improvements. For example, I plan to mess with the CSS and
322make both the video and chat panes *much* wider as well as side-by-side
323rather than on top of each other. Everything is crammed together and
324it's not a very good experience.
325
326## References
327This post has pieces taken from a few other articles and sites that also
328deserve a mention as well as a read. NGINX actually has an [official
329blog
330post](https://www.nginx.com/blog/video-streaming-for-remote-learning-with-nginx/)
331on setting up RTMP streaming (though they compile NGINX from source as
332well) that was a *massive* help. I also found another post that is very
333similar to this one about [HTML5 Live Streaming with
334MPEG-DASH.](https://www.isrv.pw/html5-live-streaming-with-mpeg-dash) A
335good number of the parts are the same but I used the NGINX module in
336Debian repos and they used a fork of it with additional features. My
337NGINX setup was mostly from the NGINX blog post and the embedded stream
338was primarily from Inanity's. I figured out some of the components I
339could use for all of this from [Drew
340DeVault.](https://live.drewdevault.com/)
341
342---
343
344This was posted as part of
345[#100DaysToOffload,](https://100daystooffload.com/) an [awesome
346idea](https://fosstodon.org/@kev/104053977554016690) from [Kev
347Quirk.](https://kevq.uk/) If you want to participate, just write
348something every day for 100 days and post a link on social media with
349the hashtag!