In case it helps anyone, I never managed to get this working using uv4l. Shame, as uv4l was the only approach I tried that could stream from the pi in near real-time to a browser.
I did find an alternate solution based on http://pi.gbaman.info/?p=150 that worked for me, but viewing the stream requires gstreamer instead of a browser on the viewing machine. That's OK for my needs.
On the pi I used rapivid, and teed the output to both gstreamer and a local file:
raspivid -vf -t 0 -w 640 -h 480 -fps 25 -hf -b 2000000 -o - | tee test_video.h264 | gst-launch-1.0 -v fdsrc ! h264parse ! rtph264pay config-interval=1 pt=96 ! gdppay ! tcpserversink host=10.0.0.239 port=5000
I viewed the stream on my laptop, and found the stream to be good quality and low lag (maybe < .3s latency, not measured).
gst-launch-1.0 -v tcpclientsrc host=10.0.0.239 port=5000 ! gdpdepay ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false
I also used iptraf to check the network traffic out of the pi, it seems to hover around 900kbps - 1Mbps. Bumping the resolution increases this as you would expect. top shows very low cpu usage.
Then to check the quality of the recorded file I made an mp4 file.
MP4Box -add test_video.h264 -fps 25 test_video.mp4
I opened the file in chrome - the quality seems just as good as the stream.