Skip to content

Commit

Permalink
feat: add hls output stream
Browse files Browse the repository at this point in the history
  • Loading branch information
jooola committed Apr 24, 2023
1 parent 2fd5b50 commit 7065884
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 5 deletions.
4 changes: 4 additions & 0 deletions dev/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ liquidsoap:

stream:
outputs:
hls:
- enabled: True
path: /app/hls

.default_icecast_output: &default_icecast_output
host: icecast
port: 8000
Expand Down
1 change: 1 addition & 0 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ services:
nginx:
volumes:
- ./legacy:/var/www/html
- ./docker/data/playout:/var/playout:ro

icecast:
ports:
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ services:
- legacy
volumes:
- libretime_assets:/var/www/html:ro
- libretime_playout:/var/playout:ro
- ${NGINX_CONFIG_FILEPATH:-./nginx.conf}:/etc/nginx/conf.d/default.conf:ro

icecast:
Expand All @@ -106,3 +107,4 @@ volumes:
libretime_storage: {}
libretime_assets: {}
libretime_playout: {}
libretime_hls: {}
19 changes: 19 additions & 0 deletions docker/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,23 @@ server {
proxy_redirect off;
proxy_pass http://api:9001;
}

location /hls {
add_header 'Cache-Control' 'no-cache';

# CORS setup
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length';

# allow CORS preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}

root /var/playout;
}
}
4 changes: 4 additions & 0 deletions playout/libretime_playout/liquidsoap/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def generate_entrypoint(
if log_filepath is not None:
paths["log_filepath"] = log_filepath.resolve()

for o in config.stream.outputs.hls:
if o.enabled:
Path(o.path).mkdir(exist_ok=True)

return templates.get_template("entrypoint.liq.j2").render(
config=config.copy(),
preferences=preferences,
Expand Down
39 changes: 39 additions & 0 deletions playout/libretime_playout/liquidsoap/templates/outputs.liq.j2
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,38 @@ output.shoutcast(
)
{%- endmacro -%}

{#-
Build an hls output configuration.
#}
{%- macro output_hls(output_id, output) -%}
# hls:{{ output_id }}
output_hls_{{ output_id }}_source = s
output_hls_{{ output_id }}_lofi = %ffmpeg(format="mpegts", codec="aac", channels=2, ar=44100, b="32k")
output_hls_{{ output_id }}_midfi = %ffmpeg(format="mpegts", codec="aac", channels=2, ar=44100, b="96k")
output_hls_{{ output_id }}_hifi = %ffmpeg(format="mpegts", codec="aac", channels=2, ar=44100, b="192k")
output.file.hls(
playlist="{{ output.playlist }}",
segment_duration={{ output.segment_duration }},
segments={{ output.segments }},
segments_overhead={{ output.segments_overhead }},
streams_info=[
# stream_name, (bandwidth, codec, extname)
("aac_lofi", (40000, "mp4a.40.29", "ts")),
("aac_midfi", (110000, "mp4a.40.2", "ts")),
("aac_hifi", (220000, "mp4a.40.2", "ts")),
],
persist=true,
persist_at="hls.conf",
"{{ output.path }}",
[
("aac_lofi", output_hls_{{ output_id }}_lofi),
("aac_midfi", output_hls_{{ output_id }}_midfi),
("aac_hifi", output_hls_{{ output_id }}_hifi),
],
output_hls_{{ output_id }}_source,
)
{%- endmacro -%}

{% for output in config.stream.outputs.system -%}
{% if output.enabled -%}
# {{ output.kind.value }}:{{ loop.index }}
Expand Down Expand Up @@ -147,3 +179,10 @@ output.{{ output.kind.value }}(id="{{ output.kind.value }}:{{ loop.index }}", s)

{% endif -%}
{% endfor -%}

{% for output in config.stream.outputs.hls -%}
{% if output.enabled -%}
{{ output_hls(loop.index, output) }}

{% endif -%}
{% endfor -%}
3 changes: 2 additions & 1 deletion playout/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def config():
},
"stream": {
"outputs": {
"hls": [{"enabled": True}],
"icecast": [
{
"enabled": True,
Expand All @@ -30,7 +31,7 @@ def config():
"source_password": "hackme",
"audio": {"format": "mp3", "bitrate": 256},
},
]
],
}
},
}
Expand Down
72 changes: 68 additions & 4 deletions playout/tests/liquidsoap/__snapshots__/entrypoint_test.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,70 @@

