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)
Веткаqt.qpa.xcb: could not connect to display
261 сообщение · –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сЫ S Отлично, теперь есть точка белого: зелёный 374, красный 238, синий 176. То есть сенсор, как и положено, сильно «зелёный» — нейтраль по нему (0.636, 1, 0.470), а выравнивающие усиления 1.57 и 2.13. Собираю профиль из этих чисел и нашей матрицы: Готово. Клади рядом с конфигом — имя он строит из совместимости платы и названия камеры: sudo cp ~/Downloads/davinci_rear.dcp "/usr/share/megapixels/config/xiaomi,davinci,rear.dcp" megapixels 2>&1 | grep -i calib Должна появиться строка «Found calibration file at /usr/share/megapixels/config/xiaomi,davinci,rear.dcp». Что внутри: матрица XYZ→камера, посчитанная из нашей imx586.yaml и твоего замера, плюс прямая матрица для рендера и код освещения D65. Проверка сошлась — белая точка профиля приходит ровно в D50, как требует формат. Одно уточнение: я пометил профиль дневным светом. Если ты снимал лист под лампой накаливания, а не у окна, скажи — пересчитаю по тёплой матрице и помечу как лампу, иначе он будет слегка желтить на дневном и уводить в синь под лампой. 💰 40.43₽ · ⬇️ 1.40M / ⬆️ 5.3k · opus-5 · max · 80сЫ S Держи командой: sudo mkdir -p /usr/share/megapixels/config base64 -d > /tmp/davinci_rear.dcp <<'EOF' SUlSQwgAAAAFABTGAgASAAAASgAAACHGCgAJAAAAXAAAAFrGAwABAAAAFQAAAPjGAgAyAAAApAAA ABTHCgAJAAAA1gAAAAAAAABYaWFvbWkgTWkgOVQgcmVhcgCOLwAAECcAALjx//8QJwAAbvr//xAn AADG8v//ECcAAOAsAAAQJwAANQYAABAnAABg////ECcAAK4EAAAQJwAAIA0AABAnAABNaSA5VCBJ TVg1ODIgKG1lYXN1cmVkIG5ldXRyYWwsIGdlbmVyaWMgc29ueSBjY20pAJIXAAAQJwAAIwwAABAn AAD1AQAAECcAAF8HAAAQJwAA/CYAABAnAAC1+P//ECcAAOL+//8QJwAAcfb//xAnAADpKgAAECcA AA== EOF sudo cp /tmp/davinci_rear.dcp "/usr/share/megapixels/config/xiaomi,davinci,rear.dcp" megapixels 2>&1 | grep -i calib Ждём строку «Found calibration file at /usr/share/megapixels/config/xiaomi,davinci,rear.dcp» и нормальный цвет в превью. 💰 17.13₽ · ⬇️ 705.5k / ⬆️ 744 · opus-5 · max · 13сЫ S Нашёл причину, и это опять их баг. Смотри цикл поиска: static const char *paths[] = { "config/%s,%s.dcp", ... }; for (const char *fmt = paths[0]; fmt; fmt++) { Он идёт не по массиву путей, а по символам первой строки: fmt++ сдвигает указатель внутри "config/%s,%s.dcp". Дальше он гуляет по соседним строкам в памяти, натыкается на что-то вроде .config, проверяет access() — а это существующий каталог, проверка проходит — и радостно объявляет его файлом калибровки. Наш профиль в /usr/share при этом не смотрится никогда. Зато первая проверка, до цикла, написана правильно. Ей и воспользуемся: mkdir -p ~/.config/megapixels/config for m in "xiaomi,davinci" "xiaomi,davinci-samsung"; do for s in rear Rear; do cp /tmp/davinci_rear.dcp ~/.config/megapixels/config/"$m,$s.dcp" done done ls ~/.config/megapixels/config/ megapixels 2>&1 | grep -i calib Теперь в строке должен быть полный путь до нашего файла. Четыре копии — потому что неизвестно, по какой строке совместимости он ищет (в дереве их две) и в каком регистре передаёт имя камеры; лишние потом уберём. Этот цикл, кстати, добавляю в список на патч вместе с bytesperline — там обе правки по одной строке. 💰 19.77₽ · ⬇️ 708.6k / ⬆️ 2.7k · opus-5 · max · 44сЫ S Значит первая, правильная проверка всё равно промахнулась, и он опять свалился в кривой цикл. Посмотрим, куда именно легли файлы и что за окружение: echo "HOME=$HOME XDG_CONFIG_HOME=$XDG_CONFIG_HOME" ls -la ~/.config/megapixels/config/ Он ищет файл по шаблону $XDG_CONFIG_HOME/megapixels/config/<совместимость>,<имя камеры>.dcp, причём имя камеры берёт из нашего же конфига — то есть буквально из строки Rear:. Если имена не совпали, проверка падает, и дальше он уходит гулять по памяти. На всякий случай накидаем ещё вариантов имени: cd ~/.config/megapixels/config/ for m in "xiaomi,davinci" "xiaomi,davinci-samsung"; do for s in rear Rear REAR imx586; do cp -n /tmp/davinci_rear.dcp "$m,$s.dcp"; done done ls megapixels 2>&1 | grep -i calib Если и это не поможет, зайдём с другой стороны: запустим из пустого каталога, где нет .config, — тогда кривой цикл ничего не найдёт и напечатает «No calibration found <имя>», а нам только это имя и нужно. 💰 20.22₽ · ⬇️ 715.3k / ⬆️ 2.4k · opus-5 · max · 40сЫ S Тогда путь у него получается /home/user/.config/megapixels/config/… — то, что нам надо. Покажи, что там реально лежит: ls -la /home/user/.config/megapixels/config/ И параллельно вытащим точное имя камеры, которое он подставляет. Запусти из пустого каталога — тогда мусорные относительные пути не сработают и он честно напечатает, чего не нашёл: mkdir -p /tmp/empty && cd /tmp/empty && megapixels 2>&1 | grep -iE "calib" ; cd ~ Ожидаю строку вида «No calibration found rear» — вот это слово и есть недостающая часть имени файла. 💰 9.55₽ · ⬇️ 360.3k / ⬆️ 518 · opus-5 · max · 11сЫ ls -la /home/user/.config/megapixels/config/ total 24 drwxr-sr-x 2 user root 4096 Aug 2 11:26 . drwxr-sr-x 3 user root 4096 Aug 2 11:26 .. -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci,Rear.dcp -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci,rear.dcp -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci-samsung,Rear.dcp -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci-samsung,rear.dcpS Файлы на месте, значит он ищет по другому имени. Точное имя он печатает сам, когда ничего не находит — но у тебя в домашнем каталоге лежит .config, и кривой цикл цепляется за него раньше. Поэтому запусти из пустого каталога: mkdir -p /tmp/empty && cd /tmp/empty && megapixels 2>&1 | grep -i calib; cd ~ Там будет строка «No calibration found <имя>» — пришли её, и я скажу, как назвать файл. Ожидаю либо Rear, либо imx586, либо, если он взял камеру не из нашего конфига, а сам, — что-нибудь вроде Qualcomm Camera Subsystem. 💰 27.66₽ · ⬇️ 1.09M / ⬆️ 2.1k · opus-5 · max · 36сЫ S Он находит корень / — то есть кривой цикл срабатывает всегда, а правильная проверка перед ним не проходит. Посмотрим, какой шаблон пути реально вкомпилирован в твою сборку (она может отличаться от того master, что я читаю): grep -ao "[^\"]*megapixels/config[^\"]*" /usr/bin/megapixels | sort -u | head grep -ao "No calibration found[^\"]*" /usr/bin/megapixels | head -3 И заодно проверим, как он вообще назвал камеру — библиотека умеет говорить подробно: MEGAPIXELS_LOGLEVEL=debug megapixels 2>&1 | grep -iE "config|camera|name" | head -15 Одно из двух: либо шаблон в твоей версии другой (например, без имени камеры), либо камера называется не так, как мы думаем. Обе вещи видно прямо в этих выводах. 💰 10.04₽ · ⬇️ 364.0k / ⬆️ 1.0k · opus-5 · max · 18сЫ S Тогда ищем шаблоны профиля напрямую: grep -ao "[^\"]*\.dcp" /usr/bin/megapixels | sort -u | head Заодно интересно, что скажет отладочный лог библиотеки — из него будет видно, какой конфиг подхватился и как названа камера: MEGAPIXELS_LOGLEVEL=debug megapixels 2>&1 | head -20 💰 9.10₽ · ⬇️ 365.2k / ⬆️ 340 · opus-5 · max · 7сЫ MEGAPIXELS_LOGLEVEL=debug megapixels 2>&1 | head -20 Found postprocessor script at /usr/share/megapixels/movie.sh Initialized OpenGL ES 3.2 Found calibration file at / Using shader /org/postmarketos/Megapixels/debayer.vert and /org/postmarketos/Megapixels/debayer_packed.frag Exposure limit: 324 Dropping frame Dropping frame Dropping frame L/Expose + 828 Gain + 4 Dropping frame Dropping frame Gain + 8 Dropping frame Gain + 12 Dropping frame Gain + 16 Dropping frame Dropping frame Gain + 20 grep -ao "[^\"]*\.dcp" /usr/bin/megapixels | sort -u | head config/%s,%s.dcpS Вот и разгадка: в твоей сборке зашит один шаблон, и тот относительный — config/%s,%s.dcp. То есть профиль он ищет не в ~/.config и не в /usr/share, а в подкаталоге config рядом с текущим рабочим каталогом. Строк с абсолютными путями в бинарнике просто нет. Значит кладём туда: mkdir -p ~/config for m in "xiaomi,davinci-samsung" "xiaomi,davinci"; do for s in Rear rear; do cp /tmp/davinci_rear.dcp ~/config/"$m,$s.dcp"; done done cd ~ && megapixels 2>&1 | grep -i calib Запускать обязательно из домашнего каталога — иначе относительный путь не совпадёт. Должно напечатать «Found calibration file at config/xiaomi,davinci-samsung,Rear.dcp». Если вместо этого снова /, значит имя камеры другое — тогда переберём: положим ещё imx586 и Qualcomm Camera Subsystem. Кстати, к списку патчей добавляется третий пункт: искать профиль в нормальных местах, а не в текущем каталоге. Итого правок в Megapixels три, все маленькие. 💰 10.05₽ · ⬇️ 365.9k / ⬆️ 1.4k · opus-5 · max · 22сЫ S Верно, моя ошибка: я пометил профиль дневным светом, а замер у тебя был под тёплой лампой. Матрицу тоже надо брать тёплую. Пересчитываю: Пересчитал под лампу — тёплая матрица и код освещения «лампа накаливания» вместо дневного: base64 -d > /tmp/davinci_rear_A.dcp <<'EOF' SUlSQwgAAAAFABTGAgASAAAASgAAACHGCgAJAAAAXAAAAFrGAwABAAAAEQAAAPjGAgAqAAAApAAA ABTHCgAJAAAAzgAAAAAAAABYaWFvbWkgTWkgOVQgcmVhcgAqMgAAECcAAMvv//8QJwAA7Pn//xAn AABr9v//ECcAAH4rAAAQJwAATAQAABAnAABe////ECcAABIGAAAQJwAA2wsAABAnAABNaSA5VCBJ TVg1ODIgKHR1bmdzdGVuLCBtZWFzdXJlZCBuZXV0cmFsKQDgFQAAECcAALMMAAAQJwAAGAMAABAn AABIBQAAECcAAI4nAAAQJwAAOvr//xAnAADo/v//ECcAAMjx//8QJwAAjC8AABAnAAA= EOF for m in "xiaomi,davinci-samsung" "xiaomi,davinci"; do for s in Rear rear; do cp /tmp/davinci_rear_A.dcp ~/config/"$m,$s.dcp"; done done cd ~ && megapixels Но по-хорошему нужен второй замер. Профиль формата DCP умеет держать две точки — тёплую и дневную, а приложение интерполирует между ними по сцене. Сейчас там только одна, поэтому при другом свете он всё равно будет промахиваться. Так что как будет светло — повтори тот же замер у окна, при дневном свете и без ламп: W=4000 H=3000 EXPOSURE=1500 GAIN=400 sh ~/lanekit/capture.sh /tmp/wb_day.raw и прогони тот же питоновский счётчик. С двумя наборами чисел соберу полноценный профиль, и цвет станет правильным при любом освещении. 💰 21.75₽ · ⬇️ 737.3k / ⬆️ 3.0k · opus-5 · max · 43сЫ S Разобрался, почему подстановка «правильной по спецификации» матрицы даёт то красноту, то синеву. У Megapixels баланс белого считается так: avg_r = r*matrix1[0] + g*matrix1[1] + b*matrix1[2]; // и так три раза new_r = g / r; new_b = g / b; // серый мир То есть он берёт ColorMatrix1 из профиля и умножает на неё сырые каналы, хотя по формату эта матрица описывает обратное направление, XYZ→камера. Поэтому чем «правильнее» я её считаю, тем сильнее уезжает баланс — она работает у него не как матрица цвета, а как коэффициенты смешивания в автобалансе. Значит правильный ответ на твой вопрос — да, надо просто положить туда нашу матрицу из libcamera как есть. Тогда серый мир будет считаться в пространстве, близком к sRGB, ровно как в libcamera, где картинка тебе и нравилась: base64 -d > /tmp/davinci_ccm.dcp <<'EOF' PLACEHOLDER EOF Секунду, соберу файл. Готово. Кладём: base64 -d > /tmp/davinci_ccm.dcp <<'EOF' SUlSQwgAAAAFABTGAgASAAAASgAAACHGCgAJAAAAXAAAAFrGAwABAAAAFQAAAPjGAgAoAAAApAAA ABTHCgAJAAAAzAAAAAAAAABYaWFvbWkgTWkgOVQgcmVhcgCAPgAAECcAALDr//8QJwAA4Pz//xAn AAAE9///ECcAAIA+AAAQJwAAjPH//xAnAABw/v//ECcAAITq//8QJwAAHD4AABAnAABNaSA5VCBJ TVg1ODIgKGxpYmNhbWVyYSBjY20gYXMgbWF0cml4MSkAkhcAABAnAAAjDAAAECcAAPUBAAAQJwAA XwcAABAnAAD8JgAAECcAALX4//8QJwAA4v7//xAnAABx9v//ECcAAOkqAAAQJwAA EOF for m in "xiaomi,davinci-samsung" "xiaomi,davinci"; do for s in Rear rear; do cp /tmp/davinci_ccm.dcp ~/config/"$m,$s.dcp"; done done cd ~ && megapixels Внутри — та самая матрица из imx586.yaml, строка в строку, плюс честная прямая матрица для сохранения в DNG. Строки матрицы суммируются в единицу, поэтому их автобаланс теперь будет считать серый мир в почти-sRGB, как это делает libcamera. Если цвет совпадёт с тем, что тебе нравилось — значит гипотеза верна, и заодно у нас на руках готовый баг-репорт: у них в автобалансе используется матрица не того направления. 💰 55.24₽ · ⬇️ 1.87M / ⬆️ 7.8k · opus-5 · max · 122сЫ S Это уже не профиль, а их автобаланс. Смотри, что он делает: считает средний цвет кадра и тянет его к серому, причём гайны применяет к сырым каналам, а среднее считает после матрицы — то есть с обратной связью. На тёплой комнате со стенами и деревом «серый мир» уводит картинку в синь, а на мониторе (он синий) — резко в красноту. Твоё наблюдение это ровно и описывает, лучше любого лога. Проверить просто: найди в интерфейсе переключатель баланса белого и выключи автоматику. Тогда останутся коэффициенты из профиля — то есть твой же замер по белому листу, — и цвет должен встать правильно для этого света. Если переключателя нет, то дальше в этой ветке ловить нечего: у них наивный серый мир без учёта освещения, и это чинится только патчем. У нас к ним и так уже накопилось три штуки: 1. длина строки берётся расчётом вместо bytesperline — из-за этого падает съёмка на 4000×3000; 2. цикл поиска профиля бежит по символам строки вместо массива путей — поэтому он «находит» каталог /; 3. матрица ColorMatrix1 используется в автобалансе не в том направлении. Все три маленькие. Предлагаю на этом megapixels отложить: снимать у нас уже есть чем, а патчи оформим спокойно, вместе с камерными правками в ядро. 💰 10.45₽ · ⬇️ 380.9k / ⬆️ 1.4k · opus-5 · max · 29сЫ S Значит всё сходится: цвет и матрица правильные, а гуляет именно автобаланс. На нейтральной стене серому миру не с чем ошибаться, вот он и попадает в точку. По уму лечится тем, что делают взрослые ISP: балансом не по среднему, а по оценке освещения — считать корреляцию с кривой Планка и выбирать ближайшую точку белого из профиля. У нас для этого как раз есть готовая заготовка — профиль с двумя источниками, тёплым и дневным. Не хватает только второго замера, дневного. Так что предлагаю так: как будет светло, сделаешь тот же снимок белого листа у окна, и я соберу профиль с двумя точками. А параллельно оформлю три патча к Megapixels — они простые и полезны всем, кто снимает через camss. Что дальше сейчас — вернёмся к камере в ядре (там висят кучи dma-buf, поворот 90 вместо 180 и телевик), или на сегодня хватит? 💰 9.77₽ · ⬇️ 382.3k / ⬆️ 497 · opus-5 · max · 13сЫ S Сделал. Правка в пяти местах: везде, где приложение вычисляет длину строки из ширины, теперь берётся то, что сказал драйвер — mode->stride, который libmegapixels уже хранит. Заодно чинится и превью на 4000×3000, там та же арифметика. Ставим зависимости и берём исходник: sudo apk add git meson ninja-build gcc musl-dev gtk4.0-dev glib-dev \ libmegapixels-dev libdng-dev mesa-dev cd ~ && git clone https://gitlab.com/megapixels-org/Megapixels.git && cd Megapixels Кладём патч и применяем: cat > /tmp/mp_stride.py <<'PYEOF' #!/usr/bin/env python3 import sys, os root = sys.argv[1] if len(sys.argv) > 1 else "." def edit(path, pairs): p = os.path.join(root, path); s = open(p).read() for old, new in pairs: if new in s and old not in s: print(" уже применено:", path); continue if old not in s: print(" НЕ НАЙДЕНО в", path); sys.exit(1) s = s.replace(old, new, 1) open(p, "w").write(s); print(" правлено:", path) edit("src/gles2_debayer.h", [( """ uint32_t src_width, uint32_t src_height,""", """ uint32_t src_width, uint32_t src_stride, uint32_t src_height,""")]) edit("src/gles2_debayer.c", [( """ const uint32_t src_width, const uint32_t src_height,""", """ const uint32_t src_width, const uint32_t src_stride, const uint32_t src_height,"""), (""" GLuint padding_bytes = libmegapixels_mode_width_to_padding(self->format, src_width);""", """ GLuint padding_bytes = src_stride > row_length ? src_stride - row_length : libmegapixels_mode_width_to_padding(self->format, src_width);"""), ]) edit("src/process_pipeline.c", [( """static void init_gl(MPPipeline *pipeline, GdkSurface **surface)""", """/* * The length of one line as the driver reports it. Computing it from the * width only works when the driver pads lines the minimum amount; camss for * instance aligns them to sixteen bytes, so at 4000 pixels a line is 5008 * bytes and not 5000. libmegapixels keeps what the driver said in stride. */ static uint32_t mode_stride(const libmegapixels_mode *mode) { if (mode->stride) return mode->stride; return libmegapixels_mode_width_to_bytes(mode->format, mode->width) + libmegapixels_mode_width_to_padding(mode->format, mode->width); } static void init_gl(MPPipeline *pipeline, GdkSurface **surface)"""), (""" libmegapixels_mode_width_to_bytes(state_proc.mode->format, state_proc.mode->width) + libmegapixels_mode_width_to_padding(state_proc.mode->format, state_proc.mode->width), state_proc.mode->height,""", """ mode_stride(state_proc.mode), state_proc.mode->height,"""), (""" (libmegapixels_mode_width_to_bytes(state_proc.mode->format, state_proc.mode->width) * state_proc.mode->height));""", """ (mode_stride(state_proc.mode) * state_proc.mode->height));"""), (""" size_t size = (libmegapixels_mode_width_to_bytes(state_proc.mode->format, state_proc.mode->width) +Ы S Логично. Файл выше, дальше так: sudo apk add git meson ninja-build gcc musl-dev gtk4.0-dev glib-dev \ libmegapixels-dev libdng-dev mesa-dev cd ~ && git clone https://gitlab.com/megapixels-org/Megapixels.git python3 ~/Downloads/patch.py ~/Megapixels cd ~/Megapixels && meson setup build --prefix=/usr --buildtype=release && ninja -C build && sudo ninja -C build install cd ~ && megapixels Скрипт идемпотентный: если запустить дважды, скажет «уже применено» и ничего не испортит. Если какое-то место не найдётся — остановится и назовёт файл, значит master уехал и я поправлю. 💰 19.57₽ · ⬇️ 794.6k / ⬆️ 476 · opus-5 · max · 15сЫ S Скорее всего это не звук как таковой, а pipewire — мы его сегодня несколько раз убивали через kill -9, а он обслуживает и звук, и камеру разом. Плюс мы подменили системную libcamera своей сборкой, а pipewire её грузит своим модулем: если он на ней падает, вместе с камерой уезжает и аудио. Проверь: systemctl --user status pipewire wireplumber --no-pager 2>&1 | grep -E "Active|Main PID|error" | head wpctl status 2>&1 | head -25 И попробуй просто перезапустить: systemctl --user restart pipewire wireplumber sleep 2; wpctl status | head -20 Если после перезапуска он снова падает, посмотрим лог: journalctl --user -u pipewire -n 40 --no-pager | tail -20 Там будет видно, роняет ли его модуль камеры. Если да — просто отключим ему модуль libcamera, звук от этого не пострадает, а камерой мы и так ходим напрямую. 💰 10.09₽ · ⬇️ 397.7k / ⬆️ 895 · opus-5 · max · 16сЫ