Ветказачем? convert работает хорошг
301 сообщение · –S Понял, convert и оставим. Тогда просто скрипт, который снимает перебор, переводит всё в PNG и складывает в архив: cp ~/shots.sh ~/lanekit/ && cd ~/lanekit && sh shots.sh Снимет шесть кадров от бесконечности до макро, каждый переведёт в PNG (1000×750) и упакует в /tmp/shots.tar.gz. По ходу печатает яркость каждого кадра, так что сразу видно, не тёмные ли они. Настройки через переменные: W=8000 H=6000 PAT=quad sh shots.sh # полный кадр POSITIONS="300 400 500 600" sh shots.sh # только интересный участок EXPOSURE=1500 GAIN=300 sh shots.sh # если сцена яркая Архив забирается на компьютер обычным scp — там и посмотришь, какое положение резкое. 💰 64.56₽ · ⬇️ 2.71M / ⬆️ 2.3k · opus-5 · max · 40сЫ S Верно, для полусотни строк это лишнее. Вот тем же куском, вставляется целиком: cat > ~/lanekit/shots.sh <<'SH' #!/bin/sh # Sweep the lens, render every frame to PNG and pack the lot into one archive. W=${W:-4000}; H=${H:-3000}; PAT=${PAT:-rggb}; BLOCK=${BLOCK:-4} EXPOSURE=${EXPOSURE:-3006}; GAIN=${GAIN:-960} POSITIONS=${POSITIONS:-"0 200 400 600 800 1000"} PREVIEW=${PREVIEW:-$HOME/raw10preview} OUT=/tmp/shots command -v convert >/dev/null || { echo "imagemagick is not installed"; exit 1; } [ -x "$PREVIEW" ] || { echo "no raw10preview at $PREVIEW"; exit 1; } rm -rf $OUT; mkdir -p $OUT D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c" 2>/dev/null) sed -i "s/^W=[0-9]*/W=$W/; s/^H=[0-9]*/H=$H/" capture.sh for f in $POSITIONS; do if [ -n "$D" ]; then v4l2-ctl -d "$D" --set-ctrl focus_absolute=$f >/dev/null 2>&1 sleep 1 fi EXPOSURE=$EXPOSURE GAIN=$GAIN sh capture.sh $OUT/f$f.raw >/dev/null 2>&1 sz=$(stat -c %s $OUT/f$f.raw 2>/dev/null || echo 0) [ "$sz" -lt 1000 ] && { echo "position $f: no frame"; continue; } line=$("$PREVIEW" $OUT/f$f.raw $OUT/f$f.ppm $W $H $PAT $BLOCK 2>&1 | tail -1) convert $OUT/f$f.ppm $OUT/f$f.png && rm -f $OUT/f$f.ppm $OUT/f$f.raw echo "position $f: $line" done tar czf /tmp/shots.tar.gz -C /tmp shots ls -l /tmp/shots.tar.gz SH cd ~/lanekit && sh shots.sh Дальше по вкусу: W=8000 H=6000 PAT=quad sh shots.sh # полный кадр POSITIONS="300 400 500 600" sh shots.sh # только интересный участок 💰 21.70₽ · ⬇️ 903.6k / ⬆️ 1.0k · opus-5 · max · 13сЫ S Значит камера работает целиком: наводка на 600 даёт резкий текст, выдержка и усиление слушаются, оба режима снимают. «Некрасиво» — это ожидаемо, и вот почему. Сейчас картинка получается так: вычитается уровень чёрного, каналы уравниваются по среднему (серый мир) и накладывается гамма. Всё, чего в ней нет: Матрица цветокоррекции. Сырые R, G, B сенсора — это не sRGB, у них другие спектральные кривые. Без пересчёта цвета выходят блёклыми и с уводом в зелень. Коррекция затемнения по краям. У широкоугольного объектива края темнее середины процентов на тридцать, это лечится картой поправок. Настоящий баланс белого. Серый мир — грубая оценка: если в кадре преобладает один цвет, он же и уедет. Приличная демозаика. У меня усреднение блоками, поэтому теряется половина разрешения. Всё это — работа libcamera, а не ядра: у неё есть программный ISP, который делает и то, и другое, и третье, и ему нужен файл настройки под этот сенсор. Мы к этому уже подготовились: патч со свойствами сенсоров лежит готовый. Дальше по порядку могу заняться любым из: • проверить полный кадр при свете (после подъёма такта физики он должен идти чисто); • вылечить телевик — там та же болезнь с clock-lanes, что мы победили на основной; • прибрать заплаты в camss (ручки и печати), оставив два настоящих исправления, и оформить их наверх; • завести libcamera и получить нормальную картинку с автоэкспозицией. Что берём? 💰 21.96₽ · ⬇️ 904.7k / ⬆️ 1.0k · opus-5 · max · 24сЫ S Телевик — это вторая задняя камера, восьмимегапиксельный OV8856 с двукратным приближением (в Mi 9T их три: широкая, которую мы подняли, телевик и сверхширокая). Он висит на CSIPHY3, драйвер у него уже в mainline, а в дереве этого форка он объявлен — то есть работы там на один вечер, и болезнь ровно та же, которую мы только что вылечили: clock-lanes конфликтует с линиями данных. Ценность — оптическое приближение без потери качества. Но раз просишь — прибираю код. Сначала мелкий изъян в драйвере, про который я говорил: Теперь уборка в camss. Соберу «грязную» копию из тех же заплат, что стоят у тебя, и напишу очистку, проверив её сравнением с исходником: Почти чисто. Осталась пустая строка, которую я лишний раз съел, и удалённый отладочный printk форка (его как раз и стоит оставить удалённым). Правлю: Теперь собираю набор начисто. Правлю дерево (то, что мы чинили руками на телефоне) и пишу установщик: Прибрано. Главное — отладочные леса и настоящие исправления теперь разделены. camss-clean.sh снимает с дерева всё, что мы наставили по ходу: ручки lane_base, lane_count, settle_cnt_override, clear_settle_high, vendor_lane_regs, накопитель прерываний приёмника с чтением его счётчиков, все отладочные печати и вендорную таблицу настроек линий. Проверено сравнением: после очистки camss-csiphy-3ph-1-0.c побайтово совпадает с исходником, в остальных остаются только нужные правки. camss-fixes.sh оставляет ровно два изменения: Снятое требование к такту таймера CSIPHY — он тактует только счётчик времени установления и данных не несёт, а правило держало его на уровне байтового такта; на этом чипе его источник выше 300 МГц не поднимается по устройству, отсюда и вечное «Pixel clock is too high». Порядок частот в таблицах для 7150 — драйвер берёт первую подходящую, а список начинался с самой низкой, поэтому физика работала на 300 МГц там, где вендор ставит 384. Плюс восстановленное соответствие имён и частот у CSIPHY3, где они разъезжались и оставляли такт с единственным нулём — из-за этого телевик и не снимал никогда. Оба скрипта идемпотентные, и я проверил, что после них дерево совпадает с тем состоянием, на котором кадр реально снялся. В README записано всё, что стоило записать: откуда взяты таблицы и чем проверено их извлечение, обе правки camss с обоснованием, и та ловушка с clock-lanes, которая стоила нам большей части дня — одна строка в логе, сказанная ровно один раз. Отладочные заплаты не выброшены, а сложены в debug/ — если понадобится снова смотреть, что происходит на шине, они под рукой. 💰 295₽ · ⬇️ 11.91M / ⬆️ 16.4k · opus-5 · max · 261сЫ S Телевик — это оптическое приближение вдвое: восьмимегапиксельный OV8856 со своим объективом, снимает то же самое, но крупнее, без цифрового растягивания. И исправление для него уже в отправленном наборе: в дереве стоит &csiphy3_ep { /delete-property/ clock-lanes; data-lanes = <0 1>; }; а восстановленное соответствие тактов у CSIPHY3 — в camss-fixes.sh. Для проверки там же лежит capture-tele.sh, который ведёт его через CSIPHY3 → CSID1 → VFE1 и снимает 1640×1232. Теперь libcamera. Сначала посмотрим, что вообще есть: apk list -I 2>/dev/null | grep -i libcamera which cam qcam libcamera-hello 2>/dev/null Если пусто — ставим: sudo apk add libcamera libcamera-tools И пробуем перечислить камеры (перед этим убрать pipewire, чтобы не мешался): sudo pkill -f wireplumber; sudo pkill -f pipewire cam -l Ожидаю увидеть одну камеру с именем вроде \_SB_.PCI0... или путь媒体-устройства. Если она появится — сразу снимем через неё: cam -c 1 --capture=2 --file=/tmp/lc_#.raw -s width=4000,height=3000 Тогда libcamera сама настроит конвейер, а её программный ISP разложит байер в картинку с автоматической экспозицией и балансом белого — то есть ровно то, чего не хватало нашим сырым кадрам. Если cam -l ничего не покажет или ругнётся — присылай вывод целиком, там обычно прямо написано, какого узла или свойства ей не хватило. 💰 22.84₽ · ⬇️ 925.2k / ⬆️ 1.5k · opus-5 · max · 28сЫ sh tmp18.sh [0:43:53.440068332] [16937] INFO Camera camera_manager.cpp:340 libcamera v0.7.1 [0:43:53.492595534] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 2 on pad 0/0: Not a tty [0:43:53.492656680] [16938] WARN CameraSensor camera_sensor_legacy.cpp:402 'ov8856 13-0036': The PixelArraySize property has been defaulted to 1640x1232 [0:43:53.492670014] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 1 on pad 0/0: Not a tty [0:43:53.492679545] [16938] WARN CameraSensor camera_sensor_legacy.cpp:413 'ov8856 13-0036': The PixelArrayActiveAreas property has been defaulted to (0, 0)/1640x1232 [0:43:53.492692149] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:43:53.492700951] [16938] WARN CameraSensor camera_sensor_legacy.cpp:421 'ov8856 13-0036': Failed to retrieve the sensor crop rectangle [0:43:53.492709545] [16938] WARN CameraSensor camera_sensor_legacy.cpp:427 'ov8856 13-0036': The sensor kernel driver needs to be fixed [0:43:53.492717253] [16938] WARN CameraSensor camera_sensor_legacy.cpp:429 'ov8856 13-0036': See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information [0:43:53.492986474] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'ov8856' [0:43:53.493025068] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:43:53.493042360] [16938] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. [0:43:53.493543977] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'imx586' [0:43:53.493583405] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:43:54.005469719] [16938] WARN CameraSensor camera_sensor_legacy.cpp:502 'imx586 12-001a': No sensor delays found in static properties. Assuming unverified defaults. [0:43:54.016475882] [16938] 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:43:54.016665570] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:43:54.016725883] [16938] WARN CameraSensor camera_sensor_legacy.cpp:881 'ov8856 13-0036': The analogue crop rectangle has been defaulted to the active area size [0:43:54.016887082] [16938] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for ov8856 [0:43:54.021853780] [16938] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36' for pipeline handler simple [0:43:54.025210363] [16938] 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:43:54.025455104] [16938] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for imx586 [0:43:54.033133329] [16938] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a' for pipeline handler simple Available cameras: 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 Обе камеры видны libcamera 0.7.1 — и широкая, и телевик, обе через обработчик simple. Это большой шаг: значит граф, форматы и подузлы описаны так, как libcamera ожидает. Сразу пробуем снять через неё — программный ISP разложит байер сам: cam -c 2 --capture=2 -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb ls -l /tmp/lc_*.rgb Камера номер 2 — это наша широкая (camera@1a). Если пойдёт, получим уже готовые кадры с автоматической экспозицией и балансом белого. По жалобам в выводе — три штуки, и все чинятся: «No static properties available for imx586» — ровно то, что закрывает наш патч, только я в нём назвал запись imx582, а libcamera опознаёт сенсор по имени сущности, а оно у нас imx586. Поправлю на оба имени. «Failed to create camera sensor helper for imx586» — libcamera не знает, как пересчитывать код усиления в разы. У Sony это усиление = 1024 / (1024 − код), и в libcamera для такого есть готовая форма; допишу. «Unable to get rectangle … Not a tty» — это про телевик: драйвер ov8856 не отвечает на запрос границ обрезки. У нашего imx586 таких жалоб нет, потому что мы это как раз добавили. Для телевика поправим позже, оно не мешает. Готовлю оба патча, а ты пока сними — интересно, что выйдет с автоматикой. 💰 24.70₽ · ⬇️ 928.5k / ⬆️ 2.1k · opus-5 · max · 42сЫ S Кадры получились: 8 294 400 байт это 1920×1080 по четыре байта на точку — программный ISP отработал, картинка уже разложена и обработана. Готовлю патчи. Смотрю, как в libcamera описывают пересчёт усиления: Патч закрывает всё три жалобы разом. Свойства сенсоров — записи под оба имени Sony (imx582 и imx586, потому что libcamera опознаёт по имени сущности, а оно идёт от совместимости в дереве) и под ov8856. Размер точки 0.8 мкм у широкой и 1.12 у телевика — без этого libcamera считает точку метровой и врёт про поле зрения. Пересчёт усиления — то, из-за чего ISP не мог управлять яркостью: gain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 }; /* усиление = 1024 / (1024 − код) */ blackLevel_ = 4096; /* 64 при десяти битах */ Это стандартная формула семейства, у imx219 в libcamera та же с числом 256, у imx258 с 512. Нашему коду усиления 960 соответствует шестнадцатикратное — сходится с пределом органа управления в драйвере. Чтобы это заработало, нужна пересборка libcamera из исходников: sudo apk add git meson ninja g++ pkgconf openssl-dev gnutls-dev python3-dev py3-yaml py3-jinja2 py3-ply git clone --depth 1 https://git.libcamera.org/libcamera/libcamera.git cd libcamera && patch -p1 < ~/libcamera-mi9t.patch meson setup build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build && sudo ninja -C build install На восьми ядрах это минут пятнадцать. Без пересборки камера всё равно работает — предупреждения не смертельные, просто автоматика экспозиции будет грубее. А пока подними телефон и сними через libcamera при свете: cam -c 2 --capture=4 -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb convert -size 1920x1080 -depth 8 bgra:/tmp/lc_cam0-stream0-000003.rgb /tmp/lc.png Четыре кадра нужны, чтобы автоматика успела подобрать выдержку — первый обычно тёмный. 💰 158₽ · ⬇️ 6.54M / ⬆️ 5.6k · opus-5 · max · 98сЫ patch -p1 < ~/libcamera-mi9t.patch patching file src/libcamera/sensor/camera_sensor_properties.cpp Hunk #1 FAILED at 325. Hunk #2 FAILED at 495. 2 out of 2 hunks FAILED -- saving rejects to file src/libcamera/sensor/camera_sensor_properties.cpp.rej patching file src/ipa/libipa/camera_sensor_helper.cpp patch: **** malformed patch at line 99: @@ -830,6 +844,17 @@S Мой заголовок куска посчитан неверно (я его правил руками при переносе в сообщение), да и номера строк у тебя другие — libcamera с тех пор поменялась. Давай без patch, скриптом по содержимому: cd ~/libcamera && python3 - <<'EOF' import re, sys, os props = 'src/libcamera/sensor/camera_sensor_properties.cpp' helper = 'src/ipa/libipa/camera_sensor_helper.cpp' sony = """\t\t{ "%s", { \t\t\t.unitCellSize = { 800, 800 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeSolidColor, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 2 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, \t\t\t\t{ controls::draft::TestPatternModePn9, 4 }, \t\t\t}, \t\t\t.sensorDelays = { \t\t\t\t.exposureDelay = 2, \t\t\t\t.gainDelay = 2, \t\t\t\t.vblankDelay = 3, \t\t\t\t.hblankDelay = 3 \t\t\t}, \t\t} }, """ omni = """\t\t{ "ov8856", { \t\t\t.unitCellSize = { 1120, 1120 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, \t\t\t}, \t\t\t.sensorDelays = { }, \t\t} }, """ s = open(props).read() if '"imx586"' in s: print("свойства уже добавлены") else: m = re.search(r'^\t\t\{ "ov', s, re.M) if not m: sys.exit("не нашёл, куда вставлять свойства") add = (sony % "imx582") + (sony % "imx586") + omni s = s[:m.start()] + add + s[m.start():] open(props, 'w').write(s) print("свойства добавлены: imx582, imx586, ov8856") s = open(helper).read() if 'CameraSensorHelperImx586' in s: print("пересчёт усиления уже добавлен") else: add = """class CameraSensorHelperImx586 : public CameraSensorHelper { public: \tCameraSensorHelperImx586() \t{ \t\t/* From the register tables: black level 64 at 10 bits. */ \t\tblackLevel_ = 4096; \t\tgain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) class CameraSensorHelperOv8856 : public CameraSensorHelper { public: \tCameraSensorHelperOv8856() \t{ \t\t/* Same 1/128 step as its sibling. */ \t\tgain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("ov8856", CameraSensorHelperOv8856) """ i = s.rindex('} /* namespace */') s = s[:i] + add + s[i:] open(helper, 'w').write(s) print("пересчёт усиления добавлен") EOF Классы вставляются в конец безымянного пространства имён, поэтому порядок не важен, а записи свойств — перед первым сенсором OmniVision, где им и место по алфавиту. Скрипт можно гонять повторно, второй раз он ничего не сделает. Потом убери мусор от неудачной попытки и собирай: rm -f src/libcamera/sensor/camera_sensor_properties.cpp.rej git diff --stat meson setup build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build 💰 24.87₽ · ⬇️ 949.4k / ⬆️ 2.9k · opus-5 · max · 38сЫ S Две помарки в моём скрипте. Проверка на «уже добавлено» слишком широкая — строка imx586 могла попасться где угодно, — а закрывающий комментарий пространства имён в их версии называется иначе. Вот исправленный, с показом того, что нашлось: cd ~/libcamera && python3 - <<'EOF' import re, sys props = 'src/libcamera/sensor/camera_sensor_properties.cpp' helper = 'src/ipa/libipa/camera_sensor_helper.cpp' s = open(props).read() print("что уже есть в свойствах:", re.findall(r'\{ "(imx58\d|ov885\d)", \{', s) or "ничего из наших") sony = """\t\t{ "%s", { \t\t\t.unitCellSize = { 800, 800 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeSolidColor, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 2 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, \t\t\t\t{ controls::draft::TestPatternModePn9, 4 }, \t\t\t}, \t\t\t.sensorDelays = { \t\t\t\t.exposureDelay = 2, \t\t\t\t.gainDelay = 2, \t\t\t\t.vblankDelay = 3, \t\t\t\t.hblankDelay = 3 \t\t\t}, \t\t} }, """ omni = """\t\t{ "ov8856", { \t\t\t.unitCellSize = { 1120, 1120 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, \t\t\t}, \t\t\t.sensorDelays = { }, \t\t} }, """ add = "".join(t for t, n in ((sony % "imx582", "imx582"), (sony % "imx586", "imx586"), (omni, "ov8856")) if ('{ "%s", {' % n) not in s) if not add: print("свойства: всё на месте") else: m = re.search(r'^\t\t\{ "ov', s, re.M) s = s[:m.start()] + add + s[m.start():] open(props, 'w').write(s) print("свойства дописаны") s = open(helper).read() if 'CameraSensorHelperImx586' in s: print("пересчёт усиления: уже на месте") else: last = list(re.finditer(r'^REGISTER_CAMERA_SENSOR_HELPER\([^\n]*\)\n', s, re.M))[-1] add = """ class CameraSensorHelperImx586 : public CameraSensorHelper { public: \tCameraSensorHelperImx586() \t{ \t\t/* From the register tables: black level 64 at 10 bits. */ \t\tblackLevel_ = 4096; \t\tgain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) class CameraSensorHelperOv8856 : public CameraSensorHelper { public: \tCameraSensorHelperOv8856() \t{ \t\t/* Same 1/128 step as its sibling. */ \t\tgain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("ov8856", CameraSensorHelperOv8856) """ s = s[:last.end()] + add + s[last.end():] open(helper, 'w').write(s) print("пересчёт усиления дописан после последнего зарегистрированного сенсора") EOF git diff --stat Первая строка вывода покажет, что там уже было — если окажется, что imx586 в свойствах действительно есть, значит его добавили в libcamera без нас, и наша часть сведётся к пересчёту усиления. 💰 25.09₽ · ⬇️ 952.4k / ⬆️ 2.1k · opus-5 · max · 27сЫ S Тогда собираем: cd ~/libcamera meson setup build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build sudo ninja -C build install sudo ldconfig 2>/dev/null || true Установится в /usr/local, поверх пакетной не полезет — то есть cam из /usr/local/bin будет пользоваться новой библиотекой, а всё остальное в системе останется на старой. Для проверки это то, что нужно. Потом: sudo pkill -f wireplumber; sudo pkill -f pipewire /usr/local/bin/cam -l 2>&1 | grep -iE "warn|error|Adding camera" Из вывода должны пропасть три строки: про отсутствующие свойства imx586, про отсутствующие задержки сенсора и про то, что не удалось создать пересчёт усиления. Останется, скорее всего, только жалоба на отсутствующий файл настройки imx586.yaml — это уже про цвет, им займёмся отдельно. И сразу снимок при свете, теперь автоматика должна работать осмысленно: /usr/local/bin/cam -c 2 --capture=6 -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb convert -size 1920x1080 -depth 8 bgra:/tmp/lc_cam0-stream0-000005.rgb /tmp/lc.png Шесть кадров — чтобы выдержка успела сойтись; последний и смотрим. 💰 23.34₽ · ⬇️ 954.5k / ⬆️ 836 · opus-5 · max · 16сЫ S cam собирается поверх libevent. Ставим и повторяем: sudo apk add libevent-dev cd ~/libcamera && meson setup --wipe build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build Если ещё чего-то не хватит — оно назовёт зависимость так же понятно, присылай строку. Или можно вовсе не собирать приложение, нам ведь нужна библиотека: meson setup --wipe build -Dpipelines=simple -Dipas=simple -Dcam=disabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build && sudo ninja -C build install а проверять пакетным cam, подсунув ему свежую библиотеку: LD_LIBRARY_PATH=/usr/local/lib cam -l Второй путь быстрее, потому что не тянет ни libevent, ни остальную обвязку приложения. 💰 22.47₽ · ⬇️ 957.7k / ⬆️ 702 · opus-5 · max · 14сЫ ../src/ipa/libipa/camera_sensor_helper.h:91:42: error: redefinition of 'libcamera::ipa::CameraSensorHelperFactory<libcamera::ipa::CameraSensorHelperImx586> libcamera::ipa::global_CameraSensorHelperImx586Factory' 91 | static CameraSensorHelperFactory<helper> global_##helper##Factory(name); | ^~~~~ ../src/ipa/libipa/camera_sensor_helper.cpp:901:1: note: in expansion of macro 'REGISTER_CAMERA_SENSOR_HELPER' 901 | REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) | ^~~~~~~~~~~~~~~~~~~~~~~~~ ../src/ipa/libipa/camera_sensor_helper.h:91:42: note: 'libcamera::ipa::CameraSensorHelperFactory<libcamera::ipa::CameraSensorHelperImx586> libcamera::ipa::global_CameraSensorHelperImx586Factory' previously declared here 91 | static CameraSensorHelperFactory<helper> global_##helper##Factory(name); | ^~~ ../src/ipa/libipa/camera_sensor_helper.cpp:900:1: note: in expansion of macro 'REGISTER_CAMERA_SENSOR_HELPER' 900 | REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ ninja: subcommand failedS Макрос создаёт глобальную переменную с именем от класса, поэтому дважды один класс зарегистрировать нельзя. Нужен второй, наследник: cd ~/libcamera && python3 - <<'EOF' p='src/ipa/libipa/camera_sensor_helper.cpp' s=open(p).read() s=s.replace('''REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586)''', '''REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) /* The same part, under the name the other compatible gives its entity. */ class CameraSensorHelperImx582 : public CameraSensorHelperImx586 { }; REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx582)''') open(p,'w').write(s) print("исправлено") EOF ninja -C build Заодно и по смыслу честнее: два имени — два класса, второй просто наследует первый без изменений. 💰 23.28₽ · ⬇️ 971.2k / ⬆️ 743 · opus-5 · max · 12с
Ы LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/cam -l [1:14:30.339139896] [19899] INFO Camera camera_manager.cpp:340 libcamera v0.0.0+1-244572db-dirty (2026-08-01T19:01:32GMT) [1:14:30.344918075] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 2 on pad 0/0: Not a tty [1:14:30.344997815] [19900] WARN CameraSensor camera_sensor_legacy.cpp:402 'ov8856 13-0036': The PixelArraySize property has been defaulted to 1640x1232 [1:14:30.345040836] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 1 on pad 0/0: Not a tty [1:14:30.345059950] [19900] WARN CameraSensor camera_sensor_legacy.cpp:413 'ov8856 13-0036': The PixelArrayActiveAreas property has been defaulted to (0, 0)/1640x1232 [1:14:30.345080263] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [1:14:30.345103075] [19900] WARN CameraSensor camera_sensor_legacy.cpp:421 'ov8856 13-0036': Failed to retrieve the sensor crop rectangle [1:14:30.345119690] [19900] WARN CameraSensor camera_sensor_legacy.cpp:427 'ov8856 13-0036': The sensor kernel driver needs to be fixed [1:14:30.345137242] [19900] WARN CameraSensor camera_sensor_legacy.cpp:429 'ov8856 13-0036': See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information [1:14:30.345991617] [19900] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. [1:14:30.891269282] [19900] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'ov8856.yaml' not found for IPA module 'simple', falling back to '/usr/local/share/libcamera/ipa/simple/uncalibrated.yaml' [1:14:30.891483345] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [1:14:30.891564855] [19900] WARN CameraSensor camera_sensor_legacy.cpp:881 'ov8856 13-0036': The analogue crop rectangle has been defaulted to the active area size [1:14:30.934827685] [19901] INFO IPAProxySoftWorker soft_ipa_proxy_worker.cpp:443 Starting worker for IPA module /usr/local/lib/libcamera/ipa/ipa_soft_simple.so with IPC fd = 24 [1:14:30.946549981] [19900] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36' for pipeline handler simple [1:14:30.952784671] [19900] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'imx586.yaml' not found for IPA module 'simple', falling back to '/usr/local/share/libcamera/ipa/simple/uncalibrated.yaml' [1:14:30.991114218] [19902] INFO IPAProxySoftWorker soft_ipa_proxy_worker.cpp:443 Starting worker for IPA module /usr/local/lib/libcamera/ipa/ipa_soft_simple.so with IPC fd = 29 [1:14:31.002176045] [19900] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a' for pipeline handler simple Available cameras: 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 исчезли — и про свойства, и про пересчёт усиления. Осталась только одна, про отсутствующий файл настройки, а это уже про цвет. Снимай при свете: sudo pkill -f wireplumber; sudo pkill -f pipewire LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/cam -c 2 --capture=8 \ -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb convert -size 1920x1080 -depth 8 bgra:/tmp/lc_cam0-stream0-000007.rgb /tmp/lc.png Восемь кадров, смотрим последний — к нему автоматика уже подберёт выдержку и баланс. Что осталось из вывода: imx586.yaml не найден — файл цветовой настройки. Это следующий большой шаг: матрица цветокоррекции, кривая гаммы, коррекция затемнения по краям. Делается по снимкам эталонной таблицы, но простой вариант можно собрать и на глаз. Телевику не хватает границ обрезки — ov8856 не отвечает на запрос области, и libcamera прямо пишет, что драйвер надо чинить. Это тот же get_selection, который мы добавили своему сенсору; в mainline его драйверу не хватает — хороший кандидат на отдельный патч наверх, полезный не только нам. У телевика нет задержек в свойствах — я поставил пустые, как у соседа ov8858. Если понадобится, проставим двойки, это одна строка. 💰 24.21₽ · ⬇️ 973.8k / ⬆️ 1.1k · opus-5 · max · 24сЫ cam0: Capture 8 frames 4532.762104 (0.00 fps) cam0-stream0 seq: 000000 bytesused: 6220800 4532.795425 (30.01 fps) cam0-stream0 seq: 000001 bytesused: 6220800 4532.828749 (30.01 fps) cam0-stream0 seq: 000002 bytesused: 6220800 4532.862080 (30.00 fps) cam0-stream0 seq: 000003 bytesused: 6220800 4532.895395 (30.02 fps) cam0-stream0 seq: 000004 bytesused: 6220800 4532.928715 (30.01 fps) cam0-stream0 seq: 000005 bytesused: 6220800 4532.962034 (30.01 fps) cam0-stream0 seq: 000006 bytesused: 6220800 4532.995354 (30.01 fps) cam0-stream0 seq: 000007 bytesused: 6220800 WARNING: The convert command is deprecated in IMv7, use "magick" instead of "convert" or "magick convert" convert: unexpected end-of-file '/tmp/lc_cam0-stream0-000007.rgb': No such file or directory @ error/bgr.c/ReadBGRImage/247. convert: no images defined `/tmp/lc.png' @ error/deprecate.c/ConvertImageCommand/3375.S Тридцать кадров в секунду, непрерывно — это уже видео, а не одиночные снимки. И размер кадра 6 220 800 это ровно 1920×1080×3, то есть на этот раз формат упакованный, без лишнего байта. Файла нет, скорее всего, из-за имени — посмотри, что получилось: ls -l /tmp/lc_* 2>/dev/null | tail -3 А переводить надо как трёхбайтовый, и с учётом того, что libcamera называет форматы по упакованному значению, порядок в памяти обратный: F=$(ls -t /tmp/lc_*.rgb | head -1); echo $F magick -size 1920x1080 -depth 8 bgr:$F /tmp/lc.png magick -size 1920x1080 -depth 8 rgb:$F /tmp/lc_rgb.png Какая из двух картинок выйдет с правильными цветами (небо синее, а не оранжевое), та и верная — заодно и запомним. Кстати, вычёркиваем ещё пункт из списка «чего не хватает до main»: видео с камеры работает, тридцать кадров при 1920×1080 через программный ISP. 💰 23.85₽ · ⬇️ 975.3k / ⬆️ 1.1k · opus-5 · max · 20сЫ S Файлы на месте, а convert ругался потому, что я велел ему читать четырёхбайтовый формат — он ждал 8 294 400 байт и упёрся в конец файла на 6 220 800. Правильно так: magick -size 1920x1080 -depth 8 bgr:/tmp/lc_cam0-stream0-000007.rgb /tmp/lc_bgr.png magick -size 1920x1080 -depth 8 rgb:/tmp/lc_cam0-stream0-000007.rgb /tmp/lc_rgb.png Смотри обе: у одной цвета будут настоящими, у другой красное с синим переставлены. Заодно интересно, как отработала автоматика — восьмой кадр снят через четверть секунды после начала, выдержка к нему уже должна сойтись. 💰 23.03₽ · ⬇️ 976.6k / ⬆️ 438 · opus-5 · max · 14сЫ S Опиши, что именно — цветной шум, полосы, или картинка узнаётся, но цвета не те? От этого зависит, где искать. Подозрение у меня такое: libcamera на запрос 1920×1080 выбрала наш режим с бинированием четыре на четыре, а при таком сведении порядок цветовых точек может сдвинуться. Драйвер же во всех режимах объявляет один и тот же SRGGB10, и если на самом деле там, скажем, GRBG, картинка получится с переставленными цветами и «грязью» на переходах. Проверяется быстро, нашим же распаковщиком — снимем тот же режим напрямую и переберём четыре варианта: cd ~/lanekit sed -i 's/^W=[0-9]*/W=1920/; s/^H=[0-9]*/H=1080/' capture.sh EXPOSURE=1000 GAIN=400 sh capture.sh for p in rggb bggr grbg gbrg; do ~/raw10preview /tmp/frame.raw /tmp/b_$p.ppm 1920 1080 $p 2 >/dev/null 2>&1 magick /tmp/b_$p.ppm /tmp/b_$p.png done ls -l /tmp/b_*.png Какой из четырёх окажется с натуральными цветами, тот и правильный для этого режима — и тогда я поправлю драйвер, чтобы он объявлял код по режиму, а не один на всех. 💰 23.58₽ · ⬇️ 977.1k / ⬆️ 1.2k · opus-5 · max · 25сЫ S Тогда сначала отделим камеру от обработки — сними тот же режим нашим путём (команду я дал в прошлом сообщении) и посмотри /tmp/b_rggb.png. Если и там мыло, дело не в libcamera. И проверь наводку: мы её ставили на 600, но libcamera про объектив ничего не знает и наверняка оставила линзу там, где она была после её собственных настроек — то есть, скорее всего, на нуле, а это бесконечность. Для комнаты нужно ближе: D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") v4l2-ctl -d "$D" --set-ctrl focus_absolute=600 v4l2-ctl -d "$D" --get-ctrl focus_absolute Потом повтори съёмку через libcamera — она положение линзы не трогает, так что оно останется. Ещё одно подозрение по этому режиму: у вендора он сделан бинированием четыре на четыре с длиной строки 2912, и если при таком сведении сенсор отдаёт кадр иначе, чем мы объявляем, то и получится каша. Наш собственный снимок это как раз и покажет. 💰 23.59₽ · ⬇️ 978.4k / ⬆️ 902 · opus-5 · max · 20сЫ S cd ~/lanekit D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") v4l2-ctl -d "$D" --set-ctrl focus_absolute=600 # наш путь, тот же режим 1920x1080 sed -i 's/^W=[0-9]*/W=1920/; s/^H=[0-9]*/H=1080/' capture.sh EXPOSURE=1000 GAIN=400 sh capture.sh for p in rggb bggr grbg gbrg; do ~/raw10preview /tmp/frame.raw /tmp/b_$p.ppm 1920 1080 $p 2 >/dev/null 2>&1 magick /tmp/b_$p.ppm /tmp/b_$p.png done # для сравнения — заведомо рабочий режим sed -i 's/^W=[0-9]*/W=4000/; s/^H=[0-9]*/H=3000/' capture.sh EXPOSURE=3006 GAIN=600 sh capture.sh ~/raw10preview /tmp/frame.raw /tmp/ctl.ppm 4000 3000 rggb 4 && magick /tmp/ctl.ppm /tmp/ctl.png ls -l /tmp/b_*.png /tmp/ctl.png Смотри ctl.png — он должен быть таким же нормальным, как раньше. Если он хороший, а все четыре b_*.png мутные, значит режим 1920×1080 у нас поднимается неверно, и я буду разбирать его таблицу отдельно: там бинирование четыре на четыре, длина строки 2912, и вполне может оказаться, что сенсор отдаёт кадр другого размера, чем мы объявляем приёмнику. 💰 23.25₽ · ⬇️ 979.4k / ⬆️ 722 · opus-5 · max · 13сЫ S Покажи, на чём именно споткнулось: sudo dmesg | grep -vE "link_setup|got IRQ" | tail -12 media-ctl -d /dev/media0 -p | grep -A3 "entity 171" | tail -3 v4l2-ctl -d $(media-ctl -d /dev/media0 -e msm_vfe0_video0) --get-fmt-video | tail -4 «Broken pipe» на запуске потока в camss означает несогласованность форматов по цепочке: либо какой-то пад не принял 1920×1080, либо на видеоузле остался прежний размер. Первая команда назовёт причину прямо, вторая покажет, что стоит у сенсора, третья — что на выходе. Подозреваю вот что: в драйвере, который сейчас загружен, предел органа pixel_rate жёстко привязан к первому режиму — я эту оплошность нашёл и поправил, но пересборки после правки ещё не было. Для 1920×1080 значение выходит за предел, установка молча не проходит, и дальше camss считает поток точек неверным. Если так и есть, лечится пересборкой драйвера из прибранного набора — там уже правильно. 💰 23.75₽ · ⬇️ 980.4k / ⬆️ 1.1k · opus-5 · max · 24сЫ sudo dmesg | grep -vE "link_setup|got IRQ" | tail -12 [ 4800.173239] qcom-camss ace0000.camss: VFE:0 HW Version = 1.3.0 [ 4800.173293] csid_reset called [ 4800.173322] qcom-camss ace0000.camss: CSID:0 HW Version = 2.0.0 [ 4800.173347] qcom-camss ace0000.camss: validating link "msm_csid0":1 -> "msm_vfe0_rdi0":0 [ 4800.173353] qcom-camss ace0000.camss: validating stream "msm_csid0":1:0 -> "msm_vfe0_rdi0":0:0 [ 4800.173362] qcom-camss ace0000.camss: validating link "msm_csiphy0":1 -> "msm_csid0":0 [ 4800.173367] qcom-camss ace0000.camss: validating stream "msm_csiphy0":1:0 -> "msm_csid0":0:0 [ 4800.173374] qcom-camss ace0000.camss: validating link "imx586 12-001a":0 -> "msm_csiphy0":0 [ 4800.173379] qcom-camss ace0000.camss: validating stream "imx586 12-001a":0:0 -> "msm_csiphy0":0:0 [ 4800.173385] qcom-camss ace0000.camss: v4l2_subdev_link_validate_default: media bus code does not match (source 0x00003007, sink 0x0000300f) [ 4800.173391] qcom-camss ace0000.camss: v4l2_subdev_link_validate_default: link was "imx586 12-001a":0 -> "msm_csiphy0":0 [ 4800.173399] qcom-camss ace0000.camss: Failed to start media pipeline: -32 media-ctl -d /dev/media0 -p | grep -A3 "entity 171" | tail -3 type V4L2 subdev subtype Sensor flags 0 device node name /dev/v4l-subdev16 pad0: SOURCE v4l2-ctl -d $(media-ctl -d /dev/media0 -e msm_vfe0_video0) --get-fmt-video | tail -4 Quantization : Full Range Plane 0 : Bytes per Line : 2400 Size Image : 2592000S Причина в логе прямым текстом: media bus code does not match (source 0x00003007, sink 0x0000300f) 0x300f это SRGGB10, а 0x3007 — SBGGR10. То есть сенсор сейчас сообщает другой порядок цветовых точек, и вот почему: libcamera выставила оба зеркала, потому что в дереве у нас rotation = <180> — модуль стоит вверх ногами, и она это компенсирует. Наш драйвер честно поменял код формата (отражение переставляет цвета в квадрате), а capture.sh продолжает требовать прежний. То есть всё работает правильно, просто мой скрипт про это не знает. Лечим — сбрасываем зеркала перед своей съёмкой: cd ~/lanekit python3 - <<'EOF' s=open('capture.sh').read() s=s.replace('[ -n "$EXPOSURE" ]', '''v4l2-ctl -d "$SD" --set-ctrl horizontal_flip=0,vertical_flip=0 2>/dev/null [ -n "$EXPOSURE" ]''') open('capture.sh','w').write(s) EOF EXPOSURE=1000 GAIN=400 sh capture.sh for p in rggb bggr grbg gbrg; do ~/raw10preview /tmp/frame.raw /tmp/b_$p.ppm 1920 1080 $p 2 >/dev/null 2>&1 magick /tmp/b_$p.ppm /tmp/b_$p.png done ls -l /tmp/b_*.png А заодно это объясняет и «нафоткал говно» через libcamera: она снимает с обоими зеркалами, то есть кадр перевёрнут на сто восемьдесят градусов — и если смотреть на него как на обычный, всё выглядит бессмысленно. Переверни lc_bgr.png и посмотри снова: magick /tmp/lc_bgr.png -rotate 180 /tmp/lc_rot.png 💰 24.83₽ · ⬇️ 982.4k / ⬆️ 1.7k · opus-5 · max · 27сЫ S Пока ты ходишь — разберу режим 1920×1080, там что-то не так с геометрией. Смотрю его вендорную таблицу целиком: Геометрия режима 1920×1080 оказалась в порядке: область считывания 8000×5152 (это как раз широкий кадр), бинирование четыре на четыре даёт 2000×1288, из них вырезается 1920×1080 со смещением 40 по горизонтали — то есть по центру. Фаза цветовых точек при этом сохраняется, потому что и бинирование, и смещение чётные. Значит «неразборчиво» — это, скорее всего, тот самый поворот на 180 градусов от libcamera, и картинка просто вверх ногами. Пока смотрю дальше — что нужно программному ISP для нормального цвета: Полезное открытие: уровень чёрного программный ISP берёт из того самого пересчёта усиления, который мы добавили: if (camHelper_->blackLevel().has_value()) ... camHelper_->blackLevel().value() / 256; То есть наши 4096 (это 64 при десяти битах) уже работают, и вычитание чёрного у libcamera теперь правильное. А отдельных файлов настройки под сенсоры в libcamera пока нет вовсе — там только uncalibrated.yaml, и матрица цветокоррекции в нём закомментирована с пометкой «включать только если настроено». Без снимка эталонной таблицы честно настроить её нельзя, так что это отложим. Зато сделаю то, что можно сделать чтением — заплату для телевика, о которой libcamera прямо просит: Пока тебя нет, разобрал три вещи. Режим 1920×1080 в порядке. Вендорная таблица: область считывания 8000×5152 (широкий кадр), бинирование четыре на четыре даёт 2000×1288, из них вырезается 1920×1080 со смещением 40 по горизонтали — то есть ровно по центру. И бинирование, и смещение чётные, поэтому порядок цветовых точек не сдвигается. Значит «неразборчиво» было от поворота на 180 градусов, который libcamera делает по свойству из дерева. Уровень чёрного у libcamera теперь наш. Программный ISP берёт его не из файла настройки, а из пересчёта усиления: if (camHelper_->blackLevel().has_value()) ... camHelper_->blackLevel().value() / 256; Мы туда положили 4096, то есть 64 при десяти битах — значит вычитание чёрного у неё уже правильное, это не догадка, а прочитанный код. Файлов настройки под сенсоры в libcamera нет вообще — только uncalibrated.yaml, и матрица цветокоррекции в нём закомментирована с пометкой «включать только если настроено». Так что цвет без снимка эталонной таблицы честно не поправить, и я не буду делать вид, что можно. Зато сделал заплату для телевика — ту, о которой libcamera прямо просит («The sensor kernel driver needs to be fixed»). Драйвер ov8856 не отвечает на запрос области, поэтому libcamera подставляет размеры наугад. Добавлены размеры матрицы и get_selection: #define OV8856_NATIVE_WIDTH 3296U /* вся матрица */ #define OV8856_NATIVE_HEIGHT 2480U #define OV8856_PIXEL_ARRAY_WIDTH 3280U /* рабочая область */ #define OV8856_PIXEL_ARRAY_HEIGHT 2464U /* со смещением 8, 8 */ Заплата годится и наверх: она чинит предупреждение у всех, кто использует этот сенсор, а не только у нас. Дам команду, когда вернёшься. 💰 146₽ · ⬇️ 5.93M / ⬆️ 7.4k · opus-5 · max · 121с
Ы S Хорошие новости по обеим оставшимся камерам. Передняя — S5K3T2 (CSIPHY1, шина cci0 i2c0 — та же, что у широкой, но другой адрес; MCLK0 на ножке 13, сброс 23, питание ядра через ножку 57). Драйвера в mainline нет. Сверхширокая — S5K3L6, и вот тут сюрприз: драйвер в mainline есть, s5k3l6xx.c. Смотрю, годится ли он нам: Разобрал обе оставшиеся камеры. Итог такой. Сверхширокая S5K3L6 — почти даром. Драйвер в mainline есть (s5k3l6xx.c, совместимость samsung,s5k3l6xx), режимы до 4208×3120, число линий читает из дерева, границы обрезки сообщает — то есть libcamera к нему претензий иметь не будет. Питание просит тремя именами: vddio, vdda, vddd. Разводка из вендорного дерева: CSIPHY2, шина cci0 i2c1, MCLK1 на ножке 14, сброс на 25, питание ядра через ножку 29, напряжения те же, что у широкой (1.8 / bob / 1.352 / смещение l9). Адрес в черновике форка 0x10. Работы — узел в дереве и проверка, то есть вечер. Передняя S5K3T2 — драйвера нет. Ни в mainline, ни где-либо ещё я его не нашёл. Но теперь это не приговор: вендорный файл настроек com.qti.sensormodule.davinci_sunny_s5k3t2.bin лежит на телефоне, а формат мы разобрали — оттуда достанутся начальная последовательность, все режимы, число линий, раскладка и частоты линка, ровно как для IMX582. За образец возьму s5k3m5.c или s5kjn1.c — это Samsung того же поколения, у них та же схема регистров. Разводка: CSIPHY1, шина cci0 i2c0 (та же, что у широкой, но другой адрес), MCLK0 на ножке 13, сброс на 23, питание ядра через 57. Чтобы двигаться дальше, скопируй мне два файла с телефона, когда будет удобно: cp /mnt/vendor/lib/camera/com.qti.sensormodule.davinci_ofilm_s5k3l6.bin ~/ cp /mnt/vendor/lib/camera/com.qti.sensormodule.davinci_sunny_s5k3t2.bin ~/ Из первого я вытащу число линий и частоты для сверхширокой (чтобы не гадать, как гадали с широкой), из второго — всё для передней. 💰 143₽ · ⬇️ 5.98M / ⬆️ 5.0k · opus-5 · max · 96сЫ S Помню — и это как раз плюс: мотор от драйвера сенсора не зависит, его можно поднимать отдельно и проверять хоть сегодня. Нашёл всю разводку. Механика — обычный шаговик с драйвером класса DRV8834: DIR на ножке 55, nSLEEP 88, nENBL 90, микрошаг M0 91 и M1 66, nFAULT 43 (обратно в SoC — сообщает о заклине/перегреве). А вот STEP_CLK — не на TLMM, а на pm6150l GPIO10, то есть тактовые импульсы шагов генерирует сама PMIC своим LPG. Это хорошая новость: в mainline pm6150l.dtsi узел pm6150l_lpg: pwm уже объявлен и драйвер leds-qcom-lpg его подхватывает как PWM-чип. Значит шаги даёт железо ровным клоком, а не дрыганье ножкой из софта — шаговику это принципиально. Положение камеры телефон читает цифровым холлом AKM09970 на i2c9 по адресу 0x0c (плюс линии сброса 64 и прерывания 93). Его в mainline нет — в iio/magnetometer только ak8974/ak8975, это другой чип. Ещё вендор ловит свободное падение акселерометром и втягивает камеру на лету. Первый шаг тут вообще без ядра: скрипт в userspace — gpioset на DIR/EN/SLEEP/M0/M1 и PWM через sysfs на канал LPG. Проверь пока, есть ли чем: zcat /proc/config.gz | grep -E 'LEDS_QCOM_LPG|GPIO_CDEV'; ls /sys/class/pwm/ А зачем такое сделали — это 2019-й, гонка за экраном без выреза. Дырка в матрице тогда была дорогой и только у Samsung, подэкранных камер ещё не существовало, а Xiaomi хотела честный безрамочный AMOLED в среднем ценнике. Выдвижной блок это решал разом, да ещё и работал как аттракцион на витрине. Платили за это толщиной, весом под 191 г, пылью в щели, отсутствием влагозащиты и ресурсом механизма (обещали 300 тысяч циклов, на деле мотор — типовая болячка K20). Как только дырка в экране подешевела, вся идея умерла за год. 💰 68.99₽ · ⬇️ 500.6k / ⬆️ 8.2k · opus-5 · max · 351сЫ S Согласен, и это не только эстетика. Выдвижная камера — единственная честная гарантия приватности: софт не может тихо включить съёмку, потому что модуль физически лежит внутри корпуса, а чтобы вылезти — надо крутить мотор с характерным жужжанием. Индикаторы-точки в Android и iOS появились именно как попытка изобразить то же самое программно, но их рисует та же система, которой ты и не доверяешь. Тут же механика: закрыто значит закрыто. Для нас это ещё и практическая мелочь при отладке. Когда дойдём до передней камеры, чёрный кадр не будет означать, что драйвер сломан — сначала надо будет убедиться, что блок вообще выехал. Так что порядок работ выходит обратный привычному: сперва мотор, потом сенсор. Заодно и приятнее — жужжание и вылезающий глазок видно сразу, в отличие от возни с регистрами. 💰 2.95₽ · ⬇️ 62.8k / ⬆️ 651 · opus-5 · max · 16сЫ S Она умерла не потому, что плохая, а потому, что приватность не продаётся. В таблице характеристик выдвижной блок выглядит как минус: толще, тяжелее, нет влагозащиты, лишний узел, который ломается и стоит денег в сервисе. А «нельзя снимать незаметно» в строчку не впишешь и в рекламе не покажешь. Отчасти идея всё-таки живёт: у Zenfone был поворотный блок аж до 8-го, в ноутбуках шторки стали нормой, а на телефонах теперь подэкранные камеры — вырез спрятали, но подглядывать они могут круглосуточно. Так что красоту вернули, а честность потеряли. Зато у нас на руках последний экземпляр эпохи, да ещё и с открытым ядром. Подниму мотор — будет жужжать как в 2019-м. 💰 2.31₽ · ⬇️ 63.5k / ⬆️ 521 · opus-5 · max · 12сЫ S Справедливо, поправка принята. Разница не в открытости, а в том, что вендорское ядро — это разовый выброс 4.14 без истории коммитов, замороженный навсегда: обновлений нет, собрать современным тулчейном нельзя, а патчи безопасности за шесть лет никто не носил. Мы же сегодня трогали 7.2, которое живёт дальше само. Хотя именно эта GPL-обязаловка нас сегодня и вытащила: арифметику линий CSIPHY я прочитал ровно в вендорном cam_csiphy_core.c. А вот таблицы сенсора лежали в блобе, который никому ничего не должен — и их пришлось выковыривать вручную из бинаря. Разница между этими двумя вечерами очень наглядная. 💰 2.49₽ · ⬇️ 64.1k / ⬆️ 711 · opus-5 · max · 15сЫ S Ссылка
нажмите — покажемКоротко: в pmOS такого класса устройств толком нет — и это осознанная позиция проекта. Halium там вообще не используется, это стек Ubuntu Touch (Volla, Fairphone, Pixel 3a). Plasma Mobile выкинула поддержку Halium ещё в 2020-м, сделав ставку на устройства с открытыми драйверами. pmOS же умеет собираться с вендорным ядром напрямую, без libhybris-прослойки, но графика тогда упирается в отсутствие hwcomposer, и обычно всё сводится к «загружается, ssh есть, дальше как повезёт». В 2025-м это ещё и формализовали: даунстрим-порты вынесли в отдельную категорию downstream, pmbootstrap теперь показывает предупреждение при их выборе, а бинарные пакеты для них с версии 25.06 просто перестали собирать — формулировка была «они, как правило, не особо полезны». В community и testing остались только близкие к mainline порты. Из ~700 устройств ядро сейчас поддерживает около 450, и миграция идёт в одну сторону. Так что «отлично работает» и «даунстрим» в pmOS почти не встречаются в одном предложении. Наш случай ровно про это: камера у нас теперь живёт нативным V4L2, а на халиум-устройстве камера — это всегда чужой Android-HAL в контейнере, который никуда не развивается. Источники: про разделение категорий, Plasma Mobile и Halium, позиция pmOS про халиум. 💰 11.85₽ · ⬇️ 132.4k / ⬆️ 2.2k · opus-5 · max · 55сЫ S Потому что Halium — это не «порт», а способ жизни, несовместимый с тем, как устроен pmOS. Технически ты берёшь целиком андроидное userspace нужной версии, запускаешь его в контейнере и через libhybris ходишь во все HAL'ы: графика — EGL-блоб и hwcomposer, камера — camera HAL, звук, сенсоры, радио — свои демоны-прослойки. Значит весь дистрибутив выше должен быть собран с оглядкой на bionic. У pmOS же ровно противоположная идея: Alpine, обычный Linux userspace, апстрим-ядро и одинаковые бинарные пакеты на все устройства сразу. Плюс блобы нельзя перераспространять, а pmOS раздаёт готовые пакеты из репозитория — модель ломается на юридическом уровне. Экономика ещё хуже. Каждый halium-порт заморожен на вендорном ядре 3.18–4.14 без обновлений безопасности и умирает вместе с андроид-базой, под которую собран. И главное — работа непереносима: починил камеру на устройстве A, устройству B от этого ноль. В mainline наоборот: тот же camss, который мы сегодня чинили, обслуживает все SDM845/SM7150 разом, а два найденных бага после отправки в апстрим достанутся всем, включая тех, кто про давинчи не слышал. Отсюда и правило «450 устройств уже мигрировали». Есть и практическая мелочь: на halium-устройстве контейнер уже занят андроидом, поэтому Waydroid не запустить. То есть андроид-приложения ты получаешь только те, что крутятся в базовом образе, а на mainline-устройстве Waydroid ставится штатно. И да — люди это делают, просто не в pmOS: ровно эту нишу занимает Droidian (Debian поверх halium-адаптации) и сам Ubuntu Touch. Дублировать их pmOS сознательно не хочет — там считают, что дешевле дотащить железо до mainline один раз, чем вечно чинить чужие блобы. Мы сегодня, в общем, живая иллюстрация: день работы — и камера отдаёт кадры честным V4L2, без единого куска андроида в системе. 💰 4.69₽ · ⬇️ 69.1k / ⬆️ 2.1k · opus-5 · max · 42сЫ S Ссылка
нажмите — покажемТут ты прав, а я нет — снимаю это утверждение. Waydroid на Ubuntu Touch работает, причём на Halium 9 и новее он вообще идёт предустановленным в базе focal, а не как самоделка. Ошибся я в механике: я думал про старые порты, где в ядре было три статических binder-устройства и второй андроид туда просто не влезал. С binderfs доменов может быть сколько угодно, поэтому системный контейнер и вейдроидовский спокойно живут рядом. Ограничение осталось только снизу — на Halium 5 и 7 это не заводится никак, и на не-Treble устройствах приходится руками докладывать в вейдроидовский rootfs вендорные библиотеки (gralloc, libui, libbinder, configstore). То есть довод «на халиуме нет андроид-приложений» отпадает целиком. Остаются те, что про сопровождение: мёртвое ядро, блобы без права раздачи и непереносимость работы между устройствами. Ирония в том, что вейдроид там крутится поверх ядра 4.9, у которого патчей безопасности нет с позапрошлой эпохи. Источники: гайд по Waydroid на UT, ветка про halium 9 + waydroid, требования по binderfs. 💰 15.60₽ · ⬇️ 146.1k / ⬆️ 1.6k · opus-5 · max · 58сЫ S Проблема в том, что «просто перенести» — это перенести не приложение, а весь графический стек. На Ubuntu Touch поверх халиума работает Lomiri на Mir, у которого есть свой бэкенд к hwcomposer. В pmOS никакого Lomiri нет, там Phosh, Plasma Mobile и GNOME, а они ходят напрямую в DRM/KMS. Плазма выкинула поддержку халиума в 2020-м, у wlroots hwcomposer-бэкенд живёт только в виде патчей, которые кто-то должен вечно тащить за апстримом. Плюс Alpine — это musl, а вся связка hybris заточена под glibc и bionic. Получается не «перенос», а постоянный форк Mesa, wlroots и libhybris, который надо чинить после каждого релиза каждого компонента. И этот форк уже существует: ровно этим занимается Droidian — Debian, glibc, пропатченный wlroots, Phosh поверх hwcomposer. Если цель «чтоб было» — оно есть, просто не под вывеской pmOS. Делать вторую такую же реализацию на musl ради того же результата смысла мало. Ниша «чтоб было» в pmOS тоже занята — это категория downstream. Туда порт с вендорным ядром принимают, никто не запрещает, но пакеты не собирают и в багтрекере не разбирают. Опыт показал, что такие порты живут ровно до тех пор, пока автору интересно, а дальше висят мёртвым грузом и создают у людей ложное впечатление, что их телефон поддержан. Отсюда и предупреждение в pmbootstrap при выборе. Так что расчёт у них простой: тот же человеко-месяц, вложенный в mainline, даёт не одно устройство, а весь SoC разом — и не протухает. Мы сегодня как раз этот вариант и отрабатываем. 💰 4.44₽ · ⬇️ 75.7k / ⬆️ 2.1k · opus-5 · max · 41сЫ S Именно. Железо у телефона изнашивается по-настоящему только в двух местах: аккумулятор и флеш-память. Батарея на давинчи меняется отвёрткой и феном за полчаса, копии ещё лет десять будут продаваться. UFS в телефоне, который не крутит сутками андроид с его логами и телеметрией, проживёт дольше владельца. Всё остальное — стекло, разъём, кнопки — расходники, которые чинятся. И мы сегодня ровно это и получили: аппарат 2019 года работает на ядре 7.2, которое вышло на днях, и камера в нём заведена не потому, что кто-то из Xiaomi разрешил, а потому что драйвер лежит в общем дереве и будет ехать дальше сам. Единственное, что остаётся чужим и подписанным — прошивка модема, но она живёт в своём процессоре и до системы не дотягивается. Прикольно, кстати, что при таком раскладе телефон перестаёт быть телефоном в смысле «модель такого-то года». Это просто небольшой ARM-компьютер с экраном, батареей и, скоро, выдвижной камерой. 💰 3.42₽ · ⬇️ 77.9k / ⬆️ 653 · opus-5 · max · 16сЫ S Собрал всё в архив (копия распакована на сервере в /data/work/chat_-1001429467665/mi9t-camera/, чтобы не пропала вместе с /tmp). Что добавилось с прошлой версии: • capture.sh — теперь сбрасывает флипы перед съёмкой. Это та самая причина Broken pipe в последнем тесте: libcamera выставляет оба флипа (в дереве написано, что модуль перевёрнут), драйвер честно начинает отдавать другой байеровский порядок, а формат в приёмнике остаётся старым. Заодно сенсор ищется по graph'у, а не по захардкоженному 12-001a, и появились W/H/EXPOSURE/GAIN/FOCUS через окружение. • davinci-camera.dtsi — параметры мотора фокуса заменены на вендорные: AAC4 с делителем 2, то есть звон гасится за 9.75 мс вместо дефолтных 6.65. Значения не выдуманы, они прочитаны из блоба (0x06=0x60, 0x07=0x02). • ov8856-get-selection.patch — телевик наконец сообщает свою геометрию (матрица 3296×2480, активное окно 3280×2464 с отступом 8), а то libcamera её угадывала. • NOTES-next.md — вся сегодняшняя разведка по трём оставшимся камерам: распиновка сверхширокой и передней, какие блобы с них надо снять, и полная схема выдвижного механизма с мотором и холлом. Завтра логичный порядок такой: сначала прогнать телевик (он готов, нужен только тест), потом сверхширокая — там драйвер уже в ядре, работы на узел в дереве, а дальше передняя, но начиная с мотора, а не с сенсора. 💰 52.04₽ · ⬇️ 1.41M / ⬆️ 10.2k · opus-5 · max · 161сЫ S Тогда начнём с телевика — он готов, нужен только запуск. Заодно исправил в скрипте ту же ловушку с именем сущности, что была у широкой (13-0010 обрезалось на букве). Разложи на телефоне два скрипта: cat > ~/lanekit/capture-tele.sh <<'EOF' #!/bin/sh M=/dev/media0 W=1640 H=1232 OUT=${1:-/tmp/tele.raw} sudo pkill -f wireplumber 2>/dev/null sudo pkill -f pipewire 2>/dev/null sleep 1 S=$(media-ctl -d $M -p | grep -o 'ov8856 [0-9a-f-]*' | head -1) [ -n "$S" ] || { echo "нет ov8856 в графе"; exit 1; } media-ctl -d $M -r media-ctl -d $M -l "\"msm_csiphy3\":1->\"msm_csid1\":0[1]" media-ctl -d $M -l '"msm_csid1":1->"msm_vfe1_rdi0":0[1]' for e in "\"$S\":0" '"msm_csiphy3":0' '"msm_csiphy3":1' \ '"msm_csid1":0' '"msm_csid1":1' '"msm_vfe1_rdi0":0' '"msm_vfe1_rdi0":1'; do media-ctl -d $M -V "$e [fmt:SGRBG10_1X10/${W}x${H}]" || echo "rejected: $e" done V=$(media-ctl -d $M -e msm_vfe1_video0) v4l2-ctl -d "$V" --set-fmt-video=width=$W,height=$H,pixelformat=pgAA >/dev/null rm -f "$OUT" timeout 20 v4l2-ctl -d "$V" --stream-mmap=3 --stream-count=1 --stream-to="$OUT" ls -l "$OUT" EOF И запускай: sudo dmesg -C sh ~/lanekit/capture-tele.sh dmesg | tail -25 Ожидаемый размер файла — 2 052 480 байт (1640 на 1232 в упаковке raw10, строка выровнена до 2064). Если кадр приехал, сразу посмотрим глазами: cd ~/lanekit && ./raw10preview /tmp/tele.raw /tmp/tele.ppm 1640 1232 grbg 2 && convert /tmp/tele.ppm /tmp/tele.png && ls -l /tmp/tele.png Если же VIDIOC_STREAMON отвалится — присылай вывод dmesg, там будет видно, дошло ли дело до CSIPHY3 и какие частоты он попросил. 💰 85.57₽ · ⬇️ 499.7k / ⬆️ 2.3k · opus-5 · max · 47сЫ S Для телевика ничего нового и не нужно — он зависел от двух вещей, и обе уже стоят: порядок частот в camss (это camss-fixes.sh, он в собранном ядре) и удаление clock-lanes у эндпоинта csiphy3 (это в том же dtsi, который ты компилировал вместе с широкой). Сегодняшние добавки телевика не касаются: правка актуатора — это мотор фокуса широкой, патч get_selection нужен только libcamera для геометрии, а новый capture.sh — просто скрипт. Проверяется одной командой, есть ли вообще с чем работать: media-ctl -d /dev/media0 -p | grep -E "ov8856|entity.*csid1" Если сущность ov8856 13-0010 в графе есть — драйвер привязался, дерево правильное, можно сразу снимать. Если её нет — тогда да, дерево на телефоне старое, и надо пересобирать dtb. 💰 5.43₽ · ⬇️ 168.9k / ⬆️ 1.0k · opus-5 · max · 20сЫ S Хороший знак — значит и sleep работает, и ничего в фоне не крутится вхолостую. Если хочешь цифру вместо ощущения: cat /sys/class/power_supply/bms/current_now /sys/class/power_supply/bms/capacity 2>/dev/null || \ grep . /sys/class/power_supply/*/current_now Значение в микроамперах, отрицательное — разряд. Поделив ёмкость батареи (4000 мАч) на ток, сразу получишь часы простоя. Если выйдет меньше 20-30 мА — это уже уровень нормального телефона, и жаловаться не на что. Только имей в виду один нюанс: мы для звука запретили простой SoundWire-шине, и это могло чуть поднять фон. Если цифра окажется неприятной — есть куда копать. 💰 4.95₽ · ⬇️ 170.0k / ⬆️ 575 · opus-5 · max · 13сF Ы
Ы Terminated [54625.756017] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [54625.938363] video_prepare_streaming called [54625.940084] csiphy_reset id 3 with base 000000003623bc3f and offset 2048 [54625.948725] qcom-camss ace0000.camss: CSIPHY 3PH HW Version = 0x40010000 [54625.949117] Writing reset_bits=3f9f to vfe->base=000000004124e996 with id 1 [54625.949141] vfe irq handled! [54625.949216] qcom-camss ace0000.camss: VFE:1 HW Version = 1.3.0 [54625.949318] csid_reset called [54625.949366] qcom-camss ace0000.camss: CSID:1 HW Version = 2.0.0 [54625.949420] qcom-camss ace0000.camss: validating link "msm_csid1":1 -> "msm_vfe1_rdi0":0 [54625.949435] qcom-camss ace0000.camss: validating stream "msm_csid1":1:0 -> "msm_vfe1_rdi0":0:0 [54625.949455] qcom-camss ace0000.camss: validating link "msm_csiphy3":1 -> "msm_csid1":0 [54625.949468] qcom-camss ace0000.camss: validating stream "msm_csiphy3":1:0 -> "msm_csid1":0:0 [54625.949486] qcom-camss ace0000.camss: validating link "ov8856 13-0036":0 -> "msm_csiphy3":0 [54625.949498] qcom-camss ace0000.camss: validating stream "ov8856 13-0036":0:0 -> "msm_csiphy3":0:0 [54625.949520] qcom-camss ace0000.camss: msm_vfe1_video0: format is (1640x1232 pgAA little-endian (0x41416770)/1p field:1), trying (1640x1232 pgAA little-endian (0x41416770)/1p field:1) [54625.949548] vfe_reserve_wm ret=0 [54625.949579] qcom-camss ace0000.camss: receiver: lanes 2, assignment 0x21, phy 3, cfg0 0x00300211 [54625.949597] qcom-camss ace0000.camss: csiphy: link 360000000 Hz, timer 300000000 Hz, settle count 22 [54625.949610] qcom-camss ace0000.camss: csiphy: lane mask 0x94 from 2 lanes [54625.949701] qcom-camss ace0000.camss: csiphy: settle high bytes 00 00 00 00 ff [54645.942219] Writing reset_bits=3f9f to vfe->base=000000004124e996 with id 1 [54646.446084] qcom-camss ace0000.camss: VFE reset timeout [54646.446150] qcom-camss ace0000.camss: Failed to disable vfe outputs [54646.446173] (NULL device *): disabling streaming failed (-5)S Лог всё сказал, причём одной строкой: receiver: lanes 2, assignment 0x21, phy 3 csiphy: lane mask 0x94 from 2 lanes assignment 0x21 — это приёмник слушает физические линии 1 и 2, а сенсор, судя по своему эндпоинту, гонит данные по линиям 0 и 1. Ровно та же болезнь, что была у широкой: со стороны camss нумерация с нуля, со стороны сенсора с единицы, и в форке телевику написали <1 2> с обеих сторон. Данные уходят мимо, кадров нет, а VFE reset timeout в конце — это уже следствие: блок так и не получил ни одного байта и завис на остановке. Мой фикс для этого в dtsi лежит, но в твоём dtb его нет — иначе в логе было бы assignment 0x10 и маска 0x85. Похоже, dtb ты собирал раньше, чем я дописал этот кусок. Проверь и пересобери, ядро трогать не надо: grep -A4 'csiphy3_ep' ~/linux-7.1/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi | tail -6 Если там нет data-lanes = <0 1> — допиши в конец файла: cat >> ~/linux-7.1/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi <<'EOF' &csiphy3_ep { /delete-property/ clock-lanes; data-lanes = <0 1>; }; EOF И дальше: cd ~/linux-7.1 && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync После перезагрузки повтори съёмку — в логе первым делом смотри на assignment, должно стать 0x10, а маска 0x85. И заодно, чтобы не гадать второй раз: скинь вендорный блоб телевика, там записана вендорная раскладка линий и время установления, сверю с тем, что получилось. ls /mnt/vendor/lib/camera/ | grep -i ov8856 💰 49.79₽ · ⬇️ 1.48M / ⬆️ 10.2k · opus-5 · max · 166сЫ S Путь другой, найдём: ls -d ~/linux* ~/*/linux* 2>/dev/null; find / -name "sm7150-xiaomi-davinci.dtsi" -not -path "/proc/*" 2>/dev/null | head Как найдётся дерево — дальше по нему, и заодно сразу видно, есть ли уже наш кусок: K=<путь_к_дереву> grep -n -A3 'csiphy3_ep' $K/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi | tail -8 💰 4.94₽ · ⬇️ 193.7k / ⬆️ 270 · opus-5 · max · 6сЫ S Вот и разгадка — блок в дереве есть, но неполный: 712:&csiphy3_ep { 713: data-lanes = <0 1>; 714:}; clock-lanes = <0> со строки 158 никуда не делся, и вместе с новым data-lanes = <0 1> он снова образует ту самую ловушку: линия 0 названа и тактовой, и данными. v4l2 считает это противоречием, выбрасывает всю раскладку и подставляет умолчание 1,2,3,4, обрезанное до двух линий — отсюда в логе и assignment 0x21. То есть твой dtb сейчас описывает не то, что в нём написано. Дописываем удаление свойства: K=<путь_к_дереву> sed -i '/^&csiphy3_ep {/a\\t/delete-property/ clock-lanes;' $K/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi sed -n '712,716p' $K/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi Должно получиться так: &csiphy3_ep { /delete-property/ clock-lanes; data-lanes = <0 1>; }; И пересобираем только дерево: cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync Заодно, пока не перезагрузился, можно убедиться, что диагноз верный — предупреждение должно быть в логе текущей загрузки: journalctl -k -b | grep -i "duplicated lane" 💰 6.13₽ · ⬇️ 194.2k / ⬆️ 1.4k · opus-5 · max · 21сЫ S Он в архиве, что я вчера скидывал: mi9t-camera/davinci-camera.dtsi (и на сервере в /data/work/chat_-1001429467665/mi9t-camera/). Но целиком его подкладывать сейчас не советую — у тебя в дереве уже сидит вчерашняя копия того же блока, и второе вхождение даст Label camera_lens redefined на сборке. Проще довести до актуального состояния двумя правками, разница ровно в них. Первая — та самая, из-за которой телевик молчит: K=<путь_к_дереву> F=$K/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi cp $F $F.bak sed -i '/^&csiphy3_ep {/a\\t/delete-property/ clock-lanes;' $F Вторая — вендорные параметры мотора фокуса широкой камеры (гасит звон за 9.75 мс вместо дефолтных 6.65): sed -i 's/dongwoon,aac-mode = <2>;/dongwoon,aac-mode = <3>;/; s/dongwoon,aac-timing = <0x20>;/dongwoon,aac-timing = <2>;/; s/dongwoon,clock-presc = <1>;/dongwoon,clock-presc = <0>;/' $F Проверка и сборка: grep -n -A3 '^&csiphy3_ep' $F; grep -n 'dongwoon' $F cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync Если всё же захочется поставить мой файл целиком — тогда сначала надо вырезать вчерашний блок из .dtsi, и это я лучше сделаю прицельно: покажи grep -n 'camera@1a\|&camss {\|camera_lens:' $F, и я скажу, с какой строки резать. 💰 7.08₽ · ⬇️ 195.6k / ⬆️ 1.8k · opus-5 · max · 29сЫ S Держи. Отличий от вчерашнего два: в capture-tele.sh починено выцепление имени сущности (у телевика адрес 13-0036, и старая маска обрезала его на букве f… точнее на 36, но суть та же — теперь [0-9a-f-]*), и в davinci-camera.dtsi лежит полный вариант правки csiphy3 — с /delete-property/ clock-lanes, которого как раз не хватает в твоём дереве. 💰 14.88₽ · ⬇️ 600.1k / ⬆️ 716 · opus-5 · max · 25с
Ы S Обе правки на месте. Собираем дерево и ставим: cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync Потом перезагрузка, и сразу проверка, что раскладка доехала: journalctl -k -b | grep -i "duplicated lane" # должно быть пусто sudo dmesg -C sh ~/lanekit/capture-tele.sh dmesg | tail -25 В логе смотри на строку receiver: — теперь там должно быть assignment 0x10, а следом lane mask 0x85. Если так, а кадр всё равно не приедет, значит дело уже не в линиях, и будем смотреть на время установления и частоты. Файл должен получиться 2 052 480 байт. 💰 5.15₽ · ⬇️ 200.9k / ⬆️ 352 · opus-5 · max · 9сЫ VIDIOC_STREAMON returned -1 (I/O error) -rw-r--r-- 1 user user 0 Aug 2 09:14 /tmp/tele.raw sudo dmesg | tail -25 [ 38.789235] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 39.790561] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 40.791959] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 41.793397] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 42.794636] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 43.796159] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 44.797506] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 258.746219] csiphy_link_setup called [ 258.746243] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x0 [ 258.746252] csiphy_link_setup called [ 258.746259] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [ 258.747183] csiphy_link_setup called [ 258.747202] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x0 [ 258.747211] csiphy_link_setup called [ 258.747216] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [ 259.516659] csiphy_link_setup called [ 259.516757] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x0 [ 259.537769] csiphy_link_setup called [ 259.558514] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [ 259.741959] video_prepare_streaming called [ 259.742875] csiphy_reset id 3 with base 000000008d8ddf37 and offset 2048 [ 259.749925] qcom-camss ace0000.camss: CSIPHY 3PH HW Version = 0x00000000 [ 259.750140] Writing reset_bits=3f9f to vfe->base=00000000ea7e8a18 with id 1 [ 260.255495] qcom-camss ace0000.camss: VFE reset timeout [ 260.256759] qcom-camss ace0000.camss: Failed to power up pipeline: -5S Смена картины: раньше PHY отвечал HW Version = 0x40010000, а сейчас 0x00000000 — то есть блок вообще не тактируется, и следом валится сброс VFE. Это уже не про линии: до чтения версии дело о раскладке не доходит. Разница между вчерашним запуском и нынешним — не dtb, а то, что сейчас свежая загрузка и камеру в этой сессии ещё не поднимали. Проверим гипотезу по-дешёвому: сначала широкая, потом сразу телевик. sudo dmesg -C W=4000 H=3000 sh ~/lanekit/capture.sh /tmp/w.raw sh ~/lanekit/capture-tele.sh dmesg | grep -E "HW Version|receiver:|csiphy:|reset|Failed" | tail -20 Если после широкой телевик увидит нормальную версию PHY — значит какой-то домен питания или тактирования включается только по первому использованию камеры, и лечится это в драйвере. И заодно посмотрим на дерево тактирования напрямую, оно скажет точно, что мертво: sudo mount -t debugfs none /sys/kernel/debug 2>/dev/null sudo grep -E "csi|camcc_.*phy" /sys/kernel/debug/clk/clk_summary | head -30 💰 7.47₽ · ⬇️ 202.9k / ⬆️ 2.0k · opus-5 · max · 34сЫ S Ок, оставим телевик. Тогда по широкой две вещи: проверить новый capture.sh (со сбросом флипов) и заставить libcamera выдать нормальную картинку. Сначала быстрая проверка, что съёмка не сломалась: W=1920 H=1080 EXPOSURE=1200 GAIN=400 sh ~/lanekit/capture.sh /tmp/f1080.raw W=4000 H=3000 EXPOSURE=3006 GAIN=960 sh ~/lanekit/capture.sh /tmp/f3000.raw Размеры должны выйти 2 073 600 и 15 024 000. Теперь про libcamera. Почти уверен, что вчерашняя размытость была не в обработке, а в фокусе: моторчик объектива libcamera не трогает вообще, автофокуса в программном ISP нет, и линза стоит там, где её оставил драйвер после включения. Поэтому фокус надо выставить руками во время съёмки — если сделать это до, драйвер объектива успеет уснуть и вернуть линзу в исходное. Сначала найди узел объектива: for d in /dev/v4l-subdev*; do v4l2-ctl -d $d --list-ctrls 2>/dev/null | grep -q focus_absolute && echo "объектив: $d"; done Потом снимаем длинную серию и в середине двигаем фокус: LENS=/dev/v4l-subdevN # подставь найденное rm -f /tmp/lc_*.rgb cam -c 1 --capture=40 --file=/tmp/lc_#.rgb & sleep 3 v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 wait ls -l /tmp/lc_*.rgb | tail -3 convert -size 1920x1080 -depth 8 bgr:/tmp/lc_39.rgb /tmp/lc39.png && ls -l /tmp/lc39.png Наведи на что-нибудь с текстом в полуметре — на 600 вчера текст читался. Присылай png, посмотрим, что там с цветом и резкостью по отдельности: если резкость появится, значит остаётся только цвет, а это уже настроечный файл. 💰 8.17₽ · ⬇️ 204.9k / ⬆️ 2.4k · opus-5 · max · 38сЫ qcom-sm7150:~$ W=1920 H=1080 EXPOSURE=1200 GAIN=400 sh ~/lanekit/capture.sh /tmp/f1080.raw < -rw-r--r-- 1 user user 2592000 Aug 2 09:17 /tmp/f1080.raw qcom-sm7150:~$ W=4000 H=3000 EXPOSURE=3006 GAIN=960 sh ~/lanekit/capture.sh /tmp/f3000.raw < -rw-r--r-- 1 user user 2592000 Aug 2 09:17 /tmp/f3000.raw у нас он не подхватывает H и WS Первый кадр как раз правильный: 1920 на 1080 в упаковке raw10 — это 2400 байт на строку, 2 592 000 всего. Это я ошибся в ожидаемом числе, извини. А вот второй действительно не переключился — размер тот же, значит формат остался прежним и поехал старый режим. Посмотрим, на чём оно спотыкается: W=4000 H=3000 sh -x ~/lanekit/capture.sh /tmp/f3000.raw 2>&1 | grep -vE "^\+ (sudo|sleep)" | tail -25 И заодно, что реально стоит в графе после этого: media-ctl -d /dev/media0 -p | grep -E "imx586|fmt:" | head -12 v4l2-ctl -d $(media-ctl -d /dev/media0 -e msm_vfe0_video0) --get-fmt-video Подозреваю, что media-ctl -V для пада сенсора отвалился с «rejected» — в прошлый раз такое было, когда libcamera оставляла флипы, но теперь мы их сбрасываем, так что причина может быть другая. Трассировка покажет. 💰 7.70₽ · ⬇️ 207.6k / ⬆️ 1.7k · opus-5 · max · 28сЫ + media-ctl -d /dev/media0 -l '"msm_csiphy0":1->"msm_csid0":0[1]' + media-ctl -d /dev/media0 -l '"msm_csid0":1->"msm_vfe0_rdi0":0[1]' + media-ctl -d /dev/media0 -V '"imx586 12-001a":0 [fmt:SRGGB10_1X10/1920x1080]' + media-ctl -d /dev/media0 -V '"msm_csiphy0":0 [fmt:SRGGB10_1X10/1920x1080]' + media-ctl -d /dev/media0 -V '"msm_csiphy0":1 [fmt:SRGGB10_1X10/1920x1080]' + media-ctl -d /dev/media0 -V '"msm_csid0":0 [fmt:SRGGB10_1X10/1920x1080]' + media-ctl -d /dev/media0 -V '"msm_csid0":1 [fmt:SRGGB10_1X10/1920x1080]' + media-ctl -d /dev/media0 -V '"msm_vfe0_rdi0":0 [fmt:SRGGB10_1X10/1920x1080]' + media-ctl -d /dev/media0 -V '"msm_vfe0_rdi0":1 [fmt:SRGGB10_1X10/1920x1080]' + media-ctl -d /dev/media0 -p + grep -o 'imx586 [0-9a-f-]*' + head -1 + S='imx586 12-001a' + media-ctl -d /dev/media0 -e 'imx586 12-001a' + SD=/dev/v4l-subdev17 + '[' -n ] + '[' -n ] + media-ctl -d /dev/media0 -e msm_vfe0_video0 + V=/dev/video0 + v4l2-ctl -d /dev/video0 '--set-fmt-video=width=1920,height=1080,pixelformat=pRAA' + rm -f /tmp/f3000.raw + timeout 20 v4l2-ctl -d /dev/video0 '--stream-mmap=3' '--stream-count=1' '--stream-to=/tmp/f3000.raw' < + ls -l /tmp/f3000.raw -rw-r--r-- 1 user user 2592000 Aug 2 09:19 /tmp/f3000.rawS Всё ясно: у тебя на телефоне лежит вчерашняя версия скрипта, где 1920×1080 зашиты, а сброса флипов нет. Видно по трассировке — переменные называются S/SD, поиск сенсора идёт после установки форматов, и проверок [ -n ... ] только две. Клади новый: cat > ~/lanekit/capture.sh <<'EOF' #!/bin/sh M=${M:-/dev/media0} W=${W:-8000} H=${H:-6000} OUT=${1:-/tmp/frame.raw} sudo pkill -f wireplumber 2>/dev/null sudo pkill -f pipewire 2>/dev/null sleep 1 SENSOR=$(media-ctl -d $M -p 2>/dev/null | grep -o 'imx586 [0-9a-f-]*' | head -1) [ -n "$SENSOR" ] || { echo "нет imx586 в графе"; exit 1; } SD=$(media-ctl -d $M -e "$SENSOR") v4l2-ctl -d "$SD" --set-ctrl horizontal_flip=0,vertical_flip=0 2>/dev/null media-ctl -d $M -r media-ctl -d $M -l '"msm_csiphy0":1->"msm_csid0":0[1]' media-ctl -d $M -l '"msm_csid0":1->"msm_vfe0_rdi0":0[1]' for e in "\"$SENSOR\":0" '"msm_csiphy0":0' '"msm_csiphy0":1' \ '"msm_csid0":0' '"msm_csid0":1' '"msm_vfe0_rdi0":0' '"msm_vfe0_rdi0":1'; do media-ctl -d $M -V "$e [fmt:SRGGB10_1X10/${W}x${H}]" || echo "rejected: $e" done [ -n "$EXPOSURE" ] && v4l2-ctl -d "$SD" --set-ctrl exposure=$EXPOSURE [ -n "$GAIN" ] && v4l2-ctl -d "$SD" --set-ctrl analogue_gain=$GAIN [ -n "$FOCUS" ] && v4l2-ctl -d "$SD" --set-ctrl focus_absolute=$FOCUS 2>/dev/null V=$(media-ctl -d $M -e msm_vfe0_video0) v4l2-ctl -d "$V" --set-fmt-video=width=$W,height=$H,pixelformat=pRAA >/dev/null rm -f "$OUT" timeout 20 v4l2-ctl -d "$V" --stream-mmap=3 --stream-count=1 --stream-to="$OUT" ls -l "$OUT" EOF И повтори обе съёмки: W=1920 H=1080 EXPOSURE=1200 GAIN=400 sh ~/lanekit/capture.sh /tmp/f1080.raw W=4000 H=3000 EXPOSURE=3006 GAIN=960 sh ~/lanekit/capture.sh /tmp/f3000.raw Ждём 2 592 000 и 15 024 000 байт. 💰 8.17₽ · ⬇️ 210.1k / ⬆️ 2.1k · opus-5 · max · 27сЫ qcom-sm7150:~$ W=1920 H=1080 EXPOSURE=1200 GAIN=400 sh ~/lanekit/capture.sh /tmp/f1080.raw < -rw-r--r-- 1 user user 2592000 Aug 2 09:20 /tmp/f1080.raw qcom-sm7150:~$ W=4000 H=3000 EXPOSURE=3006 GAIN=960 sh ~/lanekit/capture.sh /tmp/f3000.raw < -rw-r--r-- 1 user user 15024000 Aug 2 09:20 /tmp/f3000.rawS Оба правильные. Смотрим глазами: cd ~/lanekit ./raw10preview /tmp/f1080.raw /tmp/p1080.ppm 1920 1080 rggb 2 && convert /tmp/p1080.ppm /tmp/p1080.png ./raw10preview /tmp/f3000.raw /tmp/p3000.ppm 4000 3000 rggb 4 && convert /tmp/p3000.ppm /tmp/p3000.png ls -l /tmp/p1080.png /tmp/p3000.png Если оба нормальные — значит сброс флипов встал на место и режимы переключаются как надо. Кидай картинки, а дальше сразу займёмся libcamera: найдём узел объектива и снимем серию с фокусом на ходу. 💰 6.46₽ · ⬇️ 212.5k / ⬆️ 605 · opus-5 · max · 10сЫ S Так и должно быть: в режиме 1080p кадр вчетверо короче по времени (frame length 1262 строки против 3054), поэтому выдержка физически упирается примерно в 1240 строк — почти столько ты и поставил. Дальше добирать можно только усилением: W=1920 H=1080 EXPOSURE=1240 GAIN=896 sh ~/lanekit/capture.sh /tmp/f1080b.raw Теперь libcamera. Сперва найдём узел объектива: for d in /dev/v4l-subdev*; do v4l2-ctl -d $d --list-ctrls 2>/dev/null | grep -q focus_absolute && echo "объектив: $d"; done Потом снимаем серию и двигаем фокус прямо во время съёмки — если выставить заранее, драйвер объектива успевает уснуть и линза возвращается в исходное: LENS=/dev/v4l-subdevN rm -f /tmp/lc_*.rgb cam -c 1 --capture=40 --file=/tmp/lc_#.rgb & sleep 3 v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 wait convert -size 1920x1080 -depth 8 bgr:/tmp/lc_39.rgb /tmp/lc39.png && ls -l /tmp/lc39.png Наведи на текст в полуметре. Если резкость появится — останется только цвет, и это уже вопрос настроечного файла, а не пайплайна. 💰 6.14₽ · ⬇️ 213.1k / ⬆️ 945 · opus-5 · max · 17сЫ S Так это и есть развилка: резкость мы получали своим путём, руками двигая линзу через v4l2-ctl. А libcamera мотор объектива не трогает вообще — в программном ISP автофокуса нет, и линза всё время стоит там, где её оставили после включения. Поэтому наши кадры выходили резкими, а cam снимал размыто на той же камере. Отсюда и предложение: не менять ничего в libcamera, а просто подвинуть фокус, пока она снимает. Если после этого её кадр станет резким — значит весь пайплайн исправен и остаётся только цвет. Если фокус ты уже ставил именно во время работы cam и картинка всё равно мыльная — тогда дело не в линзе, и я полезу смотреть, как программный ISP собирает кадр: там свой демозаик, и при неправильном порядке байеровских пикселей картинка выглядит именно замыленной, с цветной сеткой по краям контрастных границ. 💰 6.13₽ · ⬇️ 214.2k / ⬆️ 775 · opus-5 · max · 18с
Ы S Это была заглушка — надо подставить найденный узел. Давай сразу одним куском, без ручной подстановки: 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 1 --capture=40 --file=/tmp/lc_#.rgb & sleep 3 v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 wait convert -size 1920x1080 -depth 8 bgr:/tmp/lc_39.rgb /tmp/lc39.png && ls -l /tmp/lc39.png Если строка «объектив:» окажется пустой — значит узел dw9768 в системе не поднялся, и тогда сначала смотрим dmesg | grep -i dw976. 💰 5.90₽ · ⬇️ 215.0k / ⬆️ 631 · opus-5 · max · 10сЫ S Нет, не снимает — 40 кадров при 30 в секунду должны улететь за полторы секунды. Жми Ctrl+C и смотрим, на чём встало: dmesg | tail -25 ls -l /tmp/lc_*.rgb 2>/dev/null | tail -3 И повтори коротко с логом самой libcamera: LIBCAMERA_LOG_LEVELS=*:INFO cam -c 1 --capture=4 --file=/tmp/lc_#.rgb 2>&1 | tail -30 Скорее всего наши ручные съёмки оставили граф в состоянии, которое ей не нравится: мы принудительно ставили формат 4000×3000 и пересобирали связи, а она ожидает настроить всё сама. Если так — вылечится сбросом графа перед запуском: media-ctl -d /dev/media0 -r 💰 6.46₽ · ⬇️ 215.9k / ⬆️ 1.1k · opus-5 · max · 19сЫ [ 937.317038] csiphy_link_setup called [ 937.317043] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [ 937.341334] video_prepare_streaming called [ 937.342149] csiphy_reset id 3 with base 000000008d8ddf37 and offset 2048 [ 937.349478] qcom-camss ace0000.camss: CSIPHY 3PH HW Version = 0x40010000 [ 937.349733] Writing reset_bits=3f9f to vfe->base=00000000a8151b8c with id 0 [ 937.349747] vfe irq handled! [ 937.349805] qcom-camss ace0000.camss: VFE:0 HW Version = 1.3.0 [ 937.349857] csid_reset called [ 937.349889] qcom-camss ace0000.camss: CSID:0 HW Version = 2.0.0 [ 937.349909] qcom-camss ace0000.camss: validating link "msm_csid0":1 -> "msm_vfe0_rdi0":0 [ 937.349915] qcom-camss ace0000.camss: validating stream "msm_csid0":1:0 -> "msm_vfe0_rdi0":0:0 [ 937.349923] qcom-camss ace0000.camss: validating link "msm_csiphy3":1 -> "msm_csid0":0 [ 937.349928] qcom-camss ace0000.camss: validating stream "msm_csiphy3":1:0 -> "msm_csid0":0:0 [ 937.349935] qcom-camss ace0000.camss: validating link "ov8856 13-0036":0 -> "msm_csiphy3":0 [ 937.349939] qcom-camss ace0000.camss: validating stream "ov8856 13-0036":0:0 -> "msm_csiphy3":0:0 [ 937.349947] qcom-camss ace0000.camss: msm_vfe0_video0: format is (1640x1232 pBAA little-endian (0x41414270)/1p field:1), trying (1640x1232 pBAA little-endian (0x41414270)/1p field:1) [ 937.349963] vfe_reserve_wm ret=0 [ 937.349979] qcom-camss ace0000.camss: receiver: lanes 2, assignment 0x10, phy 3, cfg0 0x00300101 [ 937.349987] qcom-camss ace0000.camss: csiphy: link 360000000 Hz, timer 300000000 Hz, settle count 22 [ 937.349992] qcom-camss ace0000.camss: csiphy: lane mask 0x85 from 2 lanes [ 937.350073] qcom-camss ace0000.camss: csiphy: settle high bytes 00 00 00 00 ff [ 1020.840236] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 1065.306063] Writing reset_bits=3f9f to vfe->base=00000000a8151b8c with id 0 [ 1065.306120] vfe irq handled!S Оно снимало телевик! Смотри на граф в логе: ov8856 13-0036 → msm_csiphy3 → msm_csid0 → msm_vfe0_rdi0, формат 1640×1232. То есть cam -c 1 — это у libcamera вторая камера, а не широкая. Отсюда и минута ожидания: телевик кадров не даёт, мы это только что выяснили. Заодно два полезных факта из этого же лога: assignment 0x10 и lane mask 0x85 — наша правка дерева доехала, линии теперь правильные. И версия PHY читается нормально (0x40010000), то есть утренний ноль был именно из-за холодного старта. Выбери широкую по списку: cam -l Там будут две строки вида 1: 'imx586 ...' и 2: 'ov8856 ...' — бери индекс с imx586 и повтори: 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) 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 convert -size 1920x1080 -depth 8 bgr:/tmp/lc_39.rgb /tmp/lc39.png && ls -l /tmp/lc39.png 💰 7.21₽ · ⬇️ 218.0k / ⬆️ 1.2k · opus-5 · max · 21сЫ [0:19:01.069409524] [3891] INFO Camera camera_manager.cpp:340 libcamera v0.7.1 [0:19:01.115365700] [3892] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 2 on pad 0/0: Not a tty [0:19:01.115424502] [3892] WARN CameraSensor camera_sensor_legacy.cpp:402 'ov8856 13-0036': The PixelArraySize property has been defaulted to 1640x1232 [0:19:01.115441013] [3892] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 1 on pad 0/0: Not a tty [0:19:01.115457471] [3892] WARN CameraSensor camera_sensor_legacy.cpp:413 'ov8856 13-0036': The PixelArrayActiveAreas property has been defaulted to (0, 0)/1640x1232 [0:19:01.115474294] [3892] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:19:01.115488878] [3892] WARN CameraSensor camera_sensor_legacy.cpp:421 'ov8856 13-0036': Failed to retrieve the sensor crop rectangle [0:19:01.115500336] [3892] WARN CameraSensor camera_sensor_legacy.cpp:427 'ov8856 13-0036': The sensor kernel driver needs to be fixed [0:19:01.115512003] [3892] WARN CameraSensor camera_sensor_legacy.cpp:429 'ov8856 13-0036': See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information [0:19:01.115761432] [3892] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'ov8856' [0:19:01.115787161] [3892] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:19:01.115809557] [3892] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. [0:19:01.116314612] [3892] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'imx586' [0:19:01.116344612] [3892] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:19:01.116388831] [3892] WARN CameraSensor camera_sensor_legacy.cpp:502 'imx586 12-001a': No sensor delays found in static properties. Assuming unverified defaults. [0:19:01.118515147] [3892] 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:19:01.118566033] [3892] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:19:01.118583585] [3892] WARN CameraSensor camera_sensor_legacy.cpp:881 'ov8856 13-0036': The analogue crop rectangle has been defaulted to the active area size [0:19:01.118629523] [3892] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for ov8856 [0:19:01.119068640] [3892] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36' for pipeline handler simple [0:19:01.119662029] [3892] 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:19:01.119720988] [3892] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for imx586 [0:19:01.120157188] [3892] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a' for pipeline handler simple Available cameras: 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 Два вывода из лога. Первый: это не наш libcamera. Все вчерашние предупреждения вернулись — «No static properties available for imx586», «Failed to create camera sensor helper». Значит запускается системная сборка из /usr, а наша пропатченная лежит в /usr/local и после перезагрузки перестала подхватываться. Второй: широкая камера — это индекс 2 (i2c-bus@0/camera@1a), а первая как раз телевик. Проверь, где что: which -a cam; ls -l /usr/local/bin/cam 2>/dev/null; ls /usr/local/lib/*/libcamera.so* /usr/local/lib/libcamera.so* 2>/dev/null | head И запусти нашей сборкой явно: export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib/aarch64-linux-gnu /usr/local/bin/cam -l 2>&1 | grep -E "properties|helper|^[0-9]:" Если предупреждения про imx586 исчезли — сборка подхватилась, и тогда сразу съёмка с фокусом: 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) rm -f /tmp/lc_*.rgb /usr/local/bin/cam -c 2 --capture=40 --file=/tmp/lc_#.rgb & sleep 3 v4l2-ctl -d $LENS --set-ctrl focus_absolute=600 wait convert -size 1920x1080 -depth 8 bgr:/tmp/lc_39.rgb /tmp/lc39.png && ls -l /tmp/lc39.png 💰 8.04₽ · ⬇️ 221.1k / ⬆️ 1.6k · opus-5 · max · 22сЫ /usr/local/bin/cam -l [0:20:03.817512167] [3953] INFO Camera camera_manager.cpp:340 libcamera v0.7.1 [0:20:03.871970442] [3954] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 2 on pad 0/0: Not a tty [0:20:03.872054765] [3954] WARN CameraSensor camera_sensor_legacy.cpp:402 'ov8856 13-0036': The PixelArraySize property has been defaulted to 1640x1232 [0:20:03.872076380] [3954] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 1 on pad 0/0: Not a tty [0:20:03.872097422] [3954] WARN CameraSensor camera_sensor_legacy.cpp:413 'ov8856 13-0036': The PixelArrayActiveAreas property has been defaulted to (0, 0)/1640x1232 [0:20:03.872119713] [3954] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:20:03.872135390] [3954] WARN CameraSensor camera_sensor_legacy.cpp:421 'ov8856 13-0036': Failed to retrieve the sensor crop rectangle [0:20:03.872153047] [3954] WARN CameraSensor camera_sensor_legacy.cpp:427 'ov8856 13-0036': The sensor kernel driver needs to be fixed [0:20:03.872167630] [3954] WARN CameraSensor camera_sensor_legacy.cpp:429 'ov8856 13-0036': See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information [0:20:03.872462424] [3954] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'ov8856' [0:20:03.872488778] [3954] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:20:03.872513622] [3954] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. [0:20:03.873057687] [3954] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'imx586' [0:20:03.873084615] [3954] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:20:04.314528137] [3954] WARN CameraSensor camera_sensor_legacy.cpp:502 'imx586 12-001a': No sensor delays found in static properties. Assuming unverified defaults. [0:20:04.325256792] [3954] 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:20:04.325471689] [3954] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:20:04.325566689] [3954] WARN CameraSensor camera_sensor_legacy.cpp:881 'ov8856 13-0036': The analogue crop rectangle has been defaulted to the active area size [0:20:04.325763149] [3954] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for ov8856 [0:20:04.327715660] [3954] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36' for pipeline handler simple [0:20:04.330399373] [3954] 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:20:04.330690832] [3954] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for imx586 [0:20:04.333111159] [3954] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a' for pipeline handler simple Available cameras: 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 Значит подхватывается всё равно системная библиотека — двоичный файл наш, а libcamera.so он берёт из /usr. Проверим: ldd /usr/local/bin/cam | grep -i camera ls -l /usr/local/lib/*/libcamera.so* /usr/local/lib/libcamera.so* 2>/dev/null ls -l /usr/lib/libcamera.so* 2>/dev/null | head -3 ls -d /usr/local/lib/*/libcamera 2>/dev/null Обрати внимание на даты: у нашей сборки они вчерашние, у системной — из пакета. Если наша библиотека на месте, надо ещё указать, откуда брать модули IPA — иначе он и их возьмёт системные: L=$(dirname $(ls /usr/local/lib/*/libcamera.so.* /usr/local/lib/libcamera.so.* 2>/dev/null | head -1)) export LD_LIBRARY_PATH=$L export LIBCAMERA_IPA_MODULE_PATH=$L/libcamera /usr/local/bin/cam -l 2>&1 | grep -E "properties|helper|^[0-9]:" Предупреждения про imx586 должны исчезнуть — тогда среда правильная, и можно снимать. Если же выяснится, что в /usr/local библиотеки вообще нет, значит вчера мы собрали, но не установили, и я дам команду доустановить. 💰 7.95₽ · ⬇️ 224.5k / ⬆️ 1.3k · opus-5 · max · 21сЫ 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с