Table of Contents
Ask an Atheist Stream Reflector (2012 Version)
Problem
Not really a problem, but KLAY recently upgraded it's internet streaming solution to one that has ads and is way^50 better quality. It's also ad supported, and it uses a technology that is a bit harder to stream.
Previously, I connected to KLAY's stream, transcoded it to MP3, and streamed it to our icecast server entirely with VLC. The new version uses abst streaming, which looks like HTTP streaming but wasn't something I could simply suss out with VLC.
Eventually, I'll create my own stream either from the studio or using a relatively clear AM receiver piped into a quiet computer (think RasPi), but as there's no internet in the studio and I've no place to put a receiver, scraping the KLAY stream is all we've got.
UPDATE 12/21/2012: For some reason, when I went to start the reflector this last Sunday, the filesystem had been completely borked. No idea why, but the fact that I've started documenting things certainly helped get me back online sooner rather than later.
There are changes, since I discovered that the KLAY stream will play commercials over the live programming.
Solution
So, I've decided to create a small virtual machine that runs a browser and lets me pipe the audio to the streaming server.
Packages Used
- Chromium
- Some form of Click-to-Flash
- alsa tools
- Flash Plugin
- wmctrl
- xdotool
- Some scripting
OS Stuff
I used SLiM to boot directly into a user, and as I tend to do on small systems or projects, I used Window Maker as my window manager. VM runs Arch Linux, as usual.
Update 12/21/2012: I dropped SLiM in favor of booting into X right from systemd using this from the Arch Linux wiki.
Chromium
The first time out, we took advantage of the fact that the stream started as soon as the page was loaded. But since we found out the stream provider plays commercials over the stream, I installed a click-to-flash plugin to prevent flash video from playing1). This meant that we had to click around on the screen to get the flash to load. Fortunately, this also allowed us to turn the volume up on the flash app.
It also meant that if the browser shutdown incorrectly, it would push the “Restore” dialog and throw the coordinates off. To avoid that, we blow away the Chromium config directory and restore from a tar'ed backup on each chromium start.
chromewatch.pl is a little more complex than it started out as. It still uses a lot of sleep()'s and usleep()'s where we can't really watch for things, though.
ALSA Tools
ALSA provides the snd-aloop module, which provides a loopback soundcard. It has two devices. Data sent to one device is sent right back out the other device. Given that the Xen virtual machine has no sound card defined, this soundcard will be the only one in the system, and used by default. Hooray for that.
- In Device: hw:Loopback,0,0
- Out Device: hw:Loopback,1,0
Flash Plugin
Chromium doesn't come with flash, so the flash plugin is installed. Both flash plugins are pretty flaky, so some scripting is required, see below.
DarkIce
DarkIce takes audio from a sound card and streams it to the server. Easy peasy.
Here's the config file:
[general] duration = 0 bufferSecs = 5 [input] device = hw:Loopback,1,0 #device = pcm.makemono sampleRate = 44100 bitsPerSample = 16 channel = 2 [icecast2-0] bitrateMode = cbr format = mp3 bitrate = 64 quality = 0.5 server = media.askanatheist.tv port = 8000 channel = 1 password = PASSWORD.GOES.HERE mountPoint = aaalive.mp3 name = Ask an Atheist Live description = Ask an Atheist Live Stream url = http://askanatheist.tv genre = Live Radio public = yes
I might play around with the quality setting in the future.
Scripting
Flash Watching
Flash in Linux is flaky. Just in testing I managed to get it to crash a few times. I wanted it to be able to recover if the plugin died. Simplest way was to watch the state of the sound card, and if it wasn't open, kill off the browser and relaunch.
chromewach.pl
#!/usr/bin/perl # Chromium Streamer 0.2 # Sam Mulvey <sam@askanatheist.tv> for Ask an Atheist # GPLv3, I guess. Just share the wealth, man. use Time::HiRes qw/usleep/; use File::Path; $watchfile = "/proc/asound/card0/pcm0p/sub0/hw_params"; $wmctrl = "/usr/bin/wmctrl"; $xdotool = "/usr/bin/xdotool"; $chromium = "/usr/bin/chromium"; $killall = "/usr/bin/killall"; $klay_url = "http://p.freestreams.com/?pid=513."; $x_loc = 199; $y_loc = 207; $c_delay = 5000; $window_id = 0; $config_dir = "/home/sam/.config"; $restore_file = "/home/sam/klay_reflect/chromium.tgz"; $SIG{CHLD} = 'IGNORE'; $ENV{'DISPLAY'} = ":0.0"; $|++; for (;;) { open(WATCH, $watchfile); chomp($check = <WATCH>); close(WATCH); if ($check eq "closed") { print "\nSound card not in use. Restarting!\n\n"; # Launch Chromium unless (fork()) { rmtree($config_dir."/chromium"); system("tar -C $config_dir -zxf $restore_file"); usleep(500); `$killall chromium`; exec("$chromium $klay_url"); } # Move Things Around unless (fork()) { # Get Window ID; $window_id = 0; do { chomp(@LIST = `$wmctrl -l`); foreach (@LIST) { (@F) = split(" ", $_, 2); $window_id = $F[0] if ($F[1] =~ m/Chromium/i); } usleep(500); } until ($window_id =~ 'x'); sleep 5; print "WINDOW ID: $window_id\n"; # Maximize! `$wmctrl -iR $window_id`; `$wmctrl -ir $window_id -b add,maximized_vert,maximized_horz`; # findclient: 10485795 # x:199 y:207 screen:0 window:10485795 `$xdotool mousemove $x_loc $y_loc click --repeat 2 --delay $c_delay 1`; exit 0; } sleep 60; print "Continuing to watch."; } else { print "."; } sleep 2; }
There's a gap between the ad and the actual audio, leading to a possible race where chromewatch.pl checks the card status in the gap, but in testing I haven't run into it. However, we've extended the wait period to after the ad play, so it's unlikely that the race will appear. For more notes on the operation of this script, see Chromium.
Start Up
At that point, all that's left is an .xinitrc script that calls all the right stuff:
/usr/bin/urxvt -name ChromeWatch -title ChromeWatch -e /home/sam/klay_reflect/chromewatch.pl & /usr/bin/urxvt -name darkicerunner -title DarkIce -e /usr/bin/darkice -c /home/sam/klay_reflect/darkice.cfg & exec wmaker
Timing
Cron on the dom0 starts and stops as necessary:
# AAA Sunday Schedule (+30 mins each side) 30 14 * * sun /usr/sbin/xl create /etc/xen/noauto/reflector.cfg 30 17 * * sun /usr/sbin/xl shutdown reflector
Screenshot
Old Version
Here is the script:
klay-reflector
cvlc -Idummy mms://vista.streamguys.com/klay1?MSWMExt=.asf \ --sout "#transcode{acodec=mp3,ab=64k,samplerate=44100,channels=2}:std{access=shout{mp3=1},bitrate=64k,mux=raw,dst=user:pass@127.0.0.1:8000/aaalive.mp3}" \ --sout-shout-name "Ask an Atheist Live!" \ --sout-shout-description "Ask an Atheist live on the radio!" \ --sout-shout-bitrate 64 \ --sout-shout-channels 2 \ --sout-shout-samplerate 44100 \ --sout-shout-url http://askanatheist.tv 1>>/home/deadstream/var/klay.log 2>&1 & echo $! > /home/deadstream/var/klay.pid