-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy pathbin.ex
430 lines (350 loc) · 13 KB
/
bin.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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
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
@type state :: any()
@type callback_return :: {[Action.t()], state()}
@typedoc """
Defines options that can be passed to `start_link/3` and received
in `c:handle_init/2` callback.
"""
@type options :: struct | nil
@typedoc """
Type that defines a bin name by which it is identified.
"""
@type name :: tuple() | atom()
@doc """
Callback invoked on initialization of bin.
This callback is synchronous: the parent waits until it finishes. Also, any failures
that happen in this callback crash the parent as well, regardless of crash groups.
For these reasons, it's important to do any long-lasting or complex work in `c:handle_setup/2`,
while `handle_init` should be used for things like parsing options, initializing state or
spawning children.
By default, it converts the opts struct to a map and sets them as the bin's state.
"""
@callback handle_init(context :: CallbackContext.t(), options :: options) ::
callback_return()
@doc """
Callback that is called when new pad has been added to bin. Executed
ONLY for dynamic pads.
Context passed to this callback contains additional field `:pad_options`.
By default, it does nothing.
"""
@callback handle_pad_added(
pad :: Pad.ref(),
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback that is called when some pad of the bin has been removed. Executed
ONLY for dynamic pads.
Context passed to this callback contains additional field `:pad_options`.
By default, it does nothing.
"""
@callback handle_pad_removed(
pad :: Pad.ref(),
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked on bin startup, right after `c:handle_init/2`.
Any long-lasting or complex initialization should happen here.
By default, it does nothing.
"""
@callback handle_setup(
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when bin switches the playback to `:playing`.
By default, it does nothing.
"""
@callback handle_playing(
context :: CallbackContext.t(),
state :: state
) ::
callback_return
@doc """
Callback invoked when a child removes its pad.
The callback won't be invoked, when you have initiated the pad removal,
eg. when you have returned `t:Membrane.Bin.Action.remove_link()` action
which made one of your children's pads be removed.
By default, it does nothing.
"""
@callback handle_child_pad_removed(
child :: Child.name(),
pad :: Pad.ref(),
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when a notification comes in from an element.
By default, it ignores the received message.
"""
@callback handle_child_notification(
notification :: Membrane.ChildNotification.t(),
element :: Child.name(),
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when a notification comes in from an parent.
By default, it ignores the received message.
"""
@callback handle_parent_notification(
notification :: Membrane.ParentNotification.t(),
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when bin receives a message that is not recognized
as an internal membrane message.
Can be used for receiving data from non-membrane processes.
By default, it logs and ignores the received message.
"""
@callback handle_info(
message :: any,
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when a child element starts processing stream via given pad.
By default, it does nothing.
"""
@callback handle_element_start_of_stream(
child :: Child.name(),
pad :: Pad.ref(),
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when a child element finishes processing stream via given pad.
By default, it does nothing.
"""
@callback handle_element_end_of_stream(
child :: Child.name(),
pad :: Pad.ref(),
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
This callback is deprecated since v1.1.0.
Callback invoked when children of `Membrane.ChildrenSpec` are started.
It is invoked, only if pipeline module contains its definition. Otherwise, nothing happens.
"""
@callback handle_spec_started(
children :: [Child.name()],
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when a child complete its setup.
By default, it does nothing.
"""
@callback handle_child_setup_completed(
child :: Child.name(),
context :: CallbackContext.t(),
state
) :: callback_return
@doc """
Callback invoked when a child enters `playing` playback.
By default, it does nothing.
"""
@callback handle_child_playing(
child :: Child.name(),
context :: CallbackContext.t(),
state
) :: callback_return
@doc """
Callback invoked upon each timer tick. A timer can be started with `t:Membrane.Bin.Action.start_timer/0`
action.
"""
@callback handle_tick(
timer_id :: any,
context :: CallbackContext.t(),
state :: state
) :: callback_return
@doc """
Callback invoked when crash of the crash group happens.
Context passed to this callback contains 2 additional fields: `:members` and `:crash_initiator`.
"""
@callback handle_crash_group_down(
group_name :: Child.group(),
context :: CallbackContext.t(),
state
) :: callback_return
@doc """
A callback invoked when the bin is being removed by its parent.
By default, it returns `t:Membrane.Bin.Action.terminate/0` with reason `:normal`.
"""
@callback handle_terminate_request(
context :: CallbackContext.t(),
state
) :: callback_return
@optional_callbacks handle_init: 2,
handle_pad_added: 3,
handle_pad_removed: 3,
handle_setup: 2,
handle_playing: 2,
handle_info: 3,
handle_spec_started: 3,
handle_child_setup_completed: 3,
handle_child_playing: 3,
handle_element_start_of_stream: 4,
handle_element_end_of_stream: 4,
handle_child_notification: 4,
handle_parent_notification: 3,
handle_tick: 3,
handle_crash_group_down: 3,
handle_terminate_request: 2,
handle_child_pad_removed: 4
@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)}
""")
@doc false
@spec membrane_clock?() :: 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.ChildrenSpec`
- `:bring_pad?` - if true (default) requires and aliases `Membrane.Pad`
"""
defmacro __using__(options) do
bring_spec =
if Keyword.get(options, :bring_spec?, true) do
quote do
import Membrane.ChildrenSpec
alias Membrane.ChildrenSpec
end
end
bring_pad =
if Keyword.get(options, :bring_pad?, true) do
quote do
require Membrane.Pad, as: Pad
end
end
quote location: :keep do
alias unquote(__MODULE__)
@behaviour unquote(__MODULE__)
@before_compile unquote(__MODULE__)
@after_compile {Membrane.Core.Parent, :check_deprecated_callbacks}
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
require Membrane.Logger
Membrane.Core.Child.PadsSpecs.ensure_default_membrane_pads()
@doc false
@spec membrane_component_type() :: :pipeline | :bin | :element
def membrane_component_type, do: :bin
@doc false
@spec membrane_bin?() :: true
def membrane_bin?, do: true
@impl true
def handle_init(_ctx, %_opt_struct{} = options),
do: {[], options |> Map.from_struct()}
@impl true
def handle_init(_ctx, options), do: {[], options}
@impl true
def handle_pad_added(_pad, _ctx, state), do: {[], state}
@impl true
def handle_pad_removed(_pad, _ctx, state), do: {[], state}
@impl true
def handle_setup(_ctx, state), do: {[], state}
@impl true
def handle_playing(_ctx, state), do: {[], state}
@impl true
def handle_info(message, _ctx, state) do
Membrane.Logger.warning("""
Received message but no handle_info callback has been specified. Ignoring.
Message: #{inspect(message)}\
""")
{[], state}
end
@impl true
def handle_child_setup_completed(_child, _ctx, state), do: {[], state}
@impl true
def handle_child_playing(_child, _ctx, state), do: {[], state}
@impl true
def handle_element_start_of_stream(_element, _pad, _ctx, state), do: {[], state}
@impl true
def handle_element_end_of_stream(_element, _pad, _ctx, state), do: {[], state}
@impl true
def handle_child_notification(_notification, _element, _ctx, state), do: {[], state}
@impl true
def handle_parent_notification(_notification, _ctx, state), do: {[], state}
@impl true
def handle_crash_group_down(_group_name, _ctx, state), do: {[], state}
@impl true
def handle_terminate_request(_ctx, state), do: {[terminate: :normal], state}
defoverridable handle_init: 2,
handle_pad_added: 3,
handle_pad_removed: 3,
handle_setup: 2,
handle_playing: 2,
handle_info: 3,
handle_child_setup_completed: 3,
handle_child_playing: 3,
handle_element_start_of_stream: 4,
handle_element_end_of_stream: 4,
handle_child_notification: 4,
handle_parent_notification: 3,
handle_crash_group_down: 3,
handle_terminate_request: 2
end
end
defguard is_bin_name?(arg) when is_atom(arg) or is_tuple(arg)
end