StreamLoop - One of the most pointless things I've ever done.

I'm really good at creating useless shit on the Internet. Someone might suggest to me a truly idiotic idea, usually in the form of [Crappy Idea] as a Service. Such as the idea of hosted booleans that you can access using a REST API. Or a Discord bot that replies to the phrase "I'm [x]" with the phrase "Hi [x], I'm Dad!". A Twitter bot that generates random sentences using only profanity?[1] How about a crappy name-tag API? FizzBuzz as a service![2] An API that just endlessly sends you an ASCII horse over HTTP streams[3]. More recently, a marginally more practical idea was pitched to me "What if you could stream a video on loop to Twitch, 24/7?" If you couldn't guess I've designed and built all of these ideas in some form. Some are still running today whilst others have been retired to the dark depths of Page 2 on my GitLab account. Chances are by the time you're reading this some of these projects I've linked are dead, so apologies for that.

Now by day I'm a Web Developer[4]. Well, I pretend to be one... I can do Web Development but no-one seems to hire me (gosh I wonder why, captain FizzBuzz as a service). Anyway, this means I have a reasonable degree of knowledge when it comes to creating web services. As you can clearly tell, I'm putting my talent to good use in light of my employment status. I specialise in back-end work; developing microservices, deploying apps to servers, Docker, you name it! And when someone comes to me with one of these weird ideas I like to see it as a chance to challenge myself. An opportunity to see whether I can, rather than worrying about whether I should. No-one in their right mind would stream the same video on loop over and over to a public page. What's the point? The point is that I have fun doing so and get a chance to expand my skill set along the way.

HOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORSE

Developing Booleans as a Service taught me about user authentication with OAuth2; the name-tag API helped me grasp programmatic image manipulation (which is an absolute pain in the arse by the way). Each of these projects taught me at least something that I didn't know before. If they were easy and I knew exactly how to go about doing each and every random task given to me, what would be the point; why would I even bother? Well, aside for the humour value I guess. I'm not entirely sure where I was going with this. Anyway, the idea for a looping video being streamed 24/7 intrigued me. It wasn't entirely pointless; you could use this for branding / advertising, or just a fun experiment trying to draw peoples' attention. After being presented the idea I said "I'll look into it another time." Fast-forward a few hours and I'd already got it up and running. I really have too much free time it seems.

The code comes as a nice self contained bash shell script. Simply save the script to your Linux server of choice (I used Ubuntu 16.04 but it should work on any distro with ffmpeg[5] installed. It's also a good idea to have a fast upload speed, duh.) Then just chmod that baby and make sure the variables are set correctly and wham!

# This is all you need to stream the video on a loop
lolpants@galahad:~/stream$ ./streamloop globe.mp4

Let me break down the script a few chunks at a time...

#!/usr/bin/env bash

This is the Bash shebang. It tells bash that when executed to use the Bash interpreter to run the script. This should be very familiar if you've worked with scripting on Linux before.

# Configure Stream Settings
CBR="2500k"
QUALITY="ultrafast"
SERVER="live-ams"
STREAM_KEY="live_**************"

This section should seem fairly self explanatory. It gives you a bit more control over the quality of your stream. As well as keeping your Stream Key saved in the script for convenience (just don't check this out in version control!)

# Handle CTRL+C and cleanup playlist file
trap cleanup INT
function cleanup () {
  echo
  echo Exiting...
  rm list.txt
  exit 1
}

This part handles the interrupt signal in the Linux shell. Whenever CTRL+C is pressed Linux tells the currently running program to stop. This intercepts that and performs some cleanup by removing a file that is generated later on in the script. Also it sends a nice message telling the user that we're exiting. Not needed but I thought it was nice.

# Generate looping playlist
for i in {1..9999}; do printf "file '%s'\n" $1 >> list.txt; done

For some reason when you create a playlist with the same file and tell ffmpeg to concatenate it, the videos loop forever. Not entirely sure why but it's helpful. Oh, and this doesn't work on Windows, sorry. It just plays the video four times and ends.
Sadly when going back to tweak some parts to make it work a bit better, this stopped working. My solution? Tell it to loop ten thousand times and restart the process when it exits. Since the video file I'm using is around 30 seconds long, that's just under three and a half days before it restarts, not bad.

# Stream the video
ffmpeg -threads 0 -re -f concat -i list.txt \
  -vcodec libx264 -crf 23 -acodec libmp3lame -ar 44100 \
  -b:v $CBR -minrate $CBR -maxrate $CBR -bufsize 6000k \
  -preset $QUALITY -strict normal -vf "scale=1280:-1,format=yuv420p" \
  -f flv "rtmp://$SERVER.twitch.tv/app/$STREAM_KEY"

Finally, the magic behind it all. ffmpeg is a program that does media transcoding. Converting mp3s, demuxing video, more technical sounding words I pretend I know the meaning of. It also can stream, that's fairly important for the working of this script. The long and scary list of arguments sets all the video stream settings so that Twitch will accept it and that it won't max out my internet connection.

As with all technology it wasn't without a few roadbumps. I started doing this on my local Windows machine and soon realised that it wouldn't loop. After some googling and some other failed solutions I decided, "fuck it". I could just make it loop two-hundred thousand times and set up a cron job to restart it every now and again. However once I actually put it on my Linux server it just worked. ¯\_(ツ)_/¯ I then ran into a few issues with Twitch disconnecting me which I managed to solve by tweaking the stream buffer. I was pretty much just fiddling with the arguments until it worked; but hey now I know how to get it working in the future! If you want to see the end result of this madness and the reason why I actually ended up doing this, see this Twitch Page[6].

If any of this kind of dumb shit tickled you in any way or you just want to learn more about some of the strange projects I work on, don't hesitate to reach out to me on Twitter. To tell me how much of an idiot I am, to have a friendly chat, or god forbid you want to pay me to do something slightly more sensible, I'll be happy to chat whatever the reason!


Addendum - 24 hours later...

I left the script running for a good 24 hours, and upon inspection I noticed the stream was offline. I had to adjust a few things about how the script worked. Most notably, the inclusion of a do while: loop to restart the process every time it exits for whatever reason. This might be because Twitch disconnects us, or if we just hit the loop limit.

I've updated the article above with some edits to reflect the tweaks I've made over the last 24 hours.


  1. https://twitter.com/NotMattophobia ↩︎

  2. https://fizzbuzz-as-a-service.herokuapp.com ↩︎

  3. https://www.longhorse.ml ↩︎

  4. https://www.jackbaron.com ↩︎

  5. https://ffmpeg.org ↩︎

  6. https://www.twitch.tv/nerdcubedbot ↩︎