Packages
membrane_core
0.8.2
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/bin.ex
defmodule Membrane.Bin do
@moduledoc """
Bins, similarly to pipelines, are containers for elements.
However, at the same time, they can be placed and linked within pipelines.
Although bin is a separate Membrane entity, it can be perceived as a pipeline within an element.
Bins can also be nested within one another.
There are two main reasons why bins are useful:
* they enable creating reusable element groups
* they allow managing their children, for instance by dynamically spawning or replacing them as the stream changes.
In order to create bin `use Membrane.Bin` in your callback module.
"""
alias __MODULE__.{Action, CallbackContext}
alias Membrane.{Child, Pad}
alias Membrane.Core.Child.PadsSpecs
alias Membrane.Core.OptionsSpecs
require Membrane.Core.Message
require Membrane.Logger
@type state_t :: map | struct
@type callback_return_t :: {:ok | {:ok, [Action.t()]} | {:error, any}, state_t} | {:error, any}
@typedoc """
Defines options that can be passed to `start_link/3` and received
in `c:handle_init/1` callback.
"""
@type options_t :: struct | nil
@typedoc """
Type that defines a bin name by which it is identified.
"""
@type name_t :: any()
@doc """
Enables to check whether module is membrane bin.
"""
@callback membrane_bin? :: true
@doc """
Callback invoked on initialization of bin process. It should parse options
and initialize bin's internal state. Internally it is invoked inside
`c:GenServer.init/1` callback.
"""
@callback handle_init(options :: options_t) :: callback_return_t()
@doc """
Callback invoked when bin is shutting down.
Internally called in `c:GenServer.terminate/2` callback.
Useful for any cleanup required.
"""
@callback handle_shutdown(reason, state :: state_t) :: :ok
when reason: :normal | :shutdown | {:shutdown, any} | term()
@doc """
Callback that is called when new pad has beed added to bin. Executed
ONLY for dynamic pads.
"""
@callback handle_pad_added(
pad :: Pad.ref_t(),
context :: CallbackContext.PadAdded.t(),
state :: state_t
) :: callback_return_t
@doc """
Callback that is called when some pad of the bin has beed removed. Executed
ONLY for dynamic pads.
"""
@callback handle_pad_removed(
pad :: Pad.ref_t(),
context :: CallbackContext.PadRemoved.t(),
state :: state_t
) :: callback_return_t
@doc """
Automatically implemented callback used to determine whether bin exports clock.
"""
@callback membrane_clock? :: boolean()
@doc """
Callback invoked when bin transition from `:stopped` to `:prepared` state has finished,
that is all of its children are prepared to enter `:playing` state.
"""
@callback handle_stopped_to_prepared(
context :: CallbackContext.PlaybackChange.t(),
state :: state_t
) ::
callback_return_t
@doc """
Callback invoked when bin transition from `:playing` to `:prepared` state has finished,
that is all of its children are prepared to be stopped.
"""
@callback handle_playing_to_prepared(
context :: CallbackContext.PlaybackChange.t(),
state :: state_t
) ::
callback_return_t
@doc """
Callback invoked when bin is in `:playing` state, i.e. all its children
are in this state.
"""
@callback handle_prepared_to_playing(
context :: CallbackContext.PlaybackChange.t(),
state :: state_t
) ::
callback_return_t
@doc """
Callback invoked when bin is in `:playing` state, i.e. all its children
are in this state.
"""
@callback handle_prepared_to_stopped(
context :: CallbackContext.PlaybackChange.t(),
state :: state_t
) ::
callback_return_t
@doc """
Callback invoked when bin is in `:terminating` state, i.e. all its children
are in this state.
"""
@callback handle_stopped_to_terminating(
context :: CallbackContext.PlaybackChange.t(),
state :: state_t
) :: callback_return_t
@doc """
Callback invoked when a notification comes in from an element.
"""
@callback handle_notification(
notification :: Membrane.Notification.t(),
element :: Child.name_t(),
context :: CallbackContext.Notification.t(),
state :: state_t
) :: callback_return_t
@doc """
Callback invoked when bin receives a message that is not recognized
as an internal membrane message.
Useful for receiving data sent from NIFs or other stuff.
"""
@callback handle_other(
message :: any,
context :: CallbackContext.Other.t(),
state :: state_t
) ::
callback_return_t
@doc """
Callback invoked when a child element starts processing stream via given pad.
"""
@callback handle_element_start_of_stream(
{Child.name_t(), Pad.ref_t()},
context :: CallbackContext.StreamManagement.t(),
state :: state_t
) :: callback_return_t
@doc """
Callback invoked when a child element finishes processing stream via given pad.
"""
@callback handle_element_end_of_stream(
{Child.name_t(), Pad.ref_t()},
context :: CallbackContext.StreamManagement.t(),
state :: state_t
) :: callback_return_t
@doc """
Callback invoked when `Membrane.ParentSpec` is linked and in the same playback
state as bin.
This callback can be started from `c:handle_init/1` callback or as
`t:Membrane.Bin.Action.spec_t/0` action.
"""
@callback handle_spec_started(
children :: [Child.name_t()],
context :: CallbackContext.SpecStarted.t(),
state :: state_t
) :: callback_return_t
@doc """
Callback invoked upon each timer tick. A timer can be started with `t:Membrane.Bin.Action.start_timer_t/0`
action.
"""
@callback handle_tick(
timer_id :: any,
context :: CallbackContext.Tick.t(),
state :: state_t
) :: callback_return_t
@optional_callbacks membrane_clock?: 0,
handle_init: 1,
handle_shutdown: 2,
handle_pad_added: 3,
handle_pad_removed: 3,
handle_stopped_to_prepared: 2,
handle_playing_to_prepared: 2,
handle_prepared_to_playing: 2,
handle_prepared_to_stopped: 2,
handle_stopped_to_terminating: 2,
handle_other: 3,
handle_spec_started: 3,
handle_element_start_of_stream: 3,
handle_element_end_of_stream: 3,
handle_notification: 4,
handle_tick: 3
@doc PadsSpecs.def_pad_docs(:input, :bin)
defmacro def_input_pad(name, spec) do
PadsSpecs.def_pad(name, :input, spec, :bin)
end
@doc PadsSpecs.def_pad_docs(:output, :bin)
defmacro def_output_pad(name, spec) do
PadsSpecs.def_pad(name, :output, spec, :bin)
end
@doc """
Defines that bin exposes a clock which is a proxy to one of its children.
If this macro is not called, no ticks will be forwarded to parent, regardless
of clock definitions in its children.
"""
defmacro def_clock(doc \\ "") do
quote do
@membrane_bin_exposes_clock true
Module.put_attribute(__MODULE__, :membrane_clock_moduledoc, """
## Clock
This bin exposes a clock of one of its children.
#{unquote(doc)}
""")
@impl true
def membrane_clock?, do: true
end
end
@doc """
Checks whether module is a bin.
"""
@spec bin?(module) :: boolean
def bin?(module) do
module |> Bunch.Module.check_behaviour(:membrane_bin?)
end
@doc """
Macro defining options that parametrize bin.
It automatically generates appropriate struct and documentation.
#{OptionsSpecs.options_doc()}
"""
defmacro def_options(options) do
OptionsSpecs.def_options(__CALLER__.module, options, :bin)
end
@doc false
defmacro __before_compile__(env) do
Membrane.Core.Child.generate_moduledoc(env.module, :bin)
end
@doc """
Brings all the stuff necessary to implement a bin.
Options:
- `:bring_spec?` - if true (default) imports and aliases `Membrane.ParentSpec`
- `:bring_pad?` - if true (default) requires and aliases `Membrane.Pad`
"""
defmacro __using__(options) do
bring_spec =
if options |> Keyword.get(:bring_spec?, true) do
quote do
import Membrane.ParentSpec
alias Membrane.ParentSpec
end
end
bring_pad =
if options |> Keyword.get(:bring_pad?, true) do
quote do
require Membrane.Pad
alias Membrane.Pad
end
end
quote location: :keep do
alias unquote(__MODULE__)
@behaviour unquote(__MODULE__)
@before_compile unquote(__MODULE__)
unquote(bring_spec)
unquote(bring_pad)
import unquote(__MODULE__),
only: [def_input_pad: 2, def_output_pad: 2, def_options: 1, def_clock: 0, def_clock: 1]
require Membrane.Core.Child.PadsSpecs
Membrane.Core.Child.PadsSpecs.ensure_default_membrane_pads()
@impl true
def membrane_bin?, do: true
@impl true
def membrane_clock?, do: false
@impl true
def handle_init(_options), do: {{:ok, spec: %Membrane.ParentSpec{}}, %{}}
@impl true
def handle_shutdown(_reason, _state), do: :ok
@impl true
def handle_pad_added(_pad, _ctx, state), do: {:ok, state}
@impl true
def handle_pad_removed(_pad, _ctx, state), do: {:ok, state}
@impl true
def handle_stopped_to_prepared(_ctx, state), do: handle_stopped_to_prepared(state)
@impl true
def handle_prepared_to_playing(_ctx, state), do: handle_prepared_to_playing(state)
@impl true
def handle_playing_to_prepared(_ctx, state), do: handle_playing_to_prepared(state)
@impl true
def handle_prepared_to_stopped(_ctx, state), do: handle_prepared_to_stopped(state)
@impl true
def handle_stopped_to_terminating(_ctx, state), do: handle_stopped_to_terminating(state)
@impl true
def handle_other(message, _ctx, state), do: handle_other(message, state)
@impl true
def handle_spec_started(new_children, _ctx, state),
do: handle_spec_started(new_children, state)
@impl true
def handle_element_start_of_stream({element, pad}, _ctx, state),
do: handle_element_start_of_stream({element, pad}, state)
@impl true
def handle_element_end_of_stream({element, pad}, _ctx, state),
do: handle_element_end_of_stream({element, pad}, state)
@impl true
def handle_notification(notification, element, _ctx, state),
do: handle_notification(notification, element, state)
# DEPRECATED:
# credo:disable-for-lines:22 Credo.Check.Readability.Specs
@doc false
def handle_stopped_to_prepared(state), do: {:ok, state}
@doc false
def handle_prepared_to_playing(state), do: {:ok, state}
@doc false
def handle_playing_to_prepared(state), do: {:ok, state}
@doc false
def handle_prepared_to_stopped(state), do: {:ok, state}
@doc false
def handle_stopped_to_terminating(state), do: {:ok, state}
@doc false
def handle_other(_message, state), do: {:ok, state}
@doc false
def handle_spec_started(_new_children, state), do: {:ok, state}
@doc false
def handle_element_start_of_stream({_element, _pad}, state), do: {:ok, state}
@doc false
def handle_element_end_of_stream({_element, _pad}, state), do: {:ok, state}
@doc false
def handle_notification(notification, _element, state),
do: {{:ok, notify: notification}, state}
deprecated = [
handle_stopped_to_prepared: 1,
handle_prepared_to_playing: 1,
handle_playing_to_prepared: 1,
handle_prepared_to_stopped: 1,
handle_stopped_to_terminating: 1,
handle_other: 2,
handle_spec_started: 2,
handle_element_start_of_stream: 2,
handle_element_end_of_stream: 2,
handle_notification: 3
]
defoverridable [
membrane_clock?: 0,
handle_init: 1,
handle_shutdown: 2,
handle_pad_added: 3,
handle_pad_removed: 3,
handle_stopped_to_prepared: 2,
handle_playing_to_prepared: 2,
handle_prepared_to_playing: 2,
handle_prepared_to_stopped: 2,
handle_stopped_to_terminating: 2,
handle_other: 3,
handle_spec_started: 3,
handle_element_start_of_stream: 3,
handle_element_end_of_stream: 3,
handle_notification: 4
] ++ deprecated
end
end
end