У меня есть радио. Проводач называется. Оно работает на основе интересного инструмента под названием «Liquidsoap». Документации по «жидкому мылу» в сети не очень-то и много: за исключением официального сайта и нескольких тредов на RadioTalk.ru я не видел вменяемых инструкций к этому достаточно мощному и удобному инструменту. В отличие от того же Qt, авторы Liquidsoap приводят достаточно скудные примеры, которые зачастую не отвечают на вопрос «да как, мать его, это сделать?!» Итак, запускаю цикл постов о Liquidsoap и решениях, применяемых мною на Проводаче для организации эфира.

В этом посте поговорим об организации живых трансляций живыми ведущими. О том, как их авторизовать, я рассказал здесь. Перед нами стоит две задачи: «врезать» ведущего в эфир (по умолчанию наполненный музыкой) и записать его эфир на память. Врезать ведущего следует плавно, нежно и медленно, чтобы слушателям не было больно, ну вы понимаете. Плавный же уход из эфира лежит на ведущем: сервер не успеет понять, что из него вышли, и сделать правильный кроссфейд. Так что, за резкое пропадание ведущим следует пивать дазды. Ну да что-то я отвлёкся.

Подключение ведущего и кроссфейд

Для того, чтобы пустить товарища говорящего на сервер, мы будем использовать так называемый harbor («гавань») — специальную сущность, «порт» в Liquidsoap. Харбор может быть разным, нас же интересует Icecast-совместимый порт, чтобы не переучивать ведущих, а просто выдать им новые данные.

Создадим харбор:

harbor = input.harbor("stream", port=1488, password="Y@3b4Ld3Ep4N@1")

Этой строчкой мы создали новый аудиопоток с названием harbor, представляющий собой HTTP-сервер, слушающий на порту 1488. Паролем для подключения будет комбинация символов «Y@3b4Ld3Ep4N@1», её-то мы и будем выдавать пользователям. Рекомендую всем делать именно такие, сложные пароли, чтобы не случалось казусов.

Итак, аудиопоток (в терминологии «мыла» это источник, source) создали, теперь нужно подключить его к основному потоку. Предполагается, что вы уже настроили вещание музыки, и у вас есть что-то типа такого:

# Making a music stream
# 1. Set up where to take music
music = mksafe(audio_to_stereo(playlist(reload=86400, prefix="replay_gain:", "/path/to/music")))
# 2. Shuffle the tracks
music = random([music])
# 3. Set up crossfade
music = crossfade(start_next=2.,fade_out=3.,fade_in=1.,music)

Создаём канал и назовём его radio, именно он будет выведен в эфир. НО! Сперва объявляем функцию, необходимую нам для того самого плавного вхождения. Честно скажу, что эту функцию я внаглую стырил с другого ресурса, правда, уже не помню откуда. Возгуглите по частям, если интересно.

def src_Xfade(a,b)
  add(normalize=false,
      [ sequence([ blank(duration=2.),
                   fade.initial(duration=2.,b) ]),
        fade.final(duration=2.,a) ])
end

А теперь уже создаём канал:

radio = fallback(track_sensitive=false, transitions = [src_Xfade, src_Xfade], [strip_blank(max_blank=30., harbor), music])

Что оно делает:
fallback — собственно функция переключения, встроена в Liquidsoap. Если эфиров нет — играет музыка.
Параметр track_sensitive в данном случае настроен в false для того чтобы переключение срабатывало всегда (иначе не пустит в эфир)
transitions как раз задаёт функцию, с помощью которой будет выполнено плавное переключение между музыкой и эфирами
strip_blank запрещает ведущим молчать и посылать в эфир целое ничего: если в течение 30 секунд ведущий молчит, его заменяет музыка.
Последний же параметр указывает тот самый наш harbor, к которым цепляются ведущие.

Итак, ведущего в эфир пустили. Но мы же строим ламповое радио, и каждый момент эфира хотелось бы запомнить надолго. Поэтому приступаем к записи эфиров.

Запись эфиров

Тут всё достаточно просто. Liquidsoap имеет в своём арсенале объект output, который умеет выводить звук куда угодно: в Icecast, в Shoutcast, в звуковую карту, и, конечно же, в файл. Последнее нам и нужно:

output.file(%mp3(bitrate=128),
        "/path/to/archive/live_%d_%m_%Y-%H_%M_%S.mp3",
        reopen_on_metadata=false,
        fallible=true,
harbor)

О параметрах:
Первая часть указывает, что мы кодируем в формате MP3 с битрейтом 128 кбит/с
Далее идёт путь к файлу, в который следует писать. Имя файла формируется в виде даты и времени начала эфира, подробнее в документации.
reopen_on_metadata, установленный в false, запрещает «мылу» переоткрывать файл при смене дорожек у ведущего.
fallible говорит о том, что этот источник может пропасть, а значит нужно правильно закрыть файл при отключении ведущего.
И, наконец, указываем поток, который следует записать. Всё просто!