Tuesday, June 2, 2026

Ziptuner v1.4

I previously said I'd have to get back to the ffmpeg for IZ2S later...  

So after I finally completed ziptuner v1.3 I did just that.  I suspect ffmpeg is gonna be a long term effort to get it to do everything I want, but at least it seems possible.  To get it started I leveraged the google search AI to help assemble a minimal configuration of mbedTLS v3.6.4, which I eventually used to build v3.6.6 despite their move to CMAKE.  Perhaps for the next round I'll try to leverage this again for a v4 build.  But once again, that's on the back burner til later.  I wound up dropping TLS 1.3 from the config for now, so it's about the same functionally as bearssl.  But it looks like mbedTLS has a future and since it's also used by tiny-curl and openwrt, so that works for me.

I added mbedTLS into the ffmpeg config mix and ran off a new build capable of running some internet radio streams.  I initially started with ffmpeg v4.4 but had some trouble compiling the channel layout code.  I forget the details, but I probably should revisit this someday.  Meanwhile the AI suggested a switch to ffmpeg v4.1 and it seemed to work. After a couple rounds of adding/subtracting components it weighs in at just under 2M (plus some external codecs like libopus).  That's pretty reasonable on the zipit so I moved into test mode. 

I tested it on a couple dozen stations and tweaked the ziptuner wrapper script to pull in the ffmpeg format for ICY metadata StreamTitle update notifications with a single filter for both mpg123 and ffmpeg.  Looks pretty good. 

The AI got really enthusiastic at this point and started suggesting all sorts of enhancements for everything, but within the nonsense were the kernels of some decent ideas that I helped it refine with a few follow up prompts.  I had to keep reminding it to keep the focus on "simple" solutions and resist the urge to over-engineer things.  I really like the way we isolated the tail | sed log file filter pipeline from the -mp3 and -p PLAY button pipelines in the IZ2S ziptuner.sh wrapper script.


At this point the ffmpeg engine could connect to most of the internet radio stations, but the opus and ogg streams seemed to be struggling.  I figured ffmpeg was probably not using the fixed point decoders.  Getting that debugged on internet streams in ffmpeg was a bit of a challenge so I backed off a little and built ogg123 instead since it seemed easier to debug with music files on the SD card.  Once I got ogg and opus running smoothly there I went back to work on ffmpeg.  After some back and forth with the AI, we eventually settled on some confusing tweaks to the configure settings, and then some hacking on ffmpeg sources.  But after all that I finally got it pulled in and working with the fixed point decoders only.

I was hoping to go with an ffmpeg only system but unfortunately ffmpeg with mbedTLS fails to complete the TLS handshake negotiation with a few stations that mpg123 with curl and bearssl handles just fine.  Oh well.  At least the dual mpg123 and ffmpeg setup is viable. While testing the dual player setup I noticed some stations were spamming stream title updates every second or so so I  patched mpg123 to only print the first update for each song change.  I also learned running mpg123 with script disrupts dialog input unless I feed it from </dev/null, so I fixed that as well.

All the testing gave me some ideas for some simple but effective ziptuner improvements, so I coded them up.  Now when the -l logfile option is used it automatically bring up the log viewer screen whenever you start to PLAY a station.  This was handy for debugging the song title filter pipeline but I decided I just like it for normal use as well.  I also added some -x,y,z command options to launch external commands from ziptuner main menu for easy access to handy things like the mixer.   
Now  I can tweak the volume in rexima without exiting the ziptuner.

At this point, with the AI onboard, I finally felt like I might be able to tackle the zombie problem on IZ2S.  I grilled it with questions, hints, and suggestions and and finally forced a few answers out of it that seemed to help.  It looks like the ancient busybox init on initial ramdisk in the stock 2.6.21.1 kernel is so old that it only reaps zombies from programs run in inittab.  Any normal init process will reap the orphaned zombies and keep the process table clean.  That finally explains why the zombies are so annoying on IZ2S.

I also learned all sorts of details about what sorta process tree I get with different approaches to run the player programs, and how to view it through the /proc filesyste.  Using system() to run the STOP command was guaranteed to create a zombie since it blocks the SIGCHLD sygnal, which thwarts any sorta waitpid trap in the ziptuner.  I fixed this, and pruned the process tree to get ffmpeg situated as a direct child of ziptuner and voila, no zombies on IZ2S with the ffmpeg player pipeline.

