Monday, June 25, 2018

Live Streaming Headless Processes To YouTube

The use cases are endless but useless all the same. In a hell-bent effort to waste Google's resources, I'm going to design a Docker image that allows you to stream a graphical process in the background. Sound interesting? Great, because this is a very short and straightforward blog post and I'm running out of things to write before the fold.

First, let's make sure this works straight on the computer before we go and Dockerize it. We'll need three things to do this:

  • ffmpeg (doing command-line video processing with anything else is a crime)
  • xvfb (The X virtual frame buffer is like a desktop that runs in the background)
  • Whatever program you want to stream.
For my example, I'll be streaming a Windows 98 Qemu instance and one of its screen savers just to get an idea of how performant this is. I imagine it'll run smoothly at the cost of more CPU than any background process should consume. Despite the fact that I'll be running a 480p@30fps stream, higher resolutions will almost certainly cripple my bandwidth (my internet isn't super duper).

So I'm going to start by installing Windows 98 SE to a hard drive image. It froze a couple of times but the installer is quite resilient. Just reboot the VM and it'll be fine.


Great, so how do we get the screensaver to start when we boot? We can just add it to the autoexec batch file, right? Well, no, because that gets executed during boot before log in. So we can't quite do that. There is, however, a list of start up tasks in the registry. I just added the command there and away we go, right?


Well, no.


Something is clearly amiss here. What is it? We need more colors. The trick to solving this is to pass the -vga cirrus flag to qemu and then force Windows to use the Cirrus Logic 5446 drivers. After a reboot, things were looking much better. If I had specified -vga cirrus earlier during the install, Windows 98 might have been smart enough to install and use the right driver, but in either case, we're looking at 16-bit color now.

Now we can move on to the real point of this post: streaming it headlessly. We want to load qemu into a virtual frame buffer and then instruct ffmpeg to stream it to YouTube. We'll do that with the following two commands:

xvfb-run -s ":99 -auth /tmp/xvfb.auth -ac -screen 0 640x480x16" qemu-system-i386 -hda windows98hd.img -vga cirrus &  
ffmpeg -y -f x11grab -video_size 640x480 -i :99 -ar 44100 -ac 2 -f s16le -i /dev/zero -codec:a copy -deinterlace -vcodec libx264 -pix_fmt yuv420p -preset fast -r 30 -acodec libmp3lame -ar 44100 -bufsize 512k -f flv "rtmp://a.rtmp.youtube.com/live2/$KEY"
The frame buffer command is straight forward, but the ffmpeg command is kinda long and hard to read. Let's break it down really quickly:
  • ffmpeg -y - don't ask questions, assume the answer is always yes
  • -f x11grab -video_size 640x480 -i :99 - Get the video data from the X11 server using desktop #99. This was manually specified in the xfvb command.
  • -ar 44100 -ac 2 -f s16le -i /dev/zero -codec:a copy - instructs ffmpeg to read raw audio samples from /dev/zero, which means it'll be silent.
  • -deinterlace -vcodec libx264 -pix_fmt yuv420p -preset fast -r 30 -acodec libmp3lame -ar 44100 -bufsize 512k -f flv - how to encode the video
  • "rtmp://a.rtmp.youtube.com/live2/$KEY" - where the stream is going. Replace $KEY with your actual streaming key.
So, does this work?


It does, indeed. The bitrate is also pretty low (about 350kbps), so it's not destroying my network connection. It's able to sustain a strong 30fps no problem. Because of the low bitrate, YouTube may think it's not getting enough data. If the picture looks good and it's not buffering, then it really doesn't matter what YouTube thinks, does it? My i7-4710MQ CPU with this running (and too many chrome tabs open at once) was sitting at a cool ~25% usage. 

Okay, so that's good! Now we want to Dockerize it, so let's write a Dockerfile that installs everything we need and automatically starts streaming. The Dockerfile copies the hard drive and the entry point script to the Ubuntu 18.04 image and starts the process. Since Docker containers really should be responsible for one process only, we'll use their multi-service container example to run both the xvfb and ffmpeg process in the same docker container. And, with that, the container works!

I've posted a Gist with the Dockerfile and the entry script so you can modify it to stream whatever you want. You won't be able to run my exact scripts unless you have the Windows 98 hard drive image which I probably shouldn't distribute. If you want to replicate this exactly, just follow the instructions above. That's why they're there.

I hope this is useful to someone, I think it's cool that this can be accomplished without too much hassle. I look forward to hosting many docker containers streaming useless stuff for the sole purpose of wasting Google's resources.

No comments:

Post a Comment