Fixing powerline flicker on your webcam feed with a udev rule

Posted on Sun 17 January 2021 in hints-and-kinks • 2 min read

If you are

  • spending a non-trivial amount of time in video calls every week (something that, at the time of writing, is true for a lot of people due to the COVID-19 pandemic), and also

  • having to use mains-powered artificial lighting in your office (true at the time of writing for significant portions of the Northern Hemisphere, as it’s winter there),

then you may be dealing with an unpleasant effect where your web cam feed produces a permanent horizontal flicker that is due to the electronic rolling shutter interacting with the (otherwise imperceptible) 50 or 60Hz AC powerline frequency.

The good news is that most webcams come with a facility to eliminate that effect, and on a Linux desktop it’s not difficult to permanently do so.

v4l2-ctl

The utility you want to use for this purpose is v4l2-ctl, which on Ubuntu ships with the v4l-utils package. v4l2-ctl allows you to read and set a bunch of parameters for your webcam. Here’s the set of parameters available for my Razer Kiyo, a piece of kit that I highly recommend:

$ v4l2-ctl --list-ctrls --device=/dev/video0
                     brightness 0x00980900 (int)    : min=0 max=255 step=1 default=128 value=128
                       contrast 0x00980901 (int)    : min=0 max=255 step=1 default=128 value=128
                     saturation 0x00980902 (int)    : min=0 max=255 step=1 default=128 value=128
 white_balance_temperature_auto 0x0098090c (bool)   : default=1 value=1
                           gain 0x00980913 (int)    : min=0 max=255 step=1 default=0 value=0
           power_line_frequency 0x00980918 (menu)   : min=0 max=2 default=2 value=2
      white_balance_temperature 0x0098091a (int)    : min=2000 max=7500 step=10 default=4000 value=4000 flags=inactive
                      sharpness 0x0098091b (int)    : min=0 max=255 step=1 default=128 value=128
         backlight_compensation 0x0098091c (int)    : min=0 max=1 step=1 default=0 value=0
                  exposure_auto 0x009a0901 (menu)   : min=0 max=3 default=3 value=1
              exposure_absolute 0x009a0902 (int)    : min=3 max=2047 step=1 default=127 value=127
         exposure_auto_priority 0x009a0903 (bool)   : default=0 value=1
                   pan_absolute 0x009a0908 (int)    : min=-36000 max=36000 step=3600 default=0 value=0
                  tilt_absolute 0x009a0909 (int)    : min=-36000 max=36000 step=3600 default=0 value=0
                 focus_absolute 0x009a090a (int)    : min=0 max=255 step=1 default=0 value=0 flags=inactive
                     focus_auto 0x009a090c (bool)   : default=1 value=1
                  zoom_absolute 0x009a090d (int)    : min=100 max=140 step=10 default=100 value=100

The value you’re looking for is power_line_frequency. Its default is 2 (compensating for a 60Hz powerline frequency), which means the camera should work out of the box and without any powerline flicker in the Americas and parts of Asia. I am in Europe though, where the mains frequency is 50Hz, so I need to set this to 1:

v4l2-ctl --device /dev/video0 --set-ctrl=power_line_frequency=1

However, it’s rather tedious to run that command every time I want to use the webcam.

udev

Thankfully, this process can be automated with a simple udev rule:

ACTION=="add", SUBSYSTEM=="video4linux", DRIVERS=="uvcvideo", RUN+="/usr/bin/v4l2-ctl --set-ctrl=power_line_frequency=1"

This way, any camera handled by the uvcvideo driver (meaning, practically any contemporary webcam) will have its power line frequency setting configured to the 50Hz value, eliminating the banding effect from the rolling shutter.

Chuck that line into a file in /etc/udev/rules.d, run sudo udevadm trigger, and you should be good to go.

Acknowledgments and further reading

I got the udev rule suggestion from user telcoM’s answer on this StackExchange post. The discussion thread on that post has a few additional suggestions, including some not using udev.