1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
|
# Decoders, enabled when the binary is detected and the os is not Win32.
register(
name="external decoders",
descr="External decoders settings",
"decoder.external",
())
register(
name="ffmpeg path",
descr="Path to ffmpeg binary",
"decoder.external.ffmpeg.path",
"ffmpeg#{exe_ext}")
register(
name="ffprobe path",
descr="Path to ffprobe binary",
"decoder.external.ffprobe.path",
"ffprobe#{exe_ext}")
register(
name="Ffmpeg decoder stream mimes",
descr="Mime types supported by the ffmpeg stream decoder",
"decoder.mime_types.ffmpeg",
[])
%ifdef add_decoder
# Enable ffmpeg decoder.
# @category Liquidsoap
def enable_external_ffmpeg_decoder() =
ffmpeg = get(default="ffmpeg#{exe_ext}","decoder.external.ffmpeg.path")
ffprobe = get(default="ffprobe#{exe_ext}","decoder.external.ffprobe.path")
mime_types = get(default=[],"decoder.mime_types.ffmpeg")
def ffprobe_test(fname) =
json = get_process_output("#{ffprobe} -print_format json -show_streams #{quote(fname)}")
default = [("streams",[[("channels",0)]])]
data = of_json(default=default,json)
streams = list.assoc(default=[],"streams",data)
stream = list.hd(default=[],streams)
list.assoc(default=0,"channels",stream)
end
if which(ffmpeg) != "" and which (ffprobe) != "" then
log(label="external.decoder","Enabling FFMPEG decoder")
add_decoder(
name="FFMPEG",
description="Decode files using the ffmpeg decoder binary",
mimes=mime_types,
test=ffprobe_test,
"#{ffmpeg} -i - -f wav - ")
else
log(label="external.decoder","Could not find ffmpeg or ffprobe binary. Please adjust using the \"decoder.external.ffmpeg.path\" setting.")
end
end
register(
name="mpcdec path",
descr="Path to mpcdec binary",
"decoder.external.mpcdec.path",
"mpcdec#{exe_ext}")
# Enable external Musepack decoder.
# @category Liquidsoap
def enable_external_mpc_decoder() =
# A list of know extensions and content-type for Musepack.
# Values from http://en.wikipedia.org/wiki/Musepack
mpc_mimes = [ "audio/x-musepack", "audio/musepack" ]
mpc_filexts = [ "mpc", "mp+", "mpp" ]
mpcdec = get(default="mpcdec#{exe_ext}","decoder.external.mpcdec.path")
def test_mpc(file) =
def get_channels(file) =
int_of_string(
list.hd(default="",
get_process_lines("#{mpcdec} -i #{quote(file)} 2>&1 \
| grep channels | cut -d' ' -f 2")))
end
# Get the file's mime
mime = get_mime(file)
# Test mime
if list.mem(mime,mpc_mimes) then
get_channels(file)
else
# Otherwise test file extension
ret = string.extract(pattern='\.(.+)$',file)
if list.length(ret) != 0 then
ext = ret["1"]
if list.mem(ext,mpc_filexts) then
get_channels(file)
else
0
end
else
get_channels(file)
end
end
end
if which(mpcdec) != "" then
log(label="external.decoder","Enabling MPCDEC external decoder.")
mpcdec_p = fun(f) -> "#{mpcdec} #{quote(f)} - 2>/dev/null"
add_oblivious_decoder(name="MPCDEC",description="Decode files using the mpcdec \
musepack decoder binary",test=test_mpc,mpcdec_p)
else
log(label="external.decoder","Could not find mpcdec binary, please adjust using the \"decoder.external.mpcdec.path\" setting.")
end
end
register(
name="flac path",
descr="Path to flac binary",
"decoder.external.flac.path",
"flac#{exe_ext}")
register(
name="metaflac path",
descr="Path to metaflac binary",
"decoder.external.metaflac.path",
"metaflac#{exe_ext}")
# Enable external FLAC decoders. Please note that built-in support for
# FLAC is available in liquidsoap if compiled and should be preferred
# over the external decoder.
# @category Liquidsoap
def enable_external_flac_decoder() =
flac = get(default="flac#{exe_ext}","decoder.external.flac.path")
metaflac = get(default="metaflac#{exe_ext}","decoder.external.metaflac.path")
if which(flac) != "" then
log(label="external.decoder","Enabling EXTERNAL_FLAC external decoder.")
flac_p = "#{flac} -d -c - 2>/dev/null"
def test_flac(file) =
if which(metaflac) != "" then
channels = list.hd(default="",
get_process_lines("#{metaflac} \
--show-channels #{quote(file)} \
2>/dev/null"))
# If the value is not an int, this returns 0 and we are ok :)
int_of_string(channels)
else
if string.match(pattern="flac",file) then
# We do not know the number of audio channels
# so setting to -1
(-1)
else
# All tests failed: no audio decodable using flac..
0
end
end
end
add_decoder(name="EXTERNAL_FLAC",description="Decode files using the flac \
decoder binary.", mimes=["audio/x-flac"], test=test_flac,flac_p)
else
log(label="decoder.external","Did not find flac binary, please adjust using the \"decoder.external.flac.path\" setting.")
end
if which(metaflac) != "" then
log(label="decoder.external","Enabling EXTERNAL_FLAC metadata \
resolver.")
def flac_meta(file)
ret = get_process_lines("#{metaflac} --export-tags-to=- \
#{quote(file)} 2>/dev/null")
ret = list.map(string.split(separator="="),ret)
# Could be made better..
def f(l',l)=
if list.length(l) >= 2 then
list.append([(list.hd(default="",l),list.nth(default="",l,1))],l')
else
if list.length(l) >= 1 then
list.append([(list.hd(default="",l),"")],l')
else
l'
end
end
end
list.fold(f,[],ret)
end
add_metadata_resolver("EXTERNAL_FLAC",flac_meta)
else
log(label="decoder.external","Did not find metaflac binary. Please adjust using the \"decoder.external.metaflac.path\" setting.")
end
end
%endif
%ifdef add_oblivious_decoder
register(
name="faad path",
descr="Path to faad binary",
"decoder.external.faad.path",
"faad#{exe_ext}")
# Enable or disable external FAAD (AAC/AAC+/M4A) decoders. Does not work
# on Win32.
# Please note that built-in support for faad is available in liquidsoap if
# compiled and should be preferred over the external decoder.
# @category Liquidsoap
def enable_external_faad_decoder() =
faad = get(default="faad#{exe_ext}","decoder.external.faad.path")
# A list of know extensions and content-type for AAC.
# Values from http://en.wikipedia.org/wiki/Advanced_Audio_Coding
# TODO: can we register a setting for that ??
aac_mimes =
["audio/aac", "audio/aacp", "audio/3gpp", "audio/3gpp2", "audio/mp4",
"audio/MP4A-LATM", "audio/mpeg4-generic", "audio/x-hx-aac-adts"]
aac_filexts = ["m4a", "m4b", "m4p", "m4v",
"m4r", "3gp", "mp4", "aac"]
# Faad is not very selective so we are checking only file that end with a
# known extension or mime type
def faad_test(file) =
# Get the file's mime
mime = get_mime(file)
# Test mime
if list.mem(mime,aac_mimes) then
true
else
# Otherwise test file extension
ret = string.extract(pattern='\.(.+)$',file)
if list.length(ret) != 0 then
ext = ret["1"]
list.mem(ext,aac_filexts)
else
false
end
end
end
if which(faad) != "" then
log(label="decoder.external","Enabling EXTERNAL_FAAD decoder and \
metadata resolver.")
faad_p = (fun (f) -> "#{faad} -w #{quote(f)} 2>/dev/null")
def test_faad(file) =
if faad_test(file) then
channels = list.hd(default="",
get_process_lines("#{faad} -i #{quote(file)} 2>&1 | \
grep 'ch,'"))
ret = string.extract(pattern=", (\d) ch,",channels)
ret =
if list.length(ret) == 0 then
# If we pass the faad_test, chances are high that the file will
# contain aac audio data..
"-1"
else
ret["1"]
end
int_of_string(default=(-1),ret)
else
0
end
end
add_oblivious_decoder(name="EXTERNAL_FAAD",description="Decode files using \
the faad binary.", test=test_faad, faad_p)
def faad_meta(file) =
if faad_test(file) then
ret = get_process_lines("#{faad} -i \
#{quote(file)} 2>&1")
# Yea, this is ugly programming (again)!
def get_meta(l,s)=
ret = string.extract(pattern="^(\w+):\s(.+)$",s)
if list.length(ret) > 0 then
list.append([(ret["1"],ret["2"])],l)
else
l
end
end
list.fold(get_meta,[],ret)
else
[]
end
end
add_metadata_resolver("EXTERNAL_FAAD",faad_meta)
else
log(label="external.decoder","Did not find faad binary. Please adjust using the \"decoder.external.faad.path\" setting.")
end
end
%endif
# Standard function for displaying metadata.
# Shows artist and title, using "Unknown" when a field is empty.
# @param m Metadata packet to be displayed.
# @category String
def string_of_metadata(m)
artist = m["artist"]
title = m["title"]
artist = if ""==artist then "Unknown" else artist end
title = if ""==title then "Unknown" else title end
"#{artist} -- #{title}"
end
# Use X On Screen Display to display metadata info.
# @param ~color Color of the text.
# @param ~position Position of the text (top|middle|bottom).
# @param ~font Font used (xfontsel is your friend...)
# @param ~display Function used to display a metadata packet.
# @category Source / Track Processing
def osd_metadata(~color="green",~position="top",
~font="-*-courier-*-r-*-*-*-240-*-*-*-*-*-*",
~display=string_of_metadata,
s)
osd = 'osd_cat -p #{position} --font #{quote(font)}'
^ ' --color #{color}'
def feedback(m)
system("echo #{quote(display(m))} | #{osd} &")
end
on_metadata(feedback,s)
end
# Use notify to display metadata info.
# @param ~urgency Urgency (low|normal|critical).
# @param ~icon Icon filename or stock icon to display.
# @param ~time Timeout in milliseconds.
# @param ~display Function used to display a metadata packet.
# @param ~title Title of the notification message.
# @category Source / Track Processing
def notify_metadata(~urgency="low",~icon="stock_smiley-22",~time=3000,
~display=string_of_metadata,
~title="Liquidsoap: new track",s)
send = 'notify-send -i #{icon} -u #{urgency}'
^ ' -t #{time} #{quote(title)} '
on_metadata(fun (m) -> system(send^quote(display(m))),s)
end
%ifdef input.external
# Stream data from mplayer
# @category Source / Input
# @param s data URI.
# @param ~restart restart on exit.
# @param ~restart_on_error restart on exit with error.
# @param ~buffer Duration of the pre-buffered data.
# @param ~max Maximum duration of the buffered data.
# @category Source / Input
def input.mplayer(~id="input.mplayer",
~restart=true,~restart_on_error=false,
~buffer=0.2,~max=10.,s) =
input.external(id=id,restart=restart,
restart_on_error=restart_on_error,
buffer=buffer,max=max,
"mplayer -really-quiet \
-af resample=#{get(default=44100,\"frame.audio.samplerate\")},channels=#{get(default=2,\"frame.audio.channels\")} \
-ao pcm:file=/dev/stdout \
-vc null -vo null #{quote(s)} 2>/dev/null")
end
%endif
%ifdef input.external.avi
# Stream video from ffmpeg
# @category Source / Input
# @param s data URI.
# @param ~restart restart on exit.
# @param ~restart_on_error restart on exit with error.
# @param ~buffer Duration of the pre-buffered data.
# @param ~max Maximum duration of the buffered data.
# @category Source / Input
def input.ffmpeg.video(~id="input.ffmpeg.video",
~restart=true,~restart_on_error=false,
~buffer=0.2,~max=10.,~format="",s) =
ffmpeg = get(default="ffmpeg#{exe_ext}","decoder.external.ffmpeg.path")
format = if format == "" then "" else "-f #{format}" end
audiorate = get(default=44100,"frame.audio.samplerate")
width = get(default=320,"frame.video.width")
height = get(default=240,"frame.video.height")
videorate = get(default=24,"frame.video.samplerate")
command = "#{ffmpeg} -v 16 #{format} -i #{quote(s)} \
-f avi -vf format=rgb24 -vcodec rawvideo -r #{videorate} -s #{width}x#{height} \
-acodec pcm_s16le -ar #{audiorate} \
pipe:1"
log("FFMpeg command: " ^ command)
input.external.avi(id=id,restart=restart,
restart_on_error=restart_on_error,
buffer=buffer,max=max,
command)
end
%endif
%ifdef input.external.rawvideo
# Decode video using memncoder
# @category Source / Input
def input.mencoder.video(~id="input.mencoder.video",
~restart=true,~restart_on_error=false,
~buffer=0.2,~max=10.,s) =
# audiorate = get(default=44100,"frame.audio.samplerate")
# width = get(default=320,"frame.video.width")
# height = get(default=240,"frame.video.height")
# videorate = get(default=24,"frame.video.samplerate")
command = "mencoder -really-quiet #{quote(s)} -of rawvideo -ovc raw -vf format=rgb24 -oac copy -o -"
log(command)
input.external.rawvideo(id=id,restart=restart,
restart_on_error=restart_on_error,
buffer=buffer,max=max,
command)
end
%endif
|