Skip to content

Commit e2c282f

Browse files
authored
Implement handle_spec_setup_completed and handle_spec_playing. Release v1.1.0-rc1. (#801)
* Implement handle_spec_setup_completed and handle_spec_playing * Add docs and tests * Update changelog * Bump version to v1.1.0-rc1 * Fix lint * Implement CR * Fix docs * Rename test * Impelement comments from CR * Remove leftovers * Remove leftowver continuation
1 parent 240d20e commit e2c282f

File tree

10 files changed

+281
-16
lines changed

10 files changed

+281
-16
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## 1.1.0-rc1
4+
* Add new callbacks `handle_child_setup_completed/3` and `handle_child_playing/3` in Bins and Pipelines. [#801](https://github.com/membraneframework/membrane_core/pull/801)
5+
36
## 1.1.0-rc0
47
* Deprecate `handle_spec_started/3` callback in Bins and Pipelines. [#708](https://github.com/membraneframework/membrane_core/pull/708)
58
* Handle buffers from input pads having `flow_control: :auto` only if demand on all output pads having `flow_control: :auto` is positive. [#693](https://github.com/membraneframework/membrane_core/pull/693)

lib/membrane/bin.ex

+32
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,28 @@ defmodule Membrane.Bin do
179179
state :: state
180180
) :: callback_return
181181

182+
@doc """
183+
Callback invoked when a child complete its setup.
184+
185+
By default, it does nothing.
186+
"""
187+
@callback handle_child_setup_completed(
188+
child :: Child.name(),
189+
context :: CallbackContext.t(),
190+
state
191+
) :: callback_return
192+
193+
@doc """
194+
Callback invoked when a child enters `playing` playback.
195+
196+
By default, it does nothing.
197+
"""
198+
@callback handle_child_playing(
199+
child :: Child.name(),
200+
context :: CallbackContext.t(),
201+
state
202+
) :: callback_return
203+
182204
@doc """
183205
Callback invoked upon each timer tick. A timer can be started with `t:Membrane.Bin.Action.start_timer/0`
184206
action.
@@ -217,6 +239,8 @@ defmodule Membrane.Bin do
217239
handle_playing: 2,
218240
handle_info: 3,
219241
handle_spec_started: 3,
242+
handle_child_setup_completed: 3,
243+
handle_child_playing: 3,
220244
handle_element_start_of_stream: 4,
221245
handle_element_end_of_stream: 4,
222246
handle_child_notification: 4,
@@ -357,6 +381,12 @@ defmodule Membrane.Bin do
357381
{[], state}
358382
end
359383

384+
@impl true
385+
def handle_child_setup_completed(_child, _ctx, state), do: {[], state}
386+
387+
@impl true
388+
def handle_child_playing(_child, _ctx, state), do: {[], state}
389+
360390
@impl true
361391
def handle_element_start_of_stream(_element, _pad, _ctx, state), do: {[], state}
362392

@@ -381,6 +411,8 @@ defmodule Membrane.Bin do
381411
handle_setup: 2,
382412
handle_playing: 2,
383413
handle_info: 3,
414+
handle_child_setup_completed: 3,
415+
handle_child_playing: 3,
384416
handle_element_start_of_stream: 4,
385417
handle_element_end_of_stream: 4,
386418
handle_child_notification: 4,

lib/membrane/child_entry.ex

+2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ defmodule Membrane.ChildEntry do
55
The public fields are:
66
- `name` - child name
77
- `module` - child module
8+
- `group` - child group name
89
- `options` - options passed to the child
910
- `component_type` - either `:element` or `:bin`
11+
- `playback` - either `:stopped` or `:playing`
1012
1113
Other fields in the struct ARE NOT PART OF THE PUBLIC API and should not be
1214
accessed or relied on.

lib/membrane/core/parent/child_life_controller.ex

+35-2
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ defmodule Membrane.Core.Parent.ChildLifeController do
319319
if Enum.all?(spec_data.children_names, &Map.fetch!(children, &1).initialized?) and
320320
Enum.empty?(spec_data.dependent_specs) do
321321
Membrane.Logger.debug("Spec #{inspect(spec_ref)} status changed to initialized")
322+
323+
state = handle_children_setup_completed(spec_data.children_names, state)
322324
do_proceed_spec_startup(spec_ref, %{spec_data | status: :initialized}, state)
323325
else
324326
{spec_data, state}
@@ -401,17 +403,22 @@ defmodule Membrane.Core.Parent.ChildLifeController do
401403
defp do_proceed_spec_startup(_spec_ref, %{status: :ready} = spec_data, state) do
402404
state =
403405
Enum.reduce(spec_data.children_names, state, fn child, state ->
404-
%{pid: pid, terminating?: terminating?} = get_in(state, [:children, child])
406+
%{pid: pid, terminating?: terminating?} = state.children[child]
405407

406408
cond do
407409
terminating? -> Message.send(pid, :terminate)
408410
state.playback == :playing -> Message.send(pid, :play)
409411
true -> :ok
410412
end
411413

412-
put_in(state, [:children, child, :ready?], true)
414+
put_in(state.children[child].ready?, true)
413415
end)
414416

417+
state =
418+
with %{playback: :playing} <- state do
419+
handle_children_playing(spec_data.children_names, state)
420+
end
421+
415422
{spec_data, state}
416423
end
417424

@@ -610,6 +617,32 @@ defmodule Membrane.Core.Parent.ChildLifeController do
610617
}
611618
end
612619

620+
@spec handle_children_setup_completed(MapSet.t(Child.name()) | [Child.name()], Parent.state()) ::
621+
Parent.state()
622+
def handle_children_setup_completed(children_names, state) do
623+
exec_child_playback_related_callbacks(:handle_child_setup_completed, children_names, state)
624+
end
625+
626+
@spec handle_children_playing(MapSet.t(Child.name()) | [Child.name()], Parent.state()) ::
627+
Parent.state()
628+
def handle_children_playing(children_names, state) do
629+
exec_child_playback_related_callbacks(:handle_child_playing, children_names, state)
630+
end
631+
632+
defp exec_child_playback_related_callbacks(callback, children_names, state) do
633+
action_handler = Component.action_handler(state)
634+
635+
Enum.reduce(children_names, state, fn child, state ->
636+
CallbackHandler.exec_and_handle_callback(
637+
callback,
638+
action_handler,
639+
%{context: &Component.context_from_state/1},
640+
[child],
641+
state
642+
)
643+
end)
644+
end
645+
613646
@spec handle_child_pad_removed(Child.name(), Pad.ref(), Parent.state()) :: Parent.state()
614647
def handle_child_pad_removed(child, child_pad_ref, state) do
615648
Membrane.Logger.debug_verbose("Child #{inspect(child)} removed pad #{inspect(child_pad_ref)}")

lib/membrane/core/parent/child_life_controller/startup_utils.ex

+4-2
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,14 @@ defmodule Membrane.Core.Parent.ChildLifeController.StartupUtils do
104104

105105
@spec exec_handle_spec_started([Membrane.Child.name()], Parent.state()) :: Parent.state()
106106
def exec_handle_spec_started(children_names, state) do
107+
callback_name = :handle_spec_started
108+
107109
# handle_spec_started/3 callback is deprecated, so we don't require its implementation
108-
if function_exported?(state.module, :handle_spec_started, 3) do
110+
if function_exported?(state.module, callback_name, 3) do
109111
action_handler = Component.action_handler(state)
110112

111113
CallbackHandler.exec_and_handle_callback(
112-
:handle_spec_started,
114+
callback_name,
113115
action_handler,
114116
%{context: &Component.context_from_state/1},
115117
[children_names],

lib/membrane/core/parent/lifecycle_controller.ex

+21-11
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ defmodule Membrane.Core.Parent.LifecycleController do
1313
}
1414

1515
alias Membrane.Core.Events
16-
alias Membrane.Core.Parent.{ChildLifeController}
16+
alias Membrane.Core.Parent.ChildLifeController
1717

1818
require Membrane.Core.Component
1919
require Membrane.Core.Message
@@ -43,19 +43,29 @@ defmodule Membrane.Core.Parent.LifecycleController do
4343

4444
activate_syncs(state.children)
4545

46-
Enum.each(state.children, fn {_name, %{pid: pid, ready?: ready?}} ->
47-
if ready?, do: Message.send(pid, :play)
48-
end)
46+
pinged_children =
47+
state.children
48+
|> Enum.flat_map(fn
49+
{child_name, %{ready?: true, terminating?: false, pid: pid}} ->
50+
Message.send(pid, :play)
51+
[child_name]
52+
53+
_other_entry ->
54+
[]
55+
end)
4956

5057
state = %{state | playback: :playing}
5158

52-
CallbackHandler.exec_and_handle_callback(
53-
:handle_playing,
54-
Component.action_handler(state),
55-
%{context: &Component.context_from_state/1},
56-
[],
57-
state
58-
)
59+
state =
60+
CallbackHandler.exec_and_handle_callback(
61+
:handle_playing,
62+
Component.action_handler(state),
63+
%{context: &Component.context_from_state/1},
64+
[],
65+
state
66+
)
67+
68+
ChildLifeController.handle_children_playing(pinged_children, state)
5969
end
6070

6171
@spec handle_terminate_request(Parent.state()) :: Parent.state()

lib/membrane/pipeline.ex

+32
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,28 @@ defmodule Membrane.Pipeline do
220220
state
221221
) :: {[Action.common_actions()], state()}
222222

223+
@doc """
224+
Callback invoked when a child completes its setup.
225+
226+
By default, it does nothing.
227+
"""
228+
@callback handle_child_setup_completed(
229+
child :: Child.name(),
230+
context :: CallbackContext.t(),
231+
state
232+
) :: {[Action.common_actions()], state()}
233+
234+
@doc """
235+
Callback invoked when a child enters `playing` playback.
236+
237+
By default, it does nothing.
238+
"""
239+
@callback handle_child_playing(
240+
child :: Child.name(),
241+
context :: CallbackContext.t(),
242+
state
243+
) :: {[Action.common_actions()], state()}
244+
223245
@doc """
224246
Callback invoked upon each timer tick. A timer can be started with `Membrane.Pipeline.Action.start_timer`
225247
action.
@@ -260,6 +282,8 @@ defmodule Membrane.Pipeline do
260282
handle_playing: 2,
261283
handle_info: 3,
262284
handle_spec_started: 3,
285+
handle_child_setup_completed: 3,
286+
handle_child_playing: 3,
263287
handle_element_start_of_stream: 4,
264288
handle_element_end_of_stream: 4,
265289
handle_child_notification: 4,
@@ -515,6 +539,12 @@ defmodule Membrane.Pipeline do
515539
{[], state}
516540
end
517541

542+
@impl true
543+
def handle_child_setup_completed(_child, _ctx, state), do: {[], state}
544+
545+
@impl true
546+
def handle_child_playing(_child, _ctx, state), do: {[], state}
547+
518548
@impl true
519549
def handle_element_start_of_stream(_element, _pad, _ctx, state), do: {[], state}
520550

@@ -538,6 +568,8 @@ defmodule Membrane.Pipeline do
538568
handle_setup: 2,
539569
handle_playing: 2,
540570
handle_info: 3,
571+
handle_child_setup_completed: 3,
572+
handle_child_playing: 3,
541573
handle_element_start_of_stream: 4,
542574
handle_element_end_of_stream: 4,
543575
handle_child_notification: 4,

lib/membrane/testing/pipeline.ex

+15
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,21 @@ defmodule Membrane.Testing.Pipeline do
408408
{custom_actions, Map.put(state, :custom_pipeline_state, custom_state)}
409409
end
410410

411+
[:handle_child_setup_completed, :handle_child_playing]
412+
|> Enum.map(fn callback ->
413+
@impl true
414+
def unquote(callback)(child, ctx, %State{} = state) do
415+
{custom_actions, custom_state} =
416+
eval_injected_module_callback(
417+
unquote(callback),
418+
[child, ctx],
419+
state
420+
)
421+
422+
{custom_actions, Map.put(state, :custom_pipeline_state, custom_state)}
423+
end
424+
end)
425+
411426
@impl true
412427
def handle_info({__MODULE__, :__execute_actions__, actions}, _ctx, %State{} = state) do
413428
{actions, state}

mix.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Membrane.Mixfile do
22
use Mix.Project
33

4-
@version "1.1.0-rc0"
4+
@version "1.1.0-rc1"
55
@source_ref "v#{@version}"
66

77
def project do

0 commit comments

Comments
 (0)