Стабильное вещание IPTV через VLC
С IPTV пришлось познакомиться поближе, когда в компании интернет-провайдера где я работал, решили организовать вещание IPTV. Выяснилось, что вещание осуществляется в linux и программой vlc, которая по слухам единственная в своем роде. Была установлена тарелка и DVB-карта SkyStar2, которая предположительно должна была без проблем заработать в linux'e. Так и было со всеми новыми машинами, где была установлена ubuntu server. На Debian заработало только после обновления ядра. Настройка прошла относительно легко, благо на то время уже было несколько статей на эту тему и документация к vlc была тоже очень полезна. После запуска начались некоторые проблемы с пропаданием звука и видео на каналах и другие проблемы, описанные ниже. После их появления были написаны скрипты, устраняющие эти проблемы. Сейчас на них работает 3 сервера с 5 dvb-картами в общей сложности и вещается 23 канала с 3х спутников. Под катом — перечисление проблем, которые могут появиться после запуска вещания IPTV через VLC в Linux и то как с ними бороться. Бороться будем с помощью bash.
Первоначальная настройка и запуск вещания IPTV хорошо описана в статье «Прием спутниковых тв каналов в linux».
Основные проблемы при вещании IPTV: утечка памяти и пропадание видео или звука в одном из каналов.
Утечка памяти
Проблема утечки памяти решается либо 1) перезагрузкой демона vlc раз в день или больше по необходимости через crontab либо 2) периодической проверкой свободной памяти и перезагрузкой демона при уменьшении сводной памяти до определенного значения.
1)
crontab:
# Перезагружаем vlc в 3 часа ночи 0 3 * * * /usr/local/script/vlc_regular_reload.sh
vlc_regular_reload.sh:
#!/bin/bash echo "`date`;VLC regular reload" >> /var/log/vlc/vlc_restart.log /usr/local/script/vlc_starter.sh
chmod +x /usr/local/script/vlc_regular_reload.sh
2)
crontab:
#Проверяем каждые 5 минут */5 * * * * /usr/local/script/memory_check.sh
memory_check.sh:
#!/bin/bash #Получаем переменную свободной памяти free_mem=$(free -m | grep ^M | awk -F' ' '{print $4 }') #Указываем предельное значение свободной памяти mem_limit=16 #Если свободной памяти меньше или же она совпадает со значением предела то происходит перезагрузка демона if [ $free_mem -le $mem_limit ] then echo "`date`;Memory Overload" >> /var/log/vlc/vlc_restart.log /usr/local/script//vlc_starter.sh fi
chmod +x /usr/local/script/memory_check.sh
Пропадание видео или звука
Для решения проблемы необходимо периодически проверять наличие видео и звука в каналах.
Реализация скриптов на примере вещания двух каналов. Вещание идет в два потока — оригинального и сжатого. Иногда помогает перезагрузка проблемного канала, но иногда необходима полная перезагрузка демона.
Для работоспособности скрипта необходимо запустить следующие команды:
cp /usr/bin/vlc /usr/bin/vlc2 touch /home/desecho/vlc/run.sh chmod +x /home/desecho/vlc/run.sh
Скрипт запуска и перезагрузки каналов:
vlc_starter.sh:
#!/bin/bash echo "`date`;Reloaded" >> /var/log/vlc/vlc_restart.log #Выключаем все запущенные vlc процессы killall -9 vlc #Запуск вещания vlc --no-stats -vvv --color --daemon --ttl 12 --ts-es-id-pid --dvb-adapter0 --programs=13,14 dvb: --dvb-frequency=10995000 --dvb-srate=20000000 --dvb-voltage=13 --sout'#duplicate{dst=std{access=http,mux=asf,url=0.0.0.0:8001},select="program=13",dst=std{access=http,mux=asf,url=0.0.0.0:8002},select="program=14"}' #Ждем несколько секунд для инициализации вещания sleep 5 #Вещаем сжатый поток в 820Kbit/s видео и 70Kbit/s аудио vlc http://127.0.0.1:8001 -vvv --daemon --color --sout '#transcode{vcodec=mp4v,vb=820,scale=1,acodec=mpga,ab=70,channels=1}:duplicate{dst=std{access=http,mux=asf,url=0.0.0.0:8011}' vlc http://127.0.0.1:8002 -vvv --daemon --color --sout '#transcode{vcodec=mp4v,vb=820,scale=1,acodec=mpga,ab=70,channels=1}:duplicate{dst=std{access=http,mux=asf,url=0.0.0.0:8012}'
chmod +x /usr/local/script/vlc_starter.sh
Скрипт проверки каналов:
vlc_restarter.sh:
#!/bin/bash #Запись канала #$1 - id порта сжатого канала function rec { #Запись видео b="vlc2 http://127.0.0.1:80$1 -vvv --color --daemon --noaudio --sout '#duplicate{dst=std{access=file,mux=asf,dst="/home/desecho/vlc/0$1.avi"}}'" #Создание временного исполняемого файла - такой способ необходим из-за наличия в строке запуска vlc различных ковычек echo $b > /home/desecho/vlc/run.sh /home/desecho/vlc/run.sh #Запись аудио b="vlc2 http://127.0.0.1:80$1 -vvv --color --daemon --novideo --sout '#duplicate{dst=std{access=file,mux=asf,dst="/home/desecho/vlc/0$1.mp3"}}'" echo $b > /home/desecho/vlc/run.sh /home/desecho/vlc/run.sh sleep 5 killall -9 vlc2 } #Полная перезагрузка vlc #$1 - имя канала function reload { echo "`date`;$1 - Reload" >> /home/desecho/vlc/logs/vlc_restart.log /home/desecho/vlc/vlc_starter.sh } #Проверка канала #$1 - id порта сжатого канала #$2 - имя канала function check { #Смотрим размер видео y="$(du /home/desecho/vlc/0$1.avi | grep -oE --regexp='[0-9]+' | sed -n '1p')" #Проверяем пустой ли файл if [ $y -gt 10 ] ; then x011=1 else x011=0 fi #Смотрим размер аудио y="$(du /home/desecho/vlc/0$1.mp3 | grep -oE --regexp='[0-9]+' | sed -n '1p')" if [ $y -gt 10 ] ; then x012=1 else x012=0 fi echo " $2 - $x011 $x012" >> /home/desecho/vlc/logs/vlc_restart.log let x01=x011+x012 #Если отсутствует звук или видео, то переменная z=0 if [ $x01 != 2 ] then z=0 else z=1 fi } #Полная проверка #$1 - id порта сжатого канала #$2 - имя канала function check_full { #Запись канала rec $1 #Проверка на отсутствие видео или звука check $1 $2 #Если видео и звук в норме то помечаем переменную z канала if [ $z = 1 ] ; then z[$1]=1 fi } #Перезагрузка канала #$1 - id порта сжатого канала #$2 - имя канала #$3 - id порта исходного канала function restart { echo "`date`;$2 - Restart" >> /home/desecho/vlc/logs/vlc_restart.log #Выключение неработающего канала a="$(ps -C vlc -o '%p%a' | grep "http://127.0.0.1:80$3 -vvv --daemon --color --sout" | grep -oE --regexp='[0-9]+' | sed -n '1p')" kill $a #Перезапуск вещания канала b="vlc http://127.0.0.1:80$3 -vvv --daemon --color --sout '#transcode{vcodec=mp4v,vb=820,scale=1,acodec=mpga,ab=70,channels=1}:duplicate{dst=std{access=http,mux=asf,url=0.0.0.0:80$1}'" echo $b > /home/desecho/vlc/run.sh /home/desecho/vlc/run.sh } #Перезагрузка канала #$1 - id порта сжатого канала #$2 - имя канала #$3 - id порта исходного канала function fix { #Если нет видео или звука на канале то перезагружается канал if [ ${z[$1]} != 1 ] ; then restart $1 $2 $3 sleep 5 #повторная проверка check_full $1 $2 fi #Если по прежнему нет звука то перезагружаем VLC полностью if [ ${z[$1]} != 1 ] ; then reload $2 exit fi } #Присваиваем значение 0 по умолчанию переменным z каналов z[11]=0 z[12]=0 #Проверка канала на отсутствие видео/звука check_full 11 perviy #Перезагрузка канала при проблемах с видео/звуком fix 11 perviy 01 check_full 12 rossiya fix 12 rossiya 02 echo "`date`;DVB - Status: Stable" >> /home/desecho/vlc/logs/vlc_restart.log
chmod +x /usr/local/script/vlc_restarter.sh
Осталось добавить запись в crontab:
*/5 * * * * /usr/local/script/vlc_restarter.sh
В итоге получаем систему, которая автоматически восстановит вещание каналов при пропадании видео или аудио. Все действия скриптов восстановления вещания будут писаться в лог файл.
Удачного вещания!
Для нейтрализации утечки памяти можно собрать vlc без флага «optimisememory» либо попробовать обновить vlc, см. статью
«Вещание видеофайлов с помощью VLC multicast. С теорией».
http://habrahabr.ru/post/66351/