Error removing children group and starting new spec in a Bin

I’m trying to remove a child group and start a new spec within a handle child notification within a bin. all the children are defined with refs like {:muxer, make_ref()} the children are also created in a group. The problem is that in that handle_child_notification, if I remove the group and the add the new spec at the same time, it fails with an error:
17:41:03.613 [error] <0.287.0>/:chunk_saver/:chunk_sink_bin/ Error occured in Membrane Bin:
%Membrane.LinkError{
message: "Attempted to link the following pads more than once: pad {Membrane.Pad, :input, #Reference<0.2414433122.3236167682.5577>} of child {Membrane.Bin, :itself}, pad {Membrane.Pad, :input, #Reference<0.2414433122.3236167682.5579>} of child {Membrane.Bin, :itself}\n"
}
(membrane_core 1.1.0) lib/membrane/core/parent/child_life_controller/link_utils.ex:204: Membrane.Core.Parent.ChildLifeController.LinkUtils.validate_links/2
(membrane_core 1.1.0) lib/membrane/core/parent/child_life_controller/link_utils.ex:166: Membrane.Core.Parent.ChildLifeController.LinkUtils.resolve_links/3
(membrane_core 1.1.0) lib/membrane/core/parent/child_life_controller.ex:145: Membrane.Core.Parent.ChildLifeController.handle_spec/2
(membrane_core 1.1.0) lib/membrane/core/callback_handler.ex:194: anonymous fn/5 in Membrane.Core.CallbackHandler.handle_callback_result/5
(elixir 1.17.1) lib/enum.ex:2531: Enum."-reduce/3-lists^foldl/2-0-"/3
(membrane_core 1.1.0) lib/membrane/core/callback_handler.ex:192: Membrane.Core.CallbackHandler.handle_callback_result/5
(membrane_core 1.1.0) lib/membrane/core/bin.ex:208: Membrane.Core.Bin.handle_info/2
(stdlib 4.3.1.4) gen_server.erl:1123: :gen_server.try_dispatch/4
17:41:03.613 [error] <0.287.0>/:chunk_saver/:chunk_sink_bin/ Error occured in Membrane Bin:
%Membrane.LinkError{
message: "Attempted to link the following pads more than once: pad {Membrane.Pad, :input, #Reference<0.2414433122.3236167682.5577>} of child {Membrane.Bin, :itself}, pad {Membrane.Pad, :input, #Reference<0.2414433122.3236167682.5579>} of child {Membrane.Bin, :itself}\n"
}
(membrane_core 1.1.0) lib/membrane/core/parent/child_life_controller/link_utils.ex:204: Membrane.Core.Parent.ChildLifeController.LinkUtils.validate_links/2
(membrane_core 1.1.0) lib/membrane/core/parent/child_life_controller/link_utils.ex:166: Membrane.Core.Parent.ChildLifeController.LinkUtils.resolve_links/3
(membrane_core 1.1.0) lib/membrane/core/parent/child_life_controller.ex:145: Membrane.Core.Parent.ChildLifeController.handle_spec/2
(membrane_core 1.1.0) lib/membrane/core/callback_handler.ex:194: anonymous fn/5 in Membrane.Core.CallbackHandler.handle_callback_result/5
(elixir 1.17.1) lib/enum.ex:2531: Enum."-reduce/3-lists^foldl/2-0-"/3
(membrane_core 1.1.0) lib/membrane/core/callback_handler.ex:192: Membrane.Core.CallbackHandler.handle_callback_result/5
(membrane_core 1.1.0) lib/membrane/core/bin.ex:208: Membrane.Core.Bin.handle_info/2
(stdlib 4.3.1.4) gen_server.erl:1123: :gen_server.try_dispatch/4
shouldn’t the pads be unlinked automatically because the child group is being removed? The Bin input is dynamic. And all of the children are part of the same group. Help would be amazing, thank you so much!
3 Replies
varsill
varsill3mo ago
Hello! Could you show me the spec action you are returning after you remove the children group? From what I can see now, it seems that it's caused by the fact, that child removal is unfortunately not synchronous - returning an action doesn't mean that children are already removed. As a workaround you could try to postpone the children recreation (for instance, use Process.send_after and add handle_info, where you would return a spec ). When it comes to a proper solution, I believe we would need to allow synchronizing on the moment when the children are removed in membrane_core.
andr-ec
andr-ec3mo ago
Sure. I’ll send what the spec looks like. The problem I see with using Process.send_after is how do I time it correctly? I see two problems, for example frames that are being received being dropped if not done in time. And since when I remove the group I’m removing all of the children of the bin, that means that the dynamic pad is being removed too right? that would mean that if I do it too late, it will close the bin. if I do it too early, it will still have the existing children. Is there a way to know when the children have been removed?
defp create_spec_for_pads(opts) do
count_string = opts.count |> to_string()
muxer_ref = {:muxer, make_ref()}
splitter_ref = {:splitter, make_ref()}
sink_ref = {:sink, make_ref()}
created_child = child(muxer_ref, %Membrane.MP4.Muxer.ISOM{fast_start: true, chunk_duration: 1 |> Membrane.Time.seconds()})
# for a single file
|> child(splitter_ref, %Membrane.Demo.Splitter{ max_size: 5})
|> child(sink_ref, %Membrane.File.Sink{location: opts.location <> count_string <> ".mp4" })
links_for_pads = opts.pads |> Enum.map(fn p -> (bin_input(p) |> get_child(muxer_ref) ) end )
{
[
created_child,
] ++ links_for_pads,
group: :chunk_sink_group
}
end
defp create_spec_for_pads(opts) do
count_string = opts.count |> to_string()
muxer_ref = {:muxer, make_ref()}
splitter_ref = {:splitter, make_ref()}
sink_ref = {:sink, make_ref()}
created_child = child(muxer_ref, %Membrane.MP4.Muxer.ISOM{fast_start: true, chunk_duration: 1 |> Membrane.Time.seconds()})
# for a single file
|> child(splitter_ref, %Membrane.Demo.Splitter{ max_size: 5})
|> child(sink_ref, %Membrane.File.Sink{location: opts.location <> count_string <> ".mp4" })
links_for_pads = opts.pads |> Enum.map(fn p -> (bin_input(p) |> get_child(muxer_ref) ) end )
{
[
created_child,
] ++ links_for_pads,
group: :chunk_sink_group
}
end
varsill
varsill3mo ago
Sure, the Process.send_after was just a suggestion to workaround the problem (or even more like to check if the problem is indeed caused by the child not being yet removed). For a proper solution, we need something else 😉 If I get it right, you would like to always use a given group of external bin's input pads, and at the same time dynamically change the internal bin's input pads links - I don't think that :on_request pads are designed for such a purpose. When you internally remove the dynamic link of a bin, the external link is also removed and cannot be "reused". What I can suggest is to: 1. Create a helper element with inputs pads that are NOT expected to be unlinked (they can either be static pads if you know in advance how many of them are needed, or dynamic pads otherwise - the crucial thing is that you will never remove links ending in these pads) and output pads for which you expect that might be removed. The element should simply forward all the incoming stream on one type of input pads to the corresponding output pad or pads. 2. similarly to the helper element inputs, make the bin's pads NON-unlinkable 2. connect the bin's input pads to the helper element inputs 3. create a new link to the helper element's output pads each time you "switch" the muxers It might sound complicated, but if you would show me how you create your bin (i.e. the spec: action where you spawn the bin) I could provide you with some code draft.
Want results from more Discord servers?
Add your server