11# Everything about pads
22
3- When developing intuition about the structure of pipelines pads are
3+ When developing intuition about the structure of pipelines, pads are
44something that can't be ignored. If you think about elements and bins (from now
5- on referred to as components) as some
5+ on collectively referred to as components) as some
66sort of containers or boxes in which processing happens, then pads are the parts
7- that with these containers are connected with. There are some constraints
8- regarding pads:
7+ that these containers are connected with.
98
10- * A pad of one component can only connect to a single pad of other component and
11- only once two pads are linked communication through them can happen.
9+ ![ image] ( assets/pads1.png )
10+
11+ There are some constraints regarding pads:
12+
13+ * A pad of one component can only connect to a single pad of another component.
14+ Only once these two pads are linked, communication through them can happen.
1215* One pad needs to be an input pad, and the other an output pad.
1316* The accepted formats of the pads need to match - stream formats passing
14- between elements through these pads need to match accepted formats of both.
17+ between elements through these pads need to match the accepted formats of both.
1518
1619When looking at the insides of components, the pads are their main way to
1720communicate with other components in the pipeline. There are four types
18- of informations that can be exchanged between components through pads -
21+ of information that can be exchanged between components through pads -
1922[ stream formats] ( `t:Membrane.StreamFormat.t/0` ) ,
2023[ events] ( `t:Membrane.Event.t/0` ) , [ buffers] ( `t:Membrane.Buffer.t/0` ) and
2124` :end_of_stream ` s.
2225
23- When an component receives one of these informations from another one, it receives
26+ When a component receives information of one of these types from another one, it receives
2427it on a pad. The reference to this pad is then also available as an argument
25- of the callback that handles the received information. For example an invocation
28+ of the callback that handles the received information. For example, an invocation
2629of a [ ` handle_buffer/4 ` ] ( `c:Membrane.Element.WithInputPads.handle_buffer/4` )
2730callback would mean that a buffer ` buffer ` has arrived on a pad ` some_pad ` :
2831
@@ -33,7 +36,7 @@ def handle_buffer(some_pad, buffer, context, state) do
3336end
3437```
3538
36- When an component wants to send an stream format, event or buffer to another
39+ When a component wants to send information to another
3740component in the pipeline, it should send it on a pad that's linked to it. It
3841can do that by using the pad reference in actions that send these types of
3942information, for example returning a
@@ -50,12 +53,12 @@ end ^^^^^^^^
5053
5154## Defining pads
5255
53- To define what pads an component will have and how they'll behave we use
56+ To define what pads a component will have and how they'll behave, we use
5457[ ` def_input_pad/2 ` ] ( `Membrane.Element.WithInputPads.def_input_pad/2` )
5558and [ ` def_output_pad/2 ` ] ( `Membrane.Element.WithOutputPads.def_output_pad/2` ) macros.
5659Input pads can only be defined for Bins, Sinks, Filters and Endpoints, and output
5760pads can only be defined for Bins, Sources, Filters and Endpoints.
58- The first argument for these macros is a name, which then will be used to
61+ The first argument for these macros is a name, which will then be used to
5962identify the pads. The second argument is a [ pad spec] ( `t:Membrane.Pad.element_spec/0` )
6063keyword list, which is used to define how this pad will work. An option we'll
6164now focus on is [ ` availability ` ] ( `t:Membrane.Pad.availability/0` ) , which
@@ -64,20 +67,20 @@ determines if the pad is _static_ or _dynamic_.
6467### Static pads
6568
6669Static pads are pretty straightforward - when a static pad is defined there
67- will always be exactly one instance of this pad and it's referenced by it's
70+ will always be exactly one instance of this pad and it's referenced by its
6871name.
6972
7073#### File Source Example
7174
72- Example of an element with only static pads is a [ File Source] ( https://hexdocs.pm/membrane_file_plugin/Membrane.File.Source.html ) .
73- This element reads contents of a file and sends them in batches through a static
75+ An example of an element with only static pads is a [ File Source] ( https://hexdocs.pm/membrane_file_plugin/Membrane.File.Source.html ) .
76+ This element reads the contents of a file and sends them in batches through a static
7477output pad. The content of the buffers sent by this element is unknown - the file
7578that's being read can contain anything - so this pad has ` :accepted_format ` set to
7679` %RemoteStream{type: :bytestream} ` . That means that any stream format that
7780matches on this struct can be sent on the output pad and this fact has to be
7881accounted for when linking a component after the source.
7982
80- A pipeline spec with a file source passing buffers to a MP4 demuxer could look
83+ A pipeline spec with a file source passing buffers to an MP4 demuxer could look
8184like this:
8285
8386``` elixir
9497```
9598
9699This spec will link a pad named ` :output ` of the source to a pad named
97- ` :input ` of the demuxer. However this can be shortened - if an output pad is
100+ ` :input ` of the demuxer. However, this can be shortened - if an output pad is
98101called ` :output ` or an input pad is called ` :input ` , their respective
99102[ ` via_in/3 ` ] ( `Membrane.ChildrenSpec.via_in/3` ) and
100103[ ` via_out/3 ` ] ( `Membrane.ChildrenSpec.via_out/3` ) calls can be omitted and
113116
114117### Dynamic pads
115118
116- Dynamic pads are a bit more complex. They're used when the amount of pads of
119+ Dynamic pads are a bit more complex. They're used when the number of pads of a
117120given type is variable - dependent on the processed stream or external factors.
118121The creation of these pads is controlled by the parent of the component - if a
119122[ ` :spec ` ] ( `t:Membrane.Pipeline.Action.spec/0` ) action linking the dynamic pad is
@@ -122,35 +125,35 @@ handle this, in most cases with
122125[ ` handle_pad_added/3 ` ] ( `c:Membrane.Element.Base.handle_pad_added/3` ) . This
123126callback is called only for dynamic pads.
124127
125- Another thing that's different are the pad references . The pad's name can't just
128+ Another thing that's different is the way the pad are referenced . The pad's name can't just
126129be used as the pad's reference, because it wouldn't be unique. Dynamic pads are
127- identified by [ ` Pad.ref/2 ` ] ( `Membrane.Pad.ref/2` ) , that takes the pad's
130+ identified by [ ` Pad.ref/2 ` ] ( `Membrane.Pad.ref/2` ) , which takes the pad's
128131name and some unique reference as arguments. The result is a unique pad reference
129- that is also associated with a given pad's specification through it's name. When
130- a new pad is linked, it's reference is made known to the element through the
132+ that is also associated with a given pad's specification through its name. When
133+ a new pad is linked, its reference is made known to the element through the
131134first argument of
132135[ ` handle_pad_added/3 ` ] ( `c:Membrane.Element.Base.handle_pad_added/3` ) .
133136
134137#### MP4 Demuxer Example
135138
136139An example of an element using dynamic pads is an
137140[ MP4 Demuxer] ( https://hexdocs.pm/membrane_mp4_plugin/Membrane.MP4.Demuxer.ISOM.html ) .
138- This element has a input pad, from which it receives contents of a MP4
141+ This element has an input pad, from which it receives the contents of an MP4
139142container, and output pads, on which it'll send the different tracks
140143that were in the container. The input pad can be static, however MP4 containers can
141144have different numbers and kinds of tracks, so the output pad needs to be dynamic.
142145
143146We'll consider the case when we don't have any prior information about the
144147tracks in this MP4 container. Because of this, the parent pipeline or bin of this
145148demuxer won't initially know how many pads should be linked. To solve this
146- problem the demuxer will identify the tracks in the incoming stream and send a
147- message to it's parent in the form of
149+ problem, the demuxer will identify the tracks in the incoming stream and send a
150+ message to its parent in the form of
148151[ ` {:new_tracks, [{track_id :: integer(), content :: struct()}]} ` ] ( https://hexdocs.pm/membrane_mp4_plugin/Membrane.MP4.Demuxer.ISOM.html#t:new_tracks_t/0 ) .
149- The list contains a list of tuples corresponding to tracks, where the first
150- element is a track id and will be used to identify corresponding pad,
151- and the second a stream format contained in the track.
152+ The list consists of tuples corresponding to tracks, where the first
153+ element is a track id and will be used to identify the corresponding pad,
154+ and the second is a stream format contained in the track.
152155
153- Initially the parent will only create the elements before and including the
156+ Initially, the parent will only create the elements before and including the
154157demuxer:
155158
156159``` elixir
@@ -164,8 +167,8 @@ def handle_init(_context, state) do
164167end
165168```
166169
167- The source will start providing the demuxer the MP4 container content,
168- from which the demuxer will identify tracks and notify it's parent
170+ The source will start providing the demuxer with the MP4 container content,
171+ from which the demuxer will identify tracks and notify its parent
169172about them. The parent now has to link an output pad of the demuxer for each
170173track received, which can look like this:
171174
184187```
185188
186189The elements that the output pads will be linked to should be based on what stream
187- format is in ` format ` variable - different formats require different approaches.
190+ format is in the ` format ` variable - different formats require different approaches.
188191
189192After this spec is returned, the demuxer will now have the
190193[ ` handle_pad_added/3 ` ] ( `c:Membrane.Element.Base.handle_pad_added/3` ) callback
200203It will now know that these pads are linked and
201204ready to pass buffers forward.
202205
203- An operation that's less common, but also important, than linking dynamic pads,
204- is unlinking them. If the parent removed a child that with
206+ An operation that's less common than linking dynamic pads, but also important ,
207+ is unlinking them. If the parent removed a child with
205208[ ` t:remove_children/0 ` ] ( `t:Membrane.Pipeline.Action.remove_children/0` ) action:
206209
207210``` elixir
213216```
214217
215218Then the child with name ` :some_child ` would be stopped and removed from the
216- pipeline, unlinking all it's pads. If an input pad of this child happened to be
219+ pipeline, unlinking all its pads. If an input pad of this child happened to be
217220connected to our demuxer, then the
218221[ ` handle_pad_removed/3 ` ] ( `c:Membrane.Element.Base.handle_pad_removed/3` )
219222would be called in the demuxer with a reference to the pad that was unlinked:
@@ -226,42 +229,42 @@ end
226229```
227230
228231The demuxer should react to this information accordingly, for example it should
229- now know that it no longer can send buffers on this pad, because it has been
232+ now know that it can no longer send buffers on this pad, because it has been
230233unlinked and essentially no longer exists.
231234
232235If a link has dynamic pads on both sides, the parent could also return a
233- [ ` t :remove_link/0 ` ] ( `t:Membrane.Pipeline.Action.remove_link/0` ) action,
236+ [ ` :remove_link ` ] ( `t:Membrane.Pipeline.Action.remove_link/0` ) action,
234237which would only remove the link, resulting in
235238[ ` handle_pad_removed/3 ` ] ( `c:Membrane.Element.Base.handle_pad_removed/3` )
236239being called in children on both sides of it.
237240
238241## Life cycle of a pad
239242
240- Life cycle of components is explored more broadly in this
241- [ guide] ( components_lifecycle.md ) . Here, we'll take a closer look at a life
243+ The life cycle of components is explored more broadly in this
244+ [ guide] ( components_lifecycle.md ) . Here, we'll take a closer look at the life
242245cycle of a pad, mostly focusing on elements.
243246
244247### Creation
245248
246- Static pad are essentially created and linked at the same time as the whole
247- component and exist alongside it for it's entire lifespan - they have to be
249+ Static pads are essentially created and linked at the same time as the whole
250+ component and exist alongside it for its entire lifespan - they have to be
248251linked at the same time the component is created.
249252
250- Dynamic pads can be linked and unlinked throughout their components lifespan.
253+ Dynamic pads can be linked and unlinked throughout their components' lifespan.
251254There can also be multiple instances of a dynamic pad.
252255Because of this, each creation can be handled separately in
253256[ ` handle_pad_added/3 ` ] ( `c:Membrane.Element.Base.handle_pad_added/3` ) callback,
254- that's being called every time a new dynamic pad is linked, and therefore
257+ which is called every time a new dynamic pad is linked, and therefore
255258created.
256259
257260### Playback
258261
259262When an element is in ` :stopped `
260- [ playback] ( `t:Membrane.Playback.t/0` ) , nothing can be sent on it's pads - the
261- pipeline is not ready. Only once an element enters ` :playing ` playback and
263+ [ playback] ( `t:Membrane.Playback.t/0` ) , nothing can be sent on its pads - the
264+ pipeline is not ready. Only when an element enters ` :playing ` playback and
262265[ ` handle_playing/2 ` ] ( `c:Membrane.Element.Base.handle_playing/2` ) callback is
263- called, then it can assume that the pipeline is ready for communication and can
264- send on and receive information from it's pads.
266+ called can it assume that the pipeline is ready for communication and can
267+ send on and receive information from its pads.
265268
266269### Removal
267270
@@ -270,3 +273,67 @@ Static pads are removed and unlinked only once their component is terminated.
270273Dynamic pads can be removed during the lifespan of their component. For each removal
271274a [ ` handle_pad_removed/3 ` ] ( `c:Membrane.Element.Base.handle_pad_removed/3` )
272275callback is called.
276+
277+ ## Pads in bins
278+
279+ We know that when it comes to linking pads between components, bins behave just like
280+ elements. However, when we take a look inside them, they are pretty different.
281+ While elements can interact with information moving through their pads
282+ directly with callbacks, bins are only containers for other components. They
283+ don't interact with information moving through their pads directly, they forward
284+ it to components inside them.
285+
286+ The insides of a bin can be thought of as a pipeline of sorts. The components
287+ are created with the [ ` :spec ` ] ( `t:Membrane.Bin.Action.spec/0` ) action and the bin is
288+ their parent. The way that these components can send and receive information on the bin's
289+ pads is by linking with [ ` bin_input ` ] ( `Membrane.ChildrenSpec.bin_input/1` ) and
290+ [ ` bin_output ` ] ( `Membrane.ChildrenSpec.bin_output/2` ) . These functions can be
291+ thought of as the "interior" part of the bin's pad.
292+
293+ ![ image] ( assets/pads2.png )
294+
295+ For example, if a bin has
296+ a dynamic input pad called ` :input ` , and a single static output pad called ` :output ` ,
297+ it can behave like this:
298+
299+ ``` elixir
300+ @impl true
301+ def handle_setup (_ctx , state) do
302+ spec =
303+ child (:comp , SomeComponent )
304+ |> bin_output (:output )
305+
306+ {[spec: spec], state}
307+ end
308+ ```
309+
310+ When the bin is being set up, it creates a ` SomeComponent ` child named ` :comp ` ,
311+ which connects its output pad to the ` bin_output ` corresponding to the bin's
312+ ` :output ` pad. Now whenever this child sends or receives something on its output pad,
313+ it's received by or sent to whatever component is connected to the bin's
314+ ` :output ` pad.
315+
316+ ``` elixir
317+ @impl true
318+ def handle_pad_added (Pad .ref (:input , id) = pad, _ctx , state) do
319+ spec =
320+ bin_input (pad)
321+ |> child (SomeOtherComponent )
322+ |> get_child (:comp )
323+
324+ {[spec: spec], state}
325+ end
326+ ```
327+
328+ Every time a new input pad of the bin is created, the
329+ [ ` handle_pad_added/3 ` ] ( `c:Membrane.Bin.handle_pad_added/3` ) callback is called.
330+ The bin has to connect this pad to something within a 5-second timeout,
331+ otherwise a ` LinkError ` will be
332+ raised. A new ` SomeOtherComponent ` child is created and its input pad is
333+ connected to a ` bin_input ` corresponding to the newly created pad. Now whenever
334+ this child sends or receives something on its input pad, it's received by or
335+ sent to whatever component is connected to the bin's newly created pad.
336+
337+ It's worth noting that pads of bins are only an abstraction. When a component
338+ links with a bin, it actually links directly to the components inside of it to
339+ avoid unnecessary forwarding of messages.
0 commit comments