mathr / blog / #

Running Zoom in a virtual machine

Zoom is a proprietary video conferencing solution that has become popular in these stay-at-home times. I am reticent about installing the Debian package on my main Linux system because that gives it root privileges, and I don't know what it might be doing (historically it has done some Bad Things on various operating systems). So my goal was to install it in a disposable virtual machine, so I can stop the virtual machine when not using Zoom, and be confident that it's not doing anything nefarious with my data or network or whatever else.

I used QEMU as the virtual machine. First step though, enable the "secure virtual machine" thing in your BIOS (for your x86_64 CPU). Otherwise QEMU won't be able to use hardware virtualization and it will be very slow. I used these options for QEMU on my 16-thread CPU with 16GB RAM:

qemu-system-x86_64 -m 8G -cpu host -accel kvm -smp 8

For graphics I want to use OpenGL in the virtual machine, and to use the host mouse in the QEMU window without having to mess with mouse grab stuff, so I set that up with these options:

-vga virtio -display gtk,gl=on -show-cursor -usb -device usb-tablet

Then create a QEMU hard drive image (I did this so long ago that I forgot the details, it wasn't too hard to figure out from the documentation or internet searches). To use it with QEMU attach it like -hda zoom.img. 10GB is plenty. Boot the QEMU image with a Debian netinst ISO also attached to the virtual CD drive and install it on the virtual hard drive. Buster (current stable) is fine for my purposes. Then shut down the VM.

I wanted to pass through access to my USB soundcard and USB webcam, so that I wouldn't have to mess with the nightmare that is Pulseaudio. First step is running lsusb on the host, to see what and where the devices are attached:

$ lsusb
Bus 005 Device 003: ID 0582:0074 Roland Corp. EDIROL UA-25
Bus 005 Device 002: ID 041e:4095 Creative Technology, Ltd Live! Cam Sync HD [VF0770]
...

I changed the ownership of the device nodes to the user running QEMU:

sudo chown claude:claude /dev/bus/usb/005/002 /dev/bus/usb/005/003

(needs to be done after each boot) and added the relevant config to my QEMU startup script:

-device qemu-xhci \
-usb -device usb-host,vendorid=0x041e,productid=0x4095 \
-usb -device usb-host,vendorid=0x0582,productid=0x0074

That was all working nicely, with screen sharing in Zoom with webcam processed by home made code, but at the last minute we needed to add an extra webcam for use as regular video conferencing chat interface. Naively adding the extra USB device gave this error from QEMU:

qemu-system-x86_64: Warning: speed mismatch trying to attach usb device "..." (high speed) to bus "...", port "..." (full speed)

It says warning, but the device is not usable in the VM.

I eventually figured out I could add an extra virtual USB controller (trying to pass through the root hub USB device didn't work, and would reset all the USB devices in the process...):

-device qemu-xhci,id=xhci -device usb-ehci \
-usb -device usb-host,vendorid=0x041e,productid=0x4095 \
-usb -device usb-host,vendorid=0x0582,productid=0x0074,bus=xhci.0 \
-usb -device usb-host,vendorid=0x1908,productid=0x2311

This almost worked: each camera worked individually, but trying to use both at the same time spat out an error:

libv4l2: error turning on stream: No space left on device

which turns out really means "insufficent USB bandwidth". The solution for this problem turned out to be adding another virtual USB controller:

-device qemu-xhci,id=xhci -device usb-ehci,id=ehci1 -device usb-ehci,id=ehci2 \
-usb -device usb-host,vendorid=0x041e,productid=0x4095,bus=ehci1.0 \
-usb -device usb-host,vendorid=0x0582,productid=0x0074,bus=xhci.0 \
-usb -device usb-host,vendorid=0x1908,productid=0x2311,bus=ehci2.0

and now two webcams and the soundcard worked fine all at once.

Here is my complete startup script; I think the Pulseaudio stuff is not needed any more (left from earlier experiments) but I didn't test with it removed yet:

#!/bin/bash
QEMU_AUDIO_DRV=pa \
QEMU_PA_SAMPLES=8192 \
QEMU_AUDIO_TIMER_PERIOD=99 \
QEMU_PA_SERVER=/run/user/1000/pulse/native \
qemu-system-x86_64 \
-soundhw hda \
-device qemu-xhci,id=xhci -device usb-ehci,id=ehci1 -device usb-ehci,id=ehci2 \
-m 8G -hda zoom.img -cpu host -accel kvm -smp 8 \
-vga virtio -display gtk,gl=on -show-cursor -usb -device usb-tablet \
-usb -device usb-host,vendorid=0x041e,productid=0x4095,bus=ehci1.0 \
-usb -device usb-host,vendorid=0x0582,productid=0x0074,bus=xhci.0 \
-usb -device usb-host,vendorid=0x1908,productid=0x2311,bus=ehci2.0