{#- Build an audio encoder usable by the icecast or shoutcast output functions, using the output audio configuration #} {%- macro icecast_encoder(audio) -%} {%- if audio.format == "ogg" -%} {%- set quality_mapping = { 32: "-0.1", 48: "-0.1", 64: "0", 96: "0.2", 128: "0.4", 160: "0.5", 192: "0.6", 224: "0.7", 256: "0.8", 320: "0.9", } -%} %ogg(%vorbis( quality={{ quality_mapping[audio.bitrate] }}, channels={{ 2 if audio.channels == "stereo" else 1 }} )) {%- elif audio.format == "mp3" -%} %mp3( bitrate={{ audio.bitrate }}, stereo={{ "true" if audio.channels == "stereo" else "false" }} ) {%- elif audio.format == "opus" -%} %opus( bitrate={{ audio.bitrate }}, channels={{ 2 if audio.channels == "stereo" else 1 }}, signal="music", application="audio", complexity=10, vbr="constrained" ) {%- elif audio.format == "aac" -%} %fdkaac( bitrate={{ audio.bitrate }}, aot={{ ("mpeg4_he_aac_v2" if audio.bitrate <= 64 else "mpeg4_aac_lc") | quote }}, afterburner={{ "false" if audio.bitrate <= 128 else "true" }}, sbr_mode=true ) {%- endif -%} {%- endmacro -%} {#- Build an icecast output the output configuration. #} {%- macro output_icecast(output_id, output) -%} # icecast:{{ output_id }} {#- Handle stereo or mono output #} {% if output.audio.channels == "stereo" -%} output_icecast_{{ output_id }}_source = s {% else -%} output_icecast_{{ output_id }}_source = mean(s) {% endif -%} {#- Add per output stream modifications. -#} {% if output.audio.format == "ogg" and not output.audio.enable_metadata -%} # Disable ogg metadata output_icecast_{{ output_id }}_source = add(normalize=false, [amplify(0.00001, noise()), output_icecast_{{ output_id }}_source]) {% endif -%} output.icecast( id="icecast:{{ output_id }}", host={{ output.host | quote }}, port={{ output.port }}, mount={{ output.mount | quote }}, user={{ output.source_user | quote }}, password={{ output.source_password | quote }}, fallible = true, name={{ (output.name or '') | quote }}, description={{ (output.description or '') | quote }}, genre={{ (output.genre or '') | quote }}, url={{ (output.website or '') | quote }}, on_connect=make_ouput_on_connect_handler("{{ output_id }}"), on_error=make_ouput_on_error_handler("{{ output_id }}"), {{ icecast_encoder(output.audio) | indent(width=2) }}, output_icecast_{{ output_id }}_source, ) {%- endmacro -%} {#- Build a shoutcast output the output configuration. #} {%- macro output_shoutcast(output_id, output) -%} # shoutcast:{{ output_id }} {#- Handle stereo or mono output #} {% if output.audio.channels == "stereo" -%} output_shoutcast_{{ output_id }}_source = s {% else -%} output_shoutcast_{{ output_id }}_source = mean(s) {% endif -%} output.shoutcast( id="shoutcast:{{ output_id }}", host={{ output.host | quote }}, port={{ output.port }}, {#- Shoutcast does not have any mount mount={{ output.mount | quote }}, #} user={{ output.source_user | quote }}, password={{ output.source_password | quote }}, fallible = true, {#- Pass output.description to name and ignore output.name name={{ (output.name or '') | quote }}, description={{ (output.description or '') | quote }}, #} name={{ (output.description or '') | quote }}, genre={{ (output.genre or '') | quote }}, url={{ (output.website or '') | quote }}, on_connect=make_ouput_on_connect_handler("{{ output_id }}"), on_error=make_ouput_on_error_handler("{{ output_id }}"), {#- The config validation will prevent from using incompatible audio encoders #} {{ icecast_encoder(output.audio) | indent(width=2) }}, output_shoutcast_{{ output_id }}_source, ) {%- endmacro -%} {#- Build a system output configuration. #} {%- macro output_system(output_id, output) -%} # {{ output.kind.value }}:{{ output_id }} %ifndef output.{{ output.kind.value }} log("output.{{ output.kind.value }} is not defined!") %endif %ifdef output.{{ output.kind.value }} output.{{ output.kind.value }}( id="{{ output.kind.value }}:{{ output_id }}", {%- if output.kind.value == "alsa" %} {%- if output.device is not none %} device="{{ output.device }}", {%- endif %} {%- elif output.kind.value == "pulseaudio" %} {%- if output.device is not none %} device="{{ output.device }}", {%- endif %} {%- endif %} s ) %endif {%- endmacro -%} {# ############################### #} {%- for output in config.stream.outputs.system -%} {% if output.enabled -%} {{ output_system(loop.index, output) }} {% endif -%} {% endfor -%} {%- for output in config.stream.outputs.icecast -%} {% if output.enabled -%} {{ output_icecast(loop.index, output) }} {% endif -%} {% endfor -%} {% for output in config.stream.outputs.shoutcast -%} {% if output.enabled -%} {# Icecast and Shoutcast streams are merged in legacy, so we need to use a custom index as stream id. -#} {{ output_shoutcast((config.stream.outputs.icecast | length) + loop.index, output) }} {% endif -%} {% endfor -%}