Привет, меня зовут Олег Герасимов, я директор центра компетенций IT-кластера Ростелекома. Наша команда среди многих задач разрабатывает прошивки камер видеонаблюдения для B2B и B2C-сервисов. В предыдущей статье я рассказывал, как мы научились самостоятельно разрабатывать софт и прошивки для IP-камер, в том числе и недорогих, и подключать их к облаку.


За прошедшее время камеры с нашей прошивкой уже появились на рынке, и, судя по данным Яндекс.Маркета, — на полках магазинов цены на них начинаются от 1500 рублей. И это уже не дешевый «ноунэйм», а качественные камеры ведущих мировых брендов: Hikvision, Dahua и Uniview. На мой взгляд, это отличный результат!



Когда мы начинали работать с прошивками, количество поддерживаемых моделей камер было небольшим. В процессе разработки общих компонентов мы самостоятельно реализовывали поддержку новых моделей камер.


Тогда у нас была возможность самим выбирать, какие модели интегрировать в первую очередь, а какие нет, исходя из технических соображений.


Например, если в камере используется сенсор, под который в SDK процессора нет драйвера, то драйвер сенсора придется разработать самим. Это очень трудоемкий процесс, требующий сложной разработки и отладки. Сенсор — технически сложный компонент, и на одно изучение технического описания (Datasheet) сенсора могут уйти недели. Это, на секундочку, сотни страниц с описанием тысяч регистров, влияющих на работу сенсора, и логики взаимодействия с ними.


Первое время мы обходили стороной камеры с незнакомыми сенсорами, чтобы не застрять в деталях настройки конкретного железа, а сосредоточиться на разработке общих компонентов, которые будут использоваться в большинстве камер. Иногда нам удавалось найти уже готовые драйверы сенсора в оригинальной прошивке камеры, но это было скорее счастливым исключением из правил. Работая в таком режиме, мы поддержали около десятка моделей камер на нескольких наиболее популярных чипсетах, делая упор не на количество моделей, а на функциональность.


Следующий этап разработки прошивок для камер совпал с объявлением тендера на закупку камер. Это уже серьезное событие. Ростелеком закупал сотни тысяч камер на открытом тендере, в который могли войти любые вендоры камер, соответствующих по своим ТТХ продуктовым и техническим требованиям.


Одно из ключевых требований к поставщикам — предоставление технической информации о схемотехнике камер, например, о GPIO-выводах, к которым подключены светодиоды, кнопки, ИК фильтры и т.д. Кроме этой информации, мы просили вендоров предоставить специфичные для оборудования патчи ядра/загрузчика и исходные тексты драйверов устройств, в первую очередь, сенсоров и WI-FI чипов.



Такой подход существенно облегчил нашу задачу по поддержке новых камер. Вместо данных, добытых реверс-инжинирингом, у нас появились официальные спецификации и исходники драйверов от вендоров.


Однако по-прежнему много времени уходило на подготовку прошивок, даже когда вендоры стали делиться информацией: данные в спецификациях были неточными, драйверы не хотели заводиться на железе, а то и вовсе отказывались собираться.


Самостоятельно найти ошибку в спецификациях или драйверах получалось не всегда, да и искать ошибки в стороннем коде — небыстрое занятие. Поэтому чаще для решения проблемы приходилось обращаться к вендорам. Как правило разработчики, которые могут ответить на вопросы о схемотехнике, найти
и поправить ошибки в драйверах, находятся в Китае. А это сразу замедляет коммуникацию — одна итерация «вопрос-ответ» может занимать несколько дней, а исправление ошибок и того дольше.
Пока поток новых камер был небольшим такой режим устраивал, но со временем запросы от маркетологов выросли. Возникла потребность в интеграции большего количества линеек: уличные камеры с Wi-Fi, домашние с PTZ, с сиреной и т.д. Но с другой стороны, по мере развития облачного видеонаблюдения увеличивалось количество вендоров, желающих работать с нами.


В этот момент мы поняли, что силами команды с возросшим в разы количеством новых камер уже не справиться. Всё время разработки
будет занимать исключительно интеграция новых камер, а на разработку новых фич и улучшения времени не останется. Нужно было найти новый подход.



Мы начали анализировать узкие места процесса. Они оказались на поверхности: больше всего времени уходило на итерации получения патчей и драйверов, багрепорты в сторону вендоров и ожидание исправлений.


Логичная мысль: дать вендорам возможность самим собирать нашу прошивку с драйверами их оборудования и конфигурациями, проверять результаты и отлаживать работу у себя, не тратя драгоценное время на переписку.


