Desktop streaming
30 August, 2016
For teaching purposes (and cool internet points!) I recently needed to share my screen and microphone online. Being the unix enthusiast that I am, I looked into how I could do it "simple" command-line tools.
And here comes ffmpeg
. ffmpeg
is the swiss
army knife for everything related to audio and video decoding/encoding.
I've been using it for multiple tasks already, from converting .ogg
to .mp3, to recording GIFs of my desktop.
Server part
I started looking into how I could "stream" my desktop online, and
quickly found about the ffserver
utility (which is part of the
ffmpeg
package).
ffserver
provides a service to do the following:
- Receive a "Feed" sent by one user
- Send a "Stream" to multiple users
A "Feed", from ffserver
point, is a URL that a user will pass to
ffmpeg as the output media, to start "uploading" or "streaming" a
video to. ffserver
will then start bufferizing this input locally,
and expose this raw buffer via a "Stream". A stream will read from
this buffer, and encode it in the specified format, with a bunch
of options.
One can specify multiple output streams for a single feed, eg, to use different encoding formats.
Enough shittalks, here is what my /etc/ffserver.conf
looked like:
# Port 80 was taken by the webserver
HTTPPort 8090
HTTPBindAddress 0.0.0.0
MaxHTTPConnections 64
MaxClients 28
MaxBandwidth 10000
CustomLog /var/log/ffserver.log
# Where to send data.
# URL will be: http://10.0.0.2:8090/0.ffm
<Feed 0.ffm>
# buffer file and max size
File /tmp/ffserver/0.ffm
FileMaxSize 200K
# Only allow this IP to send streaming data ACL
allow 10.0.0.3
</Feed>
# How to expose the stream
# URL will be: http://10.0.0.2:8090/0.flv
<Stream 0.flv>
# The feed to encode data from
Feed 0.ffm
# Video encoding options
Format flv
VideoCodec libx264
VideoFrameRate 5
VideoSize 1440x900
VideoBitRate 512
AVOptionVideo tune zerolatency
AVOptionVideo flags +global_header
# Audio encoding options
AudioCodec aac
AVOptionAudio flags +global_header
</Stream>
I limited my research for the perfect stream to either x264 or vp8 video encoding. At first, vp8 seemed appealing, being a royalty-free format. The WEBM container also seems to be pretty good for online videos. But x264 turned out to be faster, and of higher quality (especially thanks to the "zerolatency" setting). I had to switch to x264 also because I couldn't get the libvorbis codec for audio to synchronize well with the vp8 video stream.
The above configuration is the best quality/rapidity ratio I could get.
When the config is ready, you just need to fire up the server with
/usr/bin/ffserver -f /etc/ffserver.conf
Watcher part
In order to watch the stream, one has to use the URL defined by the
<Stream>
tag. I personally use mplayer
to watch it, but one can
use the ffplay
command provided by ffmpeg
:
ffplay http://10.0.0.2:8090/0.flv
And that's ALL. You can hardly do simpler to watch a stream, right?
Feeder part
In order to feed yor stream to the ffserver
, you can use ffmpeg
directly. The format of the command is pretty simple. We have 2
inputs: the video and the audio. We also have 1 output: the FFM
feed. The simplest command we can use is thus (recording our desktop,
and microphone):
ffmpeg -f x11grab -i :0.0 -f alsa -i default http://10.0.0.2:8090/0.ffm
Let's break it down a bit:
-f x11grab -i :0.0
: Record the desktop, using$DISPLAY
:0.0-f alsa -i default
: Record the microphone, using ALSA's default input devicehttp://10.0.0.2:8090/0.ffm
: Feed location
This should start recording, and sending data to the stream. If you look at it, the output will not look really great, and we need to pass a few more flags to get a nice looking output that will record the full screen in a decent way:
ffmpeg -f x11grab -r 5 -s 1440x900 -thread_queue_size 1024 -i :0.0 \
-f alsa -ac 1 -thread_queue_size 1024 -i default \
-af 'highpass=f=200, lowpass=f=2000' \
-fflags nobuffer \
http://10.0.0.2:8090/0.ffm
Ok, that was odd. no worries, I'm no wizard and didn't came up with all these flags out of nowhere! Let's review them:
-f x11grab -r 5 -s 1440x900 -i :0.0
Record our X11 desktop with a framerate (-r
) of 5 FPS, and record
the screen at size (-s
) 1440x900 (my screen size).
-f alsa -ac 1 -i default
Record from the default ALSA capture device, using MONO input
(-ac
).
-thread_queue_size 1024
For both input, this is to increase the max number of queued packets
ffmpeg
can handle. If the thread queue is full, ffmpeg
will
start dropping packets, which can lower the stream quality.
-af 'highpass=f=200, lowpass=f=2000'
Add an audio filter. My microphone is utter shit, and records a lot of white noise. This filters out frequencies below 200Hz and above 2000Hz (that's the typical voice range).
-fflags nobuffer
Avoid buffering frames when possible, so the stream is available as it is recorded.
And that's pretty much it! Note that ffmpeg can have multiple outputs, so you can record to the feed AND to a local file at the same time.
For instance, this is my ffstream
script:
#!/bin/sh
STREAM=${1:-http://10.0.0.2:8090/0.ffm}
ffmpeg -f x11grab -r 5 -s 1440x900 -thread_queue_size 1024 -i :0.0 \
-f alsa -ac 1 -thread_queue_size 1024 -i default \
-af 'highpass=f=200, lowpass=f=2000' \
-fflags nobuffer ${STREAM} \
-af 'highpass=f=200, lowpass=f=2000' \
-c:v libvpx -b:v 5M -c:a libvorbis webcast-$(date +%Y%m%d%H%M%S).webm
That's all folks!