Делаем медиаматрицу на коленке

в 12:45, , рубрики: diy или сделай сам, ffmpeg, freebsd, videolan, Работа со звуком, метки: , , ,

Зачастую возникает необходимость принять аудиосигнал с множества несвязанных устройств и выдать полученный сигнал на множество несвязанных акустических систем. Многие делают это через медиаматрицы BiAMP, Kramer в связке с управлением через Kramer,Palantir и т.д., но это оборудование стоит денег, а деньги есть не всегда.

Осмелюсь описать бюджетный вариант, который в базовом функционале не уступает вышеописанным продуктам. А с точки зрения управления может быть более гибок.
В проекте используется 15 медиасерверов(Windows 7) с каждого из которых идёт свой видеоряд отображаемый на плазмах и проекторах, а также аудиопоток сопровождающий видеоряд. Также имеется 3 акустические системы на которые аудиопоток теоретически можно вывести.

Немного о структуре.
Видеоряд транслируется из серверной комнаты через конверторы DVI и VGA сигналов на оборудование вынесенное от серверной на расстояние до 50 метров. Использование встроенных или выносных динамиков для трансляции аудиосигнала привело бы в какофонии и невозможности восприятия отображаемого контента. Для воспроизведения звука было решено разделить 15 медиасерверов на 3 группы и коммутировать аудиосигнал на 3 различных акустических системы территориально приближенных к плазмам и проекторам.

Так как переключением звука нужно управлять удалённо, значит нужно его где-то скоммутировать. Физическая коммутация вещь старая и надёжная, но мы живём в век цифровых технологий. Поэтому для коммутации звука мы будем использовать цифру, а чтобы получить цифровой поток, его нужно откуда-то взять.
Устанавливаем на компьютер драйвера виртуального аудио кабеля.
Данные драйвера представляют собой виртуальную звуковую карту в которую можно отправлять весь звук системы и снимать из неё звуковой поток в цифровом виде при помощи программ.
В нашем случае используется обычный ffmpeg.
Для получения списка аудиоустройств необходимо запустить ffmpeg со следующими ключами:


C:Progsffmpegbin>ffmpeg -list_devices true -f dshow -i dummy
[dshow @ 00000000020a77e0] DirectShow video devices
[dshow @ 00000000020a77e0]  "Logitech HD Webcam C310"
[dshow @ 00000000020a77e0] DirectShow audio devices
[dshow @ 00000000020a77e0]  "Микрофон (HD Webcam C310)"
[dshow @ 00000000020a77e0]  "CABLE Output (VB-Audio Virtual "

