From 28ff5b3aaea745338f7229bcb4d7290d5cf56aac Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Fri, 9 Feb 2024 17:11:40 +0100 Subject: [PATCH 1/6] Remove portaudio and mad installation instructions from readme (#748) --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index a7d250fc5..caed0cbdc 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,6 @@ Membrane.Pipeline.start_link(MyPipeline, mp3_url) ``` This is an [Elixir](elixir-lang.org) snippet, that streams an mp3 via HTTP and plays it on your speaker. Here's how to run it: -- Install [libmad](https://github.com/markjeee/libmad) and [portaudio](https://github.com/PortAudio/portaudio). Membrane uses these libs to decode the mp3 and to access your speaker, respectively. You can use these commands: - - On Mac OS: `brew install libmad portaudio pkg-config` - - On Debian: `apt install libmad0-dev portaudio19-dev` - - Option 1: Click the button below: [![Run in Livebook](https://livebook.dev/badge/v1/blue.svg)](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fmembraneframework%2Fmembrane_core%2Fblob%2Fmaster%2Fexample.livemd) From 12f1e8ed006b39571d4892aaf64b82b859880591 Mon Sep 17 00:00:00 2001 From: Brad Hanks Date: Mon, 12 Feb 2024 08:24:24 -0700 Subject: [PATCH 2/6] lib/membrane/utility_supervisor.ex doc edits (#746) --- lib/membrane/utility_supervisor.ex | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/membrane/utility_supervisor.ex b/lib/membrane/utility_supervisor.ex index a46bbbfd3..b60d556e2 100644 --- a/lib/membrane/utility_supervisor.ex +++ b/lib/membrane/utility_supervisor.ex @@ -1,14 +1,16 @@ defmodule Membrane.UtilitySupervisor do @moduledoc """ - A supervisor that allows to start utility processes under the pipeline's + A supervisor responsible for managing utility processes under the pipeline's supervision tree. - The supervisor is spawned with each component and can be obtained from - callback contexts. + The supervisor is spawned with each component and can be accessed from callback contexts. - The supervisor never restarts any processes, it just makes sure they - terminate when the component that started them terminates. If restarting - is needed, a dedicated supervisor should be spawned under this supervisor, like + `Membrane.UtilitySupervisor` does not restart processes. Rather, it ensures that these utility processes + terminate gracefully when the component that initiated them terminates. + + If a process needs to be able to restart, spawn a dedicated supervisor under this supervisor. + + ## Example def handle_setup(ctx, state) do Membrane.UtilitySupervisor.start_link_child( @@ -26,7 +28,7 @@ defmodule Membrane.UtilitySupervisor do @doc """ Starts a process under the utility supervisor. - Semantics of the `child_spec` argument is the same as in `Supervisor.child_spec/2`. + Semantics of the `child_spec` argument are the same as in `Supervisor.child_spec/2`. """ @spec start_child(t, Supervisor.child_spec() | {module(), term()} | module()) :: Supervisor.on_start_child() @@ -37,7 +39,7 @@ defmodule Membrane.UtilitySupervisor do @doc """ Starts a process under the utility supervisor and links it to the current process. - Semantics of the `child_spec` argument is the same as in `Supervisor.child_spec/2`. + Semantics of the `child_spec` argument are the same as in `Supervisor.child_spec/2`. """ @spec start_link_child(t, Supervisor.child_spec() | {module(), term()} | module()) :: Supervisor.on_start_child() From f112f9473a8110ede0d88e5f240969e28b4220eb Mon Sep 17 00:00:00 2001 From: DominikWolek Date: Tue, 13 Feb 2024 14:50:29 +0100 Subject: [PATCH 3/6] Add crash reason handle crash group (#720) * Add :reason to the ctx in handle_crash_group_down * Remove type reason as it is private * Add :reason to CallbackContexts * Add test for crashing reason * Apply revier's suggestions * Rename :reason to :crash_reason Specify crash_reason type to more descriptive type * Apply revier suggestions * Unify field name --------- Co-authored-by: feliks.pobiedzinski@swmansion.com --- CHANGELOG.md | 1 + lib/membrane/bin/callback_context.ex | 5 ++-- lib/membrane/core/bin/callback_context.ex | 6 ++++- .../crash_group_utils.ex | 12 ++++++---- lib/membrane/core/parent/crash_group.ex | 7 ++++-- .../core/pipeline/callback_context.ex | 6 ++++- lib/membrane/pipeline/callback_context.ex | 3 ++- .../membrane/integration/child_crash_test.exs | 17 ++++++++++++++ test/support/child_crash_test/pipeline.ex | 23 ++++++++++++++++++- 9 files changed, 67 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 000523f6c..a75560ee0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Log messages in the default handle_info implementation [#680](https://github.com/membraneframework/membrane_core/pull/680) * Fix typespecs in Membrane.UtilitySupervisor [#681](https://github.com/membraneframework/membrane_core/pull/681) * Improve callback return types and group actions types [#702](https://github.com/membraneframework/membrane_core/pull/702) + * Add `crash_reason` to `handle_crash_group_down/3` callback context in bins and pipelines. [#720](https://github.com/membraneframework/membrane_core/pull/720) ## 1.0.0 * Introduce `:remove_link` action in pipelines and bins. diff --git a/lib/membrane/bin/callback_context.ex b/lib/membrane/bin/callback_context.ex index 9608bb3c5..c6aa09527 100644 --- a/lib/membrane/bin/callback_context.ex +++ b/lib/membrane/bin/callback_context.ex @@ -12,8 +12,8 @@ defmodule Membrane.Bin.CallbackContext do Field `:start_of_stream_received?` is present only in `c:Membrane.Bin.handle_element_end_of_stream/4`. - Fields `:members` and `:crash_initiator` are present only in - `c:Membrane.Pipeline.handle_crash_group_down/3`. + Fields `:members`, `:crash_initiator` and `crash_reason` and are present only in + `c:Membrane.Bin.handle_crash_group_down/3`. """ @type t :: %{ :clock => Membrane.Clock.t(), @@ -27,6 +27,7 @@ defmodule Membrane.Bin.CallbackContext do optional(:pad_options) => map(), optional(:members) => [Membrane.Child.name()], optional(:crash_initiator) => Membrane.Child.name(), + optional(:crash_reason) => :normal | :shutdown | {:shutdown, term()} | term(), optional(:start_of_stream_received?) => boolean() } end diff --git a/lib/membrane/core/bin/callback_context.ex b/lib/membrane/core/bin/callback_context.ex index 704822720..205e90c2a 100644 --- a/lib/membrane/core/bin/callback_context.ex +++ b/lib/membrane/core/bin/callback_context.ex @@ -3,7 +3,11 @@ defmodule Membrane.Core.Bin.CallbackContext do @type optional_fields :: [pad_options: map()] - | [members: [Membrane.Child.name()], crash_initiator: Membrane.Child.name()] + | [ + members: [Membrane.Child.name()], + crash_initiator: Membrane.Child.name(), + crash_reason: :normal | :shutdown | {:shutdown, term()} | term() + ] | [start_of_stream_received?: boolean()] @spec from_state(Membrane.Core.Bin.State.t(), optional_fields()) :: diff --git a/lib/membrane/core/parent/child_life_controller/crash_group_utils.ex b/lib/membrane/core/parent/child_life_controller/crash_group_utils.ex index 6bcb0037e..8537f7d5c 100644 --- a/lib/membrane/core/parent/child_life_controller/crash_group_utils.ex +++ b/lib/membrane/core/parent/child_life_controller/crash_group_utils.ex @@ -53,12 +53,12 @@ defmodule Membrane.Core.Parent.ChildLifeController.CrashGroupUtils do end end - def handle_crash_group_member_death(child_name, %CrashGroup{} = group, _reason, state) do + def handle_crash_group_member_death(child_name, %CrashGroup{} = group, crash_reason, state) do state = if group.detonating? do state else - detonate_crash_group(child_name, group, state) + detonate_crash_group(child_name, group, crash_reason, state) end all_members_dead? = @@ -72,7 +72,7 @@ defmodule Membrane.Core.Parent.ChildLifeController.CrashGroupUtils do end end - defp detonate_crash_group(crash_initiator, %CrashGroup{} = group, state) do + defp detonate_crash_group(crash_initiator, %CrashGroup{} = group, crash_reason, state) do state = ChildLifeController.remove_children_from_specs(group.members, state) state = LinkUtils.unlink_crash_group(group, state) @@ -88,7 +88,8 @@ defmodule Membrane.Core.Parent.ChildLifeController.CrashGroupUtils do &%CrashGroup{ &1 | detonating?: true, - crash_initiator: crash_initiator + crash_initiator: crash_initiator, + crash_reason: crash_reason } ) end @@ -108,7 +109,8 @@ defmodule Membrane.Core.Parent.ChildLifeController.CrashGroupUtils do context_generator = &Component.context_from_state(&1, members: crash_group.members, - crash_initiator: crash_group.crash_initiator + crash_initiator: crash_group.crash_initiator, + crash_reason: crash_group.crash_reason ) CallbackHandler.exec_and_handle_callback( diff --git a/lib/membrane/core/parent/crash_group.ex b/lib/membrane/core/parent/crash_group.ex index 1b8327ca8..62d1631c6 100644 --- a/lib/membrane/core/parent/crash_group.ex +++ b/lib/membrane/core/parent/crash_group.ex @@ -5,6 +5,7 @@ defmodule Membrane.Core.Parent.CrashGroup do # * name - name that identifies the group # * type - responsible for restart policy of members of groups # * members - list of members of group + # * reason - reason of the crash use Bunch.Access @@ -15,9 +16,11 @@ defmodule Membrane.Core.Parent.CrashGroup do mode: :temporary, members: [Membrane.Child.name()], detonating?: boolean(), - crash_initiator: Membrane.Child.name() + crash_initiator: Membrane.Child.name(), + crash_reason: :normal | :shutdown | {:shutdown, term()} | term() } @enforce_keys [:name, :mode] - defstruct @enforce_keys ++ [members: [], detonating?: false, crash_initiator: nil] + defstruct @enforce_keys ++ + [members: [], detonating?: false, crash_initiator: nil, crash_reason: nil] end diff --git a/lib/membrane/core/pipeline/callback_context.ex b/lib/membrane/core/pipeline/callback_context.ex index d2537b760..0414147a6 100644 --- a/lib/membrane/core/pipeline/callback_context.ex +++ b/lib/membrane/core/pipeline/callback_context.ex @@ -3,7 +3,11 @@ defmodule Membrane.Core.Pipeline.CallbackContext do @type optional_fields :: [from: GenServer.from()] - | [members: [Membrane.Child.name()], crash_initiator: Membrane.Child.name()] + | [ + members: [Membrane.Child.name()], + crash_initiator: Membrane.Child.name(), + crash_reason: :normal | :shutdown | {:shutdown, term()} | term() + ] | [start_of_stream_received?: boolean()] @spec from_state(Membrane.Core.Pipeline.State.t(), optional_fields()) :: diff --git a/lib/membrane/pipeline/callback_context.ex b/lib/membrane/pipeline/callback_context.ex index b352c4896..3c5a6fa6d 100644 --- a/lib/membrane/pipeline/callback_context.ex +++ b/lib/membrane/pipeline/callback_context.ex @@ -11,7 +11,7 @@ defmodule Membrane.Pipeline.CallbackContext do Field `:start_of_stream_received?` is present only in `c:Membrane.Pipeline.handle_element_end_of_stream/4`. - Fields `:members` and `:crash_initiator` are present only in + Fields `:members`, `:crash_initiator` and `:crash_reason` are present only in `c:Membrane.Pipeline.handle_crash_group_down/3`. """ @type t :: %{ @@ -23,6 +23,7 @@ defmodule Membrane.Pipeline.CallbackContext do optional(:from) => [GenServer.from()], optional(:members) => [Membrane.Child.name()], optional(:crash_initiator) => Membrane.Child.name(), + optional(:crash_reason) => :normal | :shutdown | {:shutdown, term()} | term(), optional(:start_of_stream_received?) => boolean() } end diff --git a/test/membrane/integration/child_crash_test.exs b/test/membrane/integration/child_crash_test.exs index f90ac6942..da423a5ca 100644 --- a/test/membrane/integration/child_crash_test.exs +++ b/test/membrane/integration/child_crash_test.exs @@ -67,6 +67,23 @@ defmodule Membrane.Integration.ChildCrashTest do assert_pipeline_crash_group_down(pipeline_pid, 1) end + test "Pipeline receives correct crash reason" do + pipeline_pid = Testing.Pipeline.start_supervised!(module: ChildCrashTest.Pipeline) + ChildCrashTest.Pipeline.add_path(pipeline_pid, [], :source, 1, :group_1) + + # time for pipeline to start :source + Process.sleep(100) + + ChildCrashTest.Pipeline.inform_about_details_in_case_of_crash(pipeline_pid) + + Testing.Pipeline.get_child_pid!(pipeline_pid, :source) + |> Process.exit(:custom_crash_reason) + + assert_receive {:crash, crash_reason: :custom_crash_reason} + + Testing.Pipeline.terminate(pipeline_pid) + end + test "Crash group consisting of bin crashes" do Process.flag(:trap_exit, true) diff --git a/test/support/child_crash_test/pipeline.ex b/test/support/child_crash_test/pipeline.ex index f23be8c0e..6761a67c1 100644 --- a/test/support/child_crash_test/pipeline.ex +++ b/test/support/child_crash_test/pipeline.ex @@ -21,7 +21,7 @@ defmodule Membrane.Support.ChildCrashTest.Pipeline do child(:center_filter, Filter) |> child(:sink, Testing.Sink) - {[spec: spec], %{}} + {[spec: spec], %{send_to: nil}} end @impl true @@ -29,6 +29,22 @@ defmodule Membrane.Support.ChildCrashTest.Pipeline do {[spec: spec], state} end + @impl true + def handle_info({:inform_about_crash, send_to}, _ctx, state) do + {[], %{state | send_to: send_to}} + end + + @impl true + def handle_crash_group_down(_group_name, _ctx, %{send_to: nil} = state) do + {[], state} + end + + @impl true + def handle_crash_group_down(_group_name, %{crash_reason: crash_reason}, %{send_to: pid} = state) do + send(pid, {:crash, crash_reason: crash_reason}) + {[], state} + end + @spec add_single_source(pid(), any(), any(), any()) :: any() def add_single_source(pid, source_name, group \\ nil, source \\ Testing.Source) do spec = child(source_name, source) |> get_child(:center_filter) @@ -92,4 +108,9 @@ defmodule Membrane.Support.ChildCrashTest.Pipeline do send(pid, {:create_path, spec}) end + + @spec inform_about_details_in_case_of_crash(pid()) :: any() + def inform_about_details_in_case_of_crash(pid) do + send(pid, {:inform_about_crash, self()}) + end end From 2eacd1a79d69e28ba1d54768e07899d21df779cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Feliks=20Pobiedzi=C5=84ski?= <38541925+FelonEkonom@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:42:21 +0100 Subject: [PATCH 4/6] Bump version to v1.0.1 (#755) --- README.md | 2 +- mix.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index caed0cbdc..8c3f44399 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Apart from plugins, Membrane has stream formats, which live in `membrane_X_forma The API for creating pipelines (and custom elements too) is provided by [membrane_core](https://github.com/membraneframework/membrane_core). To install it, add the following line to your `deps` in `mix.exs` and run `mix deps.get` ```elixir -{:membrane_core, "~> 1.0.0"} +{:membrane_core, "~> 1.0"} ``` **Standalone libraries** diff --git a/mix.exs b/mix.exs index 4c96bd981..ac1df050b 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Membrane.Mixfile do use Mix.Project - @version "1.0.0" + @version "1.0.1" @source_ref "v#{@version}" def project do From 4c37a06e47b96447e79ea90d7afd878f463cca36 Mon Sep 17 00:00:00 2001 From: Brad Hanks Date: Fri, 23 Feb 2024 02:28:02 -0700 Subject: [PATCH 5/6] Next (#750) --- lib/membrane/event.ex | 18 ++++++++++++------ lib/membrane/stream_format.ex | 14 ++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/membrane/event.ex b/lib/membrane/event.ex index 2ed7405b6..c342a52f3 100644 --- a/lib/membrane/event.ex +++ b/lib/membrane/event.ex @@ -1,18 +1,24 @@ defmodule Membrane.Event do @moduledoc """ - Event is an entity that can be sent between elements. + Represents a communication event, capable of flowing both downstream and upstream. - Events can flow either downstream or upstream - they can be sent with - `t:Membrane.Element.Action.event/0`, and can be handled in - `c:Membrane.Element.Base.handle_event/4`. Each event is - to implement `Membrane.EventProtocol`, which allows to configure its behaviour. + Events are dispatched using `t:Membrane.Element.Action.event/0` and are handled via the + `c:Membrane.Element.Base.handle_event/4` callback. Each event must conform to the + `Membrane.EventProtocol` to ensure the proper configuration of its behaviour. """ alias Membrane.EventProtocol - @typedoc @moduledoc + @typedoc """ + The Membrane event, based on the `Membrane.EventProtocol`. + """ @type t :: EventProtocol.t() + @doc """ + Checks if the given argument is a Membrane event. + + Returns `true` if the `event` implements the `Membrane.EventProtocol`, otherwise `false`. + """ @spec event?(t()) :: boolean def event?(event) do EventProtocol.impl_for(event) != nil diff --git a/lib/membrane/stream_format.ex b/lib/membrane/stream_format.ex index f003f2448..9f63c9590 100644 --- a/lib/membrane/stream_format.ex +++ b/lib/membrane/stream_format.ex @@ -1,14 +1,16 @@ defmodule Membrane.StreamFormat do @moduledoc """ - Describes capabilities of some pad. + Defines the capabilities of a pad within the Membrane framework. - Every pad has some capabilities, which define a type of data that pad is - expecting. This format can be, for example, raw audio with specific sample - rate or encoded audio in given format. + Each pad in a multimedia pipeline has specific capabilities, determining the type and format + of data it can handle. For example, a pad's capabilities might include handling raw audio + with a specific sample rate or managing encoded audio in a specified format. - To link two pads together, their capabilities have to be compatible. + To successfully link two pads together, their capabilities must be compatible. """ - @typedoc @moduledoc + @typedoc """ + Represents a pad's capabilities. For more information, see: `Membrane.StreamFormat`. + """ @type t :: struct end From 475c22c045c050721cd3ffa19ebd7e73e5eff19a Mon Sep 17 00:00:00 2001 From: Brad Hanks Date: Fri, 23 Feb 2024 08:20:49 -0700 Subject: [PATCH 6/6] Improve Pipeline docs (#744) --- lib/membrane/pipeline.ex | 148 +++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 69 deletions(-) diff --git a/lib/membrane/pipeline.ex b/lib/membrane/pipeline.ex index f78be3d60..e3da30050 100644 --- a/lib/membrane/pipeline.ex +++ b/lib/membrane/pipeline.ex @@ -1,29 +1,25 @@ defmodule Membrane.Pipeline do @moduledoc """ - Module containing functions for constructing and supervising pipelines. + A behaviour module for implementing pipelines. - Pipelines are units that make it possible to instantiate, link and manage - elements and bins in convenient way (actually they should always be used inside - a pipeline). Linking pipeline children together enables them to pass data to one - another, and process it in different ways. - - To create a pipeline, use the `__using__/1` macro and implement callbacks - of `Membrane.Pipeline` behaviour. For details on instantiating and linking - children, see `Membrane.ChildrenSpec`. + `Membrane.Pipeline` contains the callbacks and functions for constructing and supervising pipelines. + Pipelines facilitate the convenient instantiation, linking, and management of elements and bins.\\ + Linking pipeline children together enables them to pass and process data. + To create a pipeline, use `use Membrane.Pipeline` and implement callbacks of `Membrane.Pipeline` behaviour. + See `Membrane.ChildrenSpec` for details on instantiating and linking children. ## Starting and supervision - Pipeline can be started with `start_link/2` or `start/2` functions. They both - return `{:ok, supervisor_pid, pipeline_pid}` in case of success, because the pipeline - is always spawned under a dedicated supervisor. The supervisor never restarts the - pipeline, but it makes sure that the pipeline and its children terminate properly. - If the pipeline needs to be restarted in case of failure, it should be spawned under - another supervisor with a proper strategy. + Start a pipeline with `start_link/2` or `start/2`. Pipelines always spawn under a dedicated supervisor, so + in the case of success, either function will return `{:ok, supervisor_pid, pipeline_pid}` . + + The supervisor never restarts the pipeline, but it does ensure that the pipeline and its children terminate properly. + If the pipeline needs to be restarted, it should be spawned under a different supervisor with the appropriate strategy. ### Starting under a supervision tree - The pipeline can be spawned under a supervision tree like any `GenServer`. Also, - `__using__/1` macro injects a `child_spec/1` function. A simple scenario can look like: + A pipeline can be spawned under a supervision tree like any other `GenServer`.\\ + `use Membrane.Pipeline` injects a `child_spec/1` function. A simple scenario could look like this: defmodule MyPipeline do use Membrane.Pipeline @@ -40,22 +36,22 @@ defmodule Membrane.Pipeline do ### Starting outside of a supervision tree - When starting a pipeline outside a supervision tree and willing to interact with - the pipeline by pid, `pipeline_pid` returned from `start_link` can be used, for example + When starting a pipeline outside a supervision tree, use the `pipeline_pid` pid to interact with the pipeline. + A simple scenario could look like this: {:ok, _supervisor_pid, pipeline_pid} = Membrane.Pipeline.start_link(MyPipeline, option: :value) send(pipeline_pid, :message) - ### Visualizing pipeline's supervision tree + ### Visualizing the supervision tree - Pipeline's internal supervision tree can be looked up with Applications tab of Erlang's Stalker - or with Livebook's `Kino` library. - For debugging (and ONLY for debugging) purposes, you may use the following configuration: + Use the [Applications tab](https://www.erlang.org/doc/apps/observer/observer_ug#applications-tab) in Erlang's Observer GUI + (or the `Kino` library in Livebook) to visualize a pipeline's internal supervision tree. Use the following configuration for debugging purposes only: config :membrane_core, unsafely_name_processes_for_stalker: [:components] - that makes the stalker's process tree graph more readable by naming pipeline's descendants, for example: - ![Stalker graph](assets/images/stalker_graph.png). + This improves the readability of the Observer's process tree graph by naming the pipeline descendants, as demonstrated here: + + ![Observer graph](assets/images/observer_graph.png). """ use Bunch @@ -67,20 +63,30 @@ defmodule Membrane.Pipeline do require Membrane.Core.Message, as: Message @typedoc """ - Defines options that can be passed to `start/3` / `start_link/3` and received - in `c:handle_init/2` callback. + Defines options passed to the `start/3` and `start_link/3` and subsequently received + in the `c:handle_init/2` callback. """ @type pipeline_options :: any + @typedoc "The Pipeline name" @type name :: GenServer.name() + @typedoc "List of configurations used by `start/3` and `start_link/3`." @type config :: [config_entry()] + + @typedoc "Defines configuration value used by the `start/3` and `start_link/3`." @type config_entry :: {:name, name()} + @typedoc """ + Defines the return value of the `start/3` and `start_link/3`." + """ @type on_start :: {:ok, supervisor_pid :: pid, pipeline_pid :: pid} | {:error, {:already_started, pid()} | term()} + @typedoc """ + The pipeline state. + """ @type state :: any() @typedoc """ @@ -88,29 +94,27 @@ defmodule Membrane.Pipeline do ## Return values - * `{[action], state}` - Return a list of actions that will be performed within the - pipeline. This can be used to start new children, or to send messages to specific children, - for example. Actions are a tuple of `{type, arguments}`, so may be written in the - form a keyword list. See `Membrane.Pipeline.Action` for more info. + * `{[action], state}` - Returns a list of actions that will be performed within the + pipeline, e.g., starting new children, sending messages to specific children, etc. + Actions are tuples of `{type, arguments}`, so they can be expressed as a keyword list. + See `Membrane.Pipeline.Action` for more info. """ @type callback_return :: {[Action.t()], state} @doc """ - Callback invoked on initialization of pipeline. + Callback invoked on initialization of the pipeline. - This callback is synchronous: the process which started the pipeline waits until `handle_init` - finishes. For that reason, 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` to a map if they're a struct and sets them as the pipeline state. + This callback is synchronous: the process that started the pipeline waits until `handle_init` + finishes, so it's important to do any long-lasting or complex work in `c:handle_setup/2`. + `handle_init` should be used for things, like parsing options, initializing state, or spawning + children. By default, `handle_init` converts `opts` to a map if they're a struct and sets them as the pipeline state. """ @callback handle_init(context :: CallbackContext.t(), options :: pipeline_options) :: {[Action.common_actions()], state()} @doc """ - Callback invoked when pipeline is requested to terminate with `terminate/2`. - + Callback invoked when the pipeline is requested to terminate with `terminate/2`. By default, it returns `t:Membrane.Pipeline.Action.terminate/0` with reason `:normal`. """ @callback handle_terminate_request(context :: CallbackContext.t(), state) :: @@ -129,7 +133,7 @@ defmodule Membrane.Pipeline do {[Action.common_actions()], state()} @doc """ - Callback invoked when pipeline switches the playback to `:playing`. + Callback invoked when the pipeline switches the playback to `:playing`. By default, it does nothing. """ @callback handle_playing( @@ -166,10 +170,10 @@ defmodule Membrane.Pipeline do ) :: {[Action.common_actions()], state()} @doc """ - Callback invoked when pipeline receives a message that is not recognized - as an internal membrane message. + Callback invoked when the pipeline receives a message that is not recognized + as an internal Membrane message. - Useful for receiving data sent from NIFs or other stuff. + Useful for receiving data sent from NIFs or other external sources. By default, it logs and ignores the received message. """ @callback handle_info( @@ -180,7 +184,7 @@ defmodule Membrane.Pipeline do {[Action.common_actions()], state()} @doc """ - Callback invoked when a child element starts processing stream via given pad. + Callback invoked when a child element starts processing a stream via the given pad. By default, it does nothing. """ @@ -192,7 +196,7 @@ defmodule Membrane.Pipeline do ) :: {[Action.common_actions()], state()} @doc """ - Callback invoked when a child element finishes processing stream via given pad. + Callback invoked when a child element finishes processing a stream via the given pad. By default, it does nothing. """ @@ -225,7 +229,7 @@ defmodule Membrane.Pipeline do ) :: {[Action.common_actions()], state()} @doc """ - Callback invoked when crash of the crash group happens. + Callback invoked when a crash group crashes. Context passed to this callback contains 2 additional fields: `:members` and `:crash_initiator`. By default, it does nothing. @@ -237,9 +241,9 @@ defmodule Membrane.Pipeline do ) :: {[Action.common_actions()], state()} @doc """ - Callback invoked when pipeline is called using a synchronous call. + Callback invoked when the pipeline is called using a synchronous call. - Context passed to this callback contains additional field `:from`. + Context passed to this callback contains an additional field `:from`. By default, it does nothing. """ @callback handle_call( @@ -264,10 +268,10 @@ defmodule Membrane.Pipeline do handle_child_pad_removed: 4 @doc """ - Starts the Pipeline based on given module and links it to the current - process. + Starts the pipeline based on the given module and links it to the current process. + - Pipeline options are passed to module's `c:handle_init/2` callback. + Pipeline options are passed to the `c:handle_init/2` callback. Note that this function returns `{:ok, supervisor_pid, pipeline_pid}` in case of success. Check the 'Starting and supervision' section of the moduledoc for details. """ @@ -276,7 +280,7 @@ defmodule Membrane.Pipeline do do: do_start(:start_link, module, pipeline_options, process_options) @doc """ - Does the same as `start_link/3` but starts process outside of supervision tree. + Starts the pipeline outside a supervision tree. Compare to `start_link/3`. """ @spec start(module, pipeline_options, config) :: on_start def start(module, pipeline_options \\ nil, process_options \\ []), @@ -328,21 +332,21 @@ defmodule Membrane.Pipeline do Terminates the pipeline. Accepts three options: - * `asynchronous?` - if set to `true`, pipline termination won't be blocking and - will be executed in the process, which pid is returned as function result. If - set to `false`, pipeline termination will be blocking and will be executed in - the process that called this function. Defaults to `false`. - * `timeout` - tells how much time (ms) to wait for pipeline to get gracefully - terminated. Defaults to 5000. - * `force?` - if set to `true` and pipeline is still alive after `timeout`, - pipeline will be killed using `Process.exit/2` with reason `:kill`, and function - will return `{:error, :timeout}`. If set to `false` and pipeline is still alive - after `timeout`, function will raise an error. Defaults to `false`. + * `asynchronous?` - if set to `true`, pipeline termination won't be blocking and + will be executed in the process whose pid is returned as a function result. + If set to `false`, pipeline termination will be blocking and will be executed in + the process that called this function. Defaults to `false`. + * `timeout` - specifies how much time (ms) to wait for the pipeline to gracefully + terminate. Defaults to 5000. + * `force?` - determines how to handle a pipeline still alive after `timeout`. + If set to `true`, `Process.exit/2` kills the pipeline with reason `:kill` and returns + `{:error, :timeout}`. + If set to `false`, it raises an error. Defaults to `false`. Returns: - * `{:ok, pid}` - if option `asynchronous?: true` was passed. - * `:ok` - if pipeline was gracefully terminated within `timeout`. - * `{:error, :timeout}` - if pipeline was killed after a `timeout`. + * `{:ok, pid}` - option `asynchronous?: true` was passed. + * `:ok` - pipeline gracefully terminated within `timeout`. + * `{:error, :timeout}` - pipeline was killed after `timeout`. """ @spec terminate(pipeline :: pid, timeout: timeout(), @@ -393,13 +397,18 @@ defmodule Membrane.Pipeline do end end + @doc """ + Calls the pipeline with a message. + + Returns the result of the pipeline call. + """ @spec call(pid, any, timeout()) :: term() def call(pipeline, message, timeout \\ 5000) do GenServer.call(pipeline, message, timeout) end @doc """ - Checks whether module is a pipeline. + Checks whether the module is a pipeline. """ @spec pipeline?(module) :: boolean def pipeline?(module) do @@ -407,9 +416,9 @@ defmodule Membrane.Pipeline do end @doc """ - Lists PIDs of all the pipelines currently running on the current node. + Returns list of pipeline PIDs currently running on the current node. - Use only for debugging purposes. + Use for debugging only. """ @spec list_pipelines() :: [pid] def list_pipelines() do @@ -423,7 +432,8 @@ defmodule Membrane.Pipeline do end @doc """ - Like `list_pipelines/0`, but allows to pass a node. + Returns list of pipeline PIDs currently running on the passed node. \\ + Compare to `list_pipelines/0`. """ @spec list_pipelines(node()) :: [pid] def list_pipelines(node) do