Concept
I had an old GoPro Hero 4 lying around that I wanted to put to use as a webcam through HomeKit. With its wide field of view, I think this is the perfect use case for old GoPros that may be outdated in terms of video quality. I specifically wanted to use my Raspberry Pi Zero for this task as it is already running Homebridge, which makes it easy to tie into Apple’s HomeKit with a nifty plug-in. This way I can access the feed at any time from anywhere without worrying about opening up a streaming server to the web. This does mean that the video stream runs through Apple’s servers if I access the stream from the internet. As I will only be pointing it out the window this isn’t a problem for me.
I poked around on the internet to see if it is even possible to get a constant feed from the GoPro into the Pi let alone if the Raspberry Pi Zero is capable of de- and encoding the video stream live. It turns out, not many people want to use a $400 action camera as a simple webcam… hmmm… guess I’m on my own on this one.
The Hardware
The first challenge is to get a live video feed from the GoPro to the Pi. The GoPro has some built-in WiFi features, but these can only be used with the GoPro app. The mini USB port on the GoPro is also only for data transfer and charging, so my only option is to get the video off of the micro HDMI port. The Raspberry Pi Zero has a mini HDMI port, but this can only be used as a signal out from the Pi for an external display or something. My only chance is to somehow capture the video with the Pi’s micro USB port.
I did some digging and came across some USB-HDMI capture cards for about $10. Not bad! Since the Raspberry Pi Zero only has USB 2.0 and all the capture cards seem to boast USB 3.0 compatibility, I was skeptical if the USB on the Pi would even be capable of receiving the HD video signal from the capture card. With a micro USB to USB adapter and a micro HDMI to HDMI cable in my shopping cart as well, I decided to give it a try anyway.
All in all, I needed the following to complete the project:
- Raspberry Pi Zero with 16GB SD-Card and Power
- GoPro Hero 4 (or any other camera with an HDMI output, I guess)
- HDMI Capture Card (I used this one)
- Micro-HDMI to HDMI Cable (BerryBase)
- Micro-USB OTG Adapter (I like this extremely minimal one)
With all the non-compatible ports and many adapters, the solution is not particularly elegant or compact. My setup looks as follows:
Make sure you note the input and output quality of the capture card. In my case, I can feed it a 4K stream and it can output a maximum of 1920x1080 video, which is perfect for my use case as the GoPro can record up to 4K and 1080p is plenty for a webcam.
Testing Video Capture
Now that everything is hooked up, it’s time to see whether or not the Pi Zero can detect the capture card and its output formats.
The first step is to make sure video4linux is installed.
sudo apt install v4l-utils
Now you can list all devices.
v4l2-ctl --list-devices
For me, I can see the following:
USB3. 0 capture: USB3. 0 captur (usb-20980000.usb-1): /dev/video0 /dev/video1
So without any further configuration, my USB capture card
is detected! Now to check the formats that are output by the
capture card using ffmpeg
. First, install
FFmpeg with sudo apt-get install ffmpeg
, and
then list the formats for the video device (in my case
/dev/video0
):
ffmpeg -f v4l2 -list_formats all -i /dev/video0
In my case, the capture card can output both raw and compressed video using the yuyv422 and MJPEG codec respectively. Each is able to output at a maximum resolution of 1920x1080, which is plenty for a webcam.
[video4linux2,v4l2 @ 0x1fffe40] Compressed: mjpeg : Motion-JPEG : 1920x1080 1600x1200 1360x768 1280x1024 1280x960 1280x720 1024x768 800x600 720x576 720x480 640x480
[video4linux2,v4l2 @ 0x1fffe40] Raw : yuyv422 : YUYV 4:2:2 : 1920x1080 1600x1200 1360x768 1280x1024 1280x960 1280x720 1024x768 800x600 720x576 720x480 640x480
Since HomeKit only accepts H.264 video codecs, we,
unfortunately, need to de- and encode one of these two video
codecs to H.264, which be a challenge for the extremely
low-powered Pi Zero. The Raspberry Pi Zero actually comes
with hardware encoding built-in using h264_omx
.
To test if everything works we can record a quick video
using FFmpeg (use Ctl+C to stop recording):
ffmpeg -f v4l2 -video_size 1920x1080 -framerate 30 -i /dev/video0 -codec h264_omx vid_1080p_30.avi
If we copy this file to our computer
scp pi@<pi_address>:/home/pi/vid_1080p_30.avi Desktop
and play it (using VNC)…
we see a grainy and slow mess…
This could be because ffmpg is using the RAW
video codec as input by default. We can force it to use the
already compressed MJPEG codec to potentially cut the Pi
Zero some slack using: -input_format mjpeg
.
For me, this did not work, because apparently, the
capture card uses a deprecated pixel format with MJPEG,
which h264_omx
cannot process. Guess I have to
stick to decoding the RAW codec, which also has a limited
frame rate of 5 fps.
Tipp: If you are doing this project with a Raspberry Pi 4 with a 64-Bit OS,
h264_omz
will not work, as it is technically deprecated. You either need to use CPU encoding (which does NOT work on the Pi Zero) withlibx264
or use the newer hardware encoderh264_v4l2m2m
. This does not work out of the box, and requires compiling ffmpeg. A good guide here.
Homebridge
The next step would be to install Homebridge and Homebridge UI by following the setup guide here. In my case, I am connecting the camera to the Pi Zero that is also running Homebridge, so I don’t need to stream the video to another device running Homebridge.
Once Homebridge is up and running, we need to install the Homebridge Camera FFmpeg Plugin by just searching for it under the Plugins tab.
Now we can go into settings and set up the stream! The settings mainly reflect those we used when testing the encoding.
- Name: GoPro
- Video Source: -f v4l2 -i /dev/video0
- Unbridge Camera: Enabled
- Video Output -> Maximum Width: 1920
- Video Output -> Maximum Height: 1080
- Video Output -> Maximum Framerate: 30
- Advanced -> Video Codec: h264_omx
- Advanced -> Encoder Options: -vsync 2
In the end, your config.json should look as follows for the camera:
{
"name": "Camera FFmpeg",
"cameras": [
{
"name": "GoPro",
"unbridge": true,
"videoConfig": {
"source": "-f v4l2 -i /dev/video0",
"maxWidth": 1920,
"maxHeight": 1080,
"maxFPS": 30,
"vcodec": "h264_omx",
"encoderOptions": "-vsync 2",
"debug": false
}
}
],
"platform": "Camera-ffmpeg"
}
The max settings we chose under Video Output are not that
important. HomeKit automatically selects which quality is
best, but this way HomeKit doesn’t try to request a higher
quality than our capture card can output. The
-vsync 2
under Encoder Options is important to
sync up the framerate of the input and output. This way we
don’t send duplicate frames or drop any frames. Lastly, we
set the camera to unbridge mode, to prevent streaming from
slowing down the rest of the Homebridge instance. If there
are not many other plug-ins running, this is not all that
important.
Now restart Homebridge to apply the settings! Since the camera is in unbridged mode, we need to add it manually by hitting the plus in the top right of the HomeKit interface, “Add Accessory”, press “I Don’t Have a Code or Cannot Scan” and the “GoPro” camera accessory should appear. Then enter the same 8-digit ID as the Homebridge instance. If everything went we should have a working stream!
It takes a little bit for the video to appear, and the quality may not be that great. Unfortunately, that is the limitation of the Raspberry Pi Zero’s horsepower. Maybe tweaking the allocated GPU memory might help, but I think this is fine for a basic webcam. If something does not work just enable the ffmpeg debug, and see what it spits out. To get the stream working just right, can take a lot of tweaking… trust me…
As for the GoPro settings, I decided to remove the battery and just power it directly through the mini USB port. The camera does get quite hot from being on 24/7, so having a battery in there subjected to that heat can’t be too good. Of course, an SD card is also not really necessary, so I got rid of that too. I set the camera to never turn off, use a resolution of 1440p at 30 FPS, and ProTune on. These settings are quite overkill for what I get in the end, but hey, at least the camera isn’t the bottleneck.