Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write upgrading guide for v1.0.0 #646

Merged
merged 4 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions guides/upgrading/v1.0.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# Upgrading to v1.0.0

Comparing to v0.12, v1.0.0 introduces many changes in the public API of the framework. This guide was created to help you migrate to v1.0.0 of `membrane_core`.
FelonEkonom marked this conversation as resolved.
Show resolved Hide resolved

### Deps

Update `membrane_core` to `v1.0.0`
```elixir
defp deps do
[
{:membrane_core, "~> 1.0.0"},
...
]
end
```

### Remove the pipeline's `:playback` action

There is no more `:playback` action available, as the pipeline will start playing automatically once its setup is completed.
If you need to start the pipeline as soon as possible, simply do not return the `:playback` action in `c:Membrane.Pipeline.handle_init/2` or `c:Membrane.Pipeline.handle_setup/2` callbacks:
```diff
@impl true
def handle_setup(_ctx, state) do
...
- {[playback: :playing], state}
+ {[], state}
end
```
In case you need to defer the moment the pipeline starts playing, and for this purpose you have returned `:playback` action in some other callback than `handle_init` or `handle_setup`, now you need to do two things - first, mark the setup as incomplete with `t:Membrane.Pipeline.Action.setup/0` action returned from `handle_setup`, to prevent it from automatically changing the playback to `:playing`:

```diff
+ @impl true
+ def handle_setup(_ctx, state) do
+ {[setup: :incomplete], state}
+ end
```

Then, you need to return `setup: :complete` instead of `playback: :playing` action in a callback after which completion you want your pipeline to start playing:
```diff
@impl true
def handle_info(:now_start_playing, _ctx, state) do
...
- {[playback: playing], state}
+ {[setup: :complete], state}
end
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this was already introduced in 0.12 🤔 https://hexdocs.pm/membrane_core/0.12.9/v0-12.html#remove-playback-action


Note that the `:setup` action is also available in elements and bins and its main purpose is to handle long, asynchronous initialization of a component. See `t:Membrane.Bin.Action.setup/0` and `t:Membrane.Element.Action.setup/0`.

### Rename `handle_process/4` and `handle_write/4` into `handle_buffer/4`

Names of the callbacks that are used to process buffers have been unified. This applies to:
* _Membrane.Filter.handle_process/4_
* _Membrane.Endpoint.handle_write/4_
* _Membrane.Sink.handle_write/4_

and they became `c:Membrane.Element.WithInputPads.handle_buffer/4`

```diff
@impl true
- def handle_process(pad, buffer, ctx, state) do
+ def handle_buffer(pad, buffer, ctx, state) do
...
end
```

### Remove `handle_process_list/4` and `handle_write_list/4` callback

Since `v1.0.0`, you have to handle every single buffer separately in `handle_buffer/4` callback, instead of handling whole list of buffers in this in `handle_process_list/4` or `handle_write_list/4`.

```diff
@impl true
- def handle_process_list(pad_ref, buffers_list, ctx, state) do
+ def handle_buffer(pad_ref, buffer, ctx, state) do
...
end
```

```diff
@impl true
- def handle_write_list(pad_ref, buffers_list, ctx, state) do
+ def handle_buffer(pad_ref, buffer, ctx, state) do
...
end
```

### Change `mode` and `demand_mode` options to `flow_control` in pads' definitions in elements

For input pads, change:

```diff
use Membrane.Filter
# or Sink or Endpoint

- def_input_pad :input, mode: :push, ...
+ def_input_pad :input, flow_control: :push, ...

- def_input_pad :input, mode: :pull, demand_mode: :auto, demand_unit: :buffers, ...
+ def_input_pad :input, ...
# (because `flow_control: :auto` is the default whenever available - currently in Filters)
# Note that having `flow_control: :auto` doesn't allow to pass `demand_unit`,
# as it's determined automatically

- def_input_pad :input, mode: :pull, demand_unit: :buffers, ...
+ def_input_pad :input, flow_control: :manual, demand_unit: :buffers, ...
```

Same goes for output pads:

```diff
use Membrane.Filter
# or Source or Endpoint

- def_output_pad :output, mode: :push, ...
+ def_output_pad :output, flow_control: :push, ...

- def_output_pad :output, mode: :pull, demand_mode: :auto, ...
+ def_output_pad :output, ...

