Packages
membrane_hls_plugin
2.0.16
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.16
2.0.15
2.0.14
2.0.13
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
Adaptive live streaming (HLS) plugin for the Membrane Framework.
Current section
Files
Jump to
Current section
Files
lib/membrane/hls/shifter.ex
defmodule Membrane.HLS.Shifter do
@moduledoc """
This element is responsible for creating a strictly monotonically increasing DTS/PTS stream
starting from a particular point in time. Used when putting discontinuities in the playlist
is not an option and for now when the output playlist is not a sliding window one.
It starts from a previous duration (e.g. the duration of the track up to now) and goes on.
At startup, it coordinates with the other shifters to obtain t_zero, from which each buffer
will compute their offsets. This is done because normally audio/video do not start exactly
at the same T and we must to preserve this offset. Note that this is going to cause a hole
in the PTS/DTS stream which is effectively a discontinuity. For most players, this does
not change a dime.
"""
use Membrane.Filter
def_input_pad(:input,
accepted_format: any_of(Membrane.H264, Membrane.AAC, Membrane.Text, Membrane.RemoteStream)
)
def_output_pad(:output,
accepted_format: any_of(Membrane.H264, Membrane.AAC, Membrane.Text, Membrane.RemoteStream)
)
def_options(
duration: [
spec: Membrane.Time.t(),
description: """
The duration of the track we're adding segments to. Timing will restart from this value.
"""
]
)
@impl true
def handle_init(_ctx, opts) do
{[],
%{
offset: opts.duration,
waiting_first_buffer: true,
waiting_t_zero: true,
queue: :queue.new(),
t_zero: nil
}}
end
@impl true
def handle_end_of_stream(:input, _ctx, state = %{t_zero: nil}) do
if :queue.is_empty(state.queue) do
{[end_of_stream: :output], state}
else
# Flush the pending buffers first. For that, we need the parent notification.
{[], state}
end
end
def handle_end_of_stream(:input, _ctx, state) do
{[end_of_stream: :output], state}
end
@impl true
def handle_parent_notification({:t_zero, t_zero}, ctx, state) do
state =
state
|> put_in([:t_zero], t_zero)
|> put_in([:waiting_t_zero], false)
# In case we did not receive the first buffer yet but we received
# the notification already, we just skip the whole t_zero communication thing.
|> put_in([:waiting_first_buffer], false)
{buffers, state} =
get_and_update_in(state, [:queue], fn q ->
buffers =
q
|> :queue.to_list()
|> Enum.map(&shift_buffer(state, &1))
{buffers, :queue.new()}
end)
actions = [buffer: {:output, buffers}]
if ctx.pads.input.end_of_stream? do
{actions ++ [end_of_stream: :output], state}
else
{actions, state}
end
end
@impl true
def handle_buffer(:input, buffer, _ctx, state = %{waiting_first_buffer: true}) do
state =
state
|> enqueue_buffer(buffer)
|> put_in([:waiting_first_buffer], false)
{[notify_parent: {:t_zero, Membrane.Buffer.get_dts_or_pts(buffer)}], state}
end
def handle_buffer(:input, buffer, _ctx, state = %{waiting_t_zero: true}) do
{[], enqueue_buffer(state, buffer)}
end
def handle_buffer(:input, buffer, _ctx, state) do
{[buffer: {:output, shift_buffer(state, buffer)}], state}
end
defp enqueue_buffer(state, buffer) do
update_in(state, [:queue], fn q -> :queue.in(buffer, q) end)
end
defp shift_buffer(state, buffer) do
%{
buffer
| pts: compute_offset(buffer.pts, state),
dts: compute_offset(buffer.dts, state),
metadata: update_metadata(buffer.metadata, state)
}
end
defp update_metadata(%{to: to} = metadata, state),
do: %{metadata | to: compute_offset(to, state)}
defp update_metadata(metadata, _state), do: metadata
defp compute_delta(t, state) when state.t_zero >= 0, do: t - state.t_zero
# When the offset is negative we have no reason to change it, it means the
# stream is just started.
defp compute_delta(t, _state), do: t
defp compute_offset(nil, _state), do: nil
defp compute_offset(t, state) do
state.offset + compute_delta(t, state)
end
end