monochromatic blog:
git clone git://
Log | Files | Refs

commit ea5d78639c0ec73e577163083b8e0502c85e198e
parent 42c232ef2987b0e735909c75ee4c6b7139231d94
Author: z3bra <willyatmailoodotorg>
Date:   Tue, 30 Aug 2016 14:13:13 +0200

ÃNew post: webstreaming with ffmpeg

2016/08/desktop-streaming.txt | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 3++-
index.txt | 1+
3 files changed, 182 insertions(+), 1 deletion(-)

diff --git a/2016/08/desktop-streaming.txt b/2016/08/desktop-streaming.txt @@ -0,0 +1,179 @@ +# [Desktop streaming](#) +## &mdash; 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 + MaxHTTPConnections 64 + MaxClients 28 + MaxBandwidth 10000 + + CustomLog /var/log/ffserver.log + + # Where to send data. + # URL will be: + <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 + </Feed> + + # How to expose the stream + # URL will be: + <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 + +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 + +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 device +* ``: 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 \ + + +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:-} + 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! + diff --git a/ b/ @@ -31,7 +31,8 @@ PAGES = index.html \ 2015/08/cross-compiling-with-pcc-and-musl.html \ 2015/08/install-alpine-at-onlinenet.html \ 2016/01/make-your-own-distro.html \ - 2016/03/hand-crafted-containers.html + 2016/03/hand-crafted-containers.html \ + 2016/08/desktop-streaming.html FEEDS = rss/feed.xml EXTRA = css img vid data errors favicon.ico diff --git a/index.txt b/index.txt @@ -1,3 +1,4 @@ +* 0x001c - [Desktop streaming](/2016/08/desktop-streaming.html) * 0x001b - [Hand-crafted containers](/2016/03/hand-crafted-containers.html) * 0x001a - [Make your own distro](/2016/01/make-your-own-distro.html) * 0x0019 - [Install Alpine at](/2015/08/install-alpine-at-onlinenet.html)