Анимированные QR и фонтанные коды: как файл летит через экран
Через один QR-код помещается пара килобайт, а файл весит мегабайты. Как тогда мерцающие QR-коды передают целый файл и почему приёмник собирает его даже с пропущенными кадрами? Разбираю фонтанные коды простыми словами — без формул.
Коротко (TL;DR)
- В один QR-код влезает лишь пара килобайт, поэтому целый файл разбивают на сотни кадров и показывают на экране потоком, а камера другого устройства всё это снимает.
- Наивный способ «пронумеровать кадры 1, 2, 3 и слать по порядку» хрупкий: камера ловит не каждый кадр, а из-за одного потерянного приходится ждать целого прокручивания потока по кругу.
- Фонтанные коды (Luby Transform) решают проблему: каждый кадр — это не «блок номер 57», а комбинация случайного набора блоков файла. Ловить нужно не конкретные кадры, а просто достаточное их количество.
- Приёмнику хватает поймать примерно на 15 процентов больше кадров, чем было исходных блоков, в любом порядке — и он восстанавливает файл целиком.
- Скорость такой передачи телефон в телефон порядка 128 КБ/с, разумный потолок около 64 МБ за передачу; целостность проверяют хэшем вроде SHA-256.
Видели когда-нибудь мерцающий чёрно-белый квадрат на экране телефона, которым будто «переливают» файл на другой телефон — без интернета, без Bluetooth, без проводов? Один экран быстро-быстро моргает узором, второй телефон наведён на него камерой, проходит несколько секунд — и файл оказался на приёмнике. Выглядит почти как магия: картинка мельтешит, часть кадров явно смазывается, а данные всё равно доезжают целиком и без ошибок. Как так?
Меня зовут Чимитдоржи Дарижапов, и я объясню, как это работает. Разберём по шагам: почему один QR-код тут бессилен, почему «пронумеровать и слать по порядку» — плохая идея, и что за красивый математический трюк под названием фонтанные коды превращает поток мерцающих квадратиков в надёжный канал связи. Формул не будет, только бытовые аналогии.
Задача: файл больше одного QR-кода
Начнём с честного ограничения. QR-код — это не бездонный контейнер. Даже самый плотный QR-код версии с максимальной ёмкостью держит порядка пары-тройки килобайт полезных данных, а если хотим, чтобы код уверенно считывался камерой в движении, реально закладывать стоит ещё меньше. Килобайт-другой на кадр — вот наш бюджет.
Теперь возьмём обычный файл: фотографию, PDF-документ, архив с настройками, небольшой дистрибутив. Это уже мегабайты. Разница между «пара килобайт» и «несколько мегабайт» — в тысячу раз. Значит, в один QR-код файл физически не помещается, и никакие ухищрения этого не изменят: сама символика кода имеет предел.
Отсюда единственный разумный выход. Раз в один кадр влезает мало, давайте нарежем файл на много маленьких кусочков — назовём их блоками — и покажем эти кусочки один за другим. Экран становится чем-то вроде проектора: он крутит слайд-шоу из QR-кодов, кадр за кадром, десятки раз в секунду. Камера на приёмнике снимает это слайд-шоу и собирает файл обратно из кусочков. Файл на пару мегабайт легко превращается в сотни таких кадров. Звучит просто — и вся сложность прячется в одном вопросе: а что делать, когда камера пропускает кадры? А пропускать она будет обязательно.
Наивный способ и почему он ломается
Самое очевидное решение приходит в голову сразу. Пронумеруем блоки: первый, второй, третий и так до последнего. Каждый кадр подписываем номером и показываем строго по порядку. Приёмник видит кадр с номером, кладёт кусочек на нужное место, ждёт следующий. Прошли все номера — файл собран. Красиво на бумаге.
Проблема в том, что камера, снимающая экран, — это не идеальный сканер, а участник в живых, неидеальных условиях. Кадр может смазаться, если рука дрогнула. Может засветиться бликом от лампы. Может попасть не в фокус в момент захвата. И есть ещё коварная штука: экран обновляется со своей частотой, камера снимает со своей, и эти два ритма не синхронизированы. В результате часть кадров камера просто не успевает поймать чётко — они проскакивают между её снимками.
Что происходит в нумерованной схеме, если потерялся, скажем, кадр номер 57? Приёмник получил блоки до 56-го, потом пропуск, потом 58-й, 59-й и дальше. Пятьдесят седьмого куска нет — а без него файл неполный. И единственный способ его добрать — дождаться, пока весь поток прокрутится по кругу и отправитель снова покажет кадр номер 57. То есть из-за одного смазанного квадратика мы ждём целый оборот всей ленты. А если на втором круге снова не поймали именно 57-й? Ждём третий круг. Каждая потеря — это отдельное ожидание, и потери накапливаются.
Особенно больно это бьёт по сценарию «телефон в руках». Никто не держит два телефона в идеально неподвижных тисках. Руки дрожат, угол плывёт, освещение меняется. Потери идут постоянно, и нумерованный поток превращается в мучительное топтание на месте: вроде почти всё поймали, но вечно не хватает трёх-четырёх конкретных кадров, за которыми приходится гоняться круг за кругом. Хрупко и медленно. Нужен принципиально другой подход, где потеря отдельного кадра вообще перестаёт быть событием.
Фонтанные коды: идея простыми словами
Вот тут на сцену выходят фонтанные коды — по-английски fountain codes, а конкретная популярная их разновидность называется Luby Transform, по имени придумавшего их математика. Название «фонтан» выбрано не случайно, и в нём вся суть.
Представьте настоящий фонтан и ведро, которое надо наполнить. Вы подставляете ведро под струю. Вам ведь совершенно неважно, какие именно капли в него попадут — вот эта капля или соседняя. Важно одно: поймать достаточно капель, чтобы ведро наполнилось. Пропустили часть брызг мимо? Не беда, фонтан бьёт дальше, подставляйте ведро и ловите следующие. Никто не ждёт «ту самую каплю номер 57».
Фонтанные коды устраивают ровно такой фонтан из данных. Ключевая идея: каждый кадр — это НЕ «блок номер 57». Каждый кадр — это смесь, комбинация случайно выбранного набора исходных блоков файла. Отправитель берёт, допустим, блоки 3, 12 и 40, определённым образом сплавляет их в один пакет и показывает как кадр. Следующий кадр — смесь блоков 1, 5, 12 и 88. Следующий — смесь двух других. И так далее. Причём таких комбинаций отправитель может нагенерировать практически бесконечно: сочетаний огромное множество, поток никогда не заканчивается и не повторяется по кругу. Это и есть фонтан — он бьёт без остановки.
Как именно блоки «сплавляются» в смесь? Через операцию, которая называется XOR, или «сложение по признаку различия». Не пугайтесь слова — на бытовом уровне это проще, чем кажется, и работает как знакомая всем арифметика восстановления по остатку.
Возьмём житейскую аналогию. Допустим, я говорю вам: сумма двух чисел равна 10. Если я потом назову одно из чисел — скажем, 7, — вы мгновенно вычислите второе: 10 минус 7, то есть 3. Зная комбинацию (сумму) и один из её элементов, вы находите недостающий. XOR ведёт себя похожим образом: это обратимое «смешивание». Если у вас есть смесь нескольких блоков и вы уже знаете все блоки в этой смеси, кроме одного, вы вычисляете этот последний неизвестный блок — «вычитаете» из смеси всё известное, и остаётся искомое. Именно на этом свойстве держится вся сборка файла на приёмнике.
Как приёмник собирает файл из любых кадров
Теперь соберём картину со стороны приёмника — того телефона, что смотрит камерой на мерцающий экран. Он ловит кадры-смеси в том порядке, в каком получилось. Какие-то кадры смазались и пропали — и это, в отличие от нумерованной схемы, совершенно не важно. Он просто ждёт следующие капли из фонтана.
Разгадывание идёт по цепочке, и запускается оно с самых простых кадров. Иногда фонтан выдаёт кадр, который является смесью всего одного блока, — то есть, по сути, чистый блок сам по себе. Такой кадр приёмник разгадывает сразу: вот он, готовый кусочек файла, кладём на место.
Дальше начинается самое интересное — цепная реакция. Как только приёмник узнал какой-то блок, он идёт по всем уже пойманным смесям и «вычитает» этот известный блок оттуда. Помните аналогию с суммой 10 и числом 7? Была у нас смесь из двух блоков, один мы теперь знаем — значит, «вычитаем» его и получаем второй блок в чистом виде. А этот новорождённый блок, в свою очередь, упрощает ещё какие-то смеси, где он участвовал. Те упрощаются, выдают новые чистые блоки, те упрощают следующие смеси — и так лавиной, пока не разгаданы все кусочки файла.
Сколько кадров надо поймать, чтобы лавина гарантированно прошла до конца? Вот красивая часть: чуть больше, чем было исходных блоков. На практике закладывают примерно на 15 процентов больше. Если файл нарезан на 200 блоков, поймать нужно порядка 230 кадров — любых, в любом порядке. Не «именно эти 200», а «любые 230 из бесконечного фонтана». Эта небольшая надбавка — плата за удобство: она гарантирует, что цепная реакция запустится и не застрянет.
Вот почему потеря кадров перестаёт быть драмой. Пропустили смазанный кадр — фонтан выдаст следующий, ничем не хуже. Нет никакого «того самого» кадра, за которым надо гоняться по кругу. Приёмник просто копит капли, пока их не наберётся достаточно, и в этот момент файл складывается целиком. Именно поэтому мерцающий квадрат так спокойно переживает дрожь рук, блики и рассинхрон частот: ему нужно количество, а не конкретика.
Последний штрих — проверка целостности. Когда файл собран, приёмник считает от него хэш, например по алгоритму SHA-256, и сверяет с эталоном, который отправитель заложил заранее. Хэш — это короткий «отпечаток» содержимого: совпал отпечаток, значит собранный файл в точности равен исходному, ни один бит не переврался. Не совпал — где-то была ошибка, и файл честно бракуется, а не выдаётся битым.
Какая реальная скорость и потолок
Будем честны: это не про скорость, это про надёжность там, где других вариантов нет. Реальная пропускная способность передачи телефон в телефон через мерцающие QR-коды — порядка 128 КБ/с. Если устройства зафиксированы устойчиво, на штативе или подставке, а не в дрожащих руках, выходит чуть выше, потому что меньше кадров теряется и меньше приходится добирать. Но порядок именно такой — сотня с небольшим килобайт в секунду.
Разумный потолок на одну передачу — около 64 МБ. Технически можно и больше, но время растёт, а вместе с ним растёт и вероятность, что кто-то сдвинет телефон, закроет камеру или устанет держать. Поэтому канал этот честнее считать средством для небольших файлов: документ, ключ, конфигурация, компактный архив, фотография. Гнать через мерцающий экран многогигабайтный дистрибутив никто в здравом уме не станет — это займёт неприлично долго.
Держите ожидания трезвыми: раздача фильма за секунды — это не сюда. Сила метода не в мегабитах, а в том, что он работает вообще без всякой общей сети между устройствами, по одному лишь свету от экрана к камере. За эту независимость и платим скоростью.
Где это применяют на практике
Ниша у метода узкая, но там, где он к месту, заменить его почти нечем. Общий знаменатель всех сценариев один: между устройствами нет и не должно быть сетевого соединения, а данные передать надо. Свет и камера остаются последним общим языком.
| Сценарий | Почему подходит |
|---|---|
| Передача в изолированный контур | В защищённый сегмент намеренно нет ни сети, ни разрешённых носителей. Оптический канал «экран — камера» переносит данные, не создавая сетевого моста и не нарушая изоляцию. |
| Снятие данных с оборудования без сети | Прибор, станок или устаревшая система умеют показать что-то на дисплее, но не подключены к сети. Их экран становится источником, камера — приёмником, файл уезжает без проводов. |
| Два устройства без общей сети | Разные экосистемы, нет общего Wi-Fi, Bluetooth заблокирован политикой. Мерцающий QR не требует сопряжения и учётных записей — навёл камеру и получил файл. |
| Работа офлайн в поле | Нет связи вообще: удалённая местность, подвал, экранированное помещение. Двум телефонам достаточно видеть экраны друг друга, чтобы обменяться данными. |
Если тема пограничных каналов вам интересна, у меня есть смежные разборы: про передачу файлов в изолированный контур и про то, как снять данные с оборудования без сети. Там те же принципы разложены под другими углами.
Частые вопросы
Почему нельзя просто засунуть весь файл в один QR-код? Потому что символика QR имеет жёсткий предел ёмкости — порядка пары килобайт полезных данных на код, а для уверенного считывания в движении и того меньше. Файл в мегабайты в тысячу раз больше этого потолка, так что дробление на кадры неизбежно.
Что будет, если камера пропустит половину кадров? Ничего страшного — просто передача займёт дольше. Фонтан бьёт бесконечно, приёмнику нужно набрать достаточное количество любых кадров, а не конкретные. Пропущенные капли замещаются следующими, ждать «тот самый» кадр не нужно.
Откуда берётся надбавка в 15 процентов и обязательна ли она? Это запас, который гарантирует, что цепная реакция разгадывания запустится и дойдёт до конца, а не застрянет на полпути. Комбинации случайные, поэтому чуть-чуть лишних кадров страхуют от неудачного расклада. Без надбавки сборка иногда не сходилась бы.
Как приёмник понимает, что собрал файл без искажений? Он считает хэш собранного файла, например SHA-256, и сверяет с эталонным отпечатком от отправителя. Совпало — файл в точности исходный. Не совпало — что-то перевралось, и такой результат отбраковывается, а не выдаётся как якобы верный.
Можно ли так передать большой архив на гигабайты? Технически поток бесконечный, но на практике потолок разумно держать около 64 МБ на передачу при скорости порядка 128 КБ/с. Гигабайты займут слишком долго, а чем дольше держишь камеру на экране, тем выше шанс, что что-то сдвинется. Метод для небольших файлов.
Коротко о главном
Мерцающий QR-код — это не фокус, а аккуратная инженерия поверх честного ограничения. В один кадр влезает мало, поэтому файл режут на сотни блоков и показывают потоком. Наивная нумерация кадров ломается на первой же потере — приходится гоняться за конкретными квадратиками по кругу. Фонтанные коды снимают эту боль элегантным ходом: каждый кадр становится смесью случайных блоков, поток бьёт бесконечно, а приёмнику достаточно поймать любые кадры числом чуть больше, чем было блоков. Потеря отдельного кадра перестаёт что-либо значить — ловим следующую каплю. Цепная реакция на основе обратимого смешивания XOR разгадывает блок за блоком, а хэш в конце подтверждает, что файл собран без единой ошибки.
Такие штуки я применяю в проектах, где сети нет по замыслу или по обстоятельствам: изолированные контуры, оборудование без подключения, обмен между устройствами в поле. Медленно, зато работает на одном лишь свете от экрана к камере. Если у вас есть похожая задача или просто хочется обсудить, где такой канал уместен, — напишите мне в Telegram, разберёмся вместе.
Что я делаю по защищённой передаче данных
- Канал переноса данных без сети и флешек
- Шифрование и журналирование передач
- Приём данных прямо в 1С / CRM / АСУ
- Развёртывание в закрытом контуре под ключ
Бесплатно: чек-лист «Готов ли ваш бизнес к 152-ФЗ»
12 пунктов, которые проверяют готовность за час: данные, согласия, уведомление в РКН, локализация, защита. Отметьте, что уже сделано, и увидите дыры, за которые сейчас штрафуют.
Готовы обсудить вашу задачу?
Бесплатная консультация — разберём, как внедрить это в вашем бизнесе под ключ. Без форм, пишите напрямую.
Разборы, кейсы и практика — без воды. Выходит регулярно, читать 3–5 минут.