Устройством с которого мы будем снимать аудиопоток будет «CABLE Output (VB-Audio Virtual ».

В качестве устройства в которое будет выводиться весь звук системы будет «CABLE Input (Virtual Audio Cable)».
Делаем медиаматрицу на коленке

Запускаем ffmpeg и транслируем данные в сеть.


C:Progsffmpegbin>ffmpeg -re -f dshow -i audio="CABLE Output (VB-Audio Virtual " -f mp3 udp://239.1.1.1:5001
ffmpeg version N-49610-gc2dd5a1 Copyright (c) 2000-2013 the FFmpeg developers
  built on Feb  5 2013 13:26:02 with gcc 4.7.2 (GCC)
  libavutil      52. 17.101 / 52. 17.101
  libavcodec     54. 91.100 / 54. 91.100
  libavformat    54. 61.104 / 54. 61.104
  libavdevice    54.  3.103 / 54.  3.103
  libavfilter     3. 35.101 /  3. 35.101
  libswscale      2.  2.100 /  2.  2.100
  libswresample   0. 17.102 /  0. 17.102
  libpostproc    52.  2.100 / 52.  2.100
[dshow @ 00000000024de080] Estimating duration from bitrate, this may be inaccurate
Guessed Channel Layout for  Input Stream #0.0 : stereo
Input #0, dshow, from 'audio=CABLE Output (VB-Audio Virtual ':
  Duration: N/A, start: 270550.167000, bitrate: 1411 kb/s
    Stream #0:0: Audio: pcm_s16le, 44100 Hz, stereo, s16, 1411 kb/s
Output #0, mp3, to 'udp://239.1.1.1:5001':
  Metadata:
    TSSE            : Lavf54.61.104
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p
Stream mapping:
  Stream #0:0 -> #0:0 (pcm_s16le -> libmp3lame)
Press [q] to stop, [?] for help
size=     100kB time=00:00:06.37 bitrate= 128.5kbits/s

В любом месте сети Вы сможете слушать данный поток любым плеером(ffplay, vlc и т.д.). При желании можно менять кодек и битрейт.
Базовая настройка сделана, звук получили, драйвера растиражировали на 15 медиасерверов, получили 15 разных потоков, а теперь возникает вопрос «как же это слушать и коммутировать».
С точки зрения экономии железа и ресурсов можно воспользоваться единственной машиной с некоторым количеством аудиокарт выводящих звук на акустику. Сейчас в достаточной мере на рынке присутствуют различные малогабаритные USB аудиокарты. С точки зрения удобства использования дешевые карты аля CMEDIA не выгодны, потому не имеют внутри серийных номеров и определить какая карта после перезагрузки на какой канал стала работать невозможно. Я использовал карты «Creative SB Play!» которые дают довольно хороший звук и имеют возможность идентификации их в системе.
Т.к. при подключении USB audio картам присваиваются динамические номера, для devd был написан набор правил:


attach 50 {
        match "device-name"                     "uaudio[0-9]+";
        match "sernum"                          "[0-9A-Za-z]+";
        match "mode"                            "host";
        action "logger USB Audio S/N:$sernum is attached";
        action "ln -fs /dev/`head -n 1 /tmp/$device-name.dsp` /dev/$device-name.dsp";
        action "ln -fs /dev/$device-name.dsp /dev/uaudio.serial.$sernum";
        action "rm /tmp/$device-name.dsp";

        match "device-name"                     "uaudio[0-9]+";
        match "hubaddr"                         "[0-9]+";
        match "port"                            "[0-9]+";
        match "devaddr"                         "[0-9]+";
        action "echo $device-name at ugen.$port.$devaddr";
        action "ln -fs /dev/$device-name.dsp /dev/uaudio.ugen.$port.$devaddr";

};

attach 50 {
        match "device-name"                     "uaudio[0-9]+";
        match "sernum"                          "$";
        match "mode"                            "host";
        action "logger USB Audio without serial number is attached";
        action "ln -fs /dev/`head -n 1 /tmp/$device-name.dsp` /dev/$device-name.dsp";
        action "rm /tmp/$device-name.dsp";

        match "device-name"                     "uaudio[0-9]+";
        match "hubaddr"                         "[0-9]+";
        match "port"                            "[0-9]+";
        match "devaddr"                         "[0-9]+";
        action "echo $device-name at ugen.$port.$devaddr";
        action "ln -fs /dev/$device-name.dsp /dev/uaudio.ugen.$port.$devaddr";

};

attach 40 {
        match "device-name"                     "pcm[0-9]+";
        match "bus"                             "uaudio[0-9]+";
        action "logger Found $device-name at $bus. Saving.";
        action "echo -n $device-name | sed -E 's/pcm/dsp/' >> /tmp/$bus.dsp";
};

detach 40 {
        match "device-name"                     "uaudio[0-9]+";
        match "bus"                             "uhub[0-9]+";
        action "logger Dropping dsp symlink for $device-name";
        action "find /dev/ -type l -ls | awk '$$NF ~ /$device-name./ {print $(NF-2)}' | xargs rm";
        action "rm -f /dev/$device-name.dsp";
};

notify 50 {
        match "type"                    "DETACH";
        match "mode"                    "host";
        match "sernum"                  "[0-9A-Za-z]+";
        action "logger USB Audio S/N:$sernum detached";
};

который формирует симлинки на подключенное оборудование в виде "/dev/uaudio.serial.<SerialNumber>", в итоге список устройств выглядит вот так


lrwxr-xr-x  1 root  wheel  16 May 15 09:27 /dev/uaudio.serial.131014000129 -> /dev/uaudio1.dsp
lrwxr-xr-x  1 root  wheel  16 May 15 09:23 /dev/uaudio.serial.140210000BB2 -> /dev/uaudio0.dsp
lrwxr-xr-x  1 root  wheel  16 May 15 09:23 /dev/uaudio.ugen.5.6 -> /dev/uaudio0.dsp
lrwxr-xr-x  1 root  wheel  16 May 15 09:27 /dev/uaudio.ugen.5.7 -> /dev/uaudio1.dsp
lrwxr-xr-x  1 root  wheel  16 May 15 08:37 /dev/uaudio.ugen.5.8 -> /dev/uaudio2.dsp
lrwxr-xr-x  1 root  wheel   9 May 15 09:23 /dev/uaudio0.dsp -> /dev/dsp6
lrwxr-xr-x  1 root  wheel   9 May 15 09:27 /dev/uaudio1.dsp -> /dev/dsp7
lrwxr-xr-x  1 root  wheel   9 May 15 08:37 /dev/uaudio2.dsp -> /dev/dsp8

Что позволяет однозначно идентифицировать карту и акустическую систему которую она обслуживает.

С картами мы разобрались. Теперь очередь за вопроизведением всего этого счастья.
Формируем Playlist.xspf:


<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/" xmlns:vlc="http://www.videolan.org/vlc/playlist/ns/0/">
        <title>AudioStreams</title>
        <trackList>
                <track>
                        <title>TV 1</title>
                        <location>udp://@239.1.1.1:5001</location>
                        <extension application="http://www.videolan.org/vlc/playlist/0">
                                <vlc:id>1</vlc:id>
                                <vlc:option>input-repeat=-1</vlc:option>
                        </extension>
                </track>
                <track>
                        <title>TV 2</title>
                        <location>udp://@239.1.1.2:5001</location>
                        <extension application="http://www.videolan.org/vlc/playlist/0">
                                <vlc:id>2</vlc:id>
                                <vlc:option>input-repeat=-1</vlc:option>
                        </extension>
                </track>
                <track>
                        <title>TV 3</title>
                        <location>udp://@239.1.1.3:5001</location>
                        <extension application="http://www.videolan.org/vlc/playlist/0">
                                <vlc:id>3</vlc:id>
                                <vlc:option>input-repeat=-1</vlc:option>
                        </extension>
                </track>
        </trackList>
</playlist>

Копируем дерево lua vlc в /usr/local/www/vlc_http
Плейлист кладём в /usr/local/www/vlc_http/playlists/Playlist.xspf

теперь запускаем пачку cvlc в следующем конфиге:


vlc -vvv --sout-transcode-venc none --network-caching 50 --sout-mux-caching 50 -Aoss --oss-audio-device=/dev/uaudio.serial.131014000129 -I http --http-src=/usr/local/www/vlc_http --http-host=192.168.5.2 --http-port=8101 --http-password=mypass1 /usr/local/www/vlc_http/playlists/Playlist.xspf
vlc -vvv --sout-transcode-venc none --network-caching 50 --sout-mux-caching 50 -Aoss --oss-audio-device=/dev/uaudio.serial.140210000BB2 -I http --http-src=/usr/local/www/vlc_http --http-host=192.168.5.2 --http-port=8102 --http-password=mypass1 /usr/local/www/vlc_http/playlists/Playlist.xspf

Далее дело за малым. Взять примеры из http управлялки vlc и сделать свои страницы управления. Благо там сплошной jquery и javascript.
В основном дереве веб сервера формируем страницу где в iframe интегрируем управлялки каждым из поточных проигрывателей и открываем это дело через телефон или планшет. Вся остальная логика управления пишется на html и не представляет большой сложности.
Т.к. мы не завязаны на источники звука, это даёт огромную гибкость в управлении звуковыми потоками. Можно на всю акустику вывести один и тот же поток с любого медиасервера, можно поменять потоки местами и т.д.

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

Вот как-то так. Будут вопросы — пишите.
© Aborche 2014
Делаем медиаматрицу на коленке

Автор: aborche

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js