Техническое решение, с одной стороны, очевидное: сделать SDK для сборки нашей прошивки. Но есть ряд требований:


  • SDK должен уметь собирать прошивки под все типы поддерживаемых SoC. На сегодня это больше 10 чипсетов от Hisilicon, Ambarella, MStar и Fullhan.
  • Нельзя передавать компоненты прошивок от одного вендора другим, потому что это интеллектуальная собственность. Мы подписываем NDA, в котором обязуемся не раскрывать передаваемую информацию.
  • Результаты интеграции, полученные от вендора, нужно уметь замерджить в общее дерево исходников прошивки в нашем Git.
  • У вендора должна быть возможность вносить патчи и дополнения во все компоненты: ядро, загрузчик, SoC SDK и прочие.
  • Нужно иметь гибкую структуру настроек, в которой будут учитываться максимальное количество возможных конфигураций оборудования.
  • Для каждой пары «вендор-SoC» должна собираться универсальная прошивка, поддерживающая все камеры вендора на базе этого процессора.
  • Сборка должна работать автономно и не требовать доступа в Интернет. Да, большинство разработчиков ПО для камер в Китае не имеют доступа в Интернет на рабочих местах.


Структура системы сборки, в тот момент, была не готова к такому подходу. Например, в некоторых файлах конфигурации были совмещены настройки камер от нескольких вендоров, а для сборки прошивки и загрузки пакетов требовался доступ к нашим внутренним репозиториям. Да и некоторые места системы сборки (чего греха таить) не предполагали такого масштабирования: их разработали в то время, когда поддерживался один тип процессора и две модели камеры...


Другая проблема: под каждый процессор поставляется отдельный SDK от его производителя, с уникальным набором системных компонентов: своя версия и набор патчей ядра, toolchain, uboot, системная библиотека (где-то uclibc, а может быть glibc). Под эти факторы приходится подстраивать систему сборки, а местами и код приложений. Для наглядности масштабов фрагментации вот табличка со списком версий компонентов:


Процессор Версия Linux Версия gcc
Hisilicon 3516a/d 3.4.y gcc 4.9
Hisilicon 3518ev100 3.0.y gcc 4.4
Hisilicon 3518ev200 3.4.y gcc 4.9
Hisilicon 3516cv300 3.18.y gcc 4.9
Hisilicon 3518ev300/3516ev200/ev300 4.9.y gcc 6.3
Hisilicon 3516cv500/dv300 4.9.y gcc 6.3
mStar i3 3.18 gcc 4.8
mStar i6 4.9 gcc 8.2
Ambarella s2l 3.10 gcc 4.9
Ambarella s3l 3.10 gcc 5.2
Fullhan fh8632 3.0.y gcc 4.3

Как видно, разброс огромный: от легаси десятилетней давности до относительно свежих ревизий.


С такими вводными нам предстоял рефакторинг системы сборки прошивок: выделить общие патчи и драйвера, которые будут доступны для всех вендоров, повторить это для 10+ поддерживаемых моделей SoC, полностью переосмыслить систему конфигурации наших компонентов. Заодно выпилить специфичные для камер костыли из init-скриптов, сделав общие и универсальные решения.


В результате мы пришли к структуре, в которой все специфичные для конкретных моделей настройки/конфигурации/makefile/патчи собраны в папках, структурированную по иерархии "Вендор > SoC > Модель камеры". Такая иерархия позволила автоматизировать сборку SDK с разделением сборок по вендорам. Вот пример, драйверы и конфигурации для камер от выдуманного вендора Megatech на чипсете Hisilicon.


Структура каталогов

Драйверы


drivers 
+ megatech/             -> драйвера и конфиги для вендора 'megatech'
| + hi3518ev200/        -> чипсет hisilicon hi3518ev200
| | + 1421              -> конфигурации модели камеры с кодом оборудования 1421
| | | | + ipcdb.1421.yml -> общая конфигурация
| | | | + mpi/entry.1421.yml -> конфигурация видеозахвата
| | | | + ptz/entry.1421.yml -> конфигурация PTZ
| | + motor             -> драйверы моторов управления PTZ
| | | + bu24036_motor   -> драйвер шагового мотора на чипе bu24036
| | | | gpio_motor      -> драйвер шагового мотора управляемый GPIO выводами
| | + wlan              -> драйверы wi-fi чипов 
| | | + Makefile        -> скрипт сборки 
| | + sensor            -> драйверы сенсоров
| | | + Makefile        -> скрипт сборки 

Патчи ядра


kernel
+ megatech/
| + hi3518ev200/
| | + mmc_hotplug.patch
| | + kernel-config.patch

Патчи uboot


uboot
+ megatech/
| + hi3518ev200/
| | + uboot-mmc.patch
| | + uboot-spi.patch

Все файлы конфигурации камер перевели в формат YAML, как самый удобный для ручного редактирования, и разделили по компонентам:


  • Настройки оборудования модели камеры (GPIO, наличие и тип Wi-Fi, сенсора, флаги наличия микрофонов, динамиков, дополнительные скрипты инициализации).

