В предыдущем названии темы было слово "не концепт". Сам не знаю, как теперь это объяснять . Просто мои улучшения строятся на основе статьи Squier: https://habr.com/ru/post/136388/
Если делить на "proof of concept" и "рабочие" решение, то тут по идее любое решение - рабочее.
Однако, как уже ответили в комментариях в статье, у кого-то даже при частоте 1/5fps был постоянно нагружен процессор. Меня это тогда заставило отказаться от идеи адаптивной подсветки (т.к. не нашел других готовый решений). К тому же, когда делаю большие обновления (типа пересборки мира), кроме постоянного нехвата производительности иногда легко словить своп, поэтому по возможности приспосабливаю свое окружение именно под такие условия.
Недавно я вернулся к этой теме, предварительно набив руку на скриптах к испольнителям tint2. Удалось получить такой вариант, что нагрузка почти незаметна (нужно пристально считать проценты в htop чтоб заметить разницу), а реакция - 30fps вместо 1/5fps (даже фейд как в acpilight лишний). В общем, думаю описать возможные улучшения, которые пришли в голову.
1. Первым улучшением был перевод максимального количества работы на ffmpeg. ImageMagick - слишком мощный и требовательный для такой задачки.
Добавляем параметры ffmpeg:
-s 1x1: съемка в наименьшем возможном разрешении (для меня 160x120)
-vf "scale=1:1" - сразу в 1x1
-pix_fmt gray: вывод в сером
-vcodec png: выбираем несложный формат с поддержкой grayscale
convert теперь остается только переводчиком в текстовый формат.
Математику переводим на awk.
Почему awk - я сравнивал bash, dash, bc и awk используя тест: i=0; while (i<1000000) i+=1;
dash быстрее bash в 3..4x, bc быстрее еще на столько же, и awk без -M (множественная точность) быстрее bc также или даже больше (хотя с -M он даже проигрывает на пару процентов).
Shell
ffmpeg -f v4l2 -s 1x1 -i /dev/video0 -vcodec png -vf scale=1:1:flags=fast_bilinear -f image2 -pix_fmt gray /tmp/snapshot.png 2>/dev/null
convert snapshot.png txt:- | sed 's/[^(]*(\s*\([0-9]*\)[,\.)].*/\1/p;d' |\
awk '{
in_max=255 # Цвет ввода для 100% подсветки
bl_max=100 # Диапазон подсветки
bl_min=0
bl = $1 * bl_max / in_max
print bl > bl_max ? bl_max : bl < bl_min ? bl_min : bl }'
2. ffmpeg может висеть на фоне с запущенной камерой, периодически обновляя файл.
Добавляем опции вывода:
-update true: включить обновление, опция мюксера image2
-r 2: частота обновлений (для примера - 2 fps)
Остальная часть обработки синхронизируется с помощью inotifywait из inotify-tools. Главный профит - камера не закрывается между обновлениями, так что их частота теперь ограничена только быстродействием системы. Вообще все кроме convert можно перенести на постоянную основу. stdbuf необходим всему, что само не сбрасывает буфера. По идее у sed есть для этого опция, но почему-то с stdbuf лучше чем с опцией.
Shell
ffmpeg -f v4l2 -s 1x1 -i /dev/video0 -vcodec png -vf scale=1:1:flags=fast_bilinear -f image2 -update true -r 2 -pix_fmt gray /tmp/snapshot.png 2>/dev/null &
{ while inotifywait -q -e modify /tmp/snapshot.png; do
convert...
done;} | stdbuf -oL sed ...| stdbuf -oL awk
Буферизацию до hexdump отключаем от слова совсем.
Shell
stdbuf -o0 ffmpeg -f v4l2 -s 1x1 -i /dev/video0 -vcodec rawvideo -vf "scale=1:1:flags=fast_bilinear" -f rawvideo -r 2 -pix_fmt gray - 2>/dev/null \
| stdbuf -oL hexdump -e '1/1 "%u\n"'
Повторяющиеся значения можно отсеять до hexdump:
Shell
| stdbuf -o0 tr -s '[\000-\377]' \
Shell
-vf scale="1:1:flags=fast_bilinear",curves=all="0/0 ${in_max}/1"
Код: Выделить всё
bl = $1 * bl_max / 255
Код: Выделить всё
curves=all="0/${bl_min} ${in_max}/${bl_max}"
Выполнение комманд:
Shell
| stdbuf -oL hexdump -e '1/1 "xbacklight -set %u\n"' | sh
Shell
| stdbuf -oL hexdump -e '1/1 "light -S %u\n"' | sh
Shell
| stdbuf -oL hexdump -e '1/1 "%u\n"' > ${ctlfile}
tr бесполезен при 16bpc (широкие символы не поддерживаются). Для фильтрации повторов используем grep после hexdump:
Shell
| stdbuf -oL hexdump -e '1/2 "%u\n"' \
| stdbuf -oL grep -v -e '^*'
Еще хорошо было бы менять частоту самой камеры, однако установка одинакового параметра -r X и для входа и для выхода дает неожиданные сюрпризы.
Допустим, вы ставите -r5, а минимальный fps камеры - 15. Камера свой параметр повышает до 15, но... параметр для выхода увеличивается на такое же соотношение - если этот параметр одинаковый для входа и выхода, то частота на выходе равна частоте камеры. Но самое интересное - если на входа -r5, а на выходе -r1, то реальная частота на выходе составит 3fps. Можно получить точный fps следующим образом:
Shell
r_in=$( v4l2-ctl --device /dev/video0 -p $(echo | awk "{ print 1/${delay} }") | awk '{print $5}' )
ffmpeg -f v4l2 -s 1x1 -r ${r_in} -i /dev/video0 ...
Код: Выделить всё
-vf scale="1:1:flags=fast_bilinear",curves=all="0/0 ${bl_mul}/1",tmix=frames=128
Проблему старшего байта можно решить, заменив hexdump на od из coreutils. Заодно минус одна внешняя зависимость. Правда, если для управления яркостью используется выполнение команд вроде xbacklight, то количество зависимостей не меняется. Можно для обертки цифр в команды для запуска использовать sed, т.к. он побыстрее awk. Сразу вариант 16-битного вывода:
Код: Выделить всё
c_mul=$( echo | awk "{print $max_color / 255}" )
bl_ceil=$( echo | awk "{print $maxbright / 65536}" )
bl_floor=$( echo | awk "{print $minbright / 65536}" )
stdbuf -o0 ffmpeg -f v4l2 -s 1x1 -i /dev/video0 -vcodec rawvideo \
-vf scale=1:1:flags=fast_bilinear,format=gray16le,scale=flags=fast_bilinear,curves=all="0/${bl_floor} ${c_mul}/${bl_ceil}" \
-f rawvideo -r 2 -pix_fmt gray16le - < /dev/null 2>/dev/null \
| stdbuf -o0 tr -s '[\000-\377]' \
| stdbuf -oL od -v -An -w1 -tu1 --endian=little \
| stdbuf -oL sed 's|.*|light -S &|' | sh