Сидел я значится между двумя ноутбуками, на одном из которых играла музыка. Динамики старые и стерео выдают в неприкрытом моно. Между звуками барабанов и синтезаторов в моей голове прозвучала мысль — «А давай‑ка одновременно включим музыку на двух ноутбуках, получу ли я музыкальное наслаждение?» — Получил это музыкальное наслаждение я аж целых три года назад, тогда же появилась идея оформить сеё чудо в виде приложения, которое бы захватывало системный звук устройства и раздавало бы его на динамики любых других устройств (Компьютеры, планшеты, телефоны...), включая устройство захвата.
Первая попытка
Начал я с трехступенчатой технологии Забрал - Отдал - Помолился
1. Забирал AudioChunk-и из аудиокарты Забрал
2. В сухом виде отдавал по WebSocket-у на фронт Отдал
3. AudioChunk-и отдавались Audio-элементу Помолился
Задержка была самая минимальная из всех используемых мною технологии, но вкладка в браузере, в которую беспощадно заливали мегатонны незакомпресированных, незасинхронизированных байт не щадила слабые устройства, из-за чего на телефоне высвечивалось предложение закрыть приложение браузера и попробовать другой способ передачи аудио

Вторая попытка
В рамках профессиональный деятельности познакомился с WebRTC, технология которая напомнила мне о моей заброшенной к этому моменту идее - Передача медиа-траффика между подписчиком и зрителем с наименьшей задержкой. Тогда я решил выйти за рамки и интегрировать WebRTC в свой личный проект. Использовал WebRTC Native библиотеку на C++, чтобы создавать RTCPeerConnection на том же уровне, на котором я забираю звук. По концепции получилась SFU сессия, где один участник, в виде аудиокарты устройства на котором запущено приложение, раздает звук всем участникам, подключающихся через фронт. Основная задача стояла в том, чтобы использовать WebRTC не только как передачу с низкой задержкой, но и засинхронизировать звук между участниками. Архитектура выглядела следующим образом
Создается
loopback, который устанавливается как дефолтный аудиовыход, за счет чего получает все звуки устройстваMASTER RTCPeerConnection, который создается при запуске приложения как мастер-пир, забирает звук сloopbackи с которым все создают соединения и получают от негоaudio,timestamp,syncTimeLOCAL RTCPeerConnection, который также создается при запуске приложения, устанавливает соединение с мастер-пиром и перенаправляет аудио-данные на физическую аудиокарту, это шаг позволяет устройству поставляющего звук, играть его синхронно с другими устройствамиBROWSER RTCPeerConnection, который создается
Выглядит просто, но настроить ее оказалось невозможно. Несмотря на то, что каждый участник получал чанки, с одинаковым syncTime для его проигрыша, рассинхрон был жуткий. Не помогала буферизация и принудительная задержка, чтобы браузер успел все обдумать и вовремя проиграть чанк.
Финальная попытка
Нашел готовый велосипед, модифицировал под свои хотелки и натянул на дизайн собственной разработки.

Snapcast - сервер, который раздает звук в идеальной синхронизации, остается только его кормить
Архитектура вышла следующей:
LOOPBACK- Создается виртуальный аудиовыход, который устанавливается как дефолтный в системеROUTER- Перенаправляет звук сLOOPBACKнаSNAPCASTSNAPCAST- Сервер, который раздаетLOCAL SNAPCLIENT- Создается на устройство, где запущено приложение, подключается к SNAPCAST и перенаправляет звук на физические динамикиBROWSER SNAPCLIENT- Любое устройство, подключившееся к приложению через фронт
Приложение позволяет превратить любое устройство в колонку, находящееся в той же локальной сети и имеющее возможность открывать вкладки в браузере.

