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. """ @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_buffer/4` callback. Invoked callback is guaranteed not to receive more data than demanded. ## `:buffers` and `:bytes` demand units When the pad's `demand_unit` is `:buffers` or `:bytes`, the demand size is a non-negative integer that overrides the existing demand, or a function that receives the current demand and returns the new demand. If only the pad ref is specified, the demand size defaults to 1. ## Timestamp demand units When the pad's `demand_unit` is `:timestamp`, `{:timestamp, :pts}`, `{:timestamp, :dts}`, or `{:timestamp, :dts_or_pts}`, the demand size is a `t:Membrane.Time.t/0` duration. The queue will deliver buffers until the elapsed timestamp span (last consumed timestamp minus first consumed timestamp) reaches the demanded duration. Unlike `:buffers`/`:bytes` demands, **timestamp demand does not decrement** as buffers are consumed. To make progress after the demanded window has elapsed, the element must re-issue a larger demand (e.g. advancing the window by the desired step). Issuing the same demand again after the window has been satisfied will result in a warning and no buffers being delivered. Allowed only when playback is playing. """ @type demand :: {:demand, {Pad.ref(), demand_size} | Pad.ref()} @type demand_size :: pos_integer | Membrane.Time.t() | (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 demand for additional buffers on an input pad. Redemanding in `handle_demand` is not allowed in Filters. In situations where the demand cannot be supplied, the element should demand on it's input pad and wait for the necessary data to arrive in `handle_buffer`, and only then call `:redemand`. A typical example of this situation is a parser that has not gotten 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_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_buffer/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. > #### Deprecated {: .warning} > The `:forward` action is deprecated. Use `t:broadcast/0` instead. """ @type forward :: {:forward, Buffer.t() | [Buffer.t()] | StreamFormat.t() | Event.t() | :end_of_stream} @typedoc """ Sends buffers/stream format/event/end of stream to all output pads of the element. The action sent to each output pad is determined by the data type: - `Membrane.Buffer` struct(s) → `:buffer` action on each output pad - Atom `:end_of_stream` → `:end_of_stream` action on each output pad - Struct implementing `Membrane.EventProtocol` → `:event` action on each output pad - Any other struct → `:stream_format` action on each output pad Allowed only when playback is `playing`. """ @type broadcast :: {:broadcast, Buffer.t() | StreamFormat.t() | Event.t() | :end_of_stream | [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 permitted only in callback `c:Membrane.Element.Base.handle_init/2`. The example of usage of these actions is [there](../../../guides/timer.md) """ @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 """ Action that can be always returned from each of the callbacks. """ @type common_actions :: notify_parent | start_timer | timer_interval | stop_timer | terminate | split @typedoc """ Actions that can be returned from callbacks when the element is in `playback: :playing` state. """ @type stream_actions :: event | stream_format | buffer | demand | pause_auto_demand | resume_auto_demand | end_of_stream | redemand @typedoc """ Type that defines a union of actions that may be returned from most of the callbacks. Depending on element type, callback, current playback and other circumstances there may be different actions available. Check the typespec and documentation of particular callbacks for details. """ @type t :: common_actions | stream_actions | latency | forward | broadcast | setup end