defmodule Membrane.Element.Action do
@moduledoc """
This module contains type specifications of actions that can be returned
from element callbacks.
Returning actions is a way of element interaction with
other elements and parts of framework. Each action may be returned by any
callback unless explicitly stated otherwise.
"""
alias Membrane.{Buffer, ChildNotification, Clock, Event, Pad, StreamFormat}
@typedoc """
Action that manages the end of the component setup.
By default, component setup ends with the end of `c:Membrane.Element.Base.handle_setup/2` callback.
If `{:setup, :incomplete}` is returned there, setup lasts until `{:setup, :complete}`
is returned from antoher callback.
Untils the setup lasts, the component won't enter `:playing` playback.
"""
@type setup :: {:setup, :incomplete | :complete}
@typedoc """
Sends a message to the parent.
"""
@type notify_parent :: {:notify_parent, ChildNotification.t()}
@typedoc """
Sends an event through a pad (input or output).
Allowed only when playback is `playing`.
"""
@type event :: {:event, {Pad.ref(), Event.t()}}
@typedoc """
Allows to split callback execution into multiple applications of another callback
(called from now sub-callback).
Executions are synchronous in the element process, and each of them passes
subsequent arguments from the args_list, along with the element state (passed
as the last argument each time).
Return value of each execution of sub-callback can be any valid return value
of the original callback (this also means sub-callback can return any action
valid for the original callback, unless explicitly stated). Returned actions
are executed immediately (they are NOT accumulated and executed after all
sub-callback executions are finished).
Useful when a long action is to be undertaken, and partial results need to
be returned before entire process finishes (e.g. default implementation of
`c:Membrane.WithInputPads.handle_buffers_batch/4` uses split action to invoke
`c:Membrane.WithInputPads.handle_buffer/4` with each buffer)
"""
@type split :: {:split, {callback_name :: atom, args_list :: [[any]]}}
@typedoc """
Sends stream format through a pad.
The pad must have output direction. Sent stream format must fit constraints on the pad.
Allowed only when playback is `playing`.
"""
@type stream_format :: {:stream_format, {Pad.ref(), StreamFormat.t()}}
@typedoc """
Sends buffers through a pad.
The pad must have output direction.
Allowed only when playback is playing.
"""
@type buffer :: {:buffer, {Pad.ref(), Buffer.t() | [Buffer.t()]}}
@typedoc """
Makes a demand on a pad.
The pad must have input direction and work in `:manual` flow control mode. This action does NOT
entail _sending_ demand through the pad, but just _requesting_ some amount
of data from pad's internal queue, which _sends_ demands automatically when it
runs out of data.
If there is any data available at the pad, the data is passed to
`c:Membrane.WithInputPads.handle_buffers_batch/4` callback. Invoked callback is
guaranteed not to receive more data than demanded.
Demand size can be either a non-negative integer, that overrides existing demand,
or a function that is passed current demand, and is to return the new demand.
Allowed only when playback is playing.
"""
@type demand :: {:demand, {Pad.ref(), demand_size}}
@type demand_size :: pos_integer | (pos_integer() -> non_neg_integer())
@typedoc """
Pauses auto-demanding on the specific pad.
The pad must have input direction and work in `:auto` flow control mode.
This action does not guarantee that no more buffers will arrive on the specific pad,
but ensures, that demand on this pad will not increase until returning
`#{inspect(__MODULE__)}.resume_auto_demand()` action. Number of buffers, that will
arrive on the pad, depends on the behaviour of the elements earlier in the pipeline.
When auto-demanding is already paused, this action has no effect.
"""
@type pause_auto_demand :: {:pause_auto_demand, Pad.ref() | [Pad.ref()]}
@typedoc """
Resumes auto-demanding on the specific pad.
The pad must have input direction and work in `:auto` flow control mode.
This action reverts the effects of `#{inspect(__MODULE__)}.pause_auto_demand()` action.
When auto demanding is not paused, this action has no effect.
"""
@type resume_auto_demand :: {:resume_auto_demand, Pad.ref() | [Pad.ref()]}
@typedoc """
Executes `c:Membrane.Element.WithOutputPads.handle_demand/5` callback
for the given pad (or pads), that have demand greater than 0.
The pad must have output direction and work in pull mode.
## Redemand in Sources and Endpoints
In case of Sources and Endpoints, `:redemand` is just a helper that simplifies element's code.
The element doesn't need to generate the whole demand synchronously at `handle_demand`
or store current demand size in its state, but it can just generate one buffer
and return `:redemand` action.
If there is still one or more buffers to produce, returning `:redemand` triggers
the next invocation of `handle_demand`. In such case, the element is to produce
next buffer and call `:redemand` again.
If there are no more buffers demanded, `handle_demand` is not invoked and
the loop ends.
One more advantage of the approach with `:redemand` action is that produced buffers
are sent one after another in separate messages and this can possibly improve
the latency.
## Redemand in Filters
Redemand in Filters is useful in a situation where not the entire demand of
output pad has been satisfied and there is a need to send a demand for additional
buffers through the input pad.
A typical example of this situation is a parser that has not demanded enough
bytes to parse the whole frame.
## Usage limitations
Allowed only when playback is playing.
"""
@type redemand :: {:redemand, Pad.ref() | [Pad.ref()]}
@typedoc """
Sends buffers/stream format/event/end of stream to all output pads of element (or to input
pads when event occurs on the output pad).
Used by default implementations of
`c:Membrane.Element.WithInputPads.handle_stream_format/4`,
`c:Membrane.Element.Base.handle_event/4` and
`c:Membrane.Element.WithInputPads.handle_end_of_stream/3` callbacks in filter.
Allowed only when _all_ below conditions are met:
- element is filter,
- callback is `c:Membrane.Element.WithInputPads.handle_buffers_batch/4`,
`c:Membrane.Element.WithInputPads.handle_buffer/4`,
`c:Membrane.Element.WithInputPads.handle_stream_format/4`,
`c:Membrane.Element.Base.handle_event/4` or `c:Membrane.Element.WithInputPads.handle_end_of_stream/3`
- playback is `playing`
Keep in mind that `c:Membrane.WithInputPads.handle_buffers_batch/4` can only
forward buffers, `c:Membrane.Element.WithInputPads.handle_stream_format/4` - stream formats.
`c:Membrane.Element.Base.handle_event/4` - events and
`c:Membrane.Element.WithInputPads.handle_end_of_stream/3` - ends of streams.
"""
@type forward ::
{:forward, Buffer.t() | [Buffer.t()] | StreamFormat.t() | Event.t() | :end_of_stream}
@typedoc """
Starts a timer that will invoke `c:Membrane.Element.Base.handle_tick/3` callback
every `interval` according to the given `clock`.
The timer's `id` is passed to the `c:Membrane.Element.Base.handle_tick/3`
callback and can be used for changing its interval via `t:timer_interval/0`
or stopping it via `t:stop_timer/0`.
If `interval` is set to `:no_interval`, the timer won't issue any ticks until
the interval is set with `t:timer_interval/0` action.
If no `clock` is passed, parent's clock is chosen.
Timers use `Process.send_after/3` under the hood.
"""
@type start_timer ::
{:start_timer,
{timer_id :: any, interval :: Ratio.t() | Membrane.Time.non_neg() | :no_interval}
| {timer_id :: any, interval :: Ratio.t() | Membrane.Time.non_neg() | :no_interval,
clock :: Clock.t()}}
@typedoc """
Changes interval of a timer started with `t:start_timer/0`.
Permitted only from `c:Membrane.Element.Base.handle_tick/3`, unless the interval
was previously set to `:no_interval`.
If the `interval` is `:no_interval`, the timer won't issue any ticks until
another `t:timer_interval/0` action. Otherwise, the timer will issue ticks every
new `interval`. The next tick after interval change is scheduled at
`new_interval + previous_time`, where previous_time is the time of the latest
tick or the time of returning `t:start_timer/0` action if no tick has been
sent yet. Note that if `current_time - previous_time > new_interval`, a burst
of `div(current_time - previous_time, new_interval)` ticks is issued immediately.
"""
@type timer_interval ::
{:timer_interval,
{timer_id :: any, interval :: Ratio.t() | Membrane.Time.non_neg() | :no_interval}}
@typedoc """
Stops a timer started with `t:start_timer/0` action.
This action is atomic: stopping timer guarantees that no ticks will arrive from it.
"""
@type stop_timer :: {:stop_timer, timer_id :: any}
@typedoc """
This action sets the latency for the element.
This action is not premitted in callback `c:Membrane.Element.Base.handle_init/2`.
"""
@type latency :: {:latency, latency :: Membrane.Time.non_neg()}
@typedoc """
Marks that processing via a pad (output) has been finished and the pad instance
won't be used anymore.
Triggers `end_of_stream/3` callback at the receiver element.
Allowed only when playback is in playing state.
"""
@type end_of_stream :: {:end_of_stream, Pad.ref()}
@typedoc """
Terminates element with given reason.
Termination reason follows the OTP semantics:
- Use `:normal` for graceful termination. Allowed only when the parent already requested termination,
i.e. after `c:Membrane.Element.Base.handle_terminate_request/2` is called
- If reason is neither `:normal`, `:shutdown` nor `{:shutdown, term}`, an error is logged
"""
@type terminate :: {:terminate, reason :: :normal | :shutdown | {:shutdown, term} | term}
@typedoc """
Type that defines a single action that may be returned from element callbacks.
Depending on element type, callback, current playback and other
circumstances there may be different actions available.
"""
@type t ::
setup
| event
| notify_parent
| split
| stream_format
| buffer
| demand
| redemand
| pause_auto_demand
| resume_auto_demand
| forward
| start_timer
| timer_interval
| stop_timer
| latency
| end_of_stream
| terminate
end