At this point I decided it was time for a ziptuner v1.4 release.

I still need to hack ffmpeg some more to get song title updates from aac and ogg/opus streams, and also figure out how to fix the tls handshake so I can drop the double player on IZ2S.

Update June 3, 2026

Wouldn't you know it...  As I was cleaning up all my messy AI browser tabs, I had some final thoughts about ffmpeg 4.4.  I happened to have an Ubuntu build of 4.4.2 on a PC so I tried it on one of the stations that was giving me trouble with my zipit build.  It worked.  I checked the build configuration and it listed --enable-gnutls.  This got me thinking, maybe I oughta try one of the full TLS implementations on the zipit after all, but first I asked the AI again if there was an openssl wrapper for bearssl.  Why not?  It must've seen my mouse hovering over the gnutls because this time it mentioned the libtls_bearssl wrapper right after it pronounced bearssl was functionally incapable of emulating openssl.  So I asked the AI if I could build ffmpeg with it.  Sure thing, it drops right in with --enable-libtlsSay what?  Did it enjoy watching me struggle?

I fetched libtls_bearssl from github and built it for the IZ2S zipit.  I had to tweak the Makefile to remove some warning flags unbeknownst to the ancient compiler, and I reverted some file opens with O_CLOEXEC to the somewhat racy code used before that flag was invented.  Otherwise the build went smooth.  It's only about 60K of shim around the smallish bearssl shared library.  Not too shabby!  Next I reconfigured my patched up ffmpeg code base with the --enable-libtls option instead of --enable-mbedtls and it rebuilt (and worked) without a hitch.  No more TLS handshake issues.

I asked the AI what would it recommend for the future when TLS 1.2 is declared obsolete and it said wolfSSL was the way to go for TLS 1.3.  Apparently tiny-curl is developed by the wolfSSL team and openwrt still supports wolfSSL despite selecting mbedTLS as the default.  Ok, I'll keep that in mind when that time comes -- if the zipits make it that far into the future.  Some of the tacky plastic on them is starting to get sticky.  That's a bit of a bad omen.

I added a lean ffmpeg/bearssl version of the ziptuner v1.4 package as v1.4.1. 


Saturday, April 18, 2026

Working Title

During the COV-ID craziness I gave up on github because Microsoft started demanding a 2 factor setup for logins, which made it feel way too much like work.  I hate that stuff.  But it's been like 5 years and I still get the urge to code so I finally gave in and set it up.  The MS github page still complains my 2 factor selection is obsolete, with threats to lock me out, but it works for now.  So I've resumed work on the ziptuner, trying to knock off some lingering items on the To Do list and clean up the crusty old code while I'm at it. 

https://github.com/deeice/ziptuner/commits/master/

Here you can see the "Now playing" line shows the station name instead of just the number.  I've also added several additional example scripts for more back-end players, including some that run in a separate window.  

  

I kinda like the way SMPlayer shows the current song title from the meta tags and provides convenient access to the volume control.  I can't really incorporate that into the main window since the Dialog program is doing all the work and I don't think it has an API for async updates.  Back in the day I borrowed alt console /dev/tty4 on the zipit for status and control of the player program because the ziptuner dialogs hog the entire 320x240 pixel screen. 

 

Unfortunately the modern internet moving from http to https has forced me to build a new curl for the zipit to use as a middle man, fetching and decrypting https streams and piping the unencrypted stream to the old players which lack support for that.  Changing standards... what a hassle.  The 15 year old libcurl from IZ2S couldn't do it so I'm now using tiny-curl with bearssl.  Seems to work, but my build skills are week.  It's significantly smaller than the old IZ2S curl, but bleeding edge openwrt is still the sleekest.  They got some amazing build config setups.

Anyhow, here's a terminal screenshot of the new tiny-curl pipes that I made in an ssh session on the zipit.  The new ziptuner is still pretty lean at 33 Kilobytes.  I'm pretty happy with that.  The mpg123-curl helper is used for http or https streams and mplayer-curl is used for the rest.  This keeps the player pipeline as lean as possible.  That's vital when you only got 32 Megabytes of RAM.

 

I still need to patch in /dev/tty4 support to catch the song titles.  Hopefully I'll can keep this up and have more to say on this soon... 
 

