Но что же дальше, какой софт запущен на этом линуксе? Есть несколько классных статей datacompboy про поиск бага которого нет, есть ещё разрозненная информация, но в целом ситуация такая: на IP-камере стоит специально пропатченное ядро, которое дает доступ программе через специальную библиотеку к железу, выдающему сжатые видеокадры.
Грустная реальность в том, что очень часто этот софт написан далеко не лучшим образом. Достаточно сказать, что большинство камер, которые висят на улице очень страдают из-за большого расстояния до сервера, потому что авторы их прошивки освоили мастерство потерь данных по TCP.
Мы решили исправить эту ситуацию своей прошивкой, причем сделав ставку на Rust.
Условия работы
Нужно сделать пару пустяков: разобраться с SDK, написать код, который настраивает железо, принимает H264 кадры и отправляет их в сеть. Пара пустяков, особенно учитывая насколько легко и просто деплоиться на IP камеры и отлаживать это всё. Ну и оставшая мелочь: мы решили писать этот код на Rust.
Rust в качестве эксперимента был выбран за его удивительное свойство: compile time гарантию целостности памяти вместе с отсутствием рантайма. Это означает, что мы можем ожидать возможность контроля за выделением памяти, что очень важно, учитывая стесненность по ресурсам.
Почему не подойдет Go, Erlang или какая-нибудь Java/C#? Потому что на IP камере флешка на 8 мегабайт и 128 мегабайт памяти из которых половина отнята у ядра под нужды видео. Понятно, что камеры есть разные, но всегда стараются делать по минимуму, чтобы не поднимать стоимость без нужды. На одной камере мы видели флешку на 64 мегабайта, там конечно можно развернуться, но хватает и совсем крохотных флешек.
Так, обычная картина на дешевой камере за 3000 рублей мы видим:
# free
total used free shared buffers cached
Mem: 60128 17376 42752 0 2708 4416
-/+ buffers/cache: 10252 49876
Swap: 0 0 0
# cat /proc/cpuinfo
Processor : ARM926EJ-S rev 5 (v5l)
BogoMIPS : 218.72
Features : swp half thumb fastmult edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant : 0x0
CPU part : 0x926
CPU revision : 5
Hardware : hi3518
Revision : 0000
Serial : 0000000000000000
В таких условиях паршиво написанный софт начинает очень сильно страдать уже от 3-4 подключений. Золотое правило при работе с IP камерами: вообще стараться больше одного подключения (или два, по одному на каждое качество) не делать и это связано не только с узким каналом до камеры, но ещё с тем, что четвертый клиент к IP камере зачастую делает невозможным просмотр у первых трех. Забегая вперед, скажу что у нас и на 50 клиентах проблем не возникло.
Как устроена камера
Прежде чем идти дальше, расскажу немножко о том устройстве камеры, с которым мы работаем на текущем этапе.
На камеру напаяна SPI флешка. Это такая же флешка, как та, на которую прошивает себя в биос какой-нибудь локер. Содержимое этой SPI флешки можно прочитать, подцепившись клещами, можно записать (если повезет), с неё же считывает данные в память процессор и выполняет их. Бывает, что флешка не SPI, а NAND, тогда всё сложнее: просто так клещами не подцепишься — приходится быть ответственнее.
В самом начале флешки находится uboot. Это загрузчик, используемый практически во всех встраиваемых устройствах: не только камеры, а роутеры и телефоны. Т.е. скорее всего можно утверждать, что экземпляров убута в мире побольше, чем экземпляров виндовса.
У uboot открытые исходники, но в нём хранятся данные, специфичные для конкретной железки. Если скопировать флешку с камеры, сделанной фирмой XM на камеру, сделанную фирмой Hikvision, то есть большой шанс, что даже uboot не загрузится.
Т.е. уже на этом этапе возникает увлекательный процесс ведения реестра известных камер, их учета, который очень здорово облегчается восхитительным умением наших соседей присылать ровно то, что ты заказал. В качестве хорошего примера можно привести свежую историю у наших клиентов (самый крупный национальный оператор страны), которые подписали контракт на 3 года о поставке камер конкретной модели и характеристик, после чего через неделю приехали камеры уже другой модели и с совершенно другими характеристиками.
Но ничего страшного, всё это решаемый вопрос, двигаемся дальше.
А дальше находится ядро линукса. Было бы слишком просто, если бы можно было одно ядро собрать для всех возможных камер и потом просто модули перетыкать. Нет, так нельзя, поэтому для разных версий чипсета нужны разные ядра: где-то 2.x.y, где-то 3.x.y. Почему так? Потому что к ядру идут закрытые модули. Где-то можно исхитриться, но всё равно унифицировать всё не получится.
После этого идет обычный хозбытовой buildroot. Здесь всё как у людей.
Дальше надо запустить хитрые скрипты, настраивающие железо через i2c (и возможно как-то ещё), загрузить правильные модули и стартовать специально написанный софт.
Захват видео
В захвате видео очень много подготовки железа. Если почитать спецификацию onvif и мануал по SDK IP камеры, то можно увидеть много общего — софтверный интерфейс отражает общую структуру большинства железяк и она следующая: видео снимается с сенсора, немножко обрабатывается, потом засовывается в энкодеры (аппаратные конечно) и потом в софтине можно забрать из определенного места в памяти готовые H264 NAL юниты. Для базового сценария осталось только приделать управление пользователями, настройки и какой-нибудь сетевой протокол. Для полноценной камеры ещё нужна поддержка всяких механизмов массовой настройки (discovery, onvif, psia, etc..) и аналитика.
А чего там про Rust
Вот как раз стример у нас ржавый. Целая пачка unsafe кода, автогенеренного из сишного кода SDK с помощью bindgen, подпатченый биндинг к libc (постараемся залить патч в апстрим) и дальше реализация RTSP на tokio. Даже уже есть возможность посмотреть видео с камеры в обычном браузере — это недостижимая роскошь для китайских камер, которые поголовно требуют установку ActiveX.
Структура очень непривычна после эрланга: ведь тут нет процессов и сообщений, есть каналы, а с ними всё становится немножко по-другому. Как я уже выше писал, современно написанный код с правильной организацией дает возможность раздавать видео не 2-3 клиентам, а более 50 без какой-либо просадки производительности.
Важный момент: за время разработки пока не случилось ни единого сегфолта. Пока есть стойкое ощущение, что Rust заставляет писать так, как в принципе пишут хорошие поседевшие сишники, повидавшие всякого нехорошего. Так что пока всё нравится.
В течение августа есть планы закончить работу по базовому сценарию, так что есть вопрос к аудитории, который идет в опросе. Ну и задавайте вопросы, которые возникли.
Комментарии (45)
datacompboy
04.08.2017 20:10+4Мяса, мяса давай! А то сплошные байки из склепа. Так-то все понятно, но со стороны — информации ноль.
datacompboy
04.08.2017 20:13+1Сделать опенсорц как хик не хотите ли?
erlyvideo Автор
04.08.2017 20:36а где у них опенсорсная прошивка?
datacompboy
04.08.2017 21:44Ух, обещали но так и нет.
Тогда как sigrand :)erlyvideo Автор
04.08.2017 21:53да и у сигранда негусто https://github.com/sigrand
datacompboy
04.08.2017 22:26Сигранд тут живёт: git://sigrand.ru/sigticam.git
erlyvideo Автор
04.08.2017 23:09+1ты прям озадачил. Надо подумать, не будет ли здесь проблем от третьих сторон.
tgz
04.08.2017 21:55+2Rust очень хорош, вот прямо очень. ЛУчший язык придуманный за последние 10 лет.
khim
05.08.2017 16:59+2Не могу назвать его лучшим в принципе, но в нише «плати только за то, что используешь» — таки да. Всё постельные приличные языки, изобретённые за последние 10 лет, требуют «тяжёлого» рантайма — независимо от того, какие фишки этого рантайма ты используешь, какие — нет.
tgz
05.08.2017 21:52-1Назовите тогда лучший.
ozkriff
05.08.2017 23:08+4Ну не бывает же чего-то абсолютно лучшего, без контекста. Только в своих нишах, только при таких-то требованиях.
tgz
06.08.2017 08:35-1Это как то мешает делать выбор и сравнивать? Есть куча параметров независящих от контекста и требований.
ozkriff
06.08.2017 08:59+5"Лучший" — это же значит что я в любой ситуации этот язык предпочту всем остальным, так?
Лично мне сильно мешает — я на вопрос "назови лучший язык" вообще ничего не могу ответить. Для меня в любом случае контекстно зависимые факторы будут весомей абсолютных. Как бы я не любил ржавчину, все равно на практике я для разных типов проектов могу посчитать на данный момент более подходящими и си, и плюсы, и хаскель, и питон и еще что.
tgz
06.08.2017 11:15-8Люди когда придумывают язык, они знают о конкретных ваших ситуациях? Как же они без этого всего решения выбирают?
P.S. так как этот пидорский сайт не дает оставлять комментарии тогда, когдя я хочу, то дискуссию я пожалуй продолжать не буду.
WFrag
05.08.2017 00:03+10Кому интересен Rust на маленьких микроконтроллерах (типа STM32), есть неплохой блог (на английском): http://blog.japaric.io
datacompboy
05.08.2017 00:30Блог — очень плохой способ структурировать редкую информацию, вроде описанной… Хорошо для апдейтов, плохо для впитывания.
WFrag
05.08.2017 00:37+2Согласен. Но для тех, кто в такие темы погружается набегами, в качестве хобби, любая информация полезна. Я просто недавно искал замену C++ в для своего маленького проекта на STM32 и без этого блога я бы самостоятельно Rust на голом STM32 не осилил бы.
grossws
05.08.2017 05:21+2У Jorge Aparicio есть ещё пара вполне структурированных мини-книжек на тему bare metal: https://japaric.github.io/discovery/ для совсем начинающих и https://japaric.github.io/copper/ для продолжающих.
9660
05.08.2017 06:44-1Очень интересно. Но очень мало :)
Единственный момент который меня озадачил
В большинстве случаев там стоит обычный линукс, причем частенько с дефолтным рутовым паролем,
Впервые слышу про дефолтовый рутовый пароль у линукса.d1f
05.08.2017 06:59+1> дефолтовый рутовый пароль
у данного типа устройства он дефолтный, один на всех.
Ну и про «обычный линукс» это сильное преувеличение.erlyvideo Автор
05.08.2017 10:24+1не, реально это достаточно обычный билдрутовый линукс.
Просто в отличие от достаточно сильно развитого openwrt в котором есть пакеты, тут всё сильно попроще, потому что почти вся система readonly.
erlyvideo Автор
05.08.2017 10:25+1если перед вами ssh (чаще telnet) к IP камере, то в 80-95% случаев вам поможет один из примерно 40 известных паролей.
Очень сложно организационно сменить пароль на камере при её установке.
codrem
06.08.2017 15:53+2Есть ли шанс заиметь вашу ржавчину чтоб поставить самому? Уж очень печальная там родная прошивка. Приходиться танцы с бубном устраивать чтоб получить вменяемый rtsp/h264
erlyvideo Автор
06.08.2017 16:21+4есть такая масса способов превратить вашу камеру в кирпич, что вы должны быть твердо уверены в себе для этого на текущем этапе.
Начните с фотографии процессора на камере. Если там что-то типа hi3518 или 3516, то шанс есть, но нужны все буквы. Так например 3516c и 3516a несовместимы вплоть до загрузчика.
erlyvideo Автор
07.08.2017 00:09наверное лучше даже по-другому: у вас есть оригинальная прошивка от вашей камеры?
codrem
07.08.2017 15:00Должна где-то быть, у меня две камеры, одна сейчас лежит без дела. А так же, насколько я помню, у меня hi3518e. Ваш cpuinfo очень похож на то, что я видел у себя но надо будет перепроверить
erlyvideo Автор
07.08.2017 21:06если найдете — попробуем помочь
codrem
07.08.2017 23:15Сравнил cpuinfo — один в один.
прошивка текущая — V4.02.R12.00006510.10010.140700.00000 / HI3518E_50H10L_S39
Вот такое нашел когда-то на просторах интернета. + есть бинари с прошивкой за июнь 2016 и сентябрь 2014
Только что-то я перемудрил в прошлый раз с их CMS и теперь не могу по телнету на камеру попасть.
И для интересующихся вот много ссылок на всякое разное по этим китайцам.
Tuxman
09.08.2017 22:42Три года назад работал в компании, предоставляющей набор охранной системы для частников — это набор беспроводных датчиков и базовой станции, которая общается с «центром» через GSM/3G и имеет бекапную батарейку (грабители обычно обесточивают дом, обрезают телефонную линию, разбивают панель ввода пинкода, думая, что он посылает сигнал тревоги). Я разрабатывал для них IP камеру (прошивку на основе hi3518 и Ambarella A5s/S2Lm) и серверную часть. SDK обычно предоставляет высокоуровневый RTSP/RTP стриминг, но я брал с уровня NAL h264 фреймов и запихивал в пропраетный протокол. Общение камеры с клаудом по типу dropcam, основан на TLS соединении (каждая камера имеет свой сертификат с UUID, подписанный единым CA, что авторизует каждую камеру, и сертификат сервера может быть проверен тоже с помощью CA паблика вшитого в камеру), и протокол на основе protobuf. Серверная часть C++ Boost::ASIO позволяет держать десятки тысяч одновременно подключенных камер на один сервер (стриминг происходит по активации со стороны приложения или по срабатыванию датчиков в доме).
Интересная тема с просмотром live и записанном видео в браузере и в мобильных клиентах. На Android и iOS мы можем просто скармливать NAL фреймы фреймворку и сама OS будет проигрывать видео, т.е. транспорт полностью может быть пропраетным. С браузерами не всё так просто, либо flash, либо html video tag. Хотя есть вариант плагинов, типа VLC для браузеров, чтобы проигрывать RTSP/RTP потоки, или какой-нибудь ActiveX — но это уже сильно устарело.
HTML5 video отлично проигрывает HLS и MPEG-DASH во всех современных браузерах, но live video запаздывает на величину 2х-3х сегментов, которые скажем по 2 сек, значит на 4-6 секунд. Мы же хотим видеть live именно live с минимальной задержкой (как в RTSP/RTP). К сожалению, я выбрал вариант Flash и с сервера лил файлы FLV просто потоком, который сразу же проигрывался. FLV формат супер прост, пихаем h264 NAL и AAC фреймы и всё. На мобильных клиентах мы написали простой FLV парсер и скармливали NAL и AAC фреймы системе.ValdikSS
10.08.2017 00:12С браузерами не всё так просто, либо flash, либо html video tag.
Вы смотрели в сторону Media Source Extensions? Мне кажется, он отлично подходит для вашего случая.
На википедии такое примечание даже:
Unreal HTML5 player uses MSE for low latency (sub-second) live playback of streams sent via WebSockets by Unreal Media Server
Tuxman
10.08.2017 00:20Нет, тогда MSE прошло мимо нашего радара. Да и сам html5 video tag был немного в диковинку, точнее список видео/аудио кодеков и контейнеров, которые поддерживаются тем или иным браузером. Как минимум мобильные браузеры не могли тогда играть html5 video, не знаю как сейчас. Кстати, AAC аудио кодек тоже немного не для нас, мы всё делали в Speex (потом стал opus). Speex хорошо жмёт голос, а не музыку, предоставляя лучший битрейт. FLV может содержать Speex. Ничего такого из html5 video не могло содержать speex.
P.S. Сейчас я мало знаю про техническую сторону того проекта, уже почти два года в другой компании.ValdikSS
10.08.2017 00:22Теперь уже OPUS в браузерах поддерживается почти повсеместно, как и webm с VP8/9. YouTube по умолчанию отдает webm VP9 + OPUS разными файлами, объединяя их через Media Source Extensions.
miolini
Молодцы!
erlyvideo Автор
ща, статью откорректирую и на ICO =)
Das_original
К сожалению качеством лучше не нашел, но всё же!