- def_output_pad :output, mode: :pull, ...
+ def_output_pad :output, flow_control: :manual, ...
```

Check `t:Membrane.Pad.element_spec/0` for details.

### Remove `mode` and `demand_unit` from pads definitions in bins

```diff
use Membrane.Bin

- def_input_pad :input, mode: :pull, demand_unit: :buffers, ...
+ def_input_pad :input, ...

- def_output_pad :output, mode: :push, ...
+ def_output_pad :output, ...
```

Check `t:Membrane.Pad.bin_spec/0` for details.

### Don't refer to callback contexts as to structs

Callback contexts are now plain maps instead of structs. So, for example, if you happen to have a match like below, convert it to a match on a map:

```diff
@impl true
- def handle_info(message, %Membrane.Element.CallbackContext.Info{pads: pads}, state) do
+ def handle_info(message, %{pads: pads}, state) do
...
end
```

Additionally, there's no `direction` field anymore in the callback context for `handle_pad_added` and `handle_pad_removed` - the direction can be determined by the pad name. All other fields remain the same.

```diff
@impl true
- def handle_pad_added(_pad, %{direction: :input}, state) do
+ def handle_pad_added(Pad.ref(:input, _id), _ctx, state) do
...
end
```

Check `t:Membrane.Element.CallbackContext.t/0`, `t:Membrane.Bin.CallbackContext.t/0` and `t:Membrane.Pipeline.CallbackContext.t/0` for outline of what can be found in the contexts. Additionally, each callback that provides extra fields in the context, has them mentioned in its docs.

### Use `Membrane.RCPipeline` instead of `Membrane.RemoteControlled.Pipeline`

```diff
- pipeline = Membrane.RemoteControlled.Pipeline.start_link!()
+ pipeline = Membrane.RCPipeline.start_link!()
```

Same goes for `Membrane.RemoteControlled.Message` -> `Membrane.RCMessage`

```diff
receive do
- %Membrane.RemoteControlled.Message.Notification{data: data} -> data
+ %Membrane.RCMessage.Notification{data: data} -> data
end
```

### Use `Membrane.Time.as_<unit>(time, :round)` instead of `Membrane.Time.round_to_<unit>(time)`

```diff
- Membrane.Time.round_to_milliseconds(time)
+ Membrane.Time.as_milliseconds(time, :round)
```

There is one exception for `round_to_timebase`, which changed to `divide_by_timebase`:
```diff
- Membrane.Time.round_to_timebase(time, timebase)
+ Membrane.Time.divide_by_timebase(time, timebase)
```

Check `Membrane.Time` for details.

### Implement you own `start/*`, `start_link/*` or `terminate/1` function (if you want to)

Until `v1.0.0-rc0` and `v0.12`, Membrane has generated `start/2`, `start_link/2`, and `terminate/1` functions in modules using `Membrane.Pipeline`, if code developer hadn't done it explicitly. Since `v1.0.0`, if you want to have these functions implemented in your pipeline module, you have to implement them on your own. Alternatively, you can always call `Membrane.Pipeline.start(YourPipelineModule, init_arg, opts)`, `Membrane.Pipeline.start_link(YourPipelineModule, init_arg, opts)`, and `Membrane.Pipeline.terminate(pipeline_pid)` from beyond `YourPipelineModule` without wrapping it into public functions.

### Rename `:structure` option passed to the `Membrane.Testing.Pipeline` into `:spec`

```diff
import Membrane.ChildrenSpec

spec =
child(:source, %Membrane.Testing.Source{some_field: some_arg})
|> child(:sink), %Membrane.Testing.Sink{another_filed: another_arg}

- pipeline = Membrane.Testing.Pipeline.start_link_supervised(structure: spec)
+ pipeline = Membrane.Testing.Pipeline.start_link_supervised(spec: spec)
```

### Adjust to possibility of receiving `end of stream` on pads, that haven't received `start of stream` yet.

Since `v1.0.0`, Membrane will execute `c:Membrane.Element.WithInputPads.handle_end_of_stream/3` for all pads, including these having `start_of_stream?: false`.
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ defmodule Membrane.Mixfile do
"guides/upgrading/v0.11.md",
"guides/upgrading/v1.0.0-rc0.md",
"guides/upgrading/v1.0.0-rc1.md",
"guides/upgrading/v1.0.0.md",
LICENSE: [title: "License"]
],
formatters: ["html"],
Expand Down