Tue, 13 Sep 2016
I recently found myself wanting to stream some video to a friend of mine in another country. This was a new experience for me so I wanted to document what I did in case it's helpful for other people.
I knew I wanted to use Icecast, the streaming media server. I was doing this on Trisquel 7, which had version 2.3.3. I later learned that this version didn't support WebM so a new version was needed:
sudo apt-get install checkinstall
tar xf icecast-2.4.3.tar.gz
sudo checkinstall make install
(Accept all of the default answers.)
A newer version of Icecast may have come out since this was written, or if you're using Trisquel 8 or later then you probably already have a new-enough version of Icecast.
Edit the file
/usr/local/etc/icecast.xml mostly to set
passwords to stream. No, this doesn't mean that people will need
passwords to view the steams. Rather, these are used by the programs
that send the audio & video to Icecast to be streamed out to the
Then start Icecast:
Icecast listens on port 8000 by default, unless you've changed this in the icecast.xml file. Whatever port icecast listens on will need to be opened in your firewall.
Now that Icecast is running it's time to send it audio and video to stream. I used FFmpeg for this. I made my own statically compiled version of FFmpeg in order to get the latest version of libvpx without having to mess with what was on the host system. Feel free to do that if you want or use the version provided by the distro. Alternatively, the FFmpeg website also has links to download static builds if you'd rather not make your own.
Anyway, once FFmpeg is available through whatever means you've decided it's time to use it to encode the source video and send it to Icecast:
~/ffmpeg -i xxx -y -f webm -codec:v libvpx -codec:a libvorbis
-aq -1 -ac 1 -af "volume=2.5,
-ar 24000 -vf "yadif=0:-1:1, fps=fps=24000/1001, scale=569:320"
-deadline realtime -cpu-used 0 -threads 3 -profile:v 0 -crf 10 -vb
256K -content_type video/webm
This may look long and scary but it's not, I promise. Let's walk through this:
This runs FFmpeg. I had placed the binary in my home directory but invoke FFmpeg as appropriate for wherever the program is at.
xxx with whatever the input of your
audio & video are. This may be a webcam so maybe /dev/something or
whatever else you're using.
-f webm -codec:v libvpx -codec:a libvorbis
This sets the format and audio and video codecs.
This sets Vorbis is use quality-based encoding, using "quality level -1." Vorbis accepts a range anywhere from -1 all the way to 10 to represent a quality level, and lower numbers generally represent lower bitrates. Keeping the bitrate low was important to me. The audio still sounded great.
This changes the audio to a single channel (mono) instead of the multi-channel audio input that the source had. This was also done to reduce the overall bandwidth needed.
-af "volume=2.5, aresample=async=44800:min_hard_comp=0.000000, asetpts=PTS-(1.15/TB)"
This needs some explaining. "af" is for "audio filter." The first increases the volume by 2.5x since I've found that the encoded audio was somewhat quiet. The others (aresample and asetpts) were used because I found myself with out of sync audio. This can be worked around in the program playing the stream (VLC in this case) by pressing J and K to adjust the audio until it looked like it was in sync with the speaker's mouth but I really wanted to find a way to fix it from the streaming server. Sadly, I never did.
The aresample with async lets you stretch/squeeze the audio to match with the video (with 44800 representing the maximum number of times per second that the audio would be adjusted) but this didn't help. asetpts did help by letting me adjust the delay of the video. First I'd watch the stream in VLC and see how far off I'd need to adjust the audio (using the J and K I mentioned earlier.) Then I'd kill the stream and adjust the audio by that number of seconds.
As a special note, VLC shows the audio adjustments in milliseconds while FFmpeg accepts the number in seconds so you need to convert.
I never did figure out why the audio and video of my stream were not in sync but these filters along with using the J and K buttons on the keyboard were helpful to compensate.
This is used to change the sampling frequency (which was originally 48000Hz) and so I am cutting it in half here, once again to try to reduce bandwidth.
-vf "yadif=0:-1:1, fps=fps=24000/1001, scale=569:320"
This is another one that needs explaining. While af stood for audio filter, vf stands for video filter. In this case I am using yadif to deinterlace the video (since it was interlaced - if yours is not the yadif filter is not needed.) fps is being used to reduced the number of video frames per second. If you do that math 24000/1001 is about 23.976. Finally, scale was being used to reduce the size of the video frame. FFmpeg can also crop the video frame too if that's needed. I didn't but look on ffmpeg.org for information on the cropping video filter if you need to crop as well.
This is used to tell libvpx to use its real-time encoding mode for live streaming. If you try other options like good or best or whatever you will probably find the encoding speed too slow for real-time use.
This sets the target CPU utilization. I went for using as much as I could. It accepts a range from 0-15.
I used this for multi-threaded encoding. The recommended number to use here is the number of real CPU cores - 1)
libvpx lets you set the encoder complexity so that lower-powered devices can play the video more easily. It accepts a range of 0-3, with 0 being a good default, unless you are having problems with playing stuff on very low power devices.
-crf 10 -vb 256K
This is a critical area for controlling the bandwidth used by the video. crf sets a constant quality mode (from 4-63). 10 is a good starting point. Lower values mean better quality. The -vb sets a maximum amount of bandwidth to use. If you want only constant bitrate leave out the -crf. If you want only constant quality and don't care of what the maximum bitrate might be, leave -vb there but set it to -vb 0M. It must be present.
This sets the MIME type that is sent to Icecast and it shown to people accessing the stream.
This last part tells FFmpeg to send the results to Icecast. Be sure
to replace "password" with whatever password you made up in the
icecast.xml file. Change the IP and port to match
whatever Icecast is listening on (for me, Icecast and FFmpeg were
running on the same machine.) Finally,
file.webm is the
file name that people will see in Icecast. It also forms part of the
URL that people would use to access the stream. In this case:
And that's it. You can give people the direct URL of the stream or direct them to the IP & port in their browser to interact with Icecast directly.
You can also invoke FFmpeg multiple times if you want multiple streams to be available in Icecast.
Copyright © 2016 Jason Self. See license.shtml for license conditions. Please copy and share.