From 01e330e7b463c40f6b8bf6d09d06859e6482456c Mon Sep 17 00:00:00 2001 From: "feliks.pobiedzinski@swmansion.com" Date: Fri, 20 Dec 2024 14:49:17 +0100 Subject: [PATCH] lifecycle guide wip --- guides/components_lifecycle.md | 57 ++++++++++++++++++++++++++++++++++ mix.exs | 2 +- mix.lock | 6 ++-- 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 guides/components_lifecycle.md diff --git a/guides/components_lifecycle.md b/guides/components_lifecycle.md new file mode 100644 index 000000000..184e18f4e --- /dev/null +++ b/guides/components_lifecycle.md @@ -0,0 +1,57 @@ +# Lifecycle of Membrane Components + +Lifecycle of Membrane Components is strictly related to execution of Membrane callbacks withing these Components. +Although there are some differences between lifecycles of Membrane Pipelines, Bins and Elements, they are very similar to each other. +So let's take a look at what the component lifecycle looks like and what are the differences depending on the type of the Component we are dealing with. + +## Initialization +The first callback, that is executed in every Membrane Component, is handle_init/2. +handle_init is always executed synchronously and blocks the parent component (the exception is Membrane Pipeline, that never has a parent component), so it's good to avoid heavy computations within it. +`handle_init` is a good place to spawn pipeline's or bin's children in the `:spec` action. + +## Setup +The next callback after handle_init is `handle_setup`, that is executed asynchronusly and it is a good place to e.g. setup some resources or execute other heavy operations, that are necessary +for the element to play. + +## Linking the pads +If the Component had some pads with `availability: :on_request` linked in the same `spec`, where it was spawned, the appropriate `handle_pad_added` callbacks will be invoked between `handle_setup` and `handle_playing`. +Linking the pad with `availability: :on_request` in the another spec than the one that spawns the element might result in invoking `handle_pad_added` after `handle_playing`. + +## Playing +After setup is completed, the component might now enter `:playing` playback, by invoking `handle_playing` callback. +Note: + - components spawned within a single `spec` will always enter `:playing` state in the same moment - it means, if setup of one of them takes longer than other, the rest will wait on the component that is the last. + - Elements and Bins always wait with execution of `handle_playing` until their parent enters `playing` playback. + - by default, after `handle_setup` callback the component's setup is considered as completed. But this behaviour can be changed by returning `setup: :incomplete` action from `handle_setup`. If so, to enter `playing` playback, the component mark its setup as completed by returning `setup: :complete` from another callback, e.g. `handle_info/3`. + +## Processing the data (applies only to `Elements`) +After execution of `handle_playing`, Elements are now ready to process the data that flows through the pads. + +### Events +The first type of the items that can be sent via Elements' pads are `events`. Handling an event takes place in `hanlde_event`. Event can be sent both upstream and downstream the direction of the pad. + +### Stream formats +Stream format specifies what type of the data will be sent in `Membrane.Buffer`'s. The stream format must be sent before first `Membrane.Buffer` and is handled in `handle_stream_format` + +### Start of stream +This callback (`handle_start_of_stream`) will be invoked just before handling the first `Membrane.Buffer` incoming from the specific input pad. + +### Buffers +The core of the multimedia processing. `Membrane.Buffer`'s contain multimedia payload, but they also may have information about some metadata or timestamps. Handling them takes place in `handle_buffer` callback. + +## After processing the data +If the element decides, that it won't send any more buffers on the specific pad, it can send `:end_of_stream` there. It will be received by the linked element in `handle_end_of_stream` callback. +When the element receives `end_of_stream` callback, it's parent (Bin or Pipeline) will be also notified about it in `handle_element_end_of_stream` callback. + +## Terminating +Usually the last callback executed within a Membrane Component is `handle_terminate_request`. By default it returns `terminate: :normal` action, which ends the lifespan of the component (with reason `:normal`). +You can change this behaviour by overriding defeault impelmentation of this callback, but remember tho return `terminate: reason` in another place! Otherwise, your Pipeline will have problems with getting termianted. + +## Callbacks not strictly related to the lifecycle +Not all the callbacks are restricted to occur in specific moments in the Membrane Component lifecycle. + +### Handling parent/child notification +`handle_parent_notification` and `handle_child_notification` are the 2 callbacks that can during the whole Component's lifecycle. They are responsible for handling nofications sent from respectively parent or child Component. + +### Handling messages from non-Membrane processes +There is also a `handle_info` callback in all of the Membrane Components and `handle_call` in Membrane Pipelines, that can be invoked in every moment, when the Component is alive. Their function is annalogical as in the `GenServer`. diff --git a/mix.exs b/mix.exs index 4e6649ab3..928b36d83 100644 --- a/mix.exs +++ b/mix.exs @@ -147,7 +147,7 @@ defmodule Membrane.Mixfile do {:bunch, "~> 1.6"}, {:ratio, "~> 3.0 or ~> 4.0"}, # Development - {:ex_doc, "~> 0.28", only: :dev, runtime: false}, + {:ex_doc, "~> 0.35", only: :dev, runtime: false}, {:makeup_diff, "~> 0.1", only: :dev, runtime: false}, {:dialyxir, "~> 1.1", only: :dev, runtime: false}, {:credo, "~> 1.7", only: :dev, runtime: false}, diff --git a/mix.lock b/mix.lock index 87d275ae4..a22b8ee0d 100644 --- a/mix.lock +++ b/mix.lock @@ -2,8 +2,8 @@ "bunch": {:hex, :bunch, "1.6.1", "5393d827a64d5f846092703441ea50e65bc09f37fd8e320878f13e63d410aec7", [:mix], [], "hexpm", "286cc3add551628b30605efbe2fca4e38cc1bea89bcd0a1a7226920b3364fe4a"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, - "credo": {:hex, :credo, "1.7.8", "9722ba1681e973025908d542ec3d95db5f9c549251ba5b028e251ad8c24ab8c5", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cb9e87cc64f152f3ed1c6e325e7b894dea8f5ef2e41123bd864e3cd5ceb44968"}, - "dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"}, + "credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"}, + "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, @@ -16,7 +16,7 @@ "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "mox": {:hex, :mox, "1.2.0", "a2cd96b4b80a3883e3100a221e8adc1b98e4c3a332a8fc434c39526babafd5b3", [:mix], [{:nimble_ownership, "~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}], "hexpm", "c7b92b3cc69ee24a7eeeaf944cd7be22013c52fcb580c1f33f50845ec821089a"}, - "nimble_ownership": {:hex, :nimble_ownership, "1.0.0", "3f87744d42c21b2042a0aa1d48c83c77e6dd9dd357e425a038dd4b49ba8b79a1", [:mix], [], "hexpm", "7c16cc74f4e952464220a73055b557a273e8b1b7ace8489ec9d86e9ad56cb2cc"}, + "nimble_ownership": {:hex, :nimble_ownership, "1.0.1", "f69fae0cdd451b1614364013544e66e4f5d25f36a2056a9698b793305c5aa3a6", [:mix], [], "hexpm", "3825e461025464f519f3f3e4a1f9b68c47dc151369611629ad08b636b73bb22d"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"}, "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"},