Test Streaming With FFMPEG

What can you say about FFMPEG other than it’s fucking brilliant. Yes there’s bugs, yes they don’t mind breaking the interface a little more often than you’d like, yes you’ll inevitably be using a build that doesn’t support that feature you wanted…okay maybe it’s got a problem or two, but seriously, what an unbelievably flexible tool. You might imagine a guy working for a video streaming startup might be using it a fair amount, and as it happens you’d be right. Below is a breakdown of my favorite testing incantation. Many parts can also be broken down for general a/v testing purposes.

TL;DR: Give this a go:

The ntpdate line is from my Getting a quick and dirty ntp sync article. This step is optional and should only be used where you generally don’t give a shit about your system clock. It’s also only necessary if you want the timestamp functionality shown below.

The loop is here so that when you break shit you don’t have to start the script again, and fuck knows that happens plenty. Thanks to the sleep afterward, a double-tap (no pun intended) on ctrl+c will be enough to kill it dead.

-re is in ffmpeg for our very purpose here. It reads from the input source at its actual framerate (as opposed to as fast as possible by default) so we can see things as we expect them to look, instead of like that time you tried an emulator that wasn’t designed for your CPU and the music sounded like a bunch of chimpmunks on speed. You can drop this one if you’re not testing a live stream.

-f lavfi is used multiple times in this incantation. It allows us to specify an arbitrary number of inputs and outputs. In this case, the inputs are testsrc and sine and the output is the test stream.

testsrc shows a motion test image with a visual counter, and has a few configurable options (see the docs for more info). We’re specifying frame size and rate here. You can have a look at life which can be fun, and mandelbrot as alternatives, but testsrc is of the most utility when attempting to diagnose issues. Click here for a screenshot.

sine generates the audio signal of a sine wave (see the docs). the f option specifies the frequency, and the b option causes a pulse at n times the carrier frequency each second. It’s very handy for determining video/audio sync, and as an audible indication that the stream is running (if you’re having video issues). Unfortunately, the sound is really fucking annoying, so I turn it down really low with -af "volume=0.1". You can also have a look at flite which would allow you to synthesize voices, but ffmpeg is rarely compiled with libflite, and it would probably be just as fucking annoying. Of course feel free to use an actual audio source for input, but then the incantation isn’t portable.

-codec:a libfdk_aac instructs ffmpeg to use the Fraunhoffer aac encoder for the audio. ffmpeg is often not compiled with the library as it’s not Free, so as an alternative you can specify -strict experimental -acodec aac instead (see here for more details). You can take a look at ffmpeg -codecs | grep -i aac to see which aac decoders and encoders you have available.

-codec:v libx264 instructs ffmpeg to encode the output as h.264, commonly conflated with its most common container MP4. Since ffmpeg affords us the opportunity to specify a few tuning parameters for x264 we’re also specifying:

  • -preset ultrafast which instructs libx264 to use only the very fastest optimizations
  • -profile:v main which instructs ffmpeg to encode to the ‘main’ h.264 profile which should be supported on all modern devices, but you can try using high if you want better reproduction for some reason, or ‘baseline’ if want really serious compatibility, see here for more info
  • -pixel_format yuv420p (or -pix_fmt in older builds) which instructs ffmpeg to encode to the YUV 4:2:0, a very well supported color space
  • -g 48 which instructs ffmpeg to provide a keyframe at every 48 frames, or two seconds in this case. It’s usually safe to specify `-g` at double your framerate unless you have some serious action in frame
  • -tune zerolatency which enables some additional latency-saving parameters in x264 (you can go even further here by using CBR, and setting maxrate & bufsize to the size of a single frame, but we’re looking for the lowest latency with the least amount of work here)

drawtext is a filter for drawing text over the video. In this case we’re using it to draw the system time across the left-hand side of the video. Combined with the synchronization of the system clock at the start, it gives you a decent idea of how long it takes for your stream to be replayed (assuming your own clock isn’t too far out of course). Here we’re specifying font file (DejaVuSans.ttf should be available at this location on Trusty), the text, color (& alpha) and coordinates. You can see some of the other options here. For this one to work you will need to have compiled ffmpeg with at least --enable-freetype.

-f flv instructs ffmpeg to package the stream in an FLV container. If you’re using this for anything other than a live stream, this is unnecessary, it will default to mp4.

And we’re done! Here’s what the finished product looks like: ffmpeg-test.flv. Have a look below for some reference materials and tips & tricks. Enjoy your streaming!

If you’re interested in building ffmpeg for this configuration, see below. Probably go have a nap after you press return on this baby.
Note: you will need multiverse enabled for libfdk-aac-dev. You can remove it from the package list and --enable-libfdk-aac from the build args to disable it.

For ffmpeg:

For libav (virtually identical):

Tips and Tricks:

If you’re wondering why you can’t get ffmpeg or ffplay to consume a particular RTMP stream, try putting quotes around the URL and adding  live=1 to the end of the URL. It forces ffmpeg to call FCSubscribe, which is required by, amongst others, EdgeCast.

If you want the basic details of another stream you’re looking at, try ffprobe $URL. A much more detailed, but way less human readable incantation is ffprobe -show_streams $URL