Low Latency HDMI Streaming on the Cheap11th March 2019
Getting HD video from one point to another, wirelessly and with low latency/delay isn't cheap. The best-known player in the market, currently, for these kinds of tasks, is Teradek with their BOLT range. Unfortunately, an entry-level Teradek Bolt goes for around R 37500 in South Africa (about $2690). This isn't affordable in a number of contexts, and as such I tried my hand at finding a cheaper solution.
A "cheaper solution" invariably involves commodity hardware, specifically commodity hardware that is also modular and modifiable - so open source. It'd need to be something supporting a wireless connection option. WiFi is ubiquitous, cheap and highly flexible.
Enter the Raspberry Pi Zero W...
This tiny little PCB runs Linux and handily has a built-in H.264 encoder as well as Bluetooth and WiFi - cool! The RPI Zero W also sports a camera connector (Camera Serial Interface or CSI) and that got me wondering: had anyone found a way of getting video from an SDI or HDMI cable into a Raspberry Pi via the CSI interface ? The CSI interface runs directly to the GPU (which does the encoding) and therefore cuts out common CPU-intensive issues that arise when using USB interfaces.
Ah yes, the B101 HDMI to CSI adapter, made by Auvidea. This board handily converts an HDMI stream into a stream that looks like a CSI camera. This board looks like it's Plug 'n Play but I soon found out that that wasn't the case.
Tons of trawling through various forums resulted in me eventually coming up with a partial solution.
You'll need a specific copy of Yet Another Video4Linux Test Application (yavta). This Yavta sets some registers on the video encoder, starts the pipeline and reads out the results to stdout. That stdout can be redirected easily, I used socat (like netcat) to send the output out to another machine via UDP. This is the final command :
./yavta -c -f UYVY -n 3 --encode-to=- -m -T /dev/video0 | socat - udp-sendto:10.0.0.20:5000 on the Pi and
ffplay -probesize 32 -sync ext -fflags nobuffer -flags low_delay -framedrop -strict experimental -i udp://10.0.0.20:5000 on the receiver
But, before running this command you paradoxically have to provide an EDID definition to the V4L drivers, like so :
v4l2-ctl --set-edid=file=1080P30EDID.txt --fix-edid-checksums
and the contents of the EDID file above :
00ffffffffffff005262888800888888 1c150103800000780aEE91A3544C9926 0F505400000001010101010101010101 010101010101011d007251d01e206e28 5500c48e2100001e8c0ad08a20e02d10 103e9600138e2100001e000000fc0054 6f73686962612d4832430a20000000FD 003b3d0f2e0f1e0a2020202020200100 020321434e041303021211012021a23c 3d3e1f2309070766030c00300080E300 7F8c0ad08a20e02d10103e9600c48e21 0000188c0ad08a20e02d10103e960013 8e210000188c0aa01451f01600267c43 00138e21000098000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000
The weird part is that this works for any resolution and frame rate provided it is progressive (not interlaced) and is part of the HD-family of resolutions (namely 1920x1080 and 1280x720, I haven't tested the ugly sister 1440x1080).
Audio, via I2S requires a whole new realm of heartache and I found it to be generally unreliable.
The result is a feed which shows a delay of 10 frames on a 1080P 25fps stream. This is about 400ms - which isn't great, but considering it's going from a camera, through an encoder, out via WiFi to an access point, through a switch, through a router, through a switch and then being decoded on another machine, I think the result is a decent first start.
The next step is to experiment with low latency options in the Pi's H.264 encoder and also test the latency when the link is peer-to-peer.