Koledze ostatnio zachciało się radia internetowego. Z racji tego iż powoli rozglądałem się za jakimś komercyjnym hostingiem, dogadałem się z nim i rozpocząłem współpracę. Podstawowy problem takiego radia, to brak czasu na ciągłe audycje na żywo. Oczywiście jest sobie IceS i można mu dołożyć skrypt w perlu w celu odtwarzania muzyki ciągle, ale rozwiązanie to jest mało estetyczne.
Po pierwsze, IceS cały czas próbuje się podłączyć do serwera i po paru próbach wychodzi. Więc trzeba go odpalić w pętli. Aby przerwać jego działanie i rozpocząć audycję na żywo należy go wykopać, czego skutkiem najczęściej jest przerwa w strumieniu. Trochę pogrzebałem w sieci i znalazłem coś cudownego…
Cudo to się nazywa „Liquidsoap”. Pomijając fakt że jest napisany w Ocaml, jest to kawałek softu który naprawdę potrafi zrobić wiele rzeczy. Co to potrafi? Zaczynając od odtwarzania playlist, zależnie od czasu bądź jakiejkolwiek logiki, potrafi sam nasłuchiwać zamiast Icecasta czy Shoutcasta, potrafi pobierać streamy z innych serwerów, miksować je, wykrywać czy działają, itp. itd. Do tego dochodzi zaawansowana logika i tak oto mamy istny szwajcarski scyzoryk.
Trochę chaotycznie to wszystko opisałem, ale może podam przykład praktyczny.
Docelowo – ktoś sobie gra co jakiś czas. W chwilach gdy nie gra, powinna lecieć automatycznie generowana play lista z zasobów które leżą na serwerze. Wszystko powinno się przełączać bezboleśnie, niezauważalnie dla słuchacza. W radiu gra się za pomocą shoutcastowego pluginu. Na serwerze jest Icecast który potrafi ten protokół obsłużyć. Icecast ma zdefiniowane i dostępne następujące mountpointy:
/live – mountpoint do którego gra sobie osoba prowadząca audycję
/mp3-hi i /mp3-lo – dwa mountpointy z wynikową audycją
Standardowo, Liquidsoap sprawdza czy na /live ktoś gra. Jeżeli nie, odtwarza piosenki z dysku (potocznie nazywa się to „autopilot”). Oczywiście jak tylko ktoś rozpocznie audycję, Liquidsoap od razu wyciszy pilota i rozpocznie retransmitować mountpoint /live. Oczywiście, sam liquidsoap nie musi zajmować się „samograjem”. Może to robić zupełnie inna aplikacja która wysyła strumień na mountpoint /auto a Liquidsoap tylko przełącza pomiędzy tymi strumieniami. Na koniec, wszystko to jest reenkodowane do dwóch strumieni mp3 o różnej jakości i nadawane z powrotem do Icecasta. Kolejny plus – nie trzeba się martwić że osoba grająca rozpocznie audycję z jakością z kosmosu. Dodatkowo pliki mp3 na serwerze mogą być także w różnym formacie. Nie tylko mp3, bo Liquidsoap obsługuje wiele różnych formatów. Ambitni mogą pokusić się o konfigurację taką, że muzyka będzie leżeć tylko na serwerze, a osoba prowadząca audycje steruje tym co ma być odtwarzane zdalnie. Liquidsoap w takiej sytuacji potrafi nawet wykrywać czy ktoś mówi do mikrofonu czy jest sama cisza i miksować na żywo mikrofon z playlistą wyciszając na czas mowy playlistę. Można by rzecz – cudo.
W praktyce konfiguracja nasza wygląda tak:
#!/usr/local/bin/liquidsoap
set("log.file.path", "/home/radio/player/player.log")
# procedura używana do zapytania o to co ma aktualnie odtworzyć pilot
# więcej informacji poniżej
def querytrack () =
request.create(audio=true, get_process_output("/home/radio/pilot/player.php"))
end
# procedura używana do płynnego przejścia pomiędzy strumieniami:
def streamcrossfade(a,b)
add(normalize=false, [ sequence([ blank(duration=5.), fade.initial(duration=10.,b) ]), fade.final(duration=10.,a) ])
end
# strumień na żywo:
live = input.http("http://localhost:8000/live")
# samograj:
pilot = request.dynamic(querytrack)
# ustaw tytuł strumienia na to co zwrócił plik player.php
pilot = rewrite_metadata([("song","$(display_title)")], pilot)
# pomiń utwory mp3 które zaczynają się od ciszy (min. 3 sekundy)
pilot = skip_blank(length=3.,pilot)
# zrób crossfade między ścieżkami
pilot = crossfade(start_netx=8. ,fade_in=5.,fade_out=5.,pilot)
# uczyń źródło pilota „bezpiecznym”, czyli generuj ciszę jeżeli nie da się nic odtworzyć:
pilot = mksafe(pilot)
# zdefiniuj radio, które ma być złożone ze strumienia live, a jak live nie zadziała to użyj strumienia
# pilot, dodatkowo przełączanie pomiędzy strumieniami ma się odbywać za pomocą
# crossfade. track_sensitive=false powoduje to, że przełączenie będzie od razu, a nie po
# końcu aktualnego utworu
radio = fallback(track_sensitive=false, transitions=[streamcrossfade,streamcrossfade], [live, pilot])
# wygeneruj strumienie do icecasta:
output.icecast.mp3(mount="mp3-hi", bitrate=128, stereo=true, samplerate=44100, host="localhost", password="***", restart=true, genre="Various", url="http://radio.pl", description="radio", public=true, name="Radio!", radio)
output.icecast.mp3(mount="mp3-lo", bitrate=32, stereo=false, samplerate=22050, host="localhost", password="***", restart=true, genre="Various", url="http://radio.pl", description="radio", public=true, name="Radio!", radio)
Skomplikowane? Też uważam że da się przeżyć. A! No i oczywiście jak działa player.php? Otóż przy każdym uruchomieniu zwraca następujący tekst:
annotate:duration="239.0",display_title="Elektroda - Fiołki":/home/radio/store/312318.mp3
Nieskomplikowane, prawda? Należy tylko dbać by duration było zawsze z kropką, nawet jak nie znamy dziesiątych części sekund. Co prawda można ten parametr pominąć, ale zawsze lepiej żeby był.
Działa to wyśmienicie i stabilnie. A to dopiero początek możliwości. Można muzykę normalizować, dorobić jej postprocessing i wiele innych bajerów. Ale żeby to poznać, musicie już sami poczytać stronę Liquidsoapa.
http://savonet.sourceforge.net/