Пример yaml
1421:
  vendor: megatech
  model: Model A
  soc: 3518ev2
  ethernet: 0
  wlan: rtl8188eu
  sensor: ov9732
  leds:            
    ir:            
      gpio: 23
      inverse: true
    red:           
      gpio: 10
    power:
      gpio: 10     
    green:
      gpio: 2
    net:
      gpio: 2
  keys:
    wps:
      gpio: 16
    reset:
      gpio: 16
  peri-out:
    pwdn:
      gpio: 1
      inverse: true
    ircut.p:
      gpio: 57
    ircut.n:
      gpio: 60
    wifi_pwr:
      gpio: 7
  flash: spi
  misc:
    microphone: true
    speaker: true
    mic_hpf_level: 3
    mic_anr_level: 4
  scripts:
    insert-sns:
      - himm 0x200f0040 0x2; # I2C0_SCL
      - himm 0x2003002c 0xc4001; # sensor unreset, clk 24MHz, VI 99MHz
    init-wlan:
      - insmod 8188eu.ko

  • Настройки видеоприложения для конкретной модели (тип сенсора, поддерживаемые разрешения, режимы синхронизации и видеозахвата, подстройки алгоритмов).

Пример yaml
1421:
  sensor:
    type: ov9732
    lib: libsns_ov9732.so
  resolutions:
    - targets: 
        - { width: 1280, height: 720, maxrate: 30 }
        - { width: 640, height: 480, maxrate: 30 }
        - { width: 640, height: 360, maxrate: 30 }
        - { width: 320, height: 240, maxrate: 30 }
      channels:
        - main
      source: { width: 1280, height: 720, rates: [30, 25] }

  combo_dev_attr:
    input_mode: CMOS_33V
  vi_dev_attr:
    interface_mode: DIGITAL_CAMERA
    component_mask: [67043328, 0]
    syn_cfg:
      vsync: field
      vsync_neg: high
      hsync: valid_signal
      hsync_neg: high
      vsync_valid: valid_signal
      vsync_valid_neg: high
      timing_blank: [ 370, 1280, 0, 6, 720, 6, 0, 0, 0 ]
  isp_image_attr:
    bayer_format: BGGR

  • Настройки PTZ (тип чипа, тайминги работы шаговых драйверов).

Пример yaml
1421:
  type: pan_controller_and_tilt_gpio_generic
  interrupt_gpio: 50
  absolute: true
  pan:
    park_ccw: false
    continuous: [-20, 20]
    relative: [-7.9, 7.9]
    absolute: [0, 355]
    channel: 0
    min_wait: 100
    max_step: 140
    max_speed: 375
    unity: 430
  tilt:
    park_ccw: true
    continuous: [-20, 20]
    relative: [-3.5, 3.5]
    absolute: [0, 90]
    max_step: 2000
    unity: 157

Выбор способа сборки дистрибуции SDK был очевидным. Docker оказался вне конкуренции, с его помощью можно легко передать подготовленную среду.
Мы использовали Docker практически с самого начала разработки, как в CI, так и для локальной отладки на компьютерах разработчиков.


Процесс сборки полностью автоматизирован. Docker-образы с нашим SDK собираются в общем CI под матрицу сочетаний «SoC-вендор».


Исходно у нас было два основных репозитория:


  • build-tools — в нем хранятся Dockerfile, скрипты установки SoC SDK и скрипты сборки общих библиотек для поддерживаемых аппаратных платформ. В CI этого репозитория собираются Docker-образы со всем необходимым для сборки прошивки и софта под целевую платформу.
  • vc-firmware — здесь хранится система сборки прошивки и компонент. К этому же репозиторию как git-submodule подключены репозитории с исходниками наших компонентов: видеоприложение, облачный агент, модуль обновления). Результатом работы CI в этом репозитории являются сами прошивки камер.

Для нового SDK добавили еще один репозиторий, vc-sdk, к которому подключили vc-firmware и build-tools как git-submodule. В CI этого репозитория сборка разделена на этапы:


  • подготовка базового Docker-образа по аналогии с репозиторием build-tools;
  • сборка пакетов с компонентами (видеоприложение, облачный агент, модуль обновления);
  • загрузка пакетов с общими компонентами (публичные библиотеки, драйверы Wi-Fi и т.д.)
  • копирование каталогов с драйверами и патчами, специфичными для вендора


В результате работы CI получается автономный Docker-образ, рассчитанный на сборку прошивок для камер на выбранном чипе вендором. Он загружался
в общий registry образов, из которого вендор может скачать свой образ.


Следующий объемный фронт работ — разработка документации. Документацию мы писали поэтапно, собирая обратную связь от вендоров и учитывая замечания. Кстати, для документации использовался наш инструмент Foliant. Про него наши ребята уже рассказывали на митапе «Write the Docs Moscow» (https://habr.com/ru/post/431210/).


Заключение


За этот год уже 8 вендоров освоило использование нашего SDK, с его помощью добавили поддержку работы с видеонаблюдением Ростелекома в нескольких десятков новых моделей камер. Некоторые из этих камер уже продаются.


Важно, что выбранный подход — разработка собственной прошивки на базе SDK — работает на потребителей: интеграция камер таким способом позволила организовать честную конкуренцию при тендере на закупку камер. Победители выявляются по объективным показателям: качеству оборудования и наименьшей стоимости.


Наконец, если раньше количество поддерживаемых моделей камер было ограничено силами и ресурсами команды, то сегодня эта проблема ушла в прошлое. Теперь вендоры камер могут сами добавлять поддержку новых моделей через облако РТК.