Turn auto-exposure off, set absolute exposure to a low level and use gain to compensate for low light if needed. E.g.:
v4l2-ctl -d /dev/video2 -c exposure_absolute=400
v4l2-ctl -d /dev/video2 -c gain=110
# check with
v4l2-ctl -d /dev/video2 -L
Sample:
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=255
power_line_frequency 0x00980918 (menu) : min=0 max=2 default=2 value=1
0: Disabled
1: 50 Hz
2: 60 Hz
white_balance_temperature 0x0098091a (int) : min=2000 max=7500 step=10 default=4000 value=3200 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=1 value=0
exposure_auto 0x009a0901 (menu) : min=0 max=3 default=3 value=1
1: Manual Mode
3: Aperture Priority Mode
exposure_absolute 0x009a0902 (int) : min=3 max=2047 step=1 default=250 value=180
exposure_auto_priority 0x009a0903 (bool) : default=0 value=0
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=5 default=0 value=10 flags=inactive
focus_auto 0x009a090c (bool) : default=1 value=1
zoom_absolute 0x009a090d (int) : min=100 max=500 step=1 default=100 value=100
Since shutter speeds can't be changed (because we're receiving an image stream), the maximum exposure you should use is the one where your target framerate is stable. The ffmpeg stats will show the current FPS, which must be exactly at your target. If it's below, decrease exposure. Gain doesn't have any effect on the frame rate. So lock in your exposure, then add as much physical light as possible, then add gain if it's still too dark - in that order.
Live preview the feed at 1:1 resolution (you can use v4l2-ctl while this is running to dial in image settings):
ffmpeg -f v4l2 -input_format mjpeg -video_size 3280x2160 -r 24 -i /dev/video2 -c:v copy -f avi - | mpv - >/dev/null 2>&1
(press q to quit)
If you change settings during the live view which affect frame rate / shutter speed, you must restart because ffmpeg's FPS measurement averages over the entire time it's been running.
Keep in mind that higher framerates have lower frametimes and require more light. I.e. to go from 30 to 60 fps, halve the exposure setting and add more light.
The MJPEG stream is capable of high framerate capture, but exposure must be lowered even further. Most interesting are
1280x720
at 90 FPS and 640x480
at 120 (haven't gotten this one to work yet).
Record 90 FPS HF and convert to 30 FPS slow-mo (last line shows off encoding):
# 90 fps HF recording
ffmpeg -f v4l2 -input_format mjpeg -video_size 1280x720 -r 90 -i /dev/video2 -c:v copy -f avi -q 0 -a
n fast.avi
# same quality, just slower
ffmpeg -i fast.avi -filter:v "setpts=3.0*PTS" -r 30 -qscale 0 slow.avi
# medium quality, encoded to 264
ffmpeg -i fast.avi -filter:v "setpts=3.0*PTS" -r 30 -vcodec libx264 -crf 27 -preset fast -an slow.mp4
Recording to a file works the same for non-HF. The recording step above doesn't do any encoding, which produces huge AVI files, but consumes basically no CPU resources. You can easily record many 4k streams at the same time.
If you do encoding at the same time, you'll need a lot of CPU power, or a GPU that can be used for encoding (NVENC for example) and a build of ffmpeg that support that.
I've found that my FPS beings to degrade when
exposure_absolute
is >= ~400. When testing, if increasing the value by 1, the FPS begins to fall (from the max of ~28.986) when issuingexposure_absolute=410
. However, the FPS does not again increase until issuingexposure_absolute=407
, when reversing the test direction.