Packages
membrane_core
1.3.3
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.7
1.2.6
1.2.5
retired
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc1
1.1.2
1.1.1
1.1.0
1.1.0-rc1
1.1.0-rc0
1.0.1
1.0.0
1.0.0-rc1
1.0.0-rc0
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
retired
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.2
0.10.1
0.10.0
0.9.0
0.8.2
0.8.1
0.8.0
0.7.0
0.6.1
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
Membrane Multimedia Framework (Core)
Current section
Files
Jump to
Current section
Files
lib/membrane/connector.ex
defmodule Membrane.Connector do
@moduledoc """
Membrane Filter with input and output dynamic pads, that forwards incoming data to the opposite
side than the one from which it came.
If pad from one side is not linked yet, the data will be buffered until both pads are linked.
"""
use Membrane.Filter
def_input_pad :input,
accepted_format: _any,
availability: :on_request,
max_instances: 1
def_output_pad :output,
accepted_format: _any,
availability: :on_request,
max_instances: 1
def_options notify_on_stream_format?: [
description: """
If `true`, #{inspect(__MODULE__)} will send `{:stream_format, pad_ref, stream_format}` \
notification to parent on every stream format arrival.
Defaults to `false`.
""",
spec: boolean(),
default: false
],
notify_on_event?: [
description: """
If `true`, #{inspect(__MODULE__)} will send `{:event, pad_ref, event}` notification to \
parent on every event arrival.
Defaults to `false`.
""",
spec: boolean(),
default: false
]
defguardp flowing?(ctx, state)
when ctx.playback == :playing and state.input != nil and state.output != nil
@impl true
def handle_init(_ctx, opts) do
state =
opts
|> Map.from_struct()
|> Map.merge(%{input: nil, output: nil, queue: [], input_demand_paused?: false})
{[], state}
end
@impl true
def handle_pad_added(Pad.ref(direction, _ref) = pad, ctx, state) do
state = state |> Map.put(direction, pad)
{maybe_resume, state} = maybe_resume_auto_demand(state)
{maybe_flush, state} = maybe_flush_queue(ctx, state)
{maybe_resume ++ maybe_flush, state}
end
@impl true
def handle_pad_removed(Pad.ref(direction, _ref), ctx, state) do
state = state |> Map.put(direction, nil)
maybe_pause_auto_demand(ctx, state)
end
@impl true
def handle_start_of_stream(_input_pad, ctx, state), do: maybe_pause_auto_demand(ctx, state)
[handle_buffer: :buffer, handle_event: :event, handle_stream_format: :stream_format]
|> Enum.map(fn {callback, action} ->
@impl true
def unquote(callback)(pad, item, ctx, state) when flowing?(ctx, state) do
actions = [{unquote(action), {opposite_pad(pad, state), item}}]
actions = actions ++ maybe_notify_parent(unquote(action), pad, item, state)
{actions, state}
end
@impl true
def unquote(callback)(pad, item, _ctx, state) do
state = unquote(action) |> store_in_queue(pad, item, state)
actions = unquote(action) |> maybe_notify_parent(pad, item, state)
{actions, state}
end
end)
@impl true
def handle_end_of_stream(pad, ctx, state) when flowing?(ctx, state) do
{[end_of_stream: opposite_pad(pad, state)], state}
end
@impl true
def handle_end_of_stream(pad, _ctx, state) do
{[], store_in_queue(:end_of_stream, pad, nil, state)}
end
defp store_in_queue(action, pad, item, state) do
state |> Map.update!(:queue, &[{action, pad, item} | &1])
end
defp maybe_pause_auto_demand(ctx, state) do
if state.output == nil and not state.input_demand_paused? and
ctx.pads[state.input].stream_format != nil,
do: {[pause_auto_demand: state.input], %{state | input_demand_paused?: true}},
else: {[], state}
end
defp maybe_resume_auto_demand(state) do
if state.input_demand_paused? and state.output != nil,
do: {[resume_auto_demand: state.input], %{state | input_demand_paused?: false}},
else: {[], state}
end
defp maybe_flush_queue(ctx, state) when flowing?(ctx, state) do
actions =
state.queue
|> Enum.reverse()
|> Enum.map(fn
{:end_of_stream, _input, nil} -> {:end_of_stream, state.output}
{action, pad, item} -> {action, {opposite_pad(pad, state), item}}
end)
{actions, %{state | queue: nil}}
end
defp maybe_flush_queue(_ctx, state), do: {[], state}
defp opposite_pad(Pad.ref(:input, _ref), state), do: state.output
defp opposite_pad(Pad.ref(:output, _ref), state), do: state.input
defp maybe_notify_parent(:event, pad, event, %{notify_on_event?: true}),
do: [notify_parent: {:event, pad, event}]
defp maybe_notify_parent(:stream_format, pad, format, %{notify_on_stream_format?: true}),
do: [notify_parent: {:stream_format, pad, format}]
defp maybe_notify_parent(_type, _pad, _item, _state), do: []
end