Skip to content

Commit

Permalink
Add to hls component: target_window_duration, persistent (#103)
Browse files Browse the repository at this point in the history
* Add to hls component: target_window_duration, persistent

* Change requests

* Update dependencies

* Make hls option :target_window_duration required and nullable
  • Loading branch information
Karolk99 authored Oct 18, 2023
1 parent 3c8c8bb commit aa75980
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 30 deletions.
45 changes: 31 additions & 14 deletions lib/jellyfish/component/hls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,26 @@ defmodule Jellyfish.Component.HLS do
alias Membrane.RTC.Engine.Endpoint.HLS.{CompositorConfig, HLSConfig, MixerConfig}
alias Membrane.Time

@cleanup_after Time.seconds(60)
@segment_duration Time.seconds(6)
@partial_segment_duration Time.milliseconds(1_100)

@type metadata :: %{
optional(:target_window_duration) => pos_integer(),
playable: boolean(),
low_latency: boolean()
low_latency: boolean(),
persistent: boolean()
}

@impl true
def config(options) do
with {:ok, valid_opts} <- OpenApiSpex.cast_value(options, Options.schema()) do
low_latency? = valid_opts.lowLatency
hls_config = create_hls_config(options.room_id, low_latency?: low_latency?)
valid_opts = valid_opts |> Map.from_struct() |> Map.new(fn {k, v} -> {underscore(k), v} end)
hls_config = create_hls_config(options.room_id, valid_opts)

metadata =
valid_opts
|> Map.put(:playable, false)
|> Enum.into(%{})

{:ok,
%{
Expand All @@ -47,10 +54,7 @@ defmodule Jellyfish.Component.HLS do
},
hls_config: hls_config
},
metadata: %{
playable: false,
low_latency: low_latency?
}
metadata: metadata
}}
else
{:error, _reason} = error -> error
Expand All @@ -63,25 +67,38 @@ defmodule Jellyfish.Component.HLS do
Path.join([base_path, "hls_output", "#{room_id}"])
end

defp create_hls_config(room_id, low_latency?: low_latency?) do
partial_duration = if low_latency?, do: @partial_segment_duration, else: nil
hls_storage = setup_hls_storage(room_id, low_latency?: low_latency?)
defp create_hls_config(
room_id,
%{
low_latency: low_latency,
target_window_duration: target_window_duration,
persistent: persistent
}
) do
partial_duration = if low_latency, do: @partial_segment_duration, else: nil
hls_storage = setup_hls_storage(room_id, low_latency: low_latency)

cleanup_after = if persistent, do: nil, else: @cleanup_after

%HLSConfig{
hls_mode: :muxed_av,
mode: :live,
target_window_duration: :infinity,
target_window_duration: target_window_duration || :infinity,
segment_duration: @segment_duration,
partial_segment_duration: partial_duration,
persist?: persistent,
cleanup_after: cleanup_after,
storage: hls_storage
}
end

defp setup_hls_storage(room_id, low_latency?: true) do
defp setup_hls_storage(room_id, low_latency: true) do
fn directory -> %LLStorage{directory: directory, room_id: room_id} end
end

defp setup_hls_storage(_room_id, low_latency?: false) do
defp setup_hls_storage(_room_id, low_latency: false) do
fn directory -> %Storage{directory: directory} end
end

defp underscore(k), do: k |> Atom.to_string() |> Macro.underscore() |> String.to_atom()
end
3 changes: 3 additions & 0 deletions lib/jellyfish/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ defmodule Jellyfish.Utils do
Map.new(map, fn {k, v} -> {snake_case_to_camel_case(k), v} end)
end

# Macro.underscore/camelize:
# Do not use it as a general mechanism for underscoring strings as it does
# not support Unicode or characters that are not valid in Elixir identifiers.
defp snake_case_to_camel_case(atom) do
[first | rest] = "#{atom}" |> String.split("_")
rest = rest |> Enum.map(&String.capitalize/1)
Expand Down
21 changes: 20 additions & 1 deletion lib/jellyfish_web/api_spec/component/hls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,18 @@ defmodule JellyfishWeb.ApiSpec.Component.HLS do
lowLatency: %Schema{
type: :boolean,
description: "Whether the component uses LL-HLS"
},
targetWindowDuration: %Schema{
type: :integer,
description: "Duration of stream available for viewer",
nullable: true
},
persistent: %Schema{
type: :boolean,
description: "Whether the video is stored after end of stream"
}
},
required: [:playable, :lowLatency]
required: [:playable, :lowLatency, :persistent, :targetWindowDuration]
})
end

Expand All @@ -42,6 +51,16 @@ defmodule JellyfishWeb.ApiSpec.Component.HLS do
type: :boolean,
description: "Whether the component should use LL-HLS",
default: false
},
targetWindowDuration: %Schema{
type: :integer,
description: "Duration of stream available for viewer",
nullable: true
},
persistent: %Schema{
type: :boolean,
description: "Whether the video is stored after end of stream",
default: false
}
},
required: []
Expand Down
7 changes: 4 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ defmodule Jellyfish.MixProject do
{:protobuf, "~> 0.12.0"},

# Membrane deps
{:membrane_rtc_engine, "~> 0.17.1"},
{:membrane_rtc_engine_webrtc, "~> 0.3.0"},
{:membrane_rtc_engine_hls, "~> 0.2.1"},
{:membrane_rtc_engine, "~> 0.17.1", override: true},
{:membrane_rtc_engine_webrtc, "~> 0.3.0", override: true},
{:membrane_rtc_engine_hls,
github: "jellyfish-dev/membrane_rtc_engine", sparse: "hls", override: true},
{:membrane_rtc_engine_rtsp, "~> 0.2.1"},
{:membrane_ice_plugin, "~> 0.16.0"},
{:membrane_telemetry_metrics, "~> 0.1.0"},
Expand Down
Loading

0 comments on commit aa75980

Please sign in to comment.