Веткаldd /usr/local/bin/cam | grep -i camera
297 сообщений · –S Ясно: наш cam слинкован с системной библиотекой из /lib, поэтому патчей и не видит. Посмотрим, что осталось от вчерашней сборки: ls -d ~/libcamera ~/libcamera/build 2>/dev/null ls -l ~/libcamera/build/src/libcamera/libcamera.so* 2>/dev/null | head -3 grep -c "imx586" ~/libcamera/src/libcamera/sensor/camera_sensor_properties.cpp 2>/dev/null ls -l /usr/local/lib/ 2>/dev/null | head Если исходник пропатчен и в build лежит свежая библиотека — проще всего запускать прямо из дерева сборки, без установки: cd ~/libcamera LD_LIBRARY_PATH=$PWD/build/src/libcamera:$PWD/build/src/libcamera/base \ LIBCAMERA_IPA_MODULE_PATH=$PWD/build/src/ipa \ LIBCAMERA_IPA_FORCE_ISOLATION=0 \ ./build/src/apps/cam/cam -l 2>&1 | grep -E "properties|helper|^[0-9]:" А если сборки не осталось — тогда переустановим поверх системной, с префиксом /usr, чтобы больше не путаться. 💰 7.13₽ · ⬇️ 225.9k / ⬆️ 1.3k · opus-5 · max · 21сЫ S Разумно — так и сделаем, чтобы всё в системе (включая pipewire) видело пропатченную версию. cd ~/libcamera grep -m1 -A2 "^project(" meson.build # версия должна быть 0.7.x, иначе имя so не совпадёт Если версия сходится, ставим поверх системной. Сначала запомним, чем откатываться: apk info -W /usr/lib/libcamera.so.0.7 2>/dev/null || apk info -W /lib/libcamera.so.0.7 Дальше сборка с правильным префиксом (если каталога build нет — первая команда его создаст): meson setup build --prefix=/usr --libdir=lib --buildtype=release 2>/dev/null || \ meson configure build --prefix=/usr --libdir=lib ninja -C build sudo ninja -C build install И проверка: ldd $(which cam) | grep -i camera cam -l 2>&1 | grep -E "properties|helper|^[0-9]:" Должно исчезнуть всё про imx586/ov8856 и остаться только список камер. Откатиться, если что, одной командой: sudo apk fix libcamera вернёт файлы из пакета. 💰 7.09₽ · ⬇️ 227.2k / ⬆️ 1.2k · opus-5 · max · 20сЫ [0:28:20.723190277] [5459] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. 1: Internal back camera (/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36) 2: Internal back camera (/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a)S Отлично — про imx586 предупреждений больше нет, значит наша сборка встала системной. У ov8856 осталась только жалоба на задержки конвейера, это мелочь и правится потом. Снимаем широкой с фокусом на ходу: LENS=$(for d in /dev/v4l-subdev*; do v4l2-ctl -d $d --list-ctrls 2>/dev/null | grep -q focus_absolute && { echo $d; break; }; done) echo "объектив: $LENS" rm -f /tmp/lc_*.rgb cam -c 2 --capture=40 --file=/tmp/lc_#.rgb & sleep 3 v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 wait ls -l /tmp/lc_*.rgb | tail -2 convert -size 1920x1080 -depth 8 bgr:/tmp/lc_39.rgb /tmp/lc39.png && ls -l /tmp/lc39.png Наведи на текст в полуметре. Присылай lc39.png — по нему сразу будет видно, что осталось: резкость мы этим чиним, а если цвет поедет, то это уже настроечный файл. 💰 6.44₽ · ⬇️ 228.7k / ⬆️ 602 · opus-5 · max · 11сЫ объектив: /dev/v4l-subdev18 [0:29:05.924783733] [5533] INFO Camera camera_manager.cpp:340 libcamera v0.0.0+1-244572db-dirty (2026-08-02T09:37:10GMT) [0:29:05.932660062] [5535] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 2 on pad 0/0: Not a tty [0:29:05.932751469] [5535] WARN CameraSensor camera_sensor_legacy.cpp:402 'ov8856 13-0036': The PixelArraySize property has been defaulted to 1640x1232 [0:29:05.932778396] [5535] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 1 on pad 0/0: Not a tty [0:29:05.932801156] [5535] WARN CameraSensor camera_sensor_legacy.cpp:413 'ov8856 13-0036': The PixelArrayActiveAreas property has been defaulted to (0, 0)/1640x1232 [0:29:05.932828188] [5535] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:29:05.932848917] [5535] WARN CameraSensor camera_sensor_legacy.cpp:421 'ov8856 13-0036': Failed to retrieve the sensor crop rectangle [0:29:05.932868448] [5535] WARN CameraSensor camera_sensor_legacy.cpp:427 'ov8856 13-0036': The sensor kernel driver needs to be fixed [0:29:05.932905636] [5535] WARN CameraSensor camera_sensor_legacy.cpp:429 'ov8856 13-0036': See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information [0:29:05.933634128] [5535] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. [0:29:05.938520968] [5535] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'ov8856.yaml' not found for IPA module 'simple', falling back to '/usr/share/libcamera/ipa/simple/uncalibrated.yaml' [0:29:05.938790083] [5535] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:29:05.938937584] [5535] WARN CameraSensor camera_sensor_legacy.cpp:881 'ov8856 13-0036': The analogue crop rectangle has been defaulted to the active area size [0:29:05.960402813] [5536] INFO IPAProxySoftWorker soft_ipa_proxy_worker.cpp:443 Starting worker for IPA module /usr/lib/libcamera/ipa/ipa_soft_simple.so with IPC fd = 24 [0:29:05.964452567] [5535] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36' for pipeline handler simple [0:29:05.966677678] [5535] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'imx586.yaml' not found for IPA module 'simple', falling back to '/usr/share/libcamera/ipa/simple/uncalibrated.yaml' [0:29:05.990261249] [5537] INFO IPAProxySoftWorker soft_ipa_proxy_worker.cpp:443 Starting worker for IPA module /usr/lib/libcamera/ipa/ipa_soft_simple.so with IPC fd = 29 [0:29:05.995499704] [5535] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a' for pipeline handler simple Using camera /base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a as cam0 [0:29:05.995929028] [5533] INFO Camera camera.cpp:1216 configuring streams: (0) 7992x6000-ABGR8888/sRGB [0:29:05.996474916] [5537] INFO IPASoft soft_simple.cpp:258 IPASoft: Exposure 4-6010, gain 1-16 (0.15) [0:29:05.996942209] [5535] INFO SoftwareIsp software_isp.cpp:299 Input 8000x6000-BGGR-10-CSI2P stride 10000 [0:29:05.997289866] [5535] ERROR DmaBufAllocator dma_buf_allocator.cpp:153 Failed to create dma buf for frame-0: Invalid argument [0:29:05.997389815] [5535] ERROR DmaBufAllocator dma_buf_allocator.cpp:231 Unable to create buffer [0:29:05.997703722] [5533] ERROR Allocator framebuffer_allocator.cpp:97 Stream is not part of /base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a active configuration Can't allocate buffers Failed to start camera sessionS Теперь видно, что происходит: libcamera выбрала полное разрешение — 7992x6000, вход 8000x6000-BGGR-10. Кадр в ABGR8888 весит 192 МБ, и аллокатор dma-buf на таком размере отваливается с «Invalid argument». Вчера мы задавали размер явно, поэтому и работало. Задаём поток руками: LENS=/dev/v4l-subdev18 rm -f /tmp/lc_*.rgb cam -c 2 -s width=1920,height=1080,pixelformat=RGB888 --capture=40 --file=/tmp/lc_#.rgb & sleep 3 v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 wait ls -l /tmp/lc_*.rgb | tail -2 convert -size 1920x1080 -depth 8 bgr:/tmp/lc_39.rgb /tmp/lc39.png && ls -l /tmp/lc39.png Кстати, в логе уже видно, что наш помощник сенсора заработал: строка IPASoft: Exposure 4-6010, gain 1-16 — это наши пределы выдержки и усиления, раньше их не было. 💰 7.54₽ · ⬇️ 231.3k / ⬆️ 1.1k · opus-5 · max · 17сЫ cam0: Capture 40 frames 1811.679601 (0.00 fps) cam0-stream0 seq: 000000 bytesused: 6220800 1811.712923 (30.01 fps) cam0-stream0 seq: 000001 bytesused: 6220800 1811.746126 (30.12 fps) cam0-stream0 seq: 000002 bytesused: 6220800 1811.779450 (30.01 fps) cam0-stream0 seq: 000003 bytesused: 6220800 1811.813072 (29.74 fps) cam0-stream0 seq: 000004 bytesused: 6220800 1811.846080 (30.30 fps) cam0-stream0 seq: 000005 bytesused: 6220800 1811.879662 (29.78 fps) cam0-stream0 seq: 000006 bytesused: 6220800 1811.912707 (30.26 fps) cam0-stream0 seq: 000007 bytesused: 6220800 1811.946052 (29.99 fps) cam0-stream0 seq: 000008 bytesused: 6220800 1811.979355 (30.03 fps) cam0-stream0 seq: 000009 bytesused: 6220800 1812.012667 (30.02 fps) cam0-stream0 seq: 000010 bytesused: 6220800 1812.046018 (29.98 fps) cam0-stream0 seq: 000011 bytesused: 6220800 1812.079308 (30.04 fps) cam0-stream0 seq: 000012 bytesused: 6220800 1812.112627 (30.01 fps) cam0-stream0 seq: 000013 bytesused: 6220800 1812.145966 (29.99 fps) cam0-stream0 seq: 000014 bytesused: 6220800 1812.179289 (30.01 fps) cam0-stream0 seq: 000015 bytesused: 6220800 1812.212607 (30.01 fps) cam0-stream0 seq: 000016 bytesused: 6220800 1812.245913 (30.02 fps) cam0-stream0 seq: 000017 bytesused: 6220800 1812.279362 (29.90 fps) cam0-stream0 seq: 000018 bytesused: 6220800 1812.312559 (30.12 fps) cam0-stream0 seq: 000019 bytesused: 6220800 1812.345873 (30.02 fps) cam0-stream0 seq: 000020 bytesused: 6220800 1812.379196 (30.01 fps) cam0-stream0 seq: 000021 bytesused: 6220800 1812.412534 (30.00 fps) cam0-stream0 seq: 000022 bytesused: 6220800 1812.445837 (30.03 fps) cam0-stream0 seq: 000023 bytesused: 6220800 1812.479157 (30.01 fps) cam0-stream0 seq: 000024 bytesused: 6220800 1812.512496 (29.99 fps) cam0-stream0 seq: 000025 bytesused: 6220800 1812.545919 (29.92 fps) cam0-stream0 seq: 000026 bytesused: 6220800 1812.579400 (29.87 fps) cam0-stream0 seq: 000027 bytesused: 6220800 1812.612601 (30.12 fps) cam0-stream0 seq: 000028 bytesused: 6220800 1812.645942 (29.99 fps) cam0-stream0 seq: 000029 bytesused: 6220800 1812.679318 (29.96 fps) cam0-stream0 seq: 000030 bytesused: 6220800 1812.712652 (30.00 fps) cam0-stream0 seq: 000031 bytesused: 6220800 1812.745800 (30.17 fps) cam0-stream0 seq: 000032 bytesused: 6220800 1812.779317 (29.84 fps) cam0-stream0 seq: 000033 bytesused: 6220800 1812.812500 (30.14 fps) cam0-stream0 seq: 000034 bytesused: 6220800 1812.845845 (29.99 fps) cam0-stream0 seq: 000035 bytesused: 6220800 1812.879181 (30.00 fps) cam0-stream0 seq: 000036 bytesused: 6220800 1812.912394 (30.11 fps) cam0-stream0 seq: 000037 bytesused: 6220800 1812.945866 (29.88 fps) cam0-stream0 seq: 000038 bytesused: 6220800 1812.979090 (30.10 fps) cam0-stream0 seq: 000039 bytesused: 6220800 -rw-r--r-- 1 user user 6220800 Aug 2 09:40 /tmp/lc_cam0-stream0-000038.rgb -rw-r--r-- 1 user user 6220800 Aug 2 09:40 /tmp/lc_cam0-stream0-000039.rgb WARNING: The convert command is deprecated in IMv7, use "magick" instead of "convert" or "magick convert" convert: unable to open image '/tmp/lc_39.rgb': No such file or directory @ error/blob.c/OpenBlob/3655. convert: no images defined `/tmp/lc39.png' @ error/deprecate.c/ConvertImageCommand/3375.S Кадры пошли ровно 30 в секунду, но 40 штук улетели за полторы секунды — то есть фокус я подвинул уже после съёмки. И имя файла у cam своё, # разворачивается в полное. Сделаем аккуратнее: подержим объектив включённым через открытый дескриптор (драйвер усыпляет мотор, как только последний закрывает узел, и линза уезжает в исходное), выставим фокус и снимем всего десяток кадров. LENS=/dev/v4l-subdev18 rm -f /tmp/lc_*.rgb exec 9<>$LENS # держим мотор под питанием v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 cam -c 2 -s width=1920,height=1080,pixelformat=RGB888 --capture=10 --file=/tmp/lc_#.rgb exec 9>&- F=$(ls -t /tmp/lc_*.rgb | head -1) magick -size 1920x1080 -depth 8 bgr:$F /tmp/lc.png && ls -l /tmp/lc.png Если картинка выйдет резкой — значит остаётся только цвет. Если всё ещё мыло, попробуй прямо во время съёмки прогнать несколько значений, чтобы исключить неудачную дистанцию: exec 9<>$LENS cam -c 2 -s width=1920,height=1080,pixelformat=RGB888 --capture=120 --file=/tmp/lcs_#.rgb & for f in 200 400 600 800; do sleep 0.8; v4l2-ctl -d $LENS --set-ctrl focus_absolute=$f; done wait; exec 9>&- 💰 9.26₽ · ⬇️ 234.2k / ⬆️ 2.4k · opus-5 · max · 38сЫ S Проверим, чёрная она по-настоящему или просто тёмная — глазами на глаз это не отличить: F=$(ls -t /tmp/lc_*.rgb | head -1) python3 -c " d=open('$F','rb').read(); print('мин',min(d),'макс',max(d),'среднее',round(sum(d)/len(d),1))" Если максимум ноль — кадр действительно пустой. Если максимум хоть сколько-то есть, то это просто недоэкспозиция: у программного ISP автоэкспозиция сходится за десяток-другой кадров, а мы снимали всего десять. Тогда дай ей время: LENS=/dev/v4l-subdev18 rm -f /tmp/lc_*.rgb exec 9<>$LENS v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 cam -c 2 -s width=1920,height=1080,pixelformat=RGB888 --capture=60 --file=/tmp/lc_#.rgb exec 9>&- F=$(ls -t /tmp/lc_*.rgb | head -1) magick -size 1920x1080 -depth 8 bgr:$F /tmp/lc.png И для сравнения — наш собственный путь на том же сюжете, он заведомо рабочий: W=1920 H=1080 EXPOSURE=1240 GAIN=896 sh ~/lanekit/capture.sh /tmp/f1080c.raw cd ~/lanekit && ./raw10preview /tmp/f1080c.raw /tmp/p.ppm 1920 1080 rggb 2 && magick /tmp/p.ppm /tmp/p1080c.png Если наш кадр видно, а у libcamera чернота — значит дело в её управлении выдержкой, и я знаю, куда смотреть. И на всякий случай: телефон сейчас не лежит объективом вниз? 💰 7.94₽ · ⬇️ 236.7k / ⬆️ 1.4k · opus-5 · max · 29сЫ S Полный ноль — значит либо сенсор отдаёт темноту, либо ISP съедает всё вычитанием уровня чёрного. Разница принципиальная, и меряется она одним запуском: посмотрим, что libcamera выставляет сенсору прямо во время съёмки. SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") LENS=/dev/v4l-subdev18 rm -f /tmp/lc_*.rgb exec 9<>$LENS v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 cam -c 2 -s width=1920,height=1080,pixelformat=RGB888 --capture=90 --file=/tmp/lc_#.rgb & sleep 2; echo "-- через 2 с:"; v4l2-ctl -d $SD --get-ctrl exposure,analogue_gain,horizontal_flip,vertical_flip sleep 1; echo "-- через 3 с:"; v4l2-ctl -d $SD --get-ctrl exposure,analogue_gain wait; exec 9>&- F=$(ls -t /tmp/lc_*.rgb | head -1) python3 -c "d=open('$F','rb').read(); print('макс',max(d),'среднее',round(sum(d)/len(d),2))" Что я жду увидеть: если автоматика работает, выдержка за пару секунд уползёт к максимуму режима (около 1240 строк), а усиление к 960. Если же она стоит на 4 — значит IPA вообще не пишет в сенсор, и копать надо туда. Кстати, вчера картинка была хоть и плохая, но видимая — и это важная зацепка: вчера работала системная libcamera без нашего помощника, то есть без уровня чёрного, и ничего не вычиталось. Сегодня помощник появился, и вместе с ним вычитание 16 единиц из восьмибитного кадра. В тёмной сцене этого хватит, чтобы всё ушло в ноль. 💰 23.14₽ · ⬇️ 720.1k / ⬆️ 4.4k · opus-5 · max · 75сЫ cam0: Capture 90 frames 2196.868027 (0.00 fps) cam0-stream0 seq: 000000 bytesused: 6220800 2196.901477 (29.90 fps) cam0-stream0 seq: 000001 bytesused: 6220800 2196.934740 (30.06 fps) cam0-stream0 seq: 000002 bytesused: 6220800 2196.968110 (29.97 fps) cam0-stream0 seq: 000003 bytesused: 6220800 2197.001322 (30.11 fps) cam0-stream0 seq: 000004 bytesused: 6220800 2197.034670 (29.99 fps) cam0-stream0 seq: 000005 bytesused: 6220800 2197.068159 (29.86 fps) cam0-stream0 seq: 000006 bytesused: 6220800 2197.101506 (29.99 fps) cam0-stream0 seq: 000007 bytesused: 6220800 2197.134708 (30.12 fps) cam0-stream0 seq: 000008 bytesused: 6220800 2197.168118 (29.93 fps) cam0-stream0 seq: 000009 bytesused: 6220800 2197.201225 (30.21 fps) cam0-stream0 seq: 000010 bytesused: 6220800 2197.234638 (29.93 fps) cam0-stream0 seq: 000011 bytesused: 6220800 2197.267906 (30.06 fps) cam0-stream0 seq: 000012 bytesused: 6220800 2197.301070 (30.15 fps) cam0-stream0 seq: 000013 bytesused: 6220800 2197.334645 (29.78 fps) cam0-stream0 seq: 000014 bytesused: 6220800 2197.367964 (30.01 fps) cam0-stream0 seq: 000015 bytesused: 6220800 2197.401297 (30.00 fps) cam0-stream0 seq: 000016 bytesused: 6220800 2197.434511 (30.11 fps) cam0-stream0 seq: 000017 bytesused: 6220800 2197.467667 (30.16 fps) cam0-stream0 seq: 000018 bytesused: 6220800 2197.501154 (29.86 fps) cam0-stream0 seq: 000019 bytesused: 6220800 2197.534599 (29.90 fps) cam0-stream0 seq: 000020 bytesused: 6220800 2197.567945 (29.99 fps) cam0-stream0 seq: 000021 bytesused: 6220800 2197.601116 (30.15 fps) cam0-stream0 seq: 000022 bytesused: 6220800 2197.634574 (29.89 fps) cam0-stream0 seq: 000023 bytesused: 6220800 2197.667629 (30.25 fps) cam0-stream0 seq: 000024 bytesused: 6220800 2197.701264 (29.73 fps) cam0-stream0 seq: 000025 bytesused: 6220800 2197.734541 (30.05 fps) cam0-stream0 seq: 000026 bytesused: 6220800 2197.767680 (30.18 fps) cam0-stream0 seq: 000027 bytesused: 6220800 2197.801140 (29.89 fps) cam0-stream0 seq: 000028 bytesused: 6220800 2197.834477 (30.00 fps) cam0-stream0 seq: 000029 bytesused: 6220800 2197.867817 (29.99 fps) cam0-stream0 seq: 000030 bytesused: 6220800 2197.901108 (30.04 fps) cam0-stream0 seq: 000031 bytesused: 6220800 2197.934288 (30.14 fps) cam0-stream0 seq: 000032 bytesused: 6220800 2197.967708 (29.92 fps) cam0-stream0 seq: 000033 bytesused: 6220800 2198.001035 (30.01 fps) cam0-stream0 seq: 000034 bytesused: 6220800 2198.034368 (30.00 fps) cam0-stream0 seq: 000035 bytesused: 6220800 2198.067566 (30.12 fps) cam0-stream0 seq: 000036 bytesused: 6220800 2198.100922 (29.98 fps) cam0-stream0 seq: 000037 bytesused: 6220800 2198.134443 (29.83 fps) cam0-stream0 seq: 000038 bytesused: 6220800 2198.167532 (30.22 fps) cam0-stream0 seq: 000039 bytesused: 6220800 2198.200811 (30.05 fps) cam0-stream0 seq: 000040 bytesused: 6220800 2198.234227 (29.93 fps) cam0-stream0 seq: 000041 bytesused: 6220800 2198.267591 (29.97 fps) cam0-stream0 seq: 000042 bytesused: 6220800 2198.300980 (29.95 fps) cam0-stream0 seq: 000043 bytesused: 6220800 2198.334087 (30.21 fps) cam0-stream0 seq: 000044 bytesused: 6220800 2198.367433 (29.99 fps) cam0-stream0 seq: 000045 bytesused: 6220800 2198.400699 (30.06 fps) cam0-stream0 seq: 000046 bytesused: 6220800 2198.434322 (29.74 fps) cam0-stream0 seq: 000047 bytesused: 6220800 2198.467442 (30.19 fps) cam0-stream0 seq: 000048 bytesused: 6220800 2198.500683 (30.08 fps) cam0-stream0 seq: 000049 bytesused: 6220800 -- через 2 с: 2198.534097 (29.93 fps) cam0-stream0 seq: 000050 bytesused: 6220800 exposure: 2260 horizontal_flip: 1 vertical_flip: 1 analogue_gain: 960 2198.567458 (29.98 fps) cam0-stream0 seq: 000051 bytesused: 6220800 2198.600719 (30.07 fps) cam0-stream0 seq: 000052 bytesused: 6220800 2198.634234 (29.84 fps) cam0-stream0 seq: 000053 bytesused: 6220800 2198.667583 (29.99 fps) cam0-stream0 seq: 000054 bytesused: 6220800 2198.700614 (30.27 fps) cam0-stream0 seq: 000055 bytesused: 6220800 2198.734105 (29.86 fps) cam0-stream0 seq: 000056 bytesused: 6220800 2198.767506 (29.94 fps) cam0-stream0 seq: 000057 bytesused: 6220800S
Ы 2198.800465 (30.34 fps) cam0-stream0 seq: 000058 bytesused: 6220800 [0:36:38.864768209] [6060] INFO Benchmark benchmark.cpp:89 Debayer processed 30 frames in 711840us, 23728 us/frame 2198.834043 (29.78 fps) cam0-stream0 seq: 000059 bytesused: 6220800 2198.867121 (30.23 fps) cam0-stream0 seq: 000060 bytesused: 6220800 2198.900599 (29.87 fps) cam0-stream0 seq: 000061 bytesused: 6220800 2198.934039 (29.90 fps) cam0-stream0 seq: 000062 bytesused: 6220800 2198.967409 (29.97 fps) cam0-stream0 seq: 000063 bytesused: 6220800 2199.000483 (30.24 fps) cam0-stream0 seq: 000064 bytesused: 6220800 2199.033803 (30.01 fps) cam0-stream0 seq: 000065 bytesused: 6220800 2199.067188 (29.95 fps) cam0-stream0 seq: 000066 bytesused: 6220800 2199.100448 (30.07 fps) cam0-stream0 seq: 000067 bytesused: 6220800 2199.134004 (29.80 fps) cam0-stream0 seq: 000068 bytesused: 6220800 2199.167120 (30.20 fps) cam0-stream0 seq: 000069 bytesused: 6220800 2199.200566 (29.90 fps) cam0-stream0 seq: 000070 bytesused: 6220800 2199.234022 (29.89 fps) cam0-stream0 seq: 000071 bytesused: 6220800 2199.267062 (30.27 fps) cam0-stream0 seq: 000072 bytesused: 6220800 2199.300364 (30.03 fps) cam0-stream0 seq: 000073 bytesused: 6220800 2199.333918 (29.80 fps) cam0-stream0 seq: 000074 bytesused: 6220800 2199.367267 (29.99 fps) cam0-stream0 seq: 000075 bytesused: 6220800 2199.400368 (30.21 fps) cam0-stream0 seq: 000076 bytesused: 6220800 2199.433722 (29.98 fps) cam0-stream0 seq: 000077 bytesused: 6220800 2199.467192 (29.88 fps) cam0-stream0 seq: 000078 bytesused: 6220800 2199.500564 (29.97 fps) cam0-stream0 seq: 000079 bytesused: 6220800 2199.533604 (30.27 fps) cam0-stream0 seq: 000080 bytesused: 6220800 -- через 3 с: exposure: 2260 analogue_gain: 960 2199.567149 (29.81 fps) cam0-stream0 seq: 000081 bytesused: 6220800 2199.600250 (30.21 fps) cam0-stream0 seq: 000082 bytesused: 6220800 2199.633587 (30.00 fps) cam0-stream0 seq: 000083 bytesused: 6220800 2199.667142 (29.80 fps) cam0-stream0 seq: 000084 bytesused: 6220800 2199.700182 (30.27 fps) cam0-stream0 seq: 000085 bytesused: 6220800 2199.733764 (29.78 fps) cam0-stream0 seq: 000086 bytesused: 6220800 2199.766741 (30.32 fps) cam0-stream0 seq: 000087 bytesused: 6220800 2199.800391 (29.72 fps) cam0-stream0 seq: 000088 bytesused: 6220800 2199.833509 (30.20 fps) cam0-stream0 seq: 000089 bytesused: 6220800 макс 205 среднее 124.37S Вот теперь всё в порядке: максимум 205, среднее 124 — нормально проэкспонированный кадр. Чернота была из-за того, что десяти кадров не хватало: автоматика стартует с минимальной выдержки, а вычитание уровня чёрного добивало остатки в ноль. Сейчас она сошлась на выдержке 2260 и усилении 960. Смотрим картинку: F=$(ls -t /tmp/lc_*.rgb | head -1) magick -size 1920x1080 -depth 8 bgr:$F /tmp/lc.png && ls -l /tmp/lc.png Кидай сюда — интересует резкость (фокус мы держали на 600 с открытым дескриптором) и цвет. Заодно из лога видно цену программной обработки: 23.7 мс на кадр, то есть демозаик съедает почти целое ядро на 30 кадрах в секунду. 💰 8.30₽ · ⬇️ 249.6k / ⬆️ 927 · opus-5 · max · 18сЫ S Отлично — значит пайплайн целиком рабочий, остался только цвет. Холодит по двум причинам: в программном ISP сейчас нет цветовой матрицы (без неё сырой отклик сенсора всегда уходит в синь), а баланс белого работает по «серому миру» и на сцене с преобладанием тёплых поверхностей уводит картинку в холод. Лечится настроечным файлом. Посмотрим, что твоя версия libcamera умеет: ls ~/libcamera/src/ipa/simple/algorithms/ cat /usr/share/libcamera/ipa/simple/uncalibrated.yaml Если среди алгоритмов есть ccm — сделаем imx586.yaml с матрицей и гаммой, и цвет сразу станет人ским. Если нет, тогда доступны только уровень чёрного, гамма и баланс, и я подберу их. Ну и в идеале сфотографировать цветовую мишень, чтобы матрицу посчитать честно, а не на глаз. Подойдёт даже распечатанная — но это уже следующий шаг. 💰 7.37₽ · ⬇️ 250.6k / ⬆️ 1.2k · opus-5 · max · 22сЫ Ы ls ~/libcamera/src/ipa/simple/algorithms/ adjust.cpp adjust.h agc.cpp agc.h algorithm.h awb.cpp awb.h blc.cpp blc.h ccm.cpp ccm.h meson.build # SPDX-License-Identifier: CC0-1.0 %YAML 1.1 --- version: 1 algorithms: - BlackLevel: - Awb: # Color correction matrices can be defined here. The CCM algorithm # has a significant performance impact, and should only be enabled # if tuned. # - Ccm: # ccms: # - ct: 6500 # ccm: [ 1, 0, 0, # 0, 1, 0, # 0, 0, 1] - Adjust: - Agc: ...S Ccm есть — значит цвет чинится файлом, без пересборки. Клади настройку: sudo tee /usr/share/libcamera/ipa/simple/imx586.yaml >/dev/null <<'EOF' # SPDX-License-Identifier: CC0-1.0 %YAML 1.1 --- version: 1 algorithms: - BlackLevel: - Awb: - Ccm: ccms: - ct: 2900 ccm: [ 1.55, -0.44, -0.11, -0.29, 1.61, -0.32, -0.03, -0.72, 1.75 ] - ct: 6500 ccm: [ 1.60, -0.52, -0.08, -0.23, 1.60, -0.37, -0.04, -0.55, 1.59 ] - Adjust: - Agc: ... EOF И повтори съёмку тем же способом: LENS=/dev/v4l-subdev18 rm -f /tmp/lc_*.rgb exec 9<>$LENS v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 cam -c 2 -s width=1920,height=1080,pixelformat=RGB888 --capture=90 --file=/tmp/lc_#.rgb 2>&1 | grep -E "imx586.yaml|Ccm|ERROR" exec 9>&- F=$(ls -t /tmp/lc_*.rgb | head -1) magick -size 1920x1080 -depth 8 bgr:$F /tmp/lc_ccm.png Матрицы пока типовые для сенсоров Sony — одна для лампы накаливания, одна для дневного света, строки нормированы так, чтобы белое оставалось белым. Это уже сильно лучше единичной матрицы, но честные коэффициенты считаются по снимку цветовой мишени, так что потом заменим. Учти, что демозаик и так ест 24 мс на кадр, а матрица добавит сверху — в самой libcamera про это прямо написано предупреждение. 💰 7.89₽ · ⬇️ 252.3k / ⬆️ 1.8k · opus-5 · max · 28сЫ S Отлично — значит цвет закрыт. Сохраню файл в набор, чтобы не потерялся: Итого по широкой камере всё замкнулось: кадры идут, режимы переключаются, фокус двигается, экспозиция сходится сама, цвет правильный. То есть камера работает через libcamera целиком, как обычное устройство — этим уже можно снимать из любого приложения, которое умеет libcamera. Что осталось в этой ветке, по убыванию пользы: Честная матрица. Нынешняя типовая, взята «как у Sony вообще». Если снимешь цветовую мишень (годится распечатанная, лишь бы при дневном свете у окна), посчитаю коэффициенты по твоему экземпляру — уйдут остаточные оттенки на коже и небе. Задержки конвейера. В логе висит No sensor delays found in static properties — libcamera не знает, через сколько кадров после записи применяется новая выдержка, и на резких сменах освещения будет промахиваться. Для imx586 мы это уже прописали, для ov8856 осталось. Телевик. Линии теперь правильные, но кадров нет. Следующий шаг там — вендорный блоб, чтобы сверить время установления и режимы. Что дальше — цветовая мишень, или лучше вернуться к телевику? 💰 13.83₽ · ⬇️ 509.1k / ⬆️ 1.1k · opus-5 · max · 22сЫ S Нет, сама не будет — ни сейчас, ни без нашей работы. Я проверил по исходникам: в обработчике simple про объектив нет ни слова, а в программном ISP всего пять алгоритмов — уровень чёрного, баланс белого, матрица, коррекция и экспозиция. Автофокуса среди них нет, и линзу оттуда никто не трогает. В libcamera автофокус есть только у пайплайнов с железным ISP (Raspberry Pi, IPU3), где его считает вендорная IPA. Два пути. Быстрый, наш. Написать маленький автофокус в userspace: снимаем поток, считаем резкость по центральному окну (сумма градиентов), ползём по focus_absolute вверх-вниз до максимума. Это полсотни строк, работает поверх нашего же raw-пути и даёт нормальную наводку за секунду-полторы. Сделаю прямо сейчас, если хочешь. Правильный, в апстрим. Научить simple отдавать объектив в IPA (в libcamera для этого есть класс CameraLens, просто этот обработчик им не пользуется), добавить в статистику метрику резкости и написать алгоритм Af рядом с остальными пятью. Это уже полноценная фича, которой в libcamera не хватает всем программным пайплайнам, а не только нам — то есть вещь, которую примут. Начать логично с первого: он сразу даёт работающую наводку и заодно проверяет, что метрика резкости на этом сенсоре ведёт себя прилично. Второе потом переиспользует ту же математику. 💰 14.07₽ · ⬇️ 511.8k / ⬆️ 1.8k · opus-5 · max · 39сЫ S Скорее всего оно открывает первую камеру — то есть телевик, а он кадров не отдаёт, и приложение честно ждёт вечно. Плюс мы по дороге убивали pipewire нашими скриптами, а приложения ходят в камеру через него. Сначала вернём pipewire: systemctl --user restart pipewire wireplumber 2>/dev/null || (pipewire & wireplumber &) А телевик пока просто уберём с глаз — драйвер отвязывается на лету, без перезагрузки и правки дерева: echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind cam -l | grep -E "^[0-9]:" Должна остаться одна камера — широкая. После этого запускай приложение, ему выбирать будет не из чего. Вернуть телевик обратно, когда займёмся им: echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/bind 💰 7.20₽ · ⬇️ 257.5k / ⬆️ 804 · opus-5 · max · 14сЫ S Похоже на нехватку памяти: /tmp у тебя в оперативке, а мы туда навалили — 90 кадров по 6.2 МБ это 560 МБ, плюс полноразмерные raw. Плюс программный ISP ест целое ядро на камеру. Проверь и прибери: free -m; df -h /tmp dmesg | grep -iE "oom|killed process|out of memory" | tail -5 rm -f /tmp/lc_*.rgb /tmp/lcs_*.rgb /tmp/f*.raw Дальше давай серии писать не в /tmp, а на диск: mkdir -p ~/cap && cam -c 2 -s width=1920,height=1080,pixelformat=RGB888 --capture=90 --file=$HOME/cap/lc_#.rgb Медленнее, зато система не задохнётся. 💰 7.01₽ · ⬇️ 258.3k / ⬆️ 740 · opus-5 · max · 14сЫ S После перезагрузки телевик снова на месте, так что первым делом убери его и проверь, не в нём ли дело: echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind cam -l | grep -E "^[0-9]:" Если приложение после этого заработает — значит оно спотыкалось именно о камеру, которая не отдаёт кадры. Если ошибка останется, надо посмотреть, что именно оно просит. Запусти его из терминала и покажи хвост: ls /usr/bin | grep -iE "camera|snapshot|megapixels" <имя_бинарника> 2>&1 | tail -40 «The configuration is not valid» — это отказ на этапе согласования формата: приложение запрашивает размер или пиксельный формат, которого программный ISP не даёт. В логе будет видно, что оно просило и что ему предложили взамен. 💰 7.15₽ · ⬇️ 259.1k / ⬆️ 864 · opus-5 · max · 16сЫ S Это plasma-camera. Запусти из терминала и покажи хвост: plasma-camera 2>&1 | tail -40 И заодно проверь, что у нас там теперь с библиотеками — есть подозрение, что мы поставили свою рядом, а не вместо: ls -l /usr/lib/libcamera*.so* | head Дело в том, что наша сборка представляется как v0.0.0+1-244572db-dirty — в дереве нет тегов, поэтому номер версии не определился. Если из-за этого имя библиотеки вышло другим (libcamera.so.0.0 вместо libcamera.so.0.7), то cam берёт нашу, а все остальные приложения по-прежнему системную. Тогда починим одним символическим линком. 💰 7.52₽ · ⬇️ 260.1k / ⬆️ 1.1k · opus-5 · max · 22сЫ qt.qpa.xcb: could not connect to display qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin. qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found. This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem. Available platform plugins are: offscreen, minimal, xcb, vnc, vkkhrdisplay, wayland-brcm, wayland-egl, wayland, linuxfb, minimalegl, eglfs. Aborted (core dumped)S Это оно ругается на отсутствие экрана — по ssh Qt не к чему подключиться. Укажи вейландовский сеанс телефона: export XDG_RUNTIME_DIR=/run/user/$(id -u) ls $XDG_RUNTIME_DIR | grep wayland export WAYLAND_DISPLAY=wayland-0 # подставь то, что нашлось QT_QPA_PLATFORM=wayland plasma-camera 2>&1 | tail -40 Окно откроется на самом телефоне, а сообщения придут в терминал. И покажи всё же список библиотек, это важно: ls -l /usr/lib/libcamera*.so* 💰 7.01₽ · ⬇️ 261.5k / ⬆️ 469 · opus-5 · max · 8сЫ S Значит сообщение рисует сама программа, а причину надо доставать сбоку. Два быстрых шага. Первое — всё-таки покажи, что лежит в системе, это ключевой вопрос: ls -l /usr/lib/libcamera*.so* Второе — проверим тот же путь другим потребителем, через gstreamer: export XDG_RUNTIME_DIR=/run/user/$(id -u); export WAYLAND_DISPLAY=wayland-0 gst-inspect-1.0 libcamerasrc >/dev/null 2>&1 && echo "плагин есть" || echo "плагина нет" gst-launch-1.0 libcamerasrc ! video/x-raw,width=1920,height=1080 ! videoconvert ! waylandsink 2>&1 | tail -15 Если картинка появится на экране телефона — значит libcamera и её потребители в порядке, а спотыкается именно plasma-camera на своём наборе форматов. Если же не появится, ошибка от gstreamer будет куда разговорчивее, чем одна строка в окне. 💰 7.70₽ · ⬇️ 262.0k / ⬆️ 1.4k · opus-5 · max · 26сЫ S Ясно, gstreamer нет — значит plasma-camera ходит в камеру через pipewire (у него есть свой модуль libcamera), либо напрямую через QtMultimedia. Проверим по очереди. Видит ли pipewire камеру: wpctl status 2>/dev/null | grep -A6 -i "video\|source" | head -20 journalctl --user -u pipewire -n 40 2>/dev/null | grep -iE "libcamera|camera|error" | tail -10 И заодно, если при сборке собрался наш собственный просмотрщик — он покажет картинку сам, без pipewire и без Qt-обвязки приложения: ls -l ~/libcamera/build/src/apps/qcam/qcam 2>/dev/null && \ XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 ~/libcamera/build/src/apps/qcam/qcam -c 2 Если qcam покажет живое видео — значит вся цепочка исправна, и разбираться надо только с тем, что просит plasma-camera. Если и он не сможет — причина глубже, и она будет в его логе развёрнуто. 💰 7.93₽ · ⬇️ 263.6k / ⬆️ 1.2k · opus-5 · max · 20сЫ ├─ Sources: │ ├─ Filters: │ └─ Streams: Video ├─ Devices: │ 44. Qualcomm Camera Subsystem [v4l2] │ 45. Qualcomm Camera Subsystem [v4l2] │ 46. Qualcomm Camera Subsystem [v4l2] │ 47. Qualcomm Camera Subsystem [v4l2] │ 48. Qualcomm Camera Subsystem [v4l2] -- ├─ Sources: │ * 56. Built-in Back Camera │ 58. Built-in Back Camera │ ├─ Filters: │S Вот и объяснение: pipewire отдаёт две «Built-in Back Camera», и по умолчанию выбрана 56 — почти наверняка телевик, который кадров не даёт. Плюс рядом торчат пять сырых узлов camss как v4l2-устройства, в которые лезть вообще нельзя, там формат надо настраивать руками. Посмотри, кто есть кто, и переключи умолчание: wpctl inspect 56 | grep -iE "node.name|device.name|object.path|api\." wpctl inspect 58 | grep -iE "node.name|device.name|object.path|api\." Тот, у которого в пути camera@1a — широкая. Ставим его по умолчанию: wpctl set-default 58 # подставь нужный номер А проще всего снова убрать телевик с глаз и перезапустить pipewire, тогда выбирать будет не из чего: echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user restart pipewire wireplumber wpctl status | grep -A4 "Sources" После этого запускай приложение. 💰 7.70₽ · ⬇️ 265.2k / ⬆️ 920 · opus-5 · max · 16сЫ wpctl inspect 56 | grep -iE "node.name|device.name|object.path|api\." api.libcamera.location = "back" api.libcamera.path = "/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36" api.libcamera.rotation = "180" device.name = "libcamera_device./base/soc@0/cci@ac4a000/i2c-bus@1/camera@36" factory.name = "api.libcamera.source" * node.name = "libcamera_input._base_soc_0_cci_ac4a000_i2c-bus_1_camera_36" * object.path = "libcamera:/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36" wpctl inspect 58 | grep -iE "node.name|device.name|object.path|api\." api.libcamera.location = "back" api.libcamera.path = "/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a" api.libcamera.rotation = "180" device.name = "libcamera_device./base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a" factory.name = "api.libcamera.source" * node.name = "libcamera_input._base_soc_0_cci_ac4a000_i2c-bus_0_camera_1a" * object.path = "libcamera:/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a"S Как и думал: 56 — телевик (camera@36), 58 — широкая (camera@1a), а по умолчанию стоит именно телевик. Переключаем: wpctl set-default 58 wpctl status | grep -A4 "Sources" Звёздочка должна переехать на 58. И запускай приложение. Если оно всё равно упрётся (некоторые программы сами перебирают устройства и берут первое, игнорируя умолчание) — тогда убираем телевик совсем: echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user restart pipewire wireplumber 💰 7.01₽ · ⬇️ 266.7k / ⬆️ 339 · opus-5 · max · 8сЫ S Тогда добьём телевик и посмотрим, какие форматы вообще предлагает широкая — подозреваю, что приложение просит YUV, а программный ISP отдаёт только RGB, и переговоры о формате рушатся. echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user restart pipewire wireplumber sleep 2 wpctl status | grep -A4 "Sources" И список форматов узла (номер возьми из свежего wpctl status, он мог поменяться): pw-cli enum-params 58 EnumFormat 2>/dev/null | grep -E "format|Video|size|framerate" | head -30 Если там окажутся только RGB/BGR/ARGB, а привычных NV12/YUY2 нет — вот и причина. Тогда варианта два: включить в pipewire промежуточное преобразование или добавить вывод в NV12 в сам программный ISP (он это умеет в свежих версиях, вопрос сборки). 💰 7.46₽ · ⬇️ 267.1k / ⬆️ 1.1k · opus-5 · max · 19сЫ S Занят — pipewire держит камеру открытой. Останови его и повтори: systemctl --user stop pipewire wireplumber pipewire.socket 2>/dev/null pkill -f wireplumber; pkill -f pipewire; sleep 1 echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user start pipewire wireplumber Но это второстепенно. Гораздо важнее список форматов — без него мы гадаем. Номер узла возьми свежий: wpctl status | grep -A4 Sources pw-cli enum-params <номер> EnumFormat 2>&1 | grep -E "format:|size:|Video" | head -30 💰 7.38₽ · ⬇️ 268.3k / ⬆️ 721 · opus-5 · max · 13сЫ S Так и есть — pipewire завис, скорее всего именно на телевике: он открыл камеру, ждёт кадры, а их нет. Это, кстати, объясняет и ошибку приложения: оно спрашивает у зависшего сервиса. Бей жёстко: sudo pkill -9 -f pipewire; sudo pkill -9 -f wireplumber; sleep 1 echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user start pipewire wireplumber 2>/dev/null || (pipewire & wireplumber &) sleep 2; wpctl status | grep -A4 Sources Должна остаться одна камера. И сразу запускай приложение — подозреваю, что теперь оно заработает без всяких форматных плясок. 💰 7.11₽ · ⬇️ 269.1k / ⬆️ 646 · opus-5 · max · 13сЫ S Ошибка не «занято», а именно ввод-вывод — значит дело не в pipewire. Проверим, как оно вообще привязано, и попробуем через модуль: ls -l /sys/bus/i2c/devices/13-0036/driver 2>/dev/null ls /sys/bus/i2c/drivers/ | grep -i ov lsmod | grep -i ov8856 Если это модуль — выгружаем целиком, так надёжнее: sudo rmmod ov8856 && dmesg | tail -5 А если он вкомпилирован в ядро, останется путь через дерево: дописать телевику status = "disabled", пересобрать dtb и перезагрузиться. Тогда он исчезнет и из libcamera, и из pipewire до тех пор, пока мы им не займёмся всерьёз. 💰 7.67₽ · ⬇️ 269.8k / ⬆️ 1.1k · opus-5 · max · 23сЫ S Ясно: модуль держит camss, потому что телевик прописан в его графе. Выгрузить camss тоже нельзя — тогда он будет ждать пропавший сенсор и не поднимет вообще ни одной камеры. Значит убираем телевик из дерева. Это одна пересборка dtb и перезагрузка, зато уходит и зависание pipewire, и ошибка приложения: cat >> $F <<'EOF' /* временно, пока телевик не отдаёт кадры: он вешает pipewire и приложения */ &cci0_i2c1 { camera@36 { status = "disabled"; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync Отключённый узел v4l2 пропускает молча, так что camss спокойно поднимется с одной камерой. После перезагрузки проверь: cam -l | grep -E "^[0-9]:" Должна остаться одна строка. Дальше приложение — думаю, заведётся сразу. Когда займёмся телевиком, вернём одной строкой: заменить disabled на okay и пересобрать dtb. 💰 8.56₽ · ⬇️ 271.1k / ⬆️ 1.7k · opus-5 · max · 30сЫ S Переменные потерялись — восстановим: F=$(find / -name "sm7150-xiaomi-davinci.dtsi" -not -path "/proc/*" 2>/dev/null | head -1) K=${F%/arch/*} echo "$K"; echo "$F" И дальше как было: cat >> $F <<'EOF' /* временно, пока телевик не отдаёт кадры: он вешает pipewire и приложения */ &cci0_i2c1 { camera@36 { status = "disabled"; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync 💰 7.40₽ · ⬇️ 272.8k / ⬆️ 456 · opus-5 · max · 10сЫ Ы S Тогда просто прямыми путями: K=/home/user/linux-7.2 F=$K/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi ls -l $F && tail -5 $F Если файл на месте — дописываем и собираем: cat >> $F <<'EOF' /* временно, пока телевик не отдаёт кадры: он вешает pipewire и приложения */ &cci0_i2c1 { camera@36 { status = "disabled"; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync 💰 7.02₽ · ⬇️ 273.4k / ⬆️ 566 · opus-5 · max · 10сЫ S Сбрасывает на диск всё, что ядро ещё держит в памяти. Запись файла на самом деле сначала попадает в кеш страниц, а на флеш уезжает потом, когда ядру удобно — обычно в пределах секунд. Нам это важно ровно перед перезагрузкой: если выключить телефон сразу после dtbs_install, есть шанс, что новый dtb физически не долетел, и на диске останется обрезанный файл. Загрузчик такой прочитает и не запустится — а чинить это придётся уже из рекавери. Пара секунд на команду против такого сценария — выгодный обмен. По той же причине я его пишу и после установки ядра. 💰 6.80₽ · ⬇️ 274.0k / ⬆️ 328 · opus-5 · max · 10сЫ S Хороший сдвиг — значит формат больше не спотыкается, теперь просто не находит устройство. Проверим, что видно на трёх уровнях: cam -l | grep -E "^[0-9]:" wpctl status | grep -A5 Sources systemctl --user status pipewire wireplumber --no-pager 2>/dev/null | grep -E "Active|Loaded" | head -4 Если cam камеру видит, а pipewire нет — просто перезапусти сеанс медиасервера: systemctl --user restart pipewire wireplumber sleep 2; wpctl status | grep -A5 Sources Подозреваю, что после перезагрузки wireplumber успел прочитать граф раньше, чем camss закончил регистрацию, и остался ни с чем. 💰 7.04₽ · ⬇️ 274.5k / ⬆️ 552 · opus-5 · max · 11сЫ S Хуже — теперь camss вообще не видит ни одного сенсора. Значит зацепило и широкую. Смотрим, что происходило при загрузке: dmesg | grep -iE "imx586|dw976|ov8856|camss|cci" | tail -30 Заодно проверим, что в собранном дереве получилось на самом деле: dtc -I fs -O dts /proc/device-tree/soc@0/cci@ac4a000 2>/dev/null | grep -A3 -E "camera@1a|camera@36" | head -20 Подозреваю одно из двух: либо мой блок приписался не туда и отключил не тот узел, либо сенсор в этот раз не поднялся по другой причине — например, объектив снова не успел, а он у нас в зависимостях. 💰 7.45₽ · ⬇️ 275.3k / ⬆️ 844 · opus-5 · max · 16сЫ sudo dmesg | grep -iE "imx586|dw976|ov8856|camss|cci" | tail -30 [ 0.643109] platform ace0000.camss: Adding to iommu group 8 [ 12.161971] calling qcom_cci_driver_init+0x0/0xff8 [i2c_qcom_cci] @ 683 [ 12.218138] /soc@0/camss@ace0000: Fixed dependency cycle(s) with /soc@0/cci@ac4a000/i2c-bus@0/camera@1a [ 12.218239] /soc@0/cci@ac4a000/i2c-bus@0/camera@1a: Fixed dependency cycle(s) with /soc@0/camss@ace0000 [ 12.245726] probe of ac4a000.cci returned 0 after 79230 usecs [ 12.261155] probe of ac4b000.cci returned 0 after 15397 usecs [ 12.277401] initcall qcom_cci_driver_init+0x0/0xff8 [i2c_qcom_cci] returned 0 after 2585 usecs [ 12.451174] calling qcom_camss_driver_init+0x0/0xff8 [qcom_camss] @ 663 [ 12.467864] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@0/endpoint of port 0: 4 lanes, first 0 [ 12.472200] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@3/endpoint of port 3: 2 lanes, first 0 [ 12.498440] probe of ace0000.camss returned 0 after 45022 usecs [ 12.502731] initcall qcom_camss_driver_init+0x0/0xff8 [qcom_camss] returned 0 after 34539 usecs [ 12.676818] calling dw9768_i2c_driver_init+0x0/0xff8 [dw9768] @ 665 [ 12.705469] initcall dw9768_i2c_driver_init+0x0/0xff8 [dw9768] returned 0 after 2480 usecs [ 12.724098] calling imx586_i2c_driver_init+0x0/0xff8 [imx586] @ 681 [ 12.737141] imx586 12-001a: IMX582 detected [ 12.742400] imx586 12-001a: signalling mode default 0x02 [ 12.762162] imx586 12-001a: lane mode default 0x03 [ 12.790466] initcall imx586_i2c_driver_init+0x0/0xff8 [imx586] returned 0 after 61465 usecsS Вот и объяснение: сенсор поднялся нормально («IMX582 detected»), но camss всё равно ждёт обе камеры — в логе видно, что он разобрал и порт 0, и порт 3. Отключённый сенсор к порту 3 больше не привязывается, ожидание не завершается никогда, и граф так и не публикуется. Отсюда «No sensor found». Значит убирать надо не только сенсор, но и вход, к которому он подключён: cat >> $F <<'EOF' /* вместе с отключённым телевиком убираем и его вход в camss, иначе приёмник ждёт сенсор, которого нет, и граф не публикуется */ &camss { ports { port@3 { /delete-node/ endpoint; }; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync После перезагрузки в логе должна остаться одна строка parsed endpoint ... port 0, а cam -l показать одну камеру. Если предпочитаешь наоборот — вернуть телевик и не трогать дерево, скажи, тогда просто уберём оба добавленных блока и спрячем лишний узел правилом wireplumber, без перезагрузки. 💰 9.01₽ · ⬇️ 277.0k / ⬆️ 1.8k · opus-5 · max · 31сЫ S Понял, больше без переменных. cat >> /home/user/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi <<'EOF' /* вместе с отключённым телевиком убираем и его вход в camss, иначе приёмник ждёт сенсор, которого нет, и граф не публикуется */ &camss { ports { port@3 { /delete-node/ endpoint; }; }; }; EOF cd /home/user/linux-7.2 && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync После перезагрузки: sudo dmesg | grep "parsed endpoint" cam -l | grep -E "^[0-9]:" Ждём одну строку про порт 0 и одну камеру в списке. 💰 7.54₽ · ⬇️ 278.9k / ⬆️ 425 · opus-5 · max · 7сЫ arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi:195.24-201.6: ERROR (phandle_references): /soc@0/cci@ac4a000/i2c-bus@1/camera@36/port/endpoint: Reference to non-existent node or label "csiphy3_ep" arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi:195.24-201.6: ERROR (phandle_references): /soc@0/cci@ac4a000/i2c-bus@1/camera@36/port/endpoint: Reference to non-existent node or label "csiphy3_ep" ERROR: Input tree has errors, aborting (use -f to force output) ERROR: Input tree has errors, aborting (use -f to force output) make[3]: *** [scripts/Makefile.dtbs:140: arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci-visionox.dtb] Error 2 make[3]: *** Waiting for unfinished jobs.... make[3]: *** [scripts/Makefile.dtbs:140: arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci-samsung.dtb] Error 2 make[2]: *** [scripts/Makefile.build:549: arch/arm64/boot/dts/qcom] Error 2 make[1]: *** [/home/user/linux-7.2/Makefile:1629: dtbs] Error 2 make: *** [Makefile:248: __sub-make] Error 2S Логично: я удалил вход, а сенсор телевика на него ссылается. Раз он нам сейчас не нужен — удалим его целиком, тогда и ссылка исчезнет: cat >> /home/user/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi <<'EOF' /* сенсор телевика ссылается на удалённый вход, поэтому убираем и его */ &cci0_i2c1 { /delete-node/ camera@36; }; EOF cd /home/user/linux-7.2 && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync Когда вернёмся к телевику, просто снесём эти три добавленных блока в конце файла — они идут подряд и помечены комментариями. 💰 7.75₽ · ⬇️ 279.9k / ⬆️ 911 · opus-5 · max · 15сЫ S Значит телевик был ни при чём — ошибка от самой широкой. Надо понять, каким путём приложение вообще ходит в камеру: ldd /usr/bin/plasma-camera | grep -iE "camera|pipewire|gstreamer|multimedia|Qt6Multimedia" ls /usr/lib/qt6/plugins/multimedia/ 2>/dev/null Подозрение такое: plasma-camera построен на QtMultimedia, а у неё на Linux по умолчанию бэкенд ffmpeg, который умеет только сырой V4L2. Он видит наши узлы camss (/dev/video0 и компанию), пытается открыть их как обычную веб-камеру — и получает отказ, потому что там нужен предварительно настроенный граф и упакованный raw10, а не готовый YUV. Если так, то приложение к нашей камере просто не подходит, и лечится это не настройкой, а другим потребителем: qcam из нашей сборки, Megapixels, или Qt с бэкендом gstreamer плюс libcamerasrc. Но сначала посмотрим на вывод — гадать не будем. 💰 8.17₽ · ⬇️ 280.8k / ⬆️ 1.3k · opus-5 · max · 22сЫ ldd /usr/bin/plasma-camera | grep -iE "camera|pipewire|gstreamer|multimedia|Qt6Multimedia" libQt6Multimedia.so.6 => /lib/libQt6Multimedia.so.6 (0xffff9ff38000) libcamera.so.0.7 => /lib/libcamera.so.0.7 (0xffff9d82a000) libcamera-base.so.0.7 => /lib/libcamera-base.so.0.7 (0xffff9fc71000) ls /usr/lib/qt6/plugins/multimedia/ 2>/dev/null libffmpegmediaplugin.so libgstreamermediaplugin.so в /lib/ если чо наша libcameraS Тогда смотрим, что именно оно просит. Включим логи QtMultimedia: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 QT_LOGGING_RULES="qt.multimedia*=true" plasma-camera 2>&1 | tail -30 И заодно проверим, кто там тянет libcamera — сама Qt или приложение: ldd /lib/libQt6Multimedia.so.6 | grep -i camera ldd /usr/lib/qt6/plugins/multimedia/libffmpegmediaplugin.so | grep -i camera Есть ещё второй бэкенд, gstreamer — его можно попробовать прямо так: QT_MEDIA_BACKEND=gstreamer plasma-camera 2>&1 | tail -20 Ключевой вопрос — открывает ли оно камеру через libcamera или лезет напрямую в /dev/video0. Во втором случае оно обречено: наши узлы отдают упакованный raw10 и требуют настроенного графа, обычная камера так себя не ведёт. 💰 9.36₽ · ⬇️ 282.4k / ⬆️ 2.1k · opus-5 · max · 32сЫ S Тогда спросим саму libcamera — она логирует независимо от Qt: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 LIBCAMERA_LOG_LEVELS=*:DEBUG plasma-camera 2>&1 | grep -iE "validate|configur|adjust|pixelformat|stream" | tail -40 Там будет видно, какой формат и размер оно запрашивает и на чём проверка говорит «нет». И для сравнения — заведомо рабочий потребитель на Qt, если он собрался: ls -l /home/user/libcamera/build/src/apps/qcam/qcam 2>/dev/null && \ /home/user/libcamera/build/src/apps/qcam/qcam 2>&1 | tail -5 Если qcam покажет живую картинку — значит виновата логика запроса в plasma-camera, и чинить надо её, а не камеру. 💰 8.20₽ · ⬇️ 284.7k / ⬆️ 736 · opus-5 · max · 13сЫ S А, понял, в чём фокус: tail ничего не покажет, пока программа не завершится, а она висит с открытым окном. Пиши в файл: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 LIBCAMERA_LOG_LEVELS=*:DEBUG plasma-camera > /tmp/pc.log 2>&1 & sleep 8 kill %1 wc -l /tmp/pc.log grep -iE "validate|configur|adjust|pixelformat|Invalid" /tmp/pc.log | tail -30 Если файл окажется пустым и после этого — значит приложение вообще не доходит до libcamera, и тогда смотрим просто первые строки: head -30 /tmp/pc.log 💰 7.49₽ · ⬇️ 285.4k / ⬆️ 650 · opus-5 · max · 10сЫ S Вот и причина, целиком видно в логе: Picked 8000x6000 ... for max processed stream size 7992x6000 Failed to open /dev/dma_heap/system: No such file or directory Using /dev/udmabuf Failed to create dma buf for frame-0: Invalid argument Приложение не задаёт размер, libcamera по умолчанию берёт максимум — 7992×6000 в ABGR8888, это 191 МБ на кадр. Куч dma-heap в ядре нет, поэтому libcamera выделяет через udmabuf, а у того по умолчанию стоит потолок в 64 МБ на буфер. Отсюда Invalid argument и «конфигурация недопустима». Потолок правится на лету: cat /sys/module/udmabuf/parameters/size_limit_mb echo 1024 | sudo tee /sys/module/udmabuf/parameters/size_limit_mb И запускай приложение снова. Оно должно завестись, но учти: полный кадр через программный демозаик — это порядка секунды на кадр, видоискатель будет ползти. Зато мы убедимся, что причина именно эта. А правильное лечение — включить в ядре кучи dma-buf, тогда libcamera возьмёт нормальный аллокатор: CONFIG_DMABUF_HEAPS=y CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMABUF_HEAPS_CMA=y Это в следующую пересборку ядра, вместе с патчем get_selection для телевика. 💰 13.59₽ · ⬇️ 296.6k / ⬆️ 1.9k · opus-5 · max · 33сЫ S Всё сходится: это не фильтр, а квадро-байер. В полном разрешении сенсор отдаёт пиксели группами 2×2 одного цвета — вместо привычного шахматного узора получается «крупная клетка», и обычный демозаик libcamera раскладывает её в зелёное с точками. Мы это уже видели в своих кадрах: полный кадр надо разбирать шаблоном quad, а не rggb. Проще говоря, режим 8000×6000 годится только как сырьё для нашего инструмента, а для просмотра нужен любой другой — они все с бинингом и дают честный байер. Плюс задержка сразу упадёт: 1080p через программный демозаик это 24 мс на кадр против почти секунды сейчас. Загляни в настройки приложения — там должен быть выбор разрешения, судя по логу оно знает про все четыре режима (1920×1080, 4000×2250, 4000×3000, 8000×6000). Поставь 1920×1080. Если селектора там нет, сделаем иначе: добавлю в драйвер параметр модуля, скрывающий полноразмерный режим от перечисления, и тогда libcamera будет брать максимумом 4000×3000. Наши собственные скрипты полный кадр всё равно снимут, они ходят напрямую. 💰 9.31₽ · ⬇️ 298.8k / ⬆️ 1.5k · opus-5 · max · 28сЫ S Понял — значит выбор разрешения у приложения не предусмотрен вообще, оно берёт умолчание libcamera, а умолчание там — «самый большой размер». В коде рядом с этим местом даже висит комментарий разработчиков «надо бы придумать способ выбирать по-умному». Придумаем. Правка на три десятка строк в нашей сборке: для видоискателя брать не максимум, а самый большой размер в пределах разумного. python3 - <<'PY' p = "/home/user/libcamera/src/libcamera/pipeline/simple/simple.cpp" s = open(p).read() anchor = """\t\tcfg.pixelFormat = formats.begin()->first; \t\tcfg.size = formats.begin()->second[0].max; """ add = """ \t\t/* \t\t * Taking the largest size is a poor default for a software ISP: \t\t * on this board it selects a 48 megapixel quad bayer mode that no \t\t * debayer can interpret and that costs about a second a frame. \t\t * Prefer the largest size that stays sane for the role. \t\t */ \t\tif (role != StreamRole::Raw) { \t\t\tconst Size limit = role == StreamRole::StillCapture \t\t\t\t\t ? Size(4096, 3072) : Size(1920, 1080); \t\t\tSize best; \t\t\tfor (const SizeRange &range : formats.begin()->second) { \t\t\t\tconst Size &sz = range.max; \t\t\t\tif (sz.width <= limit.width && sz.height <= limit.height && \t\t\t\t sz.width * sz.height > best.width * best.height) \t\t\t\t\tbest = sz; \t\t\t} \t\t\tif (!best.isNull()) \t\t\t\tcfg.size = best; \t\t} """ assert anchor in s, "не нашёл место вставки" open(p, "w").write(s.replace(anchor, anchor + add, 1)) print("вставлено") PY cd /home/user/libcamera && ninja -C build && sudo ninja -C build install Пересборка тут только одного файла, минуты две. После неё видоискатель пойдёт в 1920×1080 — это 24 мс на кадр вместо секунды, и телефон перестанет греться. Съёмка фото останется в 4000×3000, а полноразмерный квадро-байер будет доступен только нашим скриптам, которые умеют его разбирать правильно. 💰 28.10₽ · ⬇️ 905.8k / ⬆️ 5.2k · opus-5 · max · 81сЫ S Согласен, это лечение симптома в чужом коде. Тогда так. Сначала проверим, что вообще доступно как потребитель: ls /home/user/libcamera/build/src/apps/qcam/qcam 2>/dev/null && echo "qcam собран" apk search -q megapixels 2>/dev/null apk search -q snapshot 2>/dev/null | head -5 qcam — родной просмотрщик libcamera, он принимает те же ключи потока, что и cam, то есть разрешение задаётся явно: XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 \ /home/user/libcamera/build/src/apps/qcam/qcam -c 1 -s width=1920,height=1080,pixelformat=RGB888 Это сразу даст живой видоискатель без всяких патчей — годится как проверка и как рабочий инструмент на сегодня. А правильный потребитель для телефона — Megapixels: он специально сделан под такие камеры, умеет выбирать режим для превью и отдельный для снимка, и настраивается файлом описания устройства. Как раз наш случай: превью в 1920×1080, снимок в 4000×3000, а полноразмерный квадро-байер вообще не трогаем. Если он есть в репозитории, поставим и напишем ему описание для давинчи — заодно это готовый кусок для порта в pmOS. plasma-camera в этой роли слабоват: он не даёт выбрать разрешение вообще, что для телефона с четырьмя режимами странно. Но если хочешь, потом заведём им запрос — правка там небольшая. 💰 9.94₽ · ⬇️ 307.0k / ⬆️ 1.3k · opus-5 · max · 23сЫ S Значит qcam не собрался — для него нужны заголовки Qt6, которых при сборке не было. Покажи, что нашлось в репозитории: apk search -q megapixels; echo "---"; apk search -q camera | head -20 Если megapixels там есть — ставим его, это самый подходящий вариант. Если нет, вторым кандидатом идёт snapshot от GNOME (ходит через pipewire), а третьим — досборка qcam: пакеты qt6-qtbase-dev и qt6-qttools-dev, потом meson configure build -Dqcam=enabled и пересборка. 💰 7.89₽ · ⬇️ 308.3k / ⬆️ 333 · opus-5 · max · 9сЫ S Ему нужен файл описания устройства — сам он камеру не ищет, а берёт готовое описание по совместимости платы. Посмотрим, что за версия и куда класть: apk info -v megapixels apk info -L megapixels | grep -iE "config|\.ini|share" | head -20 tr '\0' '\n' < /proc/device-tree/compatible ls /usr/share/megapixels/config/ 2>/dev/null | head По этим четырём выводам я напишу описание для давинчи: превью 1920×1080, снимок 4000×3000, поворот 180, объектив через focus_absolute. Заодно это ровно тот файл, который потом уйдёт в pmOS вместе с портом. 💰 8.11₽ · ⬇️ 308.7k / ⬆️ 889 · opus-5 · max · 15сЫ Ссылка
нажмите — покажемqcom-sm7150:~$ apk info -v megapixels megapixels: GTK+4 camera app for mobile devices megapixels: https://gitlab.com/megapixels-org/Megapixels megapixels: 2389 KiB qcom-sm7150:~$ apk info -L megapixels | grep -iE "config|\.ini|share" | head -20 usr/share/applications/me.gapixels.Megapixels.desktop usr/share/glib-2.0/schemas/me.gapixels.Megapixels.gschema.xml usr/share/icons/hicolor/scalable/apps/me.gapixels.Megapixels.svg usr/share/megapixels/movie.sh usr/share/megapixels/postprocess.sh usr/share/megapixels/config/pine64,pinephone,front.dcp usr/share/megapixels/config/pine64,pinephone,rear.dcp usr/share/metainfo/me.gapixels.Megapixels.metainfo.xml qcom-sm7150:~$ tr '\0' '\n' < /proc/device-tree/compatible xiaomi,davinci-samsung xiaomi,davinci qcom,sm7150 qcom-sm7150:~$ ls /usr/share/megapixels/config/ 2>/dev/null | head google,b4s4-sdm670.conf pine64,pinephone,front.dcp pine64,pinephone,rear.dcp pine64,pinephone-pro.conf pine64,pinephone.conf pine64,pinetab.conf purism,librem5.conf samsung,midas.conf xiaomi,daisy.conf xiaomi,markw.confS Формат .conf, ключ — совместимость платы, значит нам нужен /usr/share/megapixels/config/xiaomi,davinci.conf. Покажи готовый пример, чтобы я написал наш точно по формату: cat "/usr/share/megapixels/config/xiaomi,daisy.conf" echo "==========" head -40 "/usr/share/megapixels/config/pine64,pinephone.conf" Дальше я соберу описание для давинчи: превью 1920×1080, снимок 4000×3000, поворот 180, объектив на focus_absolute — и телевик впишем сразу, чтобы потом не возвращаться. 💰 8.18₽ · ⬇️ 310.2k / ⬆️ 494 · opus-5 · max · 11сЫ Version = 1; Make: "Xiaomi"; Model: "Mi A2 Lite"; Front: { SensorDriver: "ov5675"; BridgeDriver: "qcom-camss"; Modes: ( { Width: 2592; Height: 1944; Rate: 30; Format: "GRBG10p"; Rotate: 270; Mirror: true; Pipeline: ( {Type: "Link", From: "msm_csiphy2", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_ispif0", ToPad: 0}, {Type: "Link", From: "msm_ispif0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "ov5675"}, {Type: "Mode", Entity: "msm_csiphy2"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_ispif0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); }, { Width: 1296; Height: 972; Rate: 30; Format: "GRBG10p"; Rotate: 270; Mirror: true; Pipeline: ( {Type: "Link", From: "msm_csiphy2", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_ispif0", ToPad: 0}, {Type: "Link", From: "msm_ispif0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "ov5675"}, {Type: "Mode", Entity: "msm_csiphy2"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_ispif0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); } ); }; Version = 1; Make: "PINE64"; Model: "PinePhone"; Rear: { SensorDriver: "ov5640"; BridgeDriver: "sun6i-csi"; FlashPath: "/sys/class/leds/white:flash"; IsoMin: 100; IsoMax: 64000; Modes: ( { Width: 2592; Height: 1944; Rate: 15; Format: "BGGR8"; Rotate: 270; FocalLength: 3.33; FNumber: 3.0; Pipeline: ( {Type: "Link", From: "ov5640", FromPad: 0, To: "sun6i-csi-bridge", ToPad: 0}, {Type: "Mode", Entity: "ov5640"}, {Type: "Mode", Entity: "sun6i-csi-bridge"} ); }, { Width: 1280; Height: 720; Rate: 30; Format: "BGGR8"; Rotate: 270; FocalLength: 3.33; FNumber: 3.0; Pipeline: ( {Type: "Link", From: "ov5640", FromPad: 0, To: "sun6i-csi-bridge", ToPad: 0}, {Type: "Mode", Entity: "ov5640"}, {Type: "Mode", Entity: "sun6i-csi-bridge"}S Формат понятен — Megapixels сам рулит графом через media-ctl, libcamera ему не нужна. Значит пишем описание нашей камеры: sudo tee "/usr/share/megapixels/config/xiaomi,davinci.conf" >/dev/null <<'EOF' Version = 1; Make: "Xiaomi"; Model: "Mi 9T"; Rear: { SensorDriver: "imx586"; BridgeDriver: "qcom-camss"; IsoMin: 100; IsoMax: 6400; Modes: ( { Width: 1920; Height: 1080; Rate: 30; Format: "RGGB10p"; Rotate: 180; FocalLength: 4.74; FNumber: 1.75; Pipeline: ( {Type: "Link", From: "msm_csiphy0", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "imx586"}, {Type: "Mode", Entity: "msm_csiphy0"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); }, { Width: 4000; Height: 3000; Rate: 30; Format: "RGGB10p"; Rotate: 180; FocalLength: 4.74; FNumber: 1.75; Pipeline: ( {Type: "Link", From: "msm_csiphy0", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "imx586"}, {Type: "Mode", Entity: "msm_csiphy0"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); } ); }; EOF Полноразмерный режим я сюда сознательно не вписал — он квадро-байерный, и обычным демозаиком из него получается та самая зелёная сетка. Запускай. Если снова скажет, что камеры нет, посмотрим, по какой строке совместимости он ищет: megapixels 2>&1 | head -20 💰 10.54₽ · ⬇️ 312.0k / ⬆️ 2.4k · opus-5 · max · 35сЫ S Нужен его вывод — он при падении обычно пишет причину: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 megapixels > /tmp/mp.log 2>&1; echo "код выхода: $?"; tail -30 /tmp/mp.log Скорее всего одно из трёх: не понравился формат RGGB10p (тогда он ждёт другое написание), не нашёл сущность imx586 по короткому имени, или споткнулся на моих ссылках в конвейере. В логе это будет прямым текстом. 💰 8.74₽ · ⬇️ 314.5k / ⬆️ 537 · opus-5 · max · 11сЫ megapixels Found postprocessor script at /usr/share/megapixels/movie.sh [libmegapixels] Driver rejected pixfmt try: 0x3007 MPCamera: VIDIOC_STREAMON error 32, Broken pipe Initialized OpenGL ES 3.2 Found calibration file at .config MPCamera: VIDIOC_DQBUF error 22, Invalid argument Segmentation fault (core dumped)S Ключевая строка — Driver rejected pixfmt try: 0x3007. Это код SBGGR10_1X10, то есть он просит у сенсора байер «синий первый», а драйвер сейчас отдаёт «красный первый» (0x300f). Дальше по цепочке всё рушится: поток не стартует, буфер не приходит, и на этом он падает уже своим багом. Скорее всего он сам разворачивает порядок из-за Rotate: 180 — поворот на 180 градусов действительно меняет RGGB на BGGR. Проверим и подстроимся. Сначала убедись, что флипы сброшены (их могла оставить libcamera): SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --get-ctrl horizontal_flip,vertical_flip Если оба нули — меняем строку формата на противоположную: sudo sed -i 's/"RGGB10p"/"BGGR10p"/g' "/usr/share/megapixels/config/xiaomi,davinci.conf" megapixels > /tmp/mp.log 2>&1; echo "код: $?"; grep -iE "pixfmt|STREAMON|DQBUF|error" /tmp/mp.log | head Если наоборот, флипы окажутся единицами — тогда порядок в конфиге правильный, а сбросить надо их: v4l2-ctl -d $SD --set-ctrl horizontal_flip=0,vertical_flip=0 💰 10.12₽ · ⬇️ 315.4k / ⬆️ 2.3k · opus-5 · max · 33сЫ S Вот и объяснение: флипы стоят в единицах, их оставила libcamera. С ними драйвер честно рапортует «синий первый», а всё остальное в конвейере ждёт «красный первый» — отсюда и отказ, и Broken pipe на старте потока. Сбрасываем флипы и возвращаем конфиг как был: SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --set-ctrl horizontal_flip=0,vertical_flip=0 sudo sed -i 's/"BGGR10p"/"RGGB10p"/g' "/usr/share/megapixels/config/xiaomi,davinci.conf" v4l2-ctl -d $SD --get-ctrl horizontal_flip,vertical_flip И запускай: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 megapixels > /tmp/mp.log 2>&1; echo "код: $?"; grep -iE "pixfmt|STREAMON|DQBUF|error|calib" /tmp/mp.log | head Только учти: стоит запустить libcamera-приложение — и флипы вернутся, потому что она честно отрабатывает поворот модуля из дерева. Если megapixels взлетит, я допишу ему в конфиг постоянный сброс через правило udev, чтобы они не мешали друг другу. 💰 9.84₽ · ⬇️ 317.8k / ⬆️ 1.5k · opus-5 · max · 25сЫ S Так и должно быть: у вендора для задней широкой записан roll = 90, а в дереве у нас стоит rotation = <180> — это наследство форка, и оно неверно. Отсюда и расхождение. Сейчас подгони отображение: sudo sed -i 's/Rotate: 180;/Rotate: 270;/g' "/usr/share/megapixels/config/xiaomi,davinci.conf" megapixels Если перевернётся в другую сторону — поменяй на 90. А заодно это объясняет и конфликт с флипами. Сейчас в дереве написано «модуль повёрнут на 180», и libcamera честно пытается это исправить, включая оба флипа — из-за чего меняется порядок байера и ломается megapixels. Если поставить настоящие 90 градусов, libcamera флипами компенсировать уже не сможет (поворот на прямой угол так не исправляется) и просто сообщит ориентацию приложению. Флипы останутся нулевыми, и оба потребителя перестанут мешать друг другу. Правку в дерево внесу в следующий заход — она в одну строку, вместе с включением куч dma-buf в ядре. 💰 17.62₽ · ⬇️ 639.7k / ⬆️ 1.9k · opus-5 · max · 38сЫ S Скорее всего он выставляет выдержку и усиление сам, по своим умолчаниям. Посмотри, что он реально ставит, пока окно открыто: SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --get-ctrl exposure,analogue_gain Для сравнения: на нормально освещённой комнате мы снимали с выдержкой около 3000 и усилением 960 в режиме 4000×3000, а в 1080p потолок выдержки примерно 1240. И загляни в интерфейс — у Megapixels есть панель с ISO и выдержкой, там же обычно переключатель автоматики. Если он стоит в ручном режиме с минимальными значениями, всё сходится. Если же окажется, что значения выставлены нормальные, а картинка всё равно тёмная — тогда дело в его собственной обработке: он берёт уровень чёрного и гамму из файла калибровки, а нашего у него нет, и он мог подставить чужой. 💰 8.67₽ · ⬇️ 321.7k / ⬆️ 764 · opus-5 · max · 16сЫ S Покажи, что реально стоит в драйвере, пока окно открыто — из интерфейса не видно, в каких единицах он меряет: SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --get-ctrl exposure,analogue_gain,vertical_blanking media-ctl -d /dev/media0 -p | grep -A3 "imx586" | grep fmt Смысл вот в чём: у нашего сенсора потолок выдержки зависит от режима — в 1920×1080 это около 1240 строк, в 4000×3000 около 3030. Число 360 не совпадает ни с одним, значит либо Megapixels сам ограничивает выдержку кадровой частотой из конфига (мы написали 30, а режим умеет 60), либо он показывает не строки, а что-то своё. Если окажется первое — просто поднимем предел, увеличив вертикальный интервал через тот же конфиг или сняв ограничение частоты. 💰 8.95₽ · ⬇️ 322.6k / ⬆️ 1.2k · opus-5 · max · 22сЫ S Значит сенсор уже на пределе: выдержка 1080 строк при потолке 1240, усиление максимальное 960. Кстати, это и объясняет «EXP лимит 360» — он показывает десятые доли миллисекунды, 1240 строк это как раз 36 мс. То есть темнит не сенсор, а обработка самого Megapixels. У него превью — это быстрый демозаик на видеокарте с собственной тоновой кривой, и он часто заметно темнее итогового снимка. Проверь по-настоящему: нажми затвор и посмотри результат. ls -lt ~/Pictures/ 2>/dev/null | head Файл там появляется после отработки postprocess.sh — это уже полноценная обработка, с уровнем чёрного и балансом. И заодно посмотрим, что за калибровку он нашёл, а то в логе была подозрительная строка: grep -i calib /tmp/mp.log ls -la ~/.config/megapixels* ~/.local/share/megapixels 2>/dev/null | head Если он подобрал чужой профиль (там лежат только пайнфоновские), цвет и яркость будут считаться по не тем коэффициентам — тогда просто уберём его, и он вернётся к нейтральной обработке. 💰 9.49₽ · ⬇️ 323.8k / ⬆️ 1.5k · opus-5 · max · 27сЫ S Ссылка
нажмите — покажемЭто известная бага, и её уже починили. В libmegapixels 0.2.1 есть ровно наш случай: библиотека стала брать длину строки из драйвера, потому что «не все драйверы используют минимальное дополнение для данной ширины и режима». Наш camss как раз такой — он выравнивает строку до 16 байт, и на 4000 пикселях получается 5008 вместо 5000. Отсюда и проверка, которая падает. Проверь версию: apk info -v libmegapixels megapixels 2>/dev/null Если меньше 0.2.1 — собери свежую, библиотека маленькая: cd ~ && git clone https://gitlab.com/megapixels-org/libmegapixels cd libmegapixels && meson setup build --prefix=/usr --libdir=lib --buildtype=release && ninja -C build && sudo ninja -C build install После этого снимок в 4000×3000 должен сохраниться нормально — а заодно и превью подтянется, потому что оно считает то же самое. Источники: заметки к выпуску 0.2.1, описание формата конфига, как добавлять железо в libmegapixels. 💰 32.41₽ · ⬇️ 654.1k / ⬆️ 1.8k · opus-5 · max · 54сЫ S Ы S Ы S Ы S Синева — это уже не наш конвейер, а обработка Megapixels. Он считает цвет по профилю калибровки (.dcp), а для нашего сенсора его нет — в комплекте лежат только пайнфоновские. Без профиля он берёт нейтральную матрицу, и сырой отклик сенсора уходит в синь ровно так же, как уходил в libcamera до того, как мы положили ей матрицу. Лечится тем же способом, только в его формате: снимок цветовой мишени при дневном свете, потом dcamprof считает профиль, кладём как xiaomi,davinci,rear.dcp рядом с конфигом. Это полчаса работы, но нужна мишень — сгодится и распечатанная, если печать не совсем дикая. И отдельно про кнопку съёмки. Я посмотрел исходник, проверка там до сих пор такая: assert(bytesused == (libmegapixels_mode_width_to_bytes(format, width) + libmegapixels_mode_width_to_padding(format, width)) * height); То есть приложение само вычисляет длину строки вместо того, чтобы взять её у драйвера — а camss выравнивает строку до 16 байт, и на ширине 4000 получается 5008 вместо 5000. Библиотеку это уже умеет учитывать, а приложение нет. Если хочешь снимать в 4000×3000, я сделаю патч на пару строк: брать bytesperline из VIDIOC_G_FMT. Заодно и в апстрим отправим — баг общий для всех камер на camss, не только нашей. 💰 9.62₽ · ⬇️ 333.6k / ⬆️ 1.7k · opus-5 · max · 34сЫ S Запомнил, патч на bytesperline — за мной. По синеве: сначала выясним, есть ли у него баланс белого вообще, или он просто гонит сырые каналы как есть. grep -iE "awb|balance|white|gain" /tmp/mp.log | head -10 И глянь в интерфейсе — рядом с ISO и выдержкой должен быть либо переключатель баланса, либо выбор освещения (солнце/лампа/авто). Если автоматики нет, померяем коэффициенты сами, мишень для этого не нужна. Снимем лист белой бумаги нашим путём и посчитаем, во сколько раз каналы отличаются: W=1920 H=1080 EXPOSURE=600 GAIN=200 sh ~/lanekit/capture.sh /tmp/wb.raw По этому кадру я посчитаю усиления красного и синего относительно зелёного — и дальше либо пропишем их в профиль для Megapixels, либо, что интереснее, выставим прямо в сенсор: у IMX582 есть отдельные цифровые усиления на каждый цвет (регистры 0x0210–0x0217), и их можно выставить драйвером через стандартные ручки red_balance и blue_balance. Тогда баланс будет правильным для любого потребителя, включая тех, кто про баланс вообще не думает. 💰 9.72₽ · ⬇️ 335.3k / ⬆️ 1.3k · opus-5 · max · 27сЫ S Могу — и это правильный ход, числа те же самые. Только формат другой: libcamera ест матрицу текстом, а Megapixels — профиль .dcp, это двоичный формат от Adobe с матрицей XYZ→камера. Пересчёт из нашей матрицы туда честный, я его сделаю. Сейчас посмотрю, что именно он ищет и в каком виде — в логе была строка «Found calibration file at .config», хочу понять, что он там нашёл. Да, и это самый быстрый путь: числа те же, только упаковать надо в двоичный профиль. Я разобрал их формат — это урезанный TIFF, где нужны всего два поля: матрица XYZ→камера и код освещения. Сгенерирую сам. Но одну вещь мне надо померить у тебя: точку белого. Матрица в libcamera работает поверх уже выровненных каналов, а профиль должен знать, насколько сенсор изначально «зелёный» — без этого пересчёт будет вслепую. Сними лист белой бумаги во весь кадр при том свете, где снимаешь, и посчитай средние по каналам: W=1920 H=1080 EXPOSURE=800 GAIN=300 sh ~/lanekit/capture.sh /tmp/wb.raw python3 - <<'PY' W,H,S=1920,1080,2400 d=open('/tmp/wb.raw','rb').read() def row(y): off=y*S; out=[] for i in range(0,W*5//4,5): b=d[off+i:off+i+5]; lo=b[4] out += [(b[0]<<2)|(lo&3),(b[1]<<2)|((lo>>2)&3),(b[2]<<2)|((lo>>4)&3),(b[3]<<2)|((lo>>6)&3)] return out s=[0,0,0,0]; n=0 for y in range(H//2-200,H//2+200,2): a=row(y); b=row(y+1) for x in range(W//2-200,W//2+200,2): s[0]+=a[x]; s[1]+=a[x+1]; s[2]+=b[x]; s[3]+=b[x+1]; n+=1 print("R,Gr,Gb,B =", [round(v/n-64,1) for v in s]) PY Пришли эти четыре числа. Хорошо, если они выйдут в районе 400–700: тогда и не пересвечено, и шума мало. Если больше 900 — убавь выдержку, если меньше 150 — прибавь. 💰 63.65₽ · ⬇️ 2.38M / ⬆️ 6.0k · opus-5 · max · 97сЫ S Слишком темно — значения 5–10 единиц из 1023, это почти шум, по такому балансу считать нельзя. Нужен кадр, где бумага светится хотя бы на треть шкалы. Возьми режим 4000×3000, там выдержка втрое длиннее, и посвети на лист лампой или поднеси к окну: W=4000 H=3000 EXPOSURE=3000 GAIN=960 sh ~/lanekit/capture.sh /tmp/wb.raw python3 - <<'PY' W,H,S=4000,3000,5008 d=open('/tmp/wb.raw','rb').read() def row(y): off=y*S; out=[] for i in range(0,W*5//4,5): b=d[off+i:off+i+5]; lo=b[4] out += [(b[0]<<2)|(lo&3),(b[1]<<2)|((lo>>2)&3),(b[2]<<2)|((lo>>4)&3),(b[3]<<2)|((lo>>6)&3)] return out s=[0,0,0,0]; n=0 for y in range(H//2-300,H//2+300,2): a=row(y); b=row(y+1) for x in range(W//2-300,W//2+300,2): s[0]+=a[x]; s[1]+=a[x+1]; s[2]+=b[x]; s[3]+=b[x+1]; n+=1 print("R,Gr,Gb,B =", [round(v/n-64,1) for v in s]) PY Целюсь в 300–700 по зелёному. И важно: свет должен быть один — либо только лампа, либо только окно, иначе точка белого получится смешанной и профиль будет врать в обе стороны. 💰 9.77₽ · ⬇️ 345.3k / ⬆️ 1.1k · opus-5 · max · 18сЫ