Icecast2, Ices0 или как сделать?

Этой записью хочу открыть серию публикаций о нюансах настройки различных сервисов на *nix системах. Сильных отличий в настройке на FreeBSD или Linux нет.

И первая запись будет посвящена конфигурационному файлу Icecast2 и всему остальному, что с этим будет связано.

unnamed

Icecast2 представляет из себя сервер потокового вещания. Я его использую для трансляции mp3 файлов с помощью Ices0.

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

Для подключения диджея нам нужно просто организовать 2 потока, основной и резервный. На резервном потоке постоянно транслируется наша музыкальная программа, которую мы организовали с помощью Ices0.

Обратим внимание на секцию в конфигурационном файле icecast2, которая делает возможной нашу задумку и решает одну из задач:

<!-- В эту точку монтируется поток вещания для диджея -->
/online.mp3
100
3600
<!-- Файл intro должен находиться в каталоге web root icecast2 -->
/intro.mp3
<!-- В эту точку монтируется основной музыкальный поток -->
/music.mp3
1
1

Ваши слушатели и диджей подключаются к Вашему серверу через url http://your.station.muz/online.mp3.m3u
Когда диджей "активен", у него идет эфир, то клиенты слышат то, что он вещает, как только он отключится, то сразу в эту точку монтирования пойдет поток из точки /music.mp3

Все вновь подключенные клиенты будут слышать в начале файл intro.mp3, а затем будут слышать основной эфир радио.

Теперь как настроить Ices0 для того, чтобы он мог "гнать" поток в Icecast2

localhost
8000
<!-- должен совпадать с паролем из секции authentication конфигурационного файла icecsat2 -->
123
http
<!-- Из этой точки Icecsat2 читает основной музыкальный поток -->
/music.mp3
<a href="http://localhost/">http://localhost/</a>
0
128
1
2

Здесь мы организовали поток на localhost с битрейтом 128, который получается "на лету". Т.е. вы можете заставить Ices0 читать файл с меньшим или большим битрейтом, он его конвертирует в 128 и выдаст этот поток в эфир.

На этом почти все, но осталась задача с почасовой отбивкой. Она организуется с помощью скрипта написанного на перл или питоне. Что нам это дает? Основное преимущество - это динамическое изменение плейлиста. Теперь мы сможем добавлять новые или удалять старые файлы mp3 и нам не нужно будет перезапускать сервис Ices0.

Для начала вносим изменение в конфигурацию Ices0, в раздел Playlist

<!-- Эти строчки нам больше не нужны
playlist.txt
1
builtin
Вместо них будет эта -->
perl
ices
5

Теперь идем в каталог /usr/local/etc/modules Здесь должен лежать файл ices.pm - скрипт управления плейлистом. Он должен лежать здесь и обязательно иметь название ices.pm. (хотя, если "собирать" этот сервис в "ручную", скорее всего можно указать свои параметры, но сейчас не об этом).

в начале файла добавим два модуля

>use MP3::Info;
use Math::Random;

Дальше нас будет интересовать только ф-ция "ices_get_next"

Привожу ее листинг с комментариями:

sub ices_get_next {
  # определяем время когда ices0 обращается к скрипту
  my ($s, $m, $h, $day, $month, $year) = ( localtime )[0,1,2,3,4,5];
  $date = sprintf ("%04d-%02d-%02d %02d:%02d:%02d", $year+1900, $month+1, $day, $h, $m, $s);
  # Для отбивки каждого часа проверяем не наступил ли тот самый момент, собственно поэтому время нам и нужно
  if ($m == 59 &amp;&amp; $s &gt; 54) {
    $music = "/var/ices/curanty.mp3";
    # Проверяем на месте файл или его нет. Если его нет, то играем основную программу
    if (-e $music) {
      return (( $music )[0]);
    }
    else {
      # смотрим ниже
      track();
    }
  }
  else {
    # смотрим ниже
    track();
  }
}

# Здесь у нас и обрабатывается основная музыкальная программа
sub track {
  $num=`/bin/ls /var/ices/muz/*.mp3 | /usr/bin/wc -l`;
  @music=`/bin/ls -1 /var/ices/muz/*.mp3`;
  $oldplay = $play;
  # Выбираем случайным образом номер файла
  $play=int(random_uniform(1, 0, $num));
  # Если вдруг нам так повезло и повторно выпал номер играющего файла, то попытаемся снова выбрать случайный файл
  if ($oldplay == $play) {
    $play=int(random_uniform(1, 0, $num));
  }
  chomp $music[$play];
  # Достаем TAG из mp3 файла
  my $tag = get_mp3tag($music[$play]) or die "no";
  if ($tag != "no") {
    $artist_name = $tag-&gt;{ARTIST};
    $songtitle = $tag-&gt;{TITLE};
  }
  else {
    # Если нет TAG в файле
    $artist_name = "My Radio";
    $songtitle = "Welcome!";
  }
  return $music[$play];
}

Обратим внимание еще на две функции и что они возвращают:

sub ices_get_metadata {
  return $artist_name." - ".$songtitle;
}
sub ices_get_lineno {
  return $play;
}

И в завершении файла возвращаем 1;

Все. Работа с конфигурационными файлами покончена. Теперь нам нужно каким то образом заставить Ices0 в нужное время прочитать получившейся перловый скрипт.

Здесь к нам на помощь приходит cron. Добавим в задания следующую строчку:

>59      *       *       *       *       /path/ices0-reload.sh

И создадим файл ices0-reload.sh, который будет выполнять оставшуюся "грязную" работу.
Файл содержит несколько строк:

# засыпаем до конца часа, почти на минуту
sleep 55
# определяем номер PID-а
PID=`cat /var/log/ices0/ices.pid`
# Перегружаем Ices0 или заставляем его перейти к следующей композиции.
/bin/kill -USR1 ${PID}

Вот на этом с организацией, почти профессиональной онлайн радио станции, мне хотелось бы закончить.

Да! И чуть не забыл. В начале файла для почасовой отбивки я вставил 5 сек. тишины, чтобы нормально отработал 5 секундный Crossfade

UPD
Если Вы будете использовать директиву: 3600
в конфигурационном файле Icecast2 в разделе , то клиенты будут отключены от радио ровно через час.
Увеличивать или уменьшать не имеет смысла, не работает. Строчку лучше удалить, если не нужно отключать клиентов.

UPD2
Для анализа работы программ включите логирование у сервисов:
Добавьте в конфиг Ices0 строки

1
1
/var/log

В конфиг Icecast2

access.log
error.log
<!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
2
10000