UPDATE Apr 19, 2026

I had thoughts about redirecting the output from mpg123 into a logfile and using a tailbox dialog to monitor it in the ziptuner.  I'd have to add a 4th button called <Logs> to the main screen for but the code for that is really minimal and you get something like this mockup dialog I ran from an ssh session while mpg123 whipped up a log file.  A tiny dose of grep and sed would clean that right up.

But apparently the ICY metadata with song titles is nonstandard web stuff, and curl needs special options to request it and keep it in the stream for https URLs.  It also seems like mpg123 doesn't like the ICY meta in the pipeline when you do that.  Is ICY meta not the same as ID3 tags?  Probably not.  So for the zipit the log idea would really only work for http streams because curl is not required.

This needs some research, and it may just be simpler to rebuild mplayer and mpg123.  Darn it.  I think I'll add the code anyhow since it's so small. 

UPDATE Apr 23, 2026

I compiled the latest mpg123.  It seemed about 10x bigger than the old mpg123 but a long session with the google AI eventually whittled it down to a similar size, cutting junk that's useless on the zipit with quite a few iterations thru the configure script.  Turns out mpg123 itself uses an external curl pipe for https in the current code, but the command line is like a mile long to negotiate all the ICY header junk.  The google AI only gave me tiny subsets of that nonsense in conflicting circular broken answers.  So I never woulda got it working in the ziptuner script.  The good news is the new mpg123 works for both http and https on IZ2S and may actually be tinier tighter code.  It certainly helps keep the ziptuner script sleek with mpg123 handling the curl audio pipelines instead of me.

Mplayer was another story.  I got it to compile but it refused to link.  The problems were all in the internal ffmpeg library.  Something wrong with libm and the floating point.  With an FPU-less armv5te processor any floating point code is gonna be slow and problematic on the zipit.  Meanwhile, mplayer is now mostly just a wrapper around ffmpeg so if I can get a modern ffmpeg to build then maybe I can use that for ziptuner instead.  So I dug in with the AI and eventually came up with an ffmpeg config script for a fairly small fixed point only build.  Sadly I gotta get another ssh/tls library built first if I wanna do https.  It looks like mbed tls is the future.  BearSSL is unsupported by ffmpeg, but mbed tls is currently supported by ffmpeg, curl, and openwrt.  A zipit trifecta, I can get it to build.  They switched to CMAKE after version 3.6.4 so I think I'll start with that code... but it's all taking too long so I'll have to get back to it later.

The ziptuner c code for the log viewer dialog was actually quite simple, as I said.  But making the metadata logging work from inside the ziptuner was much trickier than expected. The problem is formulating the play script I want to pass to mpg123.  The trouble with this is when you run it in the background mpg123 wants to buffer the ICY message output in 4K blocks.  That's like 100 lines buffered before you see anything.  Not good.  So I spent a few long days back and forth with the google AI before I got something that works.  It suggested stdbuf (or a one line c code shared lib that does the same thing for small systems without coreutils).  That seemed reasonable but it doesn't work with static executables.  That's a no go on the ancient zipit running IZ2S and it took like forever and a day to get the AI to admit it.

Fortunately busybox (which runs most of the zipit) comes with a script applet that does the trick, but formulating the exact command to make it work was like pulling teeth.  Again the AI sent me off in the wrong direction with many ever so slightly broken variations of the same command.  But eventually I got it down to the mp3 line in this, which gives me pretty much what you see above from my initial tests with command line scripts.  

LANG=C DIALOGRC=/usr/local/share/ziptuner/dialogrc.soho ./ziptuner $A -u \
 -mp3 'script -aqc "echo && date && mpg123 %s 2>&1" /tmp/ziptuner.log >/dev/null' \
 -p '>/dev/null 2>&1 mplayer-curl' \
 -s '{ killall mpg123; killall mplayer-10rc2; killall curl; } 2>/dev/null' \
 /usr/share/radio /mnt/sd0/gmu/playlist.m3u

It drops a date/time stamp in the log before running mpg123.  The -a tells script to append to the /tmp/ziptuner.log file or else it would overwrite whenever I change the channel.  The q is for quiet and the c runs the command in quotes.  The >/dev/null after /tmp/ziptuner.log is for stdout from script itself.  Without it script will spam your tty with the same stuff it redirects into the ziptuner.log file.  

 I want to add a sed pipeline to the script command and maybe clean up the log a bit.  This example leaves only the "Artist - Song Title" part of the ICY-META lines.

