-
Notifications
You must be signed in to change notification settings - Fork 33
/
pad.ex
191 lines (157 loc) · 7.08 KB
/
pad.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
defmodule Membrane.Pad do
@moduledoc """
Pads are units defined by elements and bins, allowing them to be linked with their
siblings. This module consists of pads typespecs and utils.
Each pad is described by its name, direction, availability, mode and possible stream format.
For pads to be linkable, these properties have to be compatible. For more
information on each of them, check appropriate type in this module.
Each link can only consist of exactly two pads.
"""
use Bunch
alias Membrane.Buffer
@availability_values [:always, :on_request]
@typedoc """
Defines the term by which the pad instance is identified.
"""
@type ref :: name | {__MODULE__, name, dynamic_id}
@typedoc """
Possible id of dynamic pad
"""
@type dynamic_id :: any
@typedoc """
Defines the name of pad or group of dynamic pads
"""
@type name :: atom
@typedoc """
Defines possible pad directions:
- `:output` - data can only be sent through such pad,
- `:input` - data can only be received through such pad.
One cannot link two pads with the same direction.
"""
@type direction :: :output | :input
@typedoc """
Describes how an element sends and receives data.
The available values for that field are:
- `:manual` - meaning that the pad works in a pull mode and the demand is manually handled and requested.
An element with output `:manual` pad can send data through such a pad only if it has already received demand
on that pad. And element with input `:manual` pad receives data through such a pad only if it has been
previously demanded, so that no undemanded data can arrive For more info, see `Membrane.Element.Action.demand`,
`Membrane.Element.Action.redemand` and `c:Membrane.Element.WithOutputPads.handle_demand/5`.
- `:auto` - meaning that the pad works in a pull mode and the demand is managed automatically:
the core ensures that there's demand on each input pad (that has `flow_control` set to `:auto`)
whenever there's demand on all output pads (that have `flow_control` set to `:auto`).
Currently works only for `Membrane.Filter`s.
- `:push` - meaning that the pad works in a push mode. An element with a `:push` output pad can send data
through that pad whenever it wants. An element with a `:push` input pad has to deal with data whenever it
comes through such a pad - note, that it needs to be done fast enough so that not to let data accumulate,
what may lead to overflow of element process erlang queue, which is highly unwanted.
Linking pads with different flow control is possible, but only in case of an output pad
working in a push mode, and an input pad in pull mode (that is - with `:manual` or `:auto` flow control).
In such case, however, an error will be raised whenever too many buffers accumulate on the input pad,
waiting to be processed.
"""
@type flow_control :: :auto | :manual | :push
@typedoc """
Values used when defining pad availability:
- `:always` - a static pad, which can remain unlinked in `stopped` state only.
- `:on_request` - a dynamic pad, instance of which is created every time it is
linked to another pad. Thus linking the pad with _k_ other pads, creates _k_
instances of the pad, and links each with another pad.
"""
@type availability :: unquote(Bunch.Typespec.enum_to_alternative(@availability_values))
@typedoc """
Type describing availability mode of a created pad:
- `:static` - there always exist exactly one instance of such pad.
- `:dynamic` - multiple instances of such pad may be created and removed (which
entails executing `handle_pad_added` and `handle_pad_removed` callbacks,
respectively).
"""
@type availability_mode :: :static | :dynamic
@typedoc """
Describes pattern, that should be matched by stream format send by element on specific
pad. Will not be evaluated during runtime, but used for matching struct passed in
`:stream_format` action.
Can be a module name, pattern describing struct, or call to `any_of` function, which
arguments are such patterns or modules names.
If a module name is passed to the `:accepted_format` option or is passed to `any_of`,
it will be converted to the match on a struct defined in that module, eg.
`accepted_format: My.Format` will have this same effect, as `accepted_format: %My.Format{}`
and `accepted_format: any_of(My.Format, %My.Another.Format{field: value} when value in
[:some, :enumeration])` will have this same effect, as `accepted_format: any_of(%My.Format{},
%My.Another.Format{field: value} when value in [:some, :enumeration])`
"""
@type accepted_format :: module() | (pattern :: term())
@typedoc """
Describes how a pad should be declared in element or bin.
"""
@type spec :: element_spec | bin_spec
@typedoc """
Describes how a pad should be declared inside a bin.
Demand unit is derived from the first element inside the bin linked to the
given input.
"""
@type bin_spec ::
{name(),
availability: availability(), accepted_format: accepted_format(), options: Keyword.t()}
@typedoc """
Describes how a pad should be declared inside an element.
"""
@type element_spec ::
{name(),
availability: availability(),
accepted_format: accepted_format(),
flow_control: flow_control(),
options: Keyword.t(),
demand_unit: Buffer.Metric.unit()}
@typedoc """
Type describing a pad. Contains data parsed from `t:spec/0`
"""
@type description :: %{
:availability => availability(),
optional(:flow_control) => flow_control(),
:name => name(),
:accepted_formats_str => [String.t()],
optional(:demand_unit) => Buffer.Metric.unit() | nil,
:direction => direction(),
:options => nil | Keyword.t()
}
@doc """
Creates a static pad reference.
"""
defmacro ref(name) do
quote do
unquote(name)
end
end
@doc """
Creates a dynamic pad reference.
"""
defmacro ref(name, id) do
quote do
{unquote(__MODULE__), unquote(name), unquote(id)}
end
end
defguard is_pad_ref(term)
when term |> is_atom or
(term |> is_tuple and term |> tuple_size == 3 and term |> elem(0) == __MODULE__ and
term |> elem(1) |> is_atom)
defguard is_pad_name(term) when is_atom(term)
defguard is_availability(term) when term in @availability_values
defguard is_availability_dynamic(availability) when availability == :on_request
defguard is_availability_static(availability) when availability == :always
@doc """
Returns pad availability mode for given availability.
"""
@spec availability_mode(availability) :: availability_mode
def availability_mode(:always), do: :static
def availability_mode(:on_request), do: :dynamic
@doc """
Returns the name for the given pad reference
"""
@spec name_by_ref(ref()) :: name()
def name_by_ref(ref(name, _id)) when is_pad_name(name), do: name
def name_by_ref(name) when is_pad_name(name), do: name
@spec opposite_direction(direction()) :: direction()
def opposite_direction(:input), do: :output
def opposite_direction(:output), do: :input
end