diff --git a/liquidsoap/Dockerfile b/liquidsoap/Dockerfile index 53ce17d..828cc4c 100644 --- a/liquidsoap/Dockerfile +++ b/liquidsoap/Dockerfile @@ -1,6 +1,7 @@ FROM archlinux -RUN yes | pacman -Sy opam make m4 gcc patch diffutils ffmpeg git automake autoconf pkg-config which taglib sudo glibc jq +RUN yes | pacman -Sy opam make m4 gcc patch diffutils ffmpeg git automake autoconf pkg-config which taglib sudo glibc jq libmad +RUN yes | pacman -Syu RUN useradd liquidsoap && mkdir /home/liquidsoap && chown -R liquidsoap /home/liquidsoap RUN usermod -aG wheel liquidsoap @@ -25,13 +26,24 @@ WORKDIR /home/liquidsoap RUN sudo touch /var/log/liquidsoap.log RUN sudo chown liquidsoap:users /var/log/liquidsoap.log -RUN opam install depext -y -RUN opam depext taglib mad lame ogg vorbis cry samplerate liquidsoap -y -RUN opam install taglib mad lame ogg vorbis cry samplerate liquidsoap -y +#RUN opam install depext -y +#RUN opam depext gavl ffmpeg taglib mad lame ogg vorbis cry samplerate liquidsoap -y +#RUN opam install gavl ffmpeg taglib mad lame ogg vorbis cry samplerate liquidsoap -y # RUN for i in ocaml-ffmpeg ocaml-dtools ocaml-duppy ocaml-mm ocaml-cry ocaml-taglib ocaml-lame ocaml-ogg ocaml-vorbis ocaml-samplerate liquidsoap; do \ # cd /home/liquidsoap && git clone https://github.com/savonet/$i && cd $i && opam pin add --yes --no-action .; \ # done # RUN opam install --yes liquidsoap ffmpeg cry taglib lame ogg vorbis samplerate +# RUN opam install depext -y +# RUN opam depext taglib mad lame ogg vorbis cry samplerate liquidsoap -y +# RUN opam install taglib mad lame ogg vorbis cry samplerate liquidsoap -y + +RUN for i in ocaml-gavl ocaml-ffmpeg ocaml-dtools ocaml-duppy ocaml-mm ocaml-cry ocaml-taglib ocaml-lame ocaml-mad ocaml-ogg ocaml-vorbis ocaml-samplerate; do \ + cd /home/liquidsoap && git clone https://github.com/savonet/$i && cd $i && opam pin add --yes --no-action .; \ +done +#RUN cd /home/liquidsoap && git clone https://github.com/savonet/liquidsoap && cd liquidsoap && git checkout v2.0.0-beta2 && opam pin add --yes --no-action . +RUN cd /home/liquidsoap && git clone https://github.com/savonet/liquidsoap && cd liquidsoap && opam pin add --yes --no-action . +RUN opam install --yes liquidsoap ffmpeg cry taglib lame mad ogg vorbis samplerate + RUN eval `opam config env`;liquidsoap --version RUN mkdir /home/liquidsoap/radio @@ -40,10 +52,13 @@ RUN mkdir /home/liquidsoap/tracks RUN chown liquidsoap:liquidsoap /home/liquidsoap/tracks RUN mkdir /home/liquidsoap/recordings RUN chown liquidsoap:liquidsoap /home/liquidsoap/recordings +RUN mkdir /home/liquidsoap/hls +RUN chown liquidsoap:liquidsoap /home/liquidsoap/hls WORKDIR /home/liquidsoap/radio RUN chown 1000:1000 /home/liquidsoap/tracks RUN chown 1000:1000 /home/liquidsoap/recordings +RUN chown 1000:1000 /home/liquidsoap/hls EXPOSE 9000 -CMD ["/bin/bash", "-c", "eval `opam config env`; liquidsoap radio.liq"] +CMD ["/bin/bash", "-c", "eval `opam config env` && liquidsoap radio.liq"] diff --git a/liquidsoap/PACKAGES b/liquidsoap/PACKAGES index be4b45c..8af17bc 100644 --- a/liquidsoap/PACKAGES +++ b/liquidsoap/PACKAGES @@ -38,7 +38,7 @@ ocaml-cry # Gstreamer is a library for constructing graphs of media-handling components. # It is currently used to provide audio and video encoding and decoding # as well as input and outputs such as V4l inputs. -# ocaml-gstreamer +ocaml-gstreamer # ============================================================================ # Codecs @@ -117,11 +117,11 @@ ocaml-flac # is very efficient and has a quality setting for # tweaking load vs. quality of the conversion. # It is the recommended module to use with video -# ocaml-gavl +ocaml-gavl # FFMPEG is currently only used to convert from # and to many formats. -# ocaml-ffmpeg +ocaml-ffmpeg # Frei0r is a minimalistic plugin API for video sources and filters. # ocaml-frei0r diff --git a/liquidsoap/radio.liq b/liquidsoap/radio.liq index e6063a9..47db937 100755 --- a/liquidsoap/radio.liq +++ b/liquidsoap/radio.liq @@ -1,11 +1,11 @@ #!/usr/local/bin/liquidsoap -%include "tunein.liq" +# %include "tunein.liq" -set("sandbox.tool","disabled") +#set("sandbox.tool","disabled") set("log.file",true) set("log.file.path","/var/log/liquidsoap.log") set("log.stdout",true) -set("log.level",4) +set("log.level",3) set("server.telnet",true) set("server.socket",true) @@ -19,13 +19,13 @@ icecast_host = "icecast" icecast_port = "8000" crossfade_enabled = getenv("CROSSFADE") -tunein_partner_id = getenv("TUNEIN_PARTNER_ID") -tunein_partner_key = getenv("TUNEIN_PARTNER_KEY") -tunein_station_id = getenv("TUNEIN_STATION_ID") -tunein_metadata_updates_enabled = getenv("TUNEIN_METADATA_UPDATES_ENABLED") +# tunein_partner_id = getenv("TUNEIN_PARTNER_ID") +# tunein_partner_key = getenv("TUNEIN_PARTNER_KEY") +# tunein_station_id = getenv("TUNEIN_STATION_ID") +# tunein_metadata_updates_enabled = getenv("TUNEIN_METADATA_UPDATES_ENABLED") # liq_secret = getenv("LIQ_SECRET") - +# def icy_update(v) = # Parse the argument l = string.split(separator=",",v) @@ -47,6 +47,9 @@ def icy_update(v) = "Done !" end +# def clear_queue() = +# end + server.register("update",namespace="metadata", description="Update metadata", usage="update title=foo,album=bar,..", @@ -54,17 +57,20 @@ server.register("update",namespace="metadata", # refs -title_prefix = ref "" +title_prefix = ref("") -current_dj = ref "" -last_dj_disconnected = ref "" -current_dj_connected_at = ref 0.0 -last_dj_disconnected_at = ref 0.0 -current_dump_filename = ref "" +current_dj = ref("") +last_dj_disconnected = ref("") +current_dj_connected_at = ref(0.0) +last_dj_disconnected_at = ref(0.0) +current_dump_filename = ref("") -stop_dump_f = ref (fun () -> ()) +stop_dump_f = ref(fun () -> ()) -live_dj = ref empty() +live_dj = ref(empty()) +live_video_audio = ref(empty()) +live_video = ref(empty()) +vj_video = ref(empty()) def dj_dummy_started() = log("dj dummy on_start") @@ -98,18 +104,18 @@ def get_password(user,password) = end #auth function -def dj_auth(user,password) = +def dj_auth(args) = log("dj auth") - u = get_user(user,password) - p = get_password(user,password) - ret = get_process_lines("./dj_auth.sh '#{u}' '#{p}' '#{radio_name}'") + u = get_user(args.user, args.password) + p = get_password(args.user, args.password) + ret = process.read.lines("./dj_auth.sh '#{u}' '#{p}' '#{radio_name}'") #ret has now the value of the live client (dj1,dj2, or djx), or "ERROR"/"unknown" ret = list.hd(default="",ret) #return true to let the client transmit data, or false to tell harbor to decline if ret == "true" then title_prefix := "LIVE -- #{u}" current_dj := "#{u}" - current_dj_connected_at := gettimeofday() + current_dj_connected_at := time() log("dj auth succeeded") log("#{current_dj}") log("#{current_dj_connected_at}") @@ -121,7 +127,7 @@ end def my_request_function () = - lines = get_process_lines("./next_song.sh") + lines = process.read.lines("./next_song.sh") result = list.hd(default="", lines) log("result: #{result}") log("lines: #{lines}") @@ -158,20 +164,20 @@ def on_connect(headers) = log("last_dj_disconnected_at: #{!last_dj_disconnected_at}") if (!last_dj_disconnected_at == 0.0) then log("*****************************************CREATING NEW DUMP, last_dj_disconnected_at is null***************************************************") - current_dump_filename := "#{!current_dj}-#{localtime(!current_dj_connected_at, format_time)}" + current_dump_filename := "#{!current_dj}-#{time.local(!current_dj_connected_at)}" log("new dump filename (first dump):") log(!current_dump_filename); log("sending live notification") - ret = get_process_lines("./live_notification.sh 'LIVE - #{!current_dj}'") + ret = process.read.lines("./live_notification.sh 'LIVE - #{!current_dj}'") ret = list.hd(default="",ret) log(ret) elsif (((!current_dj_connected_at - !last_dj_disconnected_at) > 60.0) or (!current_dj != !last_dj_disconnected)) then log("*****************************************CREATING NEW DUMP***************************************************") - current_dump_filename := "#{!current_dj}-#{localtime(!current_dj_connected_at, format_time)}" + current_dump_filename := "#{!current_dj}-#{time.local(!current_dj_connected_at)}" log("new dump filename:") log(!current_dump_filename); log("sending live notification") - ret = get_process_lines("./live_notification.sh 'LIVE - #{!current_dj}'") + ret = process.read.lines("./live_notification.sh 'LIVE - #{!current_dj}'") ret = list.hd(default="",ret) log(ret) else @@ -211,7 +217,7 @@ end def pub_metadata(m) = log("on_track"); title = m["title"] - ret = get_process_lines("./pub_metadata.sh '#{title}'") + ret = process.read.lines("./pub_metadata.sh '#{title}'") ret = list.hd(default="",ret) log(ret) #url = "https://#{radio_name}.streampusher.com/publish_metadata.json" @@ -227,18 +233,31 @@ def pub_metadata(m) = # log("desc: #{desc}"); end +live_video := input.ffmpeg(id="main_video", fallible=true, buffer=60.0, "https://viz.streampusher.com:16666/hls/datafruits.m3u8") +vj_video := input.ffmpeg(id="vj_video", fallible=true, buffer=60.0, "https://viz.streampusher.com:16666/vj/datafruits.m3u8") + +live_video_audio := audio_to_stereo(drop_video(!live_video)) + live_dj := map_metadata(new_meta, !live_dj,update=false) -live_dj := pipe(process='./stereo_tool_cmd_64 - - -s ./stereotool.sts', buffer=10.0, !live_dj) +# live_audio = fallback(id="live_audio_fallback", track_sensitive=false, [!live_video_audio, !live_dj]) +# live_audio = pipe(process='./stereo_tool_cmd_64 - - -s ./stereotool.sts', buffer=10.0, live_audio) output.dummy(fallible=true, on_start=dj_dummy_started, on_stop=dj_dummy_stopped, !live_dj) source = fallback(id="fallback",track_sensitive=false, - [!live_dj,mksafe(backup_playlist)]) + [!live_video_audio, !live_dj, mksafe(backup_playlist)]) + +single_video = noise() -source = if tunein_metadata_updates_enabled == "true" then on_track(tunein.submit(partnerid=tunein_partner_id,partnerkey=tunein_partner_key,stationid=tunein_station_id), source) else source end +video_source = fallback(id="video_fallback",track_sensitive=false, + [!live_video, + mux_audio(drop_audio(!vj_video), audio=source), + mksafe(mux_audio(single_video, audio=source))]) -source = on_track(pub_metadata, source) +#source = if tunein_metadata_updates_enabled == "true" then on_track(tunein.submit(partnerid=tunein_partner_id,partnerkey=tunein_partner_key,stationid=tunein_station_id), source) else source end + +source.on_track(pub_metadata) source = server.insert_metadata(id="fallback", source) @@ -254,3 +273,61 @@ clock.assign_new(id="mp3_icecast", [output.icecast(%mp3,id="icecast", icy_metadata="true",description="", url="", encoding="UTF-8", mksafe(buffer(source)))]) + +aac_lofi = %ffmpeg(format="mpegts", + %audio( + codec="aac", + channels=2, + ar=44100 + ), + %video( + codec="libx264", + b="5M" + ) + ) + +aac_midfi = %ffmpeg(format="mpegts", + %audio( + codec="aac", + channels=2, + ar=44100, + b="96k" + ), + %video( + codec="libx264", + b="5M" + ) + ) + +aac_hifi = %ffmpeg(format="mpegts", + %audio( + codec="aac", + channels=2, + ar=44100, + b="192k" + ), + %video( + codec="libx264", + b="5M" + ) + ) + +streams = [("aac_lofi",aac_lofi), + ("aac_midfi", aac_midfi), + ("aac_hifi", aac_hifi)] + +def segment_name(~position,~extname,stream_name) = + timestamp = int_of_float(time()) + duration = 2 + "#{stream_name}_#{duration}_#{timestamp}_#{position}.#{extname}" +end + +output.file.hls(playlist="#{radio_name}.m3u8", + segment_duration=2.0, + segments=5, + segments_overhead=5, + segment_name=segment_name, + persist_at="/home/liquidsoap/state.config", + "/home/liquidsoap/hls", + streams, + video_source)