sed -u -e "s/.*Title=.//" -e "s/\x27;.*//" 

Unfortunately incorporating that is yet another struggle.  The sed commands themselves are relatively simple, and I've already tested some that make it look pretty nice, but it leads to triple nested quotes (and maybe a subshell) when I add it the the scripty mess above.  So I suspect I should push this down to the bottom of my new To Do list.  

The AI seems to agree, or it was just trained to be obsequious.  When questioned on the evaluation order with it's choice of nested quotes it said, "You’re right to be skeptical—nested quoting is the fastest way to break a shell script."  Then reworked it's shoddy code to put the single quotes on the outside.  In the end I think this is gonna make me bring back the player helper script.  I hate to do that, but Dialog really seems to need the \r characters stripped out for the tailbox or textbox to work and it's the least ugly way to isolate quote nesting layers.

First on the list should be another button or two in the Log Viewer so I can toggle it from a live tailbox to a static textbox with scrollbars.  Also a log <DELETE> button might be nice if it gets too big.  I should be able to code that up quickly in the ziptuner c code.  Then maybe do some serious regression testing and a new release before I get bogged down in sed pipelines and triple nested quotes, or ffmpeg builds.

UPDATE Apr 28, 2026

It didn't take long for that dream to die.  Dialog doesn't support any extra buttons on the textbox or tailbox widgets.  So I lowered my expectations to a simple tailbox for the log viewer.  If I squeeze out blank lines with the filter then I get maybe an hour of song titles on the screen.  Should be enough.  I can always review the full log later in a text editor.  

Turns out Dialog doesn't like putting the tail on a named pipe either, so after many failed efforts I eventually saw the light and put the entire tail | sed filter outside the ziptuner process and managed it with the startup script.  No named pipes, just more files in the /tmp filesystem which runs in RAM so it should be fast.  And it works.  Sometimes you just gotta settle and take whatever works in a cranky old embedded box like the zipit.

Here's what I've got now in the ziptuner.sh script.

#!/bin/sh

cd /usr/local/share/ziptuner

# First, put up a dialog to ask about auto-resume.  Then launch ziptuner.
if [ -f ziptuner.fav ] ; then
  LANG=C DIALOGRC=/usr/local/share/ziptuner/dialogrc.soho dialog --clear --title "Zippy Internet Radio Tuner" --yesno "Resume Autoplay?" 0 0
  [ $? -eq 0 ] && A="-a" || A=
fi

# Start a tail process to run sed filter on ziptuner.log for tailbox dialog in ziptuner.
touch /tmp/ziptuner.log  # Could echo >ziptuner.log to restart from scratch.
tail -30 -f /tmp/ziptuner.log | sed -u -e "s/\r//g" -e "s/.*Title=.//" -e "s/\x27;.*//" -e '/^[[:blank:]]*$/d' >/tmp/logpipe &

TAILPID=$!

LANG=C DIALOGRC=/usr/local/share/ziptuner/dialogrc.soho ziptuner $A -u \
 -mp3 "script -aqc 'echo && date && mpg123 %s 2>&1' /tmp/ziptuner.log >/dev/null" \
 -p '>/dev/null 2>&1 mplayer-curl' \
 -s '{ killall mpg123; killall mplayer-10rc2; killall curl; } 2>/dev/null' \
 -l /tmp/logpipe \
 /usr/share/radio /mnt/sd0/gmu/playlist.m3u 

#cleanup
kill $TAILPID
rm /tmp/logpipe

The first part puts up a quick dialog to ask about resuming play on the favorite station.  The next part runs a continuous tail | sed pipeline in the background to filter the log for readability in the viewer dialog.  It strips out carriage returns, blank lines, and extra stuff on the StreamTitle lines.  Then it launches the ziptuner with a new -l flag to tell it the location of the filtered log file suitable for the tailbox dialog.

I'll try package it all up in a new github release with binaries for the IZ2S zipit, and openwrt too I suppose.  Then maybe I can pivot to a new embedded ffmpeg and mplayer.

https://github.com/deeice/ziptuner/releases/tag/v1.3