РАЗРАБОТКА

InfoQ разобрал, как собрать быстрый аудиостриминг в React Native

3,5 секунды на чанк и работа при 500 кбит/с: InfoQ разобрал, как построить мобильный аудиостриминг с beat-aligned переключениями.

✍️ Редакция iTech News | 10.07.2026 | ⏱ 5 мин | Источник: InfoQ
📜

InfoQ 9 июля 2026 года опубликовал подробный разбор того, как собрать мобильный аудиостриминг для iOS и Android, если пользователи листают треки быстрее, чем сеть успевает моргнуть. Кейc не про очередной плеер для фоновой музыки, а про систему, где переключение должно попадать в такт, не щелкать на стыках и работать даже примерно при 500 кбит/с.

Материал написал Владислав Мельниченко, инженер, который описал архитектуру аудиосистемы для мобильного beat-discovery-приложения в стартапе Colossal, сообщает InfoQ. Сценарий там нетривиальный: артист листает персональную ленту битов, может перескакивать между секциями внутри трека и фиксировать одну секцию, чтобы слушать именно ее при свайпе между разными композициями. Для такого UX обычная логика «нажал play, получил поток, живем дальше» не годится. Каждое действие пользователя превращается в жесткое требование к таймингу, буферизации и декодированию.

Ключевая проблема в том, что пользователь тратит на один бит всего 2-3 секунды, а средний файл весит около 3 МБ. Полная предзагрузка следующего трека в таком сценарии выглядела бы красиво только на бумаге: по расчетам автора, только на аудио понадобилось бы около 8 Мбит/с устойчивой пропускной способности, а с учетом декодирования, сетевой нестабильности и прочих ресурсов приложения практическая потребность вырастала примерно до 14 Мбит/с. Для продукта, который должен держаться на слабом 3G-классе соединения, это не просто много, а примерно в 16 раз больше доступного бюджета. Вывод довольно прозаичный: full download можно сразу вынести в корзину вместе с надеждой, что мобильная сеть все простит.

Стандартные аудиоплееры и потоковые протоколы тоже не спасли. По словам Мельниченко, готовые React Native audio-библиотеки хорошо умеют линейное воспроизведение, но плохо подходят для мгновенных beat-aligned переключений. Даже если логика переключения в приложении верная, переход через RN bridge добавлял примерно 5-10 мс задержки плюс джиттер. Для обычного стриминга это терпимо, для ритмически точного перехода уже нет: пока callback долетает до JavaScript, аудио успевает уйти вперед. HLS и DASH тоже не вписались в задачу. Они заточены под последовательное воспроизведение и адаптивный bitrate, а не под постоянные скачки между секциями. Дополнительно мешает сама природа MP3: если стартовать декодирование с произвольной границы сегмента без контекста предыдущих фреймов, на входе легко получить слышимые артефакты.

Поэтому инженер выбрал другую схему: хранить один MP3-файл на трек и резать его не на физические сегменты, а на virtual chunks, то есть логические диапазоны байтов. Клиент получает компактный дескриптор чанков и вытягивает только нужные диапазоны через HTTP Range requests. Для первой версии системы был выбран MP3 с постоянным битрейтом. Причина не в любви к ретро-форматам, а в предсказуемости: проще анализировать фреймы, проще планировать чанки, проще управлять памятью на клиенте. Оптимальным размером чанка автор назвал примерно 3,5 секунды. Меньше значит больше запросов и накладных расходов, больше значит лишний трафик на фрагменты, которые пользователь, скорее всего, свайпнет до того, как они пригодятся.

Самый интересный технический кусок связан с декодированием. В статье отдельно объясняется, что MP3-фреймы не всегда декодируются полностью независимо из-за bit reservoir: корректный старт чанка может зависеть от данных из предыдущих фреймов. Чтобы не ловить щелчки и мусор на границах, для каждого не первого чанка в планирование добавлялись девять overlap-фреймов. Клиент декодировал чанк с этим warm-up-контекстом, а затем отбрасывал разогревающие сэмплы перед записью PCM в рабочий буфер. Это важная деталь для тех, кто до сих пор верит, что достаточно аккуратно нарезать байты, и кодек послушно сделает остальное. Не сделает.

Мобильный аудиостриминг в этой реализации вообще вынесен как можно дальше от JavaScript. Критически важную логику автор собрал в нативном C++-движке поверх Superpowered и завернул в native Expo module для iOS и Android. Дескрипторы хранились в компактном бинарном формате: по 12 байт на чанк, где отдельно записаны стартовый фрейм, число фреймов, стартовый байт и длина диапазона. Сетевой слой тоже не оставили на волю платформенных различий: для одинаковой работы Range-запросов, retry и ошибок на обеих ОС был выбран libcurl. На рантайме система делила работу между аудиопотоком, worker-потоками для сети и декодирования и отдельной управляющей логикой, которая планировала переходы и приоритеты предзагрузки. Идея простая: в audio callback нельзя тащить ни блокировки, ни выделение памяти, ни тяжелую координацию, если не хочется услышать это ушами.

Отдельного внимания заслуживает алгоритм prefetch. Вместо попытки скачать все подряд система строила детерминированный список наиболее вероятных следующих действий. Если у пользователя включен section lock, в приоритете оказывалась та же секция следующего бита; если нет, первой грузилась стартовая секция следующего трека. Далее шли соседние секции и соседние треки, а все остальное добиралось по остаточному принципу. Автор прямо пишет, что вероятностную модель можно было бы построить позже, но на раннем этапе продукта данных для нее не хватало. Для инженерной команды это трезвый вывод: сначала система должна быть объяснимой, отлаживаемой и полезной, а уже потом можно добавлять магию ML туда, где она действительно улучшает метрики, а не презентацию.

Практический смысл этой истории выходит далеко за пределы музыкального стартапа. Для русскоязычных мобильных команд это хороший разбор того, где заканчиваются возможности «готового плеера» и начинается настоящая системная инженерия: формат хранения, wire format, диапазонные запросы, декодирование, real-time constraints и UX тут связаны в один узел. Если продукт требует мгновенных переходов, синхронизации с ритмом или иной строгой реакции на действия пользователя, то мобильный аудиостриминг придется проектировать как цельную транспортно-воспроизводящую систему, а не как набор библиотек, склеенных мостом React Native. Открытый вопрос здесь не в том, можно ли повторить описанную архитектуру, а в том, сколько команд готовы признать, что ради хорошего аудио им придется лезть ниже привычного уровня абстракций.

Поделиться: Telegram X LinkedIn