%include "/fake/1.4/ls_script.liq"

# hls:1
output_hls_1_source = s
output_hls_1_lofi = %ffmpeg(format="mpegts", codec="aac", channels=2, ar=44100, b="32k")
output_hls_1_midfi = %ffmpeg(format="mpegts", codec="aac", channels=2, ar=44100, b="96k")
output_hls_1_hifi = %ffmpeg(format="mpegts", codec="aac", channels=2, ar=44100, b="192k")
output.file.hls(
playlist="main.m3u8",
segment_duration=5.0,
segments=30,
segments_overhead=5,
streams_info=[
# stream_name, (bandwidth, codec, extname)
("aac_lofi", (40000, "mp4a.40.29", "ts")),
("aac_midfi", (110000, "mp4a.40.2", "ts")),
("aac_hifi", (220000, "mp4a.40.2", "ts")),
],
persist=true,
persist_at="hls.conf",
"hls",
[
("aac_lofi", output_hls_1_lofi),
("aac_midfi", output_hls_1_midfi),
("aac_hifi", output_hls_1_hifi),
],
output_hls_1_source,
)



gateway("started")

'''
# ---
# name: test_generate_entrypoint[stream_config2-1.4]
'''
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
###########################################################
# The ignore() lines are to squash unused variable warnings

# Inputs
input_main_mount = "main"
input_main_port = 8001
input_show_mount = "show"
input_show_port = 8002

# Settings
auth_path = "/fake/liquidsoap_auth.py"

set("log.file.path", "/var/log/radio.log")

set("server.telnet", true)
set("server.telnet.bind_addr", "127.0.0.1")
set("server.telnet.port", 1234)

set("harbor.bind_addrs", ["0.0.0.0"])

station_name = interactive.string("station_name", "LibreTime")

message_offline = interactive.string("message_offline", "LibreTime - offline")
message_format = interactive.string("message_format", "0")
input_fade_transition = interactive.float("input_fade_transition", 0.0)

%include "/fake/1.4/ls_script.liq"

# icecast:1
output_icecast_1_source = s
# Disable ogg metadata
Expand Down Expand Up @@ -94,7 +158,7 @@

'''
# ---
# name: test_generate_entrypoint[stream_config2-1.4]
# name: test_generate_entrypoint[stream_config3-1.4]
'''
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
###########################################################
Expand Down Expand Up @@ -150,7 +214,7 @@

'''
# ---
# name: test_generate_entrypoint[stream_config3-1.4]
# name: test_generate_entrypoint[stream_config4-1.4]
'''
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
###########################################################
Expand Down Expand Up @@ -231,7 +295,7 @@

'''
# ---
# name: test_generate_entrypoint[stream_config4-1.4]
# name: test_generate_entrypoint[stream_config5-1.4]
'''
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
###########################################################
Expand Down Expand Up @@ -274,7 +338,7 @@

'''
# ---
# name: test_generate_entrypoint[stream_config5-1.4]
# name: test_generate_entrypoint[stream_config6-1.4]
'''
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
###########################################################
Expand Down
1 change: 1 addition & 0 deletions playout/tests/liquidsoap/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def make_config_with_stream(**kwargs) -> Config:

TEST_STREAM_CONFIGS: List[Config] = [
make_config_with_stream(),
make_config_with_stream(outputs={"hls": [{"enabled": True}]}),
make_config_with_stream(
outputs={
"icecast": [
Expand Down
12 changes: 12 additions & 0 deletions shared/libretime_shared/config/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,20 @@ class SystemOutput(BaseModel):
kind: SystemOutputKind = SystemOutputKind.ALSA


class HLSOutput(BaseModel):
enabled: bool = False
kind: Literal["hls"] = "hls"

path: str = "hls"
playlist: str = "main.m3u8"
segment_duration: float = 5.0
segments: int = 30
segments_overhead: int = 5


# pylint: disable=too-few-public-methods
class Outputs(BaseModel):
hls: List[HLSOutput] = Field([], max_items=1)
icecast: List[IcecastOutput] = Field([], max_items=3)
shoutcast: List[ShoutcastOutput] = Field([], max_items=1)
system: List[SystemOutput] = Field([], max_items=1)
Expand Down

0 comments on commit 7065884

Please